Lane P wrote the expected numbers into sots-engine docs/P-events-wiring.md §4 before this run existed. Every one held. First End Turn (ref-turn2 -> Launch -> End Turn, shim.cfg.recapb3 unchanged, build eventlive-dd38117-20260908T0916Z, main dd38117 with no source change): 3 calls, 3 compared, 0 divergent, tracecmp exit 0 — where lane R's run exited 1 on side.events.after.v.next_id orig=4 ours=3. Call 0 read turn=3, events_turn_bucket_exists=true, events_next_id_in=3, events_in_turn_bucket=1, events_dedup_risk=0, no events_scan_truncated, next_id 3->4 on both sides, turns/turns_bytes 2/48 unchanged, node[144].progress 2879->5768, flag 1->2, rng identical — the whole predicted list. Five End Turns: exit 1 with 2 divergent calls instead of 3, each short by exactly 1 (next_id orig 7 ours 6, orig 12 ours 11) = the deliberately unmodelled EVENT_TECHS_UNLOCKED. A 0-divergence result there would have been suspicious. sizeof(Game::ObservedTech) = 44 measured live: observed_techs.bytes grew by exactly 44 on both completion calls (440->484, 484->528), confirming lane X's static pin behaviourally. Non-researching players measured 880 = 20 x 44 and never moved. One deviation from lane R, and it is a workload effect, not a defect: rng diverged on call 9 because the completed tech had research_roll_pending set and the OnTechResearched callback drew one word ours does not (left 374 vs 375). Lane R's "RNG matched 15 of 15" was workload luck; the honest statement is that the rng region matches on every call that does not complete a roll-triggering tech. Zuul double roll CLOSED. No species-5 save existed, so this lane made one: a custom game with only Zuul in the Available Species pool, 4 End Turns under the same compare config. 8 calls, 8 compared, 0 divergent, exit 0; all four researching calls have species=5 and advance the generator by two, not one (left 540->538, 522->520, 504->502, 485->483), and ours reproduced every post-state bit-for-bit. Save added as verify/results/saves/zuul-turn5-species5.sav. End-Turn oracle byte-identical to lane R's on the first turn, so none of this perturbs the game. clean_room_check OK and host ctest 33/33, run as separate commands. VM140 restored to the recap build at the main menu and released. |
||
|---|---|---|
| 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.