Spine phase 4, byte for byte at 0x007dc871-0x007dc8c7. Three separate stores to the same word -- clear, OR the self bit, then conditionally OR the alliance mask -- which is what makes the alliance term an OR and is invisible in any summary of the phase. The bit is the player's POSITION IN THE VECTOR. Checked against the almem bytes the game archived: 72 of 80 player-records agree, and the other 8 are predicted by the same model (FinalizeTurnRecords also runs on load, and the load path does not run the spine, so every save's earliest archived turn carries a zero mask -- true on all 11). Reported as loudly: the corpus cannot separate `1 << vectorIndex` from `1 << PlyrIdx` (0 of 80 records differ), cannot separate the OR from a plain assignment (every observed alliance mask already contains its member's own bit), and cannot separate the ALid guard from an AL != 0 guard. Those three are instruction-stream readings only. ModCount, named and enumerated. StrategyServer::Write tags both words itself, so S+0x8 is the wire's ModCount and S+0xc is Frame -- addresses.json has the name on the wrong word, and turn-driver.md's "they stay in lockstep" is corrected in place. RTTI gives the reason for the two bases: Game::StrategySim is a base sub-object at offset 4. 29 writer sites: 20 command handlers plus 6 inlined in the command-batch applier (each an unconditional bump on ENTRY, before validation), the two turn drivers, and the abandon/chaos check -- which is gated on Abdn, false on all 28 systems of all 11 saves. So the per-turn delta is 2 + one per command applied out of every player's TurnCommands block; the 0x1b4 container stride independently confirms lane Q's block layout. It is not derivable from the pre-turn save. The residual is a watchpoint, specified with its own written prediction (exactly 12 hits on turn1-state) and four falsifiers. Direct-call reachability is stated as the lower bound it is, per lane V2's indirect-edge measurement. Engine side: sots-engine wip/alliance 5e409cf. |
||
|---|---|---|
| campaign | ||
| findings | ||
| ghidra | ||
| guides | ||
| notes | ||
| objects | ||
| scripts | ||
| tools | ||
| verify | ||
| .gitignore | ||
| README.md | ||
sots-re
Reverse-engineering worklog for Sword of the Stars (2006, SOTS1) — the 32-bit DX9 original + expansions. The nitty-gritty: static/dynamic analysis notes, Ghidra & ReVa scripts, function/struct maps, D3D9 call traces, decomp progress, findings.
Where this runs
Analysis lab on spicy (PVE, 192.168.3.201):
- CT111
sots-re— Linux workspace: Ghidra + headless ReVa server, radare2/rizin/cutter, binwalk. Hosts the Samba share and this repo's working tree at/srv/re-lab/notes. - VM140
sots-re-win10— Win10 runtime + dynamic analysis (x64dbg, Cheat Engine, RenderDoc/apitrace, DXVK→CPU-Vulkan for GPU-less rendering).
Infra (guests, storage, network, share, ReVa endpoint) is documented from the
system-maintainer POV in trikilli → services/re-lab.md. This repo is everything else.
Layout
findings/— the running findings log (append-only), one file per subsystem.ghidra/— exported scripts, data-type archives, struct definitions.traces/— D3D9 / Win32 API call captures + analysis.scripts/— helper tooling (loaders, extractors, parsers).notes/— session notes, scratch, hypotheses.
Ownership / legality
Game binaries come from the owner's own GOG/Steam copy. RE is for personal
interoperability, bug-fixing, and preservation. Binaries themselves are not committed
here (see .gitignore) — they live on the lab's Samba share /srv/re-lab/samples.
Campaign (how this repo is run)
A 4-agent crew (defined in ~/.claude/agents/re-*.md) runs the exploration:
re-quartermaster (backlog + board) → re-analyst (maps via ReVa) →
re-verifier (proves vs real data; old-vs-new differential once reimpl starts) →
re-scribe (files the note, links it, commits).
campaign/board.md— live status board (start here).- Live tracking = Forgejo issues on
alex/sots-re(labelsstatus/*are the kanban columns;type/*,conf/*).campaign/board.mdis the editable mirror — publish withscripts/forgejo_campaign.py bootstrap(needsFORGEJO_TOKEN). campaign/backlog.md— prioritized target queue.campaign/open-questions.md— unresolved threads.findings/_template.md— the record format every finding follows.findings/{objects,control-flow,subsystems}/— the growing engine map.verify/{parsers,traces,harness,results}/— validation: struct parsers now, golden-trace replay + shim compare-mode for reimplementation.ghidra/— exported scripts + datatype archives.
Approach & north star: findings/00-strategy.md. Binary facts: findings/01-fingerprint.md.
Sibling repo
alex/sots-engine — the from-scratch engine source (clean-room, public-capable). This repo keeps the
evidence + planning for both; binary facts cross over only via ghidra/addresses.json → tools/gen_addresses.py.
guides/re-windows-2000s-howto.md— annotated bibliography + how-to for RE of mid-2000s MSVC/DX9 Windows games, with our-experience call-outs.