Need help getting started? Try the player guide. These notes include older work; the game overview explains what’s ready now.

Original timed completion tally and record pipeline

The native backend now recovers the single-player tally loop inside original 08002564, followed by the completion data sequence at 08002B20: 1231C weapon capture, 1E48 totals accumulation, 1CF4 checkpoint capture, then increment the level. These are available through completion.c and native_completion.c. The Windows frontend now runs this tally and commits its bonus-adjusted record before advancement, chapter panels or the ending.

Playable pause and completion screens

Esc/P opens Continue/Quit over a frozen level. Quit opens the original localized Yes/No notice with No selected, then returns to Main. Application exit is the separate bottom-row Quit on Main. Arrows/WASD, Enter/Space and mouse hover/click select; Esc cancels the confirmation or resumes from the root. The pause adapter in pause.c binds the existing recovered mode_control.c/021E4 with one player, queues its actual commands/notices and retains their prefix state changes. It is not another replacement controller. Network pause flags are outside this single-player presentation owner.

intermission.c owns a separate84-slot sprite pool/cache/OAM, borrows frozen game assets/math, and renders the original E914/E9D4 localized notice glyphs, ECF8 score digits and EE60-compatible bonus spacing in Original HUD mode. Pending digits use Y88/120; score remains at Y8. It uses the original30-entry text blend table. The PC layer darkens the captured world over30ticks and marks the selected pause row white; those composition choices are host adaptations, not pixel-equivalence claims. Draws never tick gameplay or the tally. The high-resolution software scene is retained; GBA remains240x160. Private OAM copies remove old notices from pause's backdrop and remove all old HUD sprites from completion's backdrop.

Classic HUD uses the same ROM fonts and palettes as the main menu for pause, quit confirmation, completion labels, score/bonus values and final greetings. Labels/greetings, the Game Paused heading and inactive choices are green; scores and selected choices are white. The quit-confirmation heading remains white. Classic pause adds a blank16px row after its heading: Continue/Quit are at Y72/88 with matching mouse hitboxes. All pause text, including the quit warning and both HUD styles, moved up one16px row: heading Y40 and Original choices Y56/72. Classic quit-confirmation Yes/No now use Y72/88, one row below their former positions, with matching mouse hitboxes. Its heading stays at Y40. Completion text retains its existing positions. Other text uses the original localized notices and row positions; exceptionally wide lines use the menu's smaller font. Only glyphs are overlaid on the frozen level, including opaque black outlines. Original sprite updates still supply notice readiness, blending and the final readable hold; this font selection does not change tally/input/audio behavior.

Completion starts automatically from the living native input-state3 boundary. The host calls the original tally once per60Hz presentation tick. Held mouse fire/Enter/Space accelerates counting. A fresh click/Enter/Space settles the current time/ammo bonus in full, including during its initial hold. This PC policy lives in intermission.c; the recovered completion.c remains unchanged. The final message stays for180 fully visible ticks (three seconds); a separate fresh confirm after60 visible ticks can shorten that reading hold. Glyph loading does not consume the window. Both paths still wait for the actual music player to finish, including muted playback; missing audio is the explicit silent path. Tally command16 plays the existing count cue. Music9 is confined to completion. The original wrapped negative par difference can exist briefly at stage3; the native display hides it instead of overflowing the original8-digit scratch.

After the music/reading hold finishes, the frontend calls ssa_native_completion_record once, stores totals/checkpoint rows for the campaign session, and puts the final score into the next-level carry/entry snapshot. Reload does not accumulate the same counters again. Password/standalone setting-byte policy reaches record capture; carried campaign starts use setting0. Ammo is retained. Chapter6 and ending12 routes run after recording; the next map's entry snapshot retains that bonus score. Existing progress unlock/save behavior remains separate. After the ending, qualifying final scores now enter the original name editor and persist in scores.dat; confirmed Pause Quit offers the current run score. See scores.md. Campaign-session totals themselves are not a new disk-save format.

Audio commands4/5 now suspend/resume existing native music/effect instances and their envelope/sample/sequence clocks. New menu cues remain independent; quit's command6 retires the old instances. Synth controls mute the paused music tracks and restore them on resume. Suspension is immediate in the PC mixer; the original temporary audio fade envelope and physical MIDI-device timing are not claimed identical by this change.

