Sword of the Stars (2006) reverse-engineering worklog. Infra docs live in trikilli services/re-lab.md.
Find a file
alex f9b744e2ed lane Q: TurnCommands_v5 reconciled and typed; SAVE_FORMAT section 11 corrected
findings/objects/turncommands-block.md -- why lane W's 44-item recovery and the
save's 35/38/41/61/123 items disagreed, in mechanism terms, plus the per-list
element records read off all 22 distinct helper writers.

Three separate causes, only one of which was a branch:
 * layouts.md is OFFSET-SORTED and Game::TurnCommands is one of the 89 classes
   whose offset order is not write order, because each gate bool sits after the
   payload it gates. Align saves against streams.json, never layouts.md.
 * the writer's tail is 27 std::list members, each written by its own helper as
   WriteInt(size) then size element records. The linear recovery keeps one item
   per CALL SITE, guesses its kind from an element field and drops the count.
   44 - 17 members = 27 = the list count.
 * 8 prologue items + 27 zero counts = the 35-item empty block, which is why
   every earlier save was bit-identical here.

Corrects lane O's section 11 in place: the fleet-move element is
{fleetId, nHops, nHops x systemId} -- a counted route vector, not a fixed
{fleetId, 1, destSystemId, 0}. Only the three-item reading makes the item totals
close (a four-item element needs 26 lists on human-turn2 and 28 on zuul-turn15;
the writer has 27). Also records that neither noderoute save carries a fleet
move, so the node-route UI does not queue through that list.

22 of the 27 lists are labelled hypotheses: read off the instruction stream, but
no save exercises them and no element value has ever been observed. List 14 is
observed but not understood -- issuing one fleet move also queues
{sameFleetId, 0, true} there.

save_reader.py deliberately left alone, with the reasoning written down: it reads
CD generically for the AIAgent bodies too, state_checksum's digest tree is built
from the generic tree so typing CD would add no named leaf to any diff, dispatch
by CDT ordinal would need new machinery in the oracle, and rule 8 is pointed at
exactly this move -- a layout mirrored into both readers from one reading is not
two checks. coverage: PROVED, 0 error 0 warn on all eleven saves, unchanged.

ghidra/addresses.d/lane-q.json: 6 entries (the writer, its Read pair, and the
four observed list helpers). Validated by generating to a scratch path; the
tracked header was not regenerated.
2026-09-08 09:03:26 -04:00
campaign board: VM140 holder = lane Z 2026-09-08 08:55:23 -04:00
findings lane Q: TurnCommands_v5 reconciled and typed; SAVE_FORMAT section 11 corrected 2026-09-08 09:03:26 -04:00
ghidra lane Q: TurnCommands_v5 reconciled and typed; SAVE_FORMAT section 11 corrected 2026-09-08 09:03:26 -04:00
guides guides: the 15 earned method rules, each traced to the finding that produced it 2026-09-08 08:29:20 -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 campaign: forgejo issues sync script; issues are the published board 2026-09-07 15:04:57 -04:00
tools dashboard: count addresses.d fragments (was undercounting 615 vs 722), read layouts.json (22 -> 384), fix the layouts denominator and the 'objects' type row 2026-09-08 08:30:46 -04:00
verify lane Q: TurnCommands_v5 reconciled and typed; SAVE_FORMAT section 11 corrected 2026-09-08 09:03:26 -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.