dm2-saves: checkcode missile_termination_flag never cleared; standalone-missile sub-chain uses chain mode instead of single-record mode #169
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Two divergences from the engine's standalone-missile handling in
DM2_READ_RECORD_CHECKCODE, verified againstskproject/SKWINSPX/src/v5/sksvgame.cpp:945-955:missile_termination_flagis never cleared. The engine setsddat.v1d6525 = 1, runs the sub-read, then unconditionally clears it immediately after the call returns. The crate sets it (checkcode.rs:637decoder,:990encoder) and never resets it — the comment claims it "persists (consumed by a subsequent type-15 termination record)", but no consumption/clear code exists. The context lives on the shared decoder that spans phases 4→5→6→3→7.DM2_READ_RECORD_CHECKCODE(-1, &uw_02, vl_18, 0, vl_18)withvl_18 == 0in this branch: single-record mode, no trailing has-record bit. The crate usesread_chain(0, 0, 1).Impact
Once any standalone (non-creature-possessed) missile is decoded, every subsequent type-15 record in the entire save is misparsed as a 7-bit termination with early return instead of a cloud body + 1-bit has-index + optional 10-bit — a hard bitstream desync on engine-written saves that contain a standalone missile followed anywhere later by a cloud. The encoder mirrors the bug, so crate-internal round-trips pass; the current fixtures evidently lack the missile→cloud combination.
Bug 2 alone is bit-identical for the two shapes the engine writer emits (empty slot; single termination record), but diverges if the slot ever holds a regular record, and fixing 1+2 together makes the model match the engine exactly (termination lands in
sub_records, flag cleared afterwards).Note: the unit test
read_type15_termination_after_missileencodes the termination as a sibling of the missile — a stream shape the engine reader would parse differently (missile with empty sub-chain, then a full cloud). It will need updating along with the fix.Fix
In both
read_chainandwrite_chain: use single-record mode for the standalone-missile sub-chain and clearmissile_termination_flagimmediately after the sub-call returns.Investigated in #209 but not applied — the proposed fix regresses ground truth.
Both changes (single-record-mode sub-chain read + clearing
missile_termination_flagafter the sub-call, per retailsksvgame.cpp:945-955) were implemented and reverted: they break the DOS Beta v0.9SKSAVE0fixture. The mode change desyncsdecode_fulloutright; the flag-clear breaks the byte-exact re-encode. That fixture contains a standalone missile whose stream round-trips faithfully only under the current persistent-flag / sibling-termination model, so the engine (Fires.exeat least) does not match the single-record hypothesis — contradicting this issue's assumption that the fixtures lack the missile-combination.An explanatory NB comment was added to
checkcode.rs(commite271ee59). Keeping this open: resolving the retail-vs-Beta discrepancy needs binary-level tracing (skull.exe/Fires.exe), not a fixture-blind edit.Resolved in PR #233 after the binary-level tracing this issue was gated on.
Binary trace of the Beta engine (Fires.exe, LZEXE-unpacked via scripts/unlzexe.py)
Located the CHECKCODE reader/writer through the per-type mask table (initialized image at file 0x46acb, segment 0x3e09, table offset 0x3c3b — the mask block previously documented at 0x46a5f):
The Beta engine matches retail (sksvgame.cpp:945-955) exactly. This issue's proposed fix is correct per both engines.
Why the earlier attempt (#209) seemed to refute it
Two things conflated:
The Beta fixtures needing the persistent-flag model therefore points at a residual divergence in the Beta tile walk (most likely the trailing-map accounting already tracked in docs/formats/save-record-stream.md), which the persist model was compensating for.
What landed