Sword of the Stars (2006) reverse-engineering worklog. Infra docs live in trikilli services/re-lab.md.
Find a file
alex 8dda49cfae lane J: the combat resolver, read from the instruction stream
Closes lane K's #1 ranked gap: FUN_007d5af0 under phase 6 of
OnAllCombatDone_Tail, the second RNG source in the strategic tail.

RNG inventory (the deliverable that matters). The resolver draws nothing
itself. Three sites in its subtree, each behind a function with exactly
one caller:

  R1  RNG_NextInt @0x007bb69b (node cannon)   1 word w.p. 3/4, mean 4/3
  R2  an INLINED RNG_NextFloat @0x007a84bd    1 word per back-eng candidate
  R3  RNG_NextInt @0x00852ec7 (project pick)  >=1 word per successful roll

R2 corrects combat-done-tail.md 3, which reported no NextFloat in the
subtree: the draw is inlined, so the only call-graph edge it leaves is
FUN_007a7f30 -> RNG_Twist, which reads as a bare Twist and is not one.
An image-wide instruction-boundary scan for the MT tempering immediates
finds 14 game functions with inlined draws; two of them, FUN_004f7670
(84 B) and FUN_007aa240, are reachable from StrategyServer::ProcessTurn
and are candidate mechanism for part of lane Z's unexplained 18-20
words per turn.

Also: the real body is 7641 B, not Ghidra's 7499 (which ends
mid-instruction); only 8 non-stack stores and 1 indirect call in the
whole function; 23 EVENT_* keys in the closure, five of them new to the
campaign's combat picture (plague is decided inside combat); the
resolver's subtree DOES write the SETurnResults accumulator at
S+0x2f4[PlyrIdx]+0x90 via FUN_007baef0 -> FUN_007b9df0; and
sizeof(Game::TacReport) = 0x94, enumerated twice.

Two structural errors made and corrected before publication are recorded
in 7.3 and 4 rather than quietly fixed.

Gates run separately: clean-room check OK; host ctest 36/36.
2026-09-08 09:37:02 -04:00
campaign dashboard refresh 2026-09-08 09:07:48 -04:00
findings lane J: the combat resolver, read from the instruction stream 2026-09-08 09:37:02 -04:00
ghidra lane J: the combat resolver, read from the instruction stream 2026-09-08 09:37:02 -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.