Run decomp.ps1 test-pause, test-intermission, test-completion and test-native-completion, then scripts/test-intermission.ps1 -SkipBuild after building Windows. The first uses8192 additional ROM comparisons of adapter states and ordered events; the full mode controller's existing13092 comparisons remain separate. Screen tests cover36 map/language tally starts, actual glyph pixels and repeat draws, frozen source state, late zero-time bonuses, music holds, pause confirmation and sound suspension. Windows hidden-window tests drive real Esc/P, keyboard and mouse paths in both renderers, all3languages, six next-level/ chapter/ending routes and bonus-score handoff. They use controlled end boundaries; they are not campaign playthrough evidence. Previews are written under build/intermission-tests/. See intermission-validation.json, windows-intermission-validation.json and pause-rom-validation.json.

Current results:143825 ASan/UBSan/leak checks and69917 tally frames, plus6015 Windows checks over12pause paths and6completion routes; GCC/MSVC core counters agree. The original tally still passes11466 ROM frame comparisons, and its record adapter passes410217 checks/9216records. Existing audio regression passes654862 checks across131songs without decoder errors. All121Windows libraries/frontend build. Startup/restart/concurrent launches pass both renderers. Portable and Windows pause/bonus/Sam previews were visually checked. Current frontend SHA256: bd3c2cefd14237c5f499fb0adcf2fa3f532033475fd0ca103f6c2824c96c165d. No campaign playthrough, ROM modification, process termination or new exact match was performed. Live decomp.wtf status is78/526 functions, 2816/87648catalogued bytes(3.21%),549attempts; the catalogue is incomplete.

Bonus and timing behavior

The original loop increments GAME+00 once per presentation tick while gameplay elapsed time stays frozen. It reads the twelve original par times at 0844A610: 120, 300, 300, 420, 360, 360, 300, 360, 360, 360, 360, 360 seconds.

Phase Original behavior
Time bonus Positive par - elapsed/60 becomes 1000 * (difficulty+1) points per remaining second
Time counting Wait 60 ticks, then transfer 100 points per tick, or 1000 while fire is held
Ammo bonus Sum signed ammunition for weapons3..10; positive first scaling becomes 100 * sum * (difficulty+1)^2
Ammo counting Wait 60 ticks, then use the same 100/1000-point transfer
Short wait 30 ticks after ammo counting; held fire can skip it
Final message Submit original localized text ID B9+level and sound7, then wait for music-player completion

All arithmetic uses the original wrapped 32-bit values and signed branch decisions. The ammo positivity test occurs after the first multiplication by 10*(difficulty+1), before the second; do not replace it with a saturating formula. Ammo is not consumed. Weapons1 and2 never contribute. Score wraps.

Normal time completion goes directly to ammo setup. The original stage2 short wait exists in the jump table but is not entered by the ordinary recovered path; the native controller preserves and tests it. Counting emits original command16 only on frame-counter multiples of8 and only for nonfinal transfers. The final message cannot dismiss on its first submission tick. Thereafter a fresh fire press can bypass the remaining 60-tick hold, but music must be finished. Without a fresh press, it waits for that hold to reach zero too. Held fire and pressed fire are separate inputs.

Final Sam text and audio: corrected interpretation

Original02906 requests sound7 through08D5C. Song7 uses ROM player1 and consists of two PSG tracks, not a recorded Sam sample. Its native sequence lasts36ticks (about0.6seconds). All twelve levels use that same cue; localized text IDsB9..C4 provide the different Sam lines. There is no per-line speech lookup here.

Original02932 instead reads signed03006994, the status of player0 at03006990. The ROM player table at083E99A0 (file offset3E99A0) has entry0 that points to03006990, while entry1 points to030069D0. Player0 owns music, including completion command9 (Egypt song78/Rome7F). The old host incorrectly checked effect instances of song7. screen_completion_music_finished now reads player0 under the mixer lock. The native tests render both real completion tunes and the cue, confirming that music continues after the cue ends and the cue uses no PCM speech sample. Windows tests distinguish their statuses independently.

