Save Files
The Tower stores an account as a single playerInfo.dat.
- Android —
Android/data/com.TechTreeGames.TheTower/files/playerInfo.dat, on a device or inside an emulator. - macOS — the native App Store build keeps it in its app container under
~/Library/Containers, with non-sandboxed installs under~/Library/Application Support.
Getting The File
adb-bridge finds the save and hands the bytes to whatever needs them. On Android it talks to a connected phone or a running emulator over ADB. On a Mac it reads the native build's container directly, so there is no device to connect and no ADB in the picture.
// Terminal: npx adb-bridge
// Browser: connect and receive the save, then again on every write.
const socket = new WebSocket('ws://127.0.0.1:8787')
socket.onmessage = async (event) => {
const bytes = new Uint8Array(await event.data.arrayBuffer())
const { parsedRoot } = decodePlayerInfoSaveBytes(bytes)
const labs = readLabsFromSaveRoot(parsedRoot)
render(`${labs.researchedCount} researched, ${labs.maxedCount} maxed`)
}
// Ask for the current save; the bridge re-sends whenever the game writes.
socket.onopen = () => socket.send(JSON.stringify({ type: 'watch', game: 'the-tower' }))- Installs Google's official platform-tools for you if
adbis not already on the machine. The macOS path needs no tooling at all. - 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 without an upload step. - Can watch the save and re-send it whenever the game writes, which keeps a tracker live while you play instead of forcing a manual re-import.
- Reads only — it never modifies anything on the device, and it touches only the games you enable.
- One install covers multiple games; adding another registers it with the bridge you already have.
Feed those bytes to decodePlayerInfoSaveBytes below and the rest of this page applies unchanged.
Decode And Read
Decode in Node with thetowersdk/node, then use extractors in thetowersdk/save (labs, modules, cards, UWs, run history, and more).
import { readFile } from 'node:fs/promises'
import { decodePlayerInfoSaveBytes } from 'thetowersdk/node'
import { readLabsFromSaveRoot } from 'thetowersdk/save'
const { parsedRoot } = decodePlayerInfoSaveBytes(
await readFile('playerInfo.dat')
)
const labs = readLabsFromSaveRoot(parsedRoot)
if (!labs) throw new Error('no lab data in this save')
console.log(`${labs.researchedCount} researched, ${labs.maxedCount} maxed`)