Need help getting started? Try the player guide. These notes include older work; the game overview explains what’s ready now.
Original inventory restoration at level entry
The original level transition restores weapons and ammunition onto a newly constructed player. It starts the next level at 100 health, with both protection fields and the pending acquisition queue cleared. The earlier PC prototype carried the complete inventory snapshot across advancement, including health and protection. R restart still needs that exact snapshot behavior.
The original completion flow calls 1231C at 02B26 to save the weapon record at
player+28, then 1E48 accumulates level statistics. Level setup calls 05E4C at 02076,
which clears the four player records and constructs the active body with
health 100. F84C calls 1240C for weapon setup. On the existing-progress path,
020C8 calls 123C4 with the new player+28 record. That helper restores only:
| Record offset | Restored field |
|---|---|
| +00 | Equipped weapon, 32-bit |
| +0C | Owned weapon mask, 16-bit |
| +0E..+20 | Ten ammunition halfwords |
| +50 | Requested weapon, set from equipped weapon |
It then calls FC04 on the existing sprite with animation (current-1)&FFFF and
period 3. It does not restore health, protection, pending acquisitions or flags.
An invalid animation leaves the sprite unchanged; the inventory copy still
completes. The native weapon_progress.c preserves that behavior, with null
pointers rejected before writes.
ssa_native_game_carry_next now prepares the advancement entry after a real
completion. It resets the original vitality/queue fields and captures the
equipped usable weapon rather than an unfinished request. The existing PC
fallback for an empty equipped weapon remains. Earned ownership/ammo, score,
H-view and Q history persist. It rejects unfinished or busy games without
changing the output. carry_capture/carry_apply remain exact entry snapshots
for R and diagnostic resumes; init_campaign applies the supplied entry.
The Windows Enter path and normal-input campaign checker use carry_next.
The Windows transition fixture now expects the original reset at advancement,
then verifies that R restores that entry after gameplay has changed it. It also
requests a weapon immediately before its completion fixture: advancement must
restore the still-equipped gun rather than the unfinished request. The older
test's hard-coded requested-gun expectation failed and was corrected; production
advancement retains the recovered equipped-gun behavior.
Original completion capture and weapon statistics
Native ssa_weapon_progress_capture now recovers all of 1231C, the original
completion-side save. It copies current weapon, ownership, ten ammunition
halfwords and the pending requested weapon into the saved record. Unlike the
restore helper, capture preserves the distinction between equipped and requested.
The original helper also updates ten 16-byte rows at GAME+0x90:
| Row offset | Value |
|---|---|
| +00 | Ownership bit, as a 32-bit zero or one |
| +04 | Ammunition, sign-extended from the inventory halfword |
| +08 | Shot counter |
| +0C | Hit counter |
Every row receives ownership and ammunition, including unowned weapons and
negative ammunition values. With original settings+0x1B == 0, shot/hit counters
remain unchanged. Any nonzero byte clears both counters in every row. No other
saved-record, source-record, settings or game bytes change.
ssa_native_game_weapon_progress exposes the ongoing-campaign (setting zero)
projection using actual weapon_game.shots and weapon.hits. It rejects busy or
invalid game states before changing either output and never mutates the game.
The existing carry_capture now obtains its gear through this original native
routine while retaining its exact-entry snapshot policy. This does not add
frontend cross-level total persistence, original end statistics, NETRICSA or score
screens; those outer systems remain unfinished.
test_weapon_progress_rom.py compares 8,192 whole original 123C4 calls, including
all 256 record bytes and the real FC04 sprite writes. It also compares 4,096
whole 1231C calls, all 256 possible setting bytes, the full saved/source/game
records and 512 actual capture/restore compositions. Eight null guards and UBSan
pass. test_player_spawn_rom.py adds 512 actual 05E4C/1240C/123C4 compositions
on all 16 maps. Vitality writes and restoration instructions are never hooked;
only sprite allocation and the existing platform memory services are supplied.
These prove construction/restore behavior, not the entire outer campaign loop.
The password/start suite covers advancement and exact restart separately on
every admitted map and difficulty, plus 33 read-only statistics captures with
all six busy guards and complete game snapshots. Its 3,736 checks pass on GCC
ASan/UBSan/leak detection and MSVC. Reports are weapon-progress-validation.json,
native-player-spawn-validation.json and password-start-validation.json.
The original vitality reset changes later combat and supply choices. The normal-input eleven-map replay now takes 140,712 frames and ends with 1,544,000 score / 45 health. The earlier 136,552-frame full-vitality-carry run is a historical checkpoint. The final map has a separate standalone completion; its earned-inventory campaign path remains unfinished and unavailable.
The decomp.wtf candidate in recovered/weapon_progress_restore.c matches all 72
bytes / 36 instructions of 123C4 exactly. Export now rebuilds the original ROM with
75 functions in C; its remaining code/data stays assembly. This is separate
from native behavioral coverage and does not make the whole port complete.
The capture candidate recovered/weapon_progress_capture.c reached 86.8% after
five plain-C compiler attempts, with the same 168-byte / 84-instruction length.
Register allocation, literal placement and loop addressing differ. It is not
an exact match and its claim was released with notes. Exact matching therefore
was 75 functions / 2,224 of 87,648 catalogued bytes (2.54%) at that checkpoint.
Subsequent statistics helpers add three exact matches; current live progress is
78/526 and 2,816/87,648 bytes (3.21%). See statistics.md.