From-scratch reimplementation of the Sword of the Stars (2006) engine. Clean-room; engine code only — bring your own game copy. Planning/RE evidence live in sots-re.
Find a file
alex 6699f543bc watch.mode=cont: both containers, both ends, re-armed every turn; and hooks=off no longer disarms the watchpoints
Lane L3 needed to watch the trade-route and spy-program vectors across a whole game
played forward, not one turn of one save. `modcount` and `tshn` arm once on purpose --
their targets are picked from one turn's state and re-picking them would move the
measurement -- so this is a third mode rather than a change to either.

`cont` puts all four debug slots on the two containers, `_Myfirst` as well as `_Mylast`.
Both vectors are default-constructed with all three pointers zero, so the first element
writes all three: watching only `_Mylast` cannot separate "allocated for the first time"
from "appended to an existing buffer", and those are different events in the model this
lane set out to falsify. It re-arms and re-logs on every End Turn, and the canary
self-test's counter is therefore read as a delta -- on the arm-once modes the delta is
the old value, so their log lines are byte-identical.

`ReportContainer` is factored out of `ArmTshnSlots` so both modes emit the same container
line. Lane W3's published count=0 is the control every later count is compared against,
and a reformatted line would have made that comparison a judgement call.

The defect: `Shim_Init` returned before `install_watchpoints` whenever the trace mode was
`off`, so `hooks=off watch=on` printed `watch=on` in the banner and armed absolutely
nothing -- a config that reports a confident zero, which is the failure method rule 1
exists to catch. The watchpoints are an independent instrument with their own arming
detour and no trace records, and a long play session wants them without paying 30-45 s
per End Turn for template hooks that measure nothing it is asking about. MinHook is now
initialised and the module installed on the `hooks=off` path when `watch=on`.

Configs: shim.cfg.l3cont / .l3control differ in exactly one key for rule 19;
.l3probe is lane H's hp11 verbatim plus the three watch keys, so the entry counts stay
comparable to lane H's empty-container baseline line for line.

Gates: clean_room_check OK; host ctest 54/54; CT111 shim cross-build OK, exports 66
names identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARBgSooAfokKUy6wKUKEyZ
2026-09-08 19:10:10 -04:00
cmake m0: binkw32 proxy shim, minhook, trace hook on Application::Initialize; build/deploy tooling 2026-09-07 17:22:15 -04:00
docs watch.mode=cont: both containers, both ends, re-armed every turn; and hooks=off no longer disarms the watchpoints 2026-09-08 19:10:10 -04:00
include/generated sync generated header: 13 same-address duplicates resolved across 8 lane fragments 2026-09-08 16:48:32 -04:00
src watch.mode=cont: both containers, both ends, re-armed every turn; and hooks=off no longer disarms the watchpoints 2026-09-08 19:10:10 -04:00
tests merge lane SV: script-object event bus (CMake lists merged into their constructs, rule 22) 2026-09-08 16:48:49 -04:00
third_party mars/vfs: gob ZIP reader + native override, miniz 3.1.2; oracle-verified 8352+2035 entries 2026-09-07 17:40:51 -04:00
tools b4: colony + movement hooks; 22 formula corrections (growth curve has no capacity term, range margin +0.05f, ties-to-even rounding); 3 verified signatures 2026-09-08 00:57:55 -04:00
.gitignore init commit 2026-09-07 17:07:54 -04:00
CMakeLists.txt merge lane W2: watchpoints + multiplayer Tier 0 (CMake list and main.cpp union-resolved to keep both instruments; header regenerated) 2026-09-08 15:28:28 -04:00
CMakePresets.json m0: binkw32 proxy shim, minhook, trace hook on Application::Initialize; build/deploy tooling 2026-09-07 17:22:15 -04:00
CONTRIBUTING.md CONTRIBUTING: note uv tool install cmake as the local workaround 2026-09-08 09:07:47 -04:00
LICENSE init commit 2026-09-07 17:07:54 -04:00
README.md src/app: the standalone -- load a save, run a turn, write a save 2026-09-08 10:35:45 -04:00

sots-engine

A from-scratch, functional reimplementation of the engine behind Sword of the Stars (2006). Not a byte-for-byte decompilation: behavior-equivalent code, built up one verified piece at a time (OpenRCT2-style) until the tree can build the whole application on its own.

Bring your own game. This repository contains engine code only. Game data, assets, saves, and the original binaries are never included; tests and tools read an owner-supplied copy via $SOTS_DATA_DIR. See CONTRIBUTING.md for the clean-room rules.

Status

Phase 2. M0 done — the shim frontend (src/shim/, a proxy binkw32.dll the original game loads) builds, deploys, hooks, and logs from a real game launch. Engine code accrues under src/mars/ and src/game/; each module is oracle-verified against the owner's game data before merge:

  • mars/parse — brace-block + .effect readers (1,531/1,531 files agree with the reference)
  • mars/text — flat key/value tables, id manifests, CSV (64/64 files agree)
  • game/sim — strategic formulas (economy, research, colonies, movement) as pure functions; 356 hand-computed checks
  • mars/vfs — .gob (ZIP) archive reader + loose-file override; entry counts and bytes verified against unzip
  • mars/stream — the game's self-describing save/serialization format (reader, writer, typed shapes) + gzip; three real saves round-trip byte-identical
  • mars/rng — MT19937 with save-state load/store; layout confirmed against real saves, draw mappings read off the binary (docs/mars-rng.md)
  • game/data — typed catalogs (weapons, ship sections, turrets, id registries, tech tree, strings) with cross-reference checks; 229k values agree with the reference
  • game/design — ship-design assembly/fit/tech-gating rules and derived stats; validates all 127 stock designs from real saves
  • app — the standalone: sots_turn loads a save, runs one strategic turn over the published phase order of all three turn drivers, and writes a save. 14 of the 44 turn-driver phases are modelled; every phase that is not appears in the run log as a named no-op. How far it is from the byte-match, and what stands in the way, is in docs/S-standalone.md

Build: cmake --preset host && cmake --build --preset host && ctest --preset host (Linux); tools/sync-build.sh cross-builds the shim on the lab box and stages it for deployment.

Layout (grows with the work)

  • src/shim/ — binkw32 proxy + hooks + old-vs-new compare harness (frontend #1)
  • src/mars/, src/game/ — the engine and game reimplementation (accruing)
  • src/app/ — the standalone turn driver (frontend #2): load a save, run a turn, write a save
  • include/generated/sots_addresses.h — binary facts (RVAs/prototypes), generated from the RE repo
  • tests/ — host tests; real-data tests skip unless $SOTS_DATA_DIR is set
  • tools/ — build (MinGW i686 cross) and deploy scripts

Planning, findings, and verification evidence are tracked in the private RE repo (sots-re).