The recovered controller's60-tick hold is preserved. The three-second readable window and direct bonus skip are explicit PC presentation changes requested by the user, not claims of exact original timing. Completion music still starts at tally entry, not at the final line, and is not restarted there.

Original message IDs are B6 (time), B7 (time zero/ammo), B8 (both zero), and B9..C4 for the twelve final Sam messages. The pending-number Y coordinate is GAME+75, initially88 and later120; it is separate from the notice text's Y40 at GAME+73. The controller retains notice-timer/flag prefix effects of E914 and uses the original blend coefficients at 0844A5D4, selecting flash29 down to0. The caller still owns actual notice text formatting, character animation, draws and audio services. No silent production presentation stubs are provided.

Native composition

ssa_native_completion_init requires an idle, completed, living game in original input mode3. It copies the current score, frame counter, elapsed time, ammunition and difficulty into the tally. The source game remains frozen and unchanged. Call ssa_completion_step at the original 60Hz presentation cadence, with explicit held/pressed input and original voice-finished status. Device or render rate must not alter the tally's timing.

After the tally finishes, ssa_native_completion_record builds a value-only SsaCompletionRecord from caller-owned prior totals. It captures actual equipped and requested weapons separately, ownership and ammunition; refreshes weapon statistics with the original setting byte; accumulates the bonus-adjusted score and level counters; saves the checkpoint rows; and supplies next-level index. Setting0 retains weapon shots/hits; any nonzero byte clears both before accumulation and checkpoint capture.

First success marks the tally recorded; duplicate and premature attempts fail before output changes. It rejects busy/error/source changes, including score, counter, elapsed, bonus ammunition and completion state. Null failures preserve output and the recorded flag. Canonical inventory health/protection supplies the native projection of original body/player vitality. Source game and prior totals remain unchanged, including source weapon counters. The resulting record is safe to retain after game teardown; it contains no borrowed pointers.

This remains an explicit composition API. It does not itself update SsaNativeCarry, mutate the live game score, write save.dat, grant equipment, unlock/admit levels, run next-level player construction, or route chapters/endings. All twelve maps are now admitted at the user's request for manual validation. The active E914/E9D4 service supplies gameplay's original localized pickup/boss/death messages; see hud-notices.md. The original pre-tally world capture, tally-specific notice calls, on-screen bonus digits and final audio dismissal are now connected by the host described above. That host replaces the old direct Enter transition with the timed sequence; it never commits the earlier pre-bonus score or instantly awards the bonus instead of counting.

Verification

.\decomp.ps1 test-completion
.\decomp.ps1 test-native-completion
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/test-completion.ps1 -SkipBuild

completion-validation.json compares 7,370 consecutive original loop frames on all12 campaign maps / all3 difficulties, plus 4,096 directed loop states. It starts the actual original02564 and controls world capture, drawing/animation, VBlank, polling and audio boundaries. The original E914 notice setter executes; only its formatting service is supplied. Tests compare ordered events and their prefix states, counter/score/pending/flags/timer/position, every stage/hold/wait, blend values, source ammo and elapsed. They cover overflow, negative ammo, late voice completion, fresh versus held fire and otherwise unreachable stage2. UBSan, 13 invalid-input guards, 865 callback reentry checks and3 callback failure paths pass. This is a tally-loop comparison, not whole-function/UI parity.

native-completion-validation.json and its Windows counterpart agree on 410,217 checks: 36 map/difficulty starts, 1,152 actual gameplay frames with jump/H input, 29,148 tally frames, 9,216 completion records covering every setting byte, 36 chained records, 10,152 rejection cases and3,254 events. GCC ASan/UBSan/leak detection and MSVC pass. Directed exit/counter fixtures validate adapter wiring; they are not normal-input campaign-completion proof and do not justify admission of the final map.

At the historical backend-only milestone,111 portable and112 Windows native libraries built. The existing Windows statistics regression also passed, and the playable executable then remained SHA256 498e3e0071607a4c2a7a9b69aa71be1740d19c78babb8db1f3dfb35df0638504. No matching claim, imported catalogue entry or exact match was added for02564. Live decomp.wtf status remains78/526 functions and2,816/87,648 catalogued bytes (3.21%),541 attempts; discovery remains incomplete. Its78-C rebuild is unchanged.