Sword of the Stars (2006) reverse-engineering worklog. Infra docs live in trikilli services/re-lab.md.
Find a file
lane-b6 5cc66f6fff findings: ship construction -- the build queue read byte for byte, and the missing destroyer is the AI's
BuildQueue::ProcessTurn 0x00890d50 disassembled to the next function start (Ghidra says 1230
bytes; the body is 1264 and its end lands inside the epilogue -- rule 17 again). Corrects the
recorded prototype: the first stack argument is the StrategyServer S frame, not the system, so
the build-completed event goes on the sim and the hull's turn stamp is Frame.

THE HEADLINE IS A CORRECTION. Lane E2's open census leaf (shpt[0], one destroyer short on both
reference pairs) cannot be closed by S11's build-queue sub-pass: measured over the corpus, both
reference saves carry three empty BQ frames, every hbq false, and the one TurnCommands_v5 block
empty. The order is created inside the turn by the AI -- PvSav shows 11,900 leaving the treasury
before spine phase 0. The blocker moves to game/ai.

Also: the per-class built counter has EXACTLY ONE writer in the image (image-wide byte scan);
ShipRecords sized by enumeration; the ship/fleet birth chain end to end, correcting lane B5 --
FtFlg 0x400 is set by EVERY fleet born through the create path, not by retreat; one home fleet
per system cached at ServerSystem+0x238; and a third indirection class vtable_map.py cannot see
(the ship-borne wrapper is dispatched from a stack-built pointer table).

21 addresses in ghidra/addresses.d/lane-b6.json, validated to a scratch path against the merged
set (1,077 entries, no duplicate names); two entries dropped as duplicates of lane B5's and
recorded as agreements instead.
2026-09-08 14:54:03 -04:00
campaign roadmap: item 1 done (E1+C3); new item 1b S11 civilian growth, 1c ship repair cost 2026-09-08 14:53:33 -04:00
findings findings: ship construction -- the build queue read byte for byte, and the missing destroyer is the AI's 2026-09-08 14:54:03 -04:00
ghidra findings: ship construction -- the build queue read byte for byte, and the missing destroyer is the AI's 2026-09-08 14:54:03 -04:00
guides rules 19-20: the instrument can perturb the measurement; a count cannot separate did-not-fire from fired-and-found-nothing 2026-09-08 13:56:06 -04:00
notes init commit 2026-09-07 12:31:56 -04:00
objects regenerate both headers (669 addresses); track objects/generated so the shared clone stops sitting dirty 2026-09-08 07:36:16 -04:00
scripts lane V2: vtable inversion — resolve indirect call edges image-wide 2026-09-08 12:05:05 -04:00
tools lane H: five live probes, the oracle restored, and a harness perturbation bisected to one hook 2026-09-08 13:53:36 -04:00
verify lane C3: ComputeOutput on the turn path -- the money ComputeBudget actually sums 2026-09-08 14:50:50 -04:00
.gitignore lane D: automated struct recovery from the IStreamable serializers 2026-09-08 05:51:37 -04:00
README.md guides: RE how-to for 2000s MSVC/DX9 games (98 sources); queue replay-checksum, class recovery, apitrace/TTD 2026-09-07 18:05:58 -04:00

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 (labels status/* are the kanban columns; type/*, conf/*). campaign/board.md is the editable mirror — publish with scripts/forgejo_campaign.py bootstrap (needs FORGEJO_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.