fix(dm2-saves): engine-exact standalone-missile termination for retail saves (#169) #233

Merged
jqueuniet merged 0 commits from refs/pull/233/head into main 2026-07-16 07:56:26 +02:00
jqueuniet commented 2026-07-16 07:53:00 +02:00 (Migrated from codeberg.org)

Resolves the binary-tracing gate on #169.

Binary trace (Beta Fires.exe, unpacked via scripts/unlzexe.py)

The Beta engine matches retail exactly on standalone-missile handling:

  • Reader (entry 0x1e76a): sets termination flag ds:0x3c85 at 0x1eaa4, recursive call with chain-mode argument 0 (single-record), clears the flag at 0x1eac6.
  • Writer (entry 0x1daef): mirrors with its own flag ds:0x3c7d at 0x1de41/0x1de60; single-record epilogue writes no trailing has-record bit.

So both engines nest the missile's timer link as a single-record sub-read with the flag cleared afterwards, exactly as retail sksvgame.cpp:945-955 says.

Change

  • Retail decode/encode now follows the engine: single-record missile sub-chain, flag cleared after the sub-call. A type-15 after a standalone missile parses as a full cloud, not a 7-bit termination. No retail fixture regresses (none combines a standalone missile with a later cloud; the Mac saves' nested terminations are bit-identical under both modes).
  • Beta keeps the previous chain-mode / persistent-flag model, gated on set_beta, documented as a compensating model: it contradicts the Beta binary, but the fixtures only decode to completion under it — engine-exact semantics desync DATA/SKSAVE0.DAT, pointing at the known-incomplete trailing-map tile-walk accounting. Because the Beta walk is truncated-and-spliced, byte-exact round-trips cannot arbitrate the model; revisit with the walk work.
  • New unit tests for the retail nested-termination + subsequent-cloud shape (decode and encoder round-trip); the previous sibling-termination tests are retained as explicit Beta-mode tests.
  • docs/formats/save-record-stream.md: new section "Standalone-missile termination: engine vs Beta model (#169)".

Closes #169

Resolves the binary-tracing gate on #169. ## Binary trace (Beta Fires.exe, unpacked via scripts/unlzexe.py) The Beta engine matches retail exactly on standalone-missile handling: - **Reader** (entry `0x1e76a`): sets termination flag `ds:0x3c85` at `0x1eaa4`, recursive call with chain-mode argument **0** (single-record), clears the flag at `0x1eac6`. - **Writer** (entry `0x1daef`): mirrors with its own flag `ds:0x3c7d` at `0x1de41`/`0x1de60`; single-record epilogue writes no trailing has-record bit. So both engines nest the missile's timer link as a single-record sub-read with the flag cleared afterwards, exactly as retail `sksvgame.cpp:945-955` says. ## Change - **Retail** decode/encode now follows the engine: single-record missile sub-chain, flag cleared after the sub-call. A type-15 after a standalone missile parses as a full cloud, not a 7-bit termination. No retail fixture regresses (none combines a standalone missile with a later cloud; the Mac saves' nested terminations are bit-identical under both modes). - **Beta** keeps the previous chain-mode / persistent-flag model, gated on `set_beta`, documented as a *compensating* model: it contradicts the Beta binary, but the fixtures only decode to completion under it — engine-exact semantics desync `DATA/SKSAVE0.DAT`, pointing at the known-incomplete trailing-map tile-walk accounting. Because the Beta walk is truncated-and-spliced, byte-exact round-trips cannot arbitrate the model; revisit with the walk work. - New unit tests for the retail nested-termination + subsequent-cloud shape (decode and encoder round-trip); the previous sibling-termination tests are retained as explicit Beta-mode tests. - `docs/formats/save-record-stream.md`: new section "Standalone-missile termination: engine vs Beta model (#169)". Closes #169
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jqueuniet/dm2-tools!233
No description provided.