Skip to content

Getting a Save File

Josh Dionne edited this page Sep 4, 2026 · 2 revisions

Generated from the thetowersdk repository. Edits made here are overwritten on the next push — change the source file instead.

Getting a Save File

playerInfo.dat is The Tower's save file.

  • AndroidAndroid/data/com.TechTreeGames.TheTower/files/playerInfo.dat
  • Android emulator (BlueStacks, LDPlayer, WSA…) — the same path inside the emulated device
  • macOS — the native App Store build stores it in its app container under ~/Library/Containers/<bundle-id>/Data/Library/Application Support/…, with non-sandboxed installs under ~/Library/Application Support/…
  • iOS — inside an encrypted device backup; not practical to read directly

On Windows the save always comes from an emulator. On a Mac the native build is right there on disk.

Copying that file by hand is the tedious part of building anything save-driven, so adb-bridge removes the step. It talks to a connected phone or a running emulator over ADB, or reads the macOS container directly, and hands the bytes straight to whatever needs them:

npx adb-bridge
  • Installs Google's official platform-tools for you if adb isn't already on the machine (Android only — the macOS path needs no tooling).
  • Serves the save to a local page over a WebSocket bound to 127.0.0.1, so a browser app can read a real account with no upload step and no file picker.
  • Can watch the save and re-send it whenever the game writes, which keeps a view live while you play instead of forcing a manual re-import.
  • Pulls only. It never modifies anything on the device, needs no root, and touches only the games you enable.
  • One install covers multiple games — adding another registers it with the bridge you already have.

Feed the bytes it gives you to decodePlayerInfoSaveBytes and everything above applies unchanged.

Save files are personal data. If your tool uploads them anywhere, tell your users plainly.

Decoding In a Browser

The decoder is in thetowersdk/node only because it uses node:zlib. In a browser, gunzip with DecompressionStream and call the NRBF reader directly — both are exported and pure:

import { NRBFReader, nrbfToJSON } from 'thetowersdk/node'

async function decodeInBrowser (file: File): Promise<Record<string, unknown>> {
  let bytes = new Uint8Array(await file.arrayBuffer())
  if (bytes[0] === 0x1f && bytes[1] === 0x8b) {
    const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream('gzip'))
    bytes = new Uint8Array(await new Response(stream).arrayBuffer())
  }
  return nrbfToJSON(NRBFReader.readStream(bytes)) as Record<string, unknown>
}

Everything in thetowersdk/save and thetowersdk/data then works on the result unchanged.


Clone this wiki locally