# SOTS1 — RE strategy Founding strategy for reverse-engineering **Sword of the Stars (2006)** — original + expansions, GOG Complete Collection v1.8.1, a 32-bit DirectX 9 MSVC C++ Windows game. Retail binary only; no source was released. Personal, owned copy. Lab: Ghidra 12 + ReVa (AI-assisted, MCP) in CT111; Win10 VM140 with x64dbg / Cheat Engine / RenderDoc / apitrace / DXVK; Samba share `/srv/re-lab`. Infra: trikilli `services/re-lab.md`. ## North star (decided 2026-09-07) **A functional reimplementation of the engine — behavior-equivalent, NOT byte-for-byte** — reached by broad engine understanding first. Think OpenRCT2 / OpenMW / DevilutionX: a re-written engine that reads the *original* assets and reproduces behavior, not a byte-identical source rebuild. Byte-matching decompilation is explicitly **out of scope**. Bug-fixes and mods are welcome byproducts and useful correctness checks along the way, not the primary goal. ## Goals (priority order) 1. **Broad engine understanding** — map the whole architecture, not just hot spots: main loop & tick, subsystem boundaries, the core object model (empires, systems, fleets, tech), turn/AI logic, the renderer, and save/load + asset I/O. 2. Functional reimplementation, built incrementally on that understanding (see method below). 3. Bug-fixing / modding as validation and early wins (battle-load slowness; 2 GB/4 GB ceiling). 4. Preservation and interop tools (save/mod editors, format parsers). ## Approach decision Five styles evaluated: | Style | Verdict | Why | |---|---|---| | Matching (byte-identical) decomp | **Reject** | Optimized MSVC C++ (inlining, STL, RTTI); no PC-C++ prior art; and not our goal | | **Functional reimplementation** | **North star** | Behavior-equivalent engine reading original assets; incremental, community-scalable | | Ghidra/ReVa annotated RE | **Core near-term** | Builds the broad understanding every later step needs; our lab is built for it | | Binary patching / modding | **Byproduct** | Fast wins + behavioral baselines; not the driver | | File-format / interop RE | **Opportunistic** | Safe, partly already done by the community | ## Reimplementation method (the bridge from RE → new engine) Follow the **OpenRCT2 incremental model**: as understanding of a subsystem solidifies, reimplement it in modern C++ in a **shim DLL that the original `sots.exe` calls into**, verifying *behavior* (not bytes) against the original at each step; grow the reimplemented surface until the original code is fully displaced. This keeps a runnable, testable artifact at every stage instead of a big-bang rewrite. Golden rule for any future distribution: **ship engine code only, never original assets — users bring their owned copy.** Clean-room boundaries where it matters. ## Known ground (community — don't re-derive) - **Archives:** `sots.gob` and expansion `.gob`s are **renamed, uncompressed ZIPs** — 7-Zip opens them. - **Modding is file-override:** files under `./Mods/` at matching relative paths win per `sots.ini` load order. Textures = DDS; models = custom format with community Xporter v1.19. - **Save format partly RE'd:** BardezAnAvatar/Sots.Sots1.SavedGameEditor (C#, v1.8+, player/tech/system structs), ghbplayer/SOTSedit. A Rosetta Stone for the in-memory object model — accelerates goal 1. - **Compiler:** confirm from the binary — PE **Rich header** + CRT imports (`msvcr71/80/90`). MSVC C++ ⇒ **RTTI present** (Ghidra Windows x86 PE RTTI Analyzer recovers vftables + class hierarchy — a major head start toward the object model); MSVC name mangling; heavily inlined STL. - **Bug baselines to validate:** LAA/4GB PE flag (safe on GOG; Steam build breaks, "error 51"); `[CPU] ForceSingleCore=1` (engine is single-threaded). ## Phased plan - **Phase 0 — baseline & fingerprint.** Unpack `sots.gob`; inventory data + `sots.ini`. Ghidra auto-analysis + **RTTI analyzer** + demangle; pin the compiler (Rich header / CRT). Import the save-editor struct definitions as a naming seed. Validate LAA + `ForceSingleCore` for behavioral baselines. Commit project + notes. - **Phase 1 — broad architecture map** (core near-term work). Recover and document, breadth-first: the entry/init path and **main loop**; the **object model** (empires, star systems, fleets, tech tree — cross-referenced to the save structs); the **turn/AI** update; the **renderer** (D3D9 init → present loop); **save/load** and **asset/gob I/O**. Produce a living architecture doc + a class/subsystem map in the repo. Good early anchors (they cut across the above): the battle-load path, the allocator/arena, D3D9 device init. - **Phase 2 — reimplement incrementally.** Start with the most self-contained, well-understood subsystem (likely a file-format parser or a leaf subsystem) as a shim-DLL reimplementation; verify behavior; expand. - **Phase 3 — fixes, tools, preservation** (ongoing byproducts). Patcher for LAA + battle-load; save editor; published format specs. - **Deferred / out of scope:** byte-matching decompilation. ## First concrete targets Compiler fingerprint · object-model structs (seeded from save editor) · main loop · D3D9 init · battle-load path · allocator. Breadth first — these anchor the wider map. ## Key sources - Modding & file structure — https://swordofthestars.fandom.com/wiki/Introduction_to_modding , https://swordofthestars.fandom.com/wiki/Community_Mods - Fixes/compat — https://www.pcgamingwiki.com/wiki/Sword_of_the_Stars - Save-format head start — https://github.com/BardezAnAvatar/Sots.Sots1.SavedGameEditor , https://github.com/ghbplayer/SOTSedit - MSVC C++ RE — https://dennisbabkin.com/blog/?t=reverse-engineer-virtual-functions-vs-cpp-compiler-vtable-purecall-cfg , https://www.christophbrill.de/en/posts/ghidra_msvc_win32/ - Incremental reimplementation model — https://desosa.nl/projects/openrct2/2020/03/02/openrct2,-porting-rollercoaster-tycoon-into-2020.html ## Open uncertainties - Exact compiler/CRT unconfirmed — resolve from Rich header + CRT imports first. - Many-core battle-load root cause inferred (single-threaded engine + core-scaling) — confirm empirically. - Save-editor struct coverage may be partial (strong hint set, not a full spec). - Reimplementation is a long arc; the value gate is the *understanding* (goal 1), which stands on its own. _Strategy set 2026-09-07. Direction: broad understanding → functional reimplementation (not byte-matching)._ ## Phase 2 — decided 2026-09-07 - **Two repos.** This repo = evidence + planning (private, binary-derived). **`alex/sots-engine`** = the from-scratch engine source, **public-capable from day one**: clean-room (own code only; no game data, decompiles, saves), MIT, C++17/CMake, assets external via `$SOTS_DATA_DIR`. Tracking for both stays here. Binary facts cross over only via `ghidra/addresses.json` → `tools/gen_addresses.py` → `sots-engine/include/generated/sots_addresses.h`. - **Straight to the C++ shim** (no Python prototype): a proxy `binkw32.dll` (MinGW i686 on CT111, MinHook) with per-hook `off|trace|compare|replace` modes; compare runs original + ours on a snapshot and diffs; deterministic saves are the strongest equivalence oracle. The shim is the engine's first frontend (`src/shim/`); a standalone `src/app/` comes when enough engine exists. - **Slow accrual:** scaffold only what each milestone needs. Order: M0 bootstrap → M1 flat-KV config loader → M2 id manifests → M3 Mars brace-block parser → M4 gobio VFS → serializers/RNG → turn pipeline. - **Battle-load bug is parked** (not on the critical path); resume recipe in the backlog.