32-to-64-bit pointer-width fixes D451-D462; scaffolding removal batch 1

Tick-based auto-fire gate (D451), knife keyframe pointers (D454), font/text
slots (D456), memp arena start (D453), exe-address truncation census (D455),
global bank/RDRAM and background slots (D457 a/b), item-slot allocation
stride (D461), vtxstore table stride (D462). Mechanical, semantics-preserving
ABI/layout edits only. Diagnostic probes removed (batch 1).

Refs: D451 D453 D454 D455 D456 D457 D461 D462 D408 D294
This commit is contained in:
James Dansereau committed 2026-10-06 18:35:39 -04:00
1 parent ad3856892a
commit 41d690f7ca
32 files changed
+514 -179

No files matched your search

+5 -5
View File
@@ -29,13 +29,13 @@ Two classes:
| `GE_FAKE_DECK` | `port/src/video.c:490` (`videoDetectSteamDeck`) | D283: `=1` forces the Steam Deck path on any host (Windows/Linux desktop) so the once-per-ini Deck preset logic (`Video.DeckPresetApplied`, applied only while `Video.Fullscreen=0` and `Window.Maximized=0`) can be tested; `=0` forces it off on a real Deck (skips DMI + `STEAMOS`). Unset = real detection (DMI `Valve` + `Jupiter`/`Galileo`, then `STEAMOS`). **Writes the ini** (preset + flag) — use a throwaway data dir. | **live** (test hook) |
| `GE_OPTIONSOVERLAY` | `port/src/optionsoverlay.c:231` | Force-enable the F10 port-layer options overlay (D184 / PR #10) regardless of ini. `atoi != 0`. | **live** |
| `GE_D204` | `port/src/audio.c` (`audioSetNextBuffer`) | **Audio-health monitor (D204).** One line per 5 s of wall clock: `rt=` produced-audio/wall ratio (1.000 healthy; below 1 = the 30 Hz audio retrace is starving and the device is padding with silence), `q=` SDL queue depth / `queueLimit` (pinning at the limit with `drop=` climbing = overproduction, the "eventually no audio" state), `drop=` blocks discarded, `max=` largest block vs its `g_MaxFrameSize*4` allocation. **Deliberately cheap (~30 clock reads/s) — safe to leave on for a full playtest, unlike `GE_MIXERTRACE`.** The instrument of record for D202. | **live** |
| `GE_D204_OLD` | `port/src/audio.c` (`d204OldMode`) | Restores pre-D204 behaviour (`osAiGetLength()` reports the whole SDL queue depth; `queueLimit` back to 8192) so before/after can be A/B'd in **one binary**, without build-to-build variance. Temporary — remove when D204 closes. | **live** (D204 A/B) |
| `GE_D204_OLD` | `port/src/audio.c` (`d204OldMode`) | Restores pre-D204 behaviour (`osAiGetLength()` reports the whole SDL queue depth; `queueLimit` back to 8192) so before/after can be A/B'd in **one binary**, without build-to-build variance. Temporary — remove when D204 closes. | **dead** (D204 closed) — removed 2026-09-30 (ROADMAP §7 strip pass, batch 1, branch `chore/scaffolding-removal-1`); tombstone — the generator flags it GONE |
| `GE_AUDIOTRACE` | `src/snd.c` (`sndPlaySfx`, `sndDeactivate`), `src/game/propobj.c` (door sounds), `src/libultra/audio/load.c` (`alLoadParam`) | Per-`sndPlaySfx` resolution trace to `audiotrace.log`: `soundIndex` → `ALSound*`/keyMap/wavetable/base/len/book, the returned `ALSoundState*`, every `sndDeactivate`, and wavetable bind events. M-65 additions: `[VOICE+]`/`[VOICE-]` pairing every voice acquire/release with sound/state/flags (NB: written from two threads to one unbuffered FILE* — occasional torn lines, parse defensively), `[SLOTWRITE]` door slot ownership, `[RETRIGGER-POST]`, `[WAVELOOP]` (per-sound `ALADPCMloop` presence), `[DOORSND]` every door sound state change incl. an `ORPHAN-both-slots-busy` verdict. M-66b addition: `[EXPIRE]` — posted whenever the port-side ownerless-infinite-loop expiration is scheduled (sound/state/delay/fade); this line is **permanent** (part of the D202 option-C fix), unlike the rest of the probe set which is removable on close. M-67/M-69 additions: `[WIRE] t=µs filter=… <- table=…` (`src/libultra/audio/load.c`, every wavetable→decoder bind, µs-timestamped) and `[VOICEMAP]` (`src/libultra/audio/synthesizer.c`, full PVoice→decoder/resampler/env-state address map at synth init — this is how offline voicedump slot addresses tie to WIRE filter addresses). M-68 additions: `[EVT] t=µs type=N state=P` + `[STOP-EVT] … deltaUs=N` (event types per `src/snd.h`: 1=PLAY, 2=STOP, 8=VOL, 64=DECAY, 128=END). M-71 additions: `[VOL] t=µs state=… rawVol=… playing=…` (`src/snd.c`, per-VOL-event value before the slot-volume law) and `[DISTVOL] pos=(…) player=(…) dist=… vol=…` (`src/game/propobj.c`, the distance-attenuation funnel `sub_GAME_7F053894` — this is how an SFX's final volume is computed from hit position). NB: lines from the multiple file handles interleave — occasional torn/corrupt lines, parse defensively. `dumppos=` gives the exact byte offset into `audiodump.raw` (quantised to the 2880-byte audio block). Low cost. | **live** (D202/D205) |
| `GE_PULLTRACE` | `src/libultra/audio/load.c` (`alAdpcmPull`) | `[PASTEND]` assertion: fires if a voice's `f->sample` exceeds its wave's total samples while `nOver == 0` (i.e. an unstopped voice keeps decoding past the end instead of zero-filling). Rate-limited to 40 lines. M-65: 0 hits — exhausted voices go silent correctly. | **live** (D202) |
| `GE_DMEMWIPE` | `port/src/mixer.c` (`aClearBufferImpl`) | Zeroes the whole software DMEM at the frame boundary (detected as `alMainBusPull`'s clear of `AL_MAIN_L_OUT`, addr 1088). Tests whether audio state survives across frames in DMEM. M-65: no-op on output — ruled out. | **live** (D202) |
| `GE_NOWET` | `port/src/mixer.c` (`aSetVolumeImpl`) | Mutes the reverb send (`wetamt = 0`), isolating the reverb delay lines as a possible source of persistent signal. M-65: drone unchanged — ruled out (NB `aPoleFilterImpl` is still an unimplemented no-op, D199). | **live** (D202) |
| `GE_BANKDUMP` | `port/src/romdata.c` | `BANKSRC` lines: the source ROM-layout bank tree per `soundIndex` (instrument → sound → envelope pointers + `decayTime`). Used to prove sound 203's `decay=-1` is genuine ROM data, not a conversion bug. One-shot at load. | **live** (D202) |
| `GE_FORCEALARM` | `src/game/propobj.c` (`handle_alarm_gas_timer_calldamage`, `#ifdef PORT`) | **D207 diag (temporary).** Forces the in-game alarm klaxon (`alarmActivate`) on a frame counter — on ~2–10 s, off, on again — and emits a per-frame `[D207] f=… active=… ptr_alarm_sfx=… playstate=… locked=…` line to `audiotrace.log`. Repro harness for the "alarm SFX overrides other audio" report when a level's own alarm can't be triggered headlessly. Remove when D207 closes. | **live** (D207) — temporary |
| `GE_FORCEALARM` | `src/game/propobj.c` (`handle_alarm_gas_timer_calldamage`, `#ifdef PORT`) | **D207 diag (temporary).** Forces the in-game alarm klaxon (`alarmActivate`) on a frame counter — on ~2–10 s, off, on again — and emits a per-frame `[D207] f=… active=… ptr_alarm_sfx=… playstate=… locked=…` line to `audiotrace.log`. Repro harness for the "alarm SFX overrides other audio" report when a level's own alarm can't be triggered headlessly. Remove when D207 closes. | **dead** (D207 closed) — removed 2026-09-30 (ROADMAP §7 strip pass, batch 1, branch `chore/scaffolding-removal-1`); tombstone — the generator flags it GONE |
| `GE_KEYMAPDUMP` | `port/src/romdata.c` | `KEYMAP` + `ENVSRC` lines: raw source bytes of every `ALKeyMap` / `ALEnvelope` in the ROM-layout blob, for byte-wise comparison against runtime values. One-shot at load. | **live** (D202) |
| `GE_AUDIODUMP` | `port/src/audio.c` (`audioSetNextBuffer`) | Raw-dumps every mixed s16/stereo/22050 Hz buffer to `audiodump.raw` in the CWD for offline waveform inspection. Length ÷ 88200 = seconds of audio produced. | **live** (D202) |
| `GE_VOICEDUMP` | `port/src/mixer.c` (`aEnvMixerImpl`) | Dumps each voice's resampled mono stream (post-resample/pre-envelope, i.e. the env-mixer input) to `voicedump.raw` as records of `[u32 stateAddr][u32 nSamples][u64 us][s16 × n]`. **The µs stamp is when the block was MIXED — records are rendered ahead, so a record's samples END at its stamp** (M-69: treating stamps as start offsets misplaces content by up to one ~33 ms block). `stateAddr` = the env-mixer state address from `[VOICEMAP]`. Used with `scratchpad/exact_match.py` for full-corpus per-voice sample verification. | **live** (D202) |
@@ -67,10 +67,10 @@ Two classes:
| `GE_D288` | `port/fast3d/gfx_pc.cpp` (`gfx_sp_tri1`, right after the D233 trivial-reject guard; cached `ge_d288`) | M-152 diag for the Silo intro stray triangle: frame-gated to `num_dls` 550–755 (M-155), now **skips `any_behind_camera==1` triangles** (M-155 follow-up — the x/w divide is meaningless for w≤0; that subset was pure false-lead noise) and logs the render-mode word (`oml`) so a live capture can ID the culprit asset against level tables. Logs any triangle whose clip-space NDC bbox covers >50% of the screen or goes non-finite, with each vertex's `w`. Bare `-level_20` re-run: 1965 sane hits in the window, still zero for the target color RGB(83,62,44) — consistent with M-155's working theory that the bare boot's scripted camera never reaches the bug state; **headless probing is at its limit, live capture is next**. | **dead** (D288 closed) — removed 2026-09-30 (ROADMAP §7 strip pass, branch `chore/strip-temp-probes`); tombstone — the generator flags it GONE |
| `GE_D245V` | `src/game/sky.c:962` (`skyPortRenderPoly`-adjacent, `#ifdef PORT`) | D245 water-seam diag: per-call min/max of the recomputed S/T texture-coordinate ranges for the water fan vertices (rate-limited), to check whether the seam tracks a tc-shift boundary. Inert unset. | **live** (D245 OPEN) |
| `GE_D245_FIXEDSHIFT` | `src/game/sky.c:946` (`#ifdef PORT`) | D245 test override: pins the water tc shift `k` to the given integer (0..SKY_TC_MAX_SHIFT) instead of the computed per-vertex value, to A/B the allowShift=TRUE behavior. Off by default (unset = current behavior). | **inert** (D245 fixed) — since the s16 sky path was removed 2026-09-30 nothing consumes the forced shift; strip candidate |
| `GE_D252` | `port/fast3d/gfx_pc.cpp:733` (`import_texture`, cached `s_d252`) | D252 rainbow-particle diag: one-shot per (address,size) import log of the tile state (tmem/fmt/siz/addr/size/line) for small textures so a suspect particle tile can be checked against its source bytes by hand. Remove once D252 is resolved. | **live** (D252 OPEN — non-reproducible) |
| `GE_D252` | `port/fast3d/gfx_pc.cpp:733` (`import_texture`, cached `s_d252`) | D252 rainbow-particle diag: one-shot per (address,size) import log of the tile state (tmem/fmt/siz/addr/size/line) for small textures so a suspect particle tile can be checked against its source bytes by hand. Remove once D252 is resolved. | **dead** (D252 closed) — removed 2026-09-30 (ROADMAP §7 strip pass, batch 1, branch `chore/scaffolding-removal-1`); tombstone — the generator flags it GONE |
| `GE_D252POOL` | `src/game/image.c` (`texLoad`), `src/game/initmttex.c`, `port/src/gimgfixup.c` | **TEMP** (M-201): one line per new texture load (pool main/other, free bytes) + `POOL-FULL` events + stage pool range; `D252SYNC: N slots re-synced, M changed` per stage load (the D252 fix at work: M>0 from the 2nd stage on). Load-time only, no fps cost. | **dead** (D252 fixed) — removed 2026-09-30 (ROADMAP §7 strip pass, branch `chore/strip-temp-probes`); tombstone — the generator flags it GONE |
| `GE_D245_OLDVTX=1` / `GE_D245_OLDTEX=1` | `src/game/sky.c` (`skyPortRenderPoly`) / `port/fast3d/gfx_pc.cpp` (CI8 import + tri-path dims) | **A/B** (M-201): restore the pre-fix s16 sky/water vertex path / the pre-fix 32x43, 16-wide water texture geometry. | **dead** (D245 fixed) — removed 2026-09-30 (ROADMAP §7 strip pass, branch `chore/strip-temp-probes`); tombstone — the generator flags it GONE. The pre-fix s16 sky/water vertex path (`skyPortRenderPoly` tail + `skyPortEmitTileShift`) went with it; the float path is now unconditional |
| `GE_DYNTEXHASH_OFF=1` | `port/fast3d/gfx_pc.cpp` (`import_texture` cache key) | **A/B** (M-201, D341): disable the content-hashed cache key for dyn-pool textures (reverts to the stale address-only key). | **TEMP** (A/B switch) |
| `GE_DYNTEXHASH_OFF=1` | `port/fast3d/gfx_pc.cpp` (`import_texture` cache key) | **A/B** (M-201, D341): disable the content-hashed cache key for dyn-pool textures (reverts to the stale address-only key). | **dead** (D341 closed) — removed 2026-09-30 (ROADMAP §7 strip pass, batch 1, branch `chore/scaffolding-removal-1`); tombstone — the generator flags it GONE |
| `GE_D207S` | `src/game/gunfire.c` (player shot-sound block, `#ifdef PORT`) | **TEMP** (M-201): one line per player shot-sound decision: `D207S t=<g_GlobalTimer> hand item rate -> H1|H2|SKIP` (SKIP = both per-hand sound handles still held). Read-only, uncapped, cheap; use instead of `GE_AUDIOTRACE` for live captures (that probe does file I/O on the audio path). | **dead** (D207/D241 closed) — removed 2026-09-30 (ROADMAP §7 strip pass, branch `chore/strip-temp-probes`); tombstone — the generator flags it GONE |
| `GE_D306C="f0 f1 [wmax]"` | `port/fast3d/gfx_pc.cpp` (after `zf_note_emit`) | **TEMP** (M-201): depth-state census of every emitted triangle in frames f0..f1 (GL depth mode, geometry_mode, other_mode_l, tile-0 texture, NDC centroid, z/w range, min w); optional `wmax` keeps near geometry only. Cap 60000 lines. | **TEMP** (D306 closed; remove post-release) |
| `GE_INTROCAM=<n>` | `src/game/bondview_r.c` (`#ifdef PORT`, intro camera pick) | **TEMP/test-only**: force the level intro's first camera index (Dam truck shot = 5). The PRNG draw is still consumed. Logs `D306: intro camera index` when set (or with `GE_D306C`). | **TEMP** (useful repro tool) |
@@ -139,7 +139,7 @@ Two classes:
| `GE_D314` | `port/src/optionsoverlay.c:681` (cached `s_d314Enabled`) | D314 F10-flicker probe: logs the overlay's viGetY / visible-row count / scroll / selection each poll. Inert unset; strip candidate once D314 is triaged. | **live** (D314 pending triage) |
| `GE_D318R` | `port/src/random.c:69,147` (`#ifdef PORT`) | PRNG golden probe (D284/D318): logs the first 4000 gameplay-PRNG draws with tick + caller return address, and every `randomSeed` set — verifies the bit-exact port of `random.s` and which call sites consume the stream. Inert unset. | **live** (D284 verification) |
| `GE_RSEED` | `port/src/random.c:134` (`#ifdef PORT`) | Test-only boot-seed override: `GE_RSEED=<hex64>` pins `g_randomSeed` at first seed instead of the wall-clock-derived value — for determinism and N64-parity tests. Inert unset. | **live, test-only** |
| `GE_RSEED_LV` | `port/src/d318watchdog.c:232` (`#ifdef PORT`) | One-shot level-start seed log: logs `g_randomSeed` at the first tick after level start (optionally armed for a specific tick via `<hex>@<tick>`) — ties a saved/loaded game's PRNG state to a level for D318-class repro. Inert unset. | **live** (D318 verification) |
| `GE_RSEED_LV` | `port/src/d318watchdog.c:232` (`#ifdef PORT`) | One-shot level-start seed log: logs `g_randomSeed` at the first tick after level start (optionally armed for a specific tick via `<hex>@<tick>`) — ties a saved/loaded game's PRNG state to a level for D318-class repro. Inert unset. | **dead** (D318 closed) — removed 2026-09-30 (ROADMAP §7 strip pass, batch 1, branch `chore/scaffolding-removal-1`); tombstone — the generator flags it GONE |
| `GE_D318T` | `port/src/d318watchdog.c:270` (`#ifdef PORT`) | D318 watchdog timeline probe: per-tick log of the Facility ai_22 softlock signature window (c78 list/offset/attack state, c67 health) while the watchdog watches. Inert unset. | **live** (D318 verification) |
| `GE_D322` | `port/src/audio.c` (`audioSetNextBuffer`), `src/snd.c` (`sndD322PoolSummary`) | D322 pool-telemetry probe: one line per 5 s of wall clock with the three pool layers a long session can exhaust — `sfx=count/max` (8-voice SFX soft cap), `evtq=n/64` (event queue occupancy; full = silent post drops, M-71), `pv free/lame/alloc` (24 physical synth voices shared by SFX + music; `-1`s before sndInit), plus the D204 pipeline fields. Cheap (~30 reads/s), campaign-safe; read together with `GE_D204=1`. | **live** (D322 capture) |
| `GE_D318W` | `port/src/d318watchdog.c` (`d318WatchdogTick`, `#ifdef PORT`) | **The D318/D320 pin detector, ON by default, DETECT-AND-LOG ONLY since 2026-09-30.** Watches the Facility ai_22 / ai_19 and Control ai_9 hold-pose pin signatures (findings D318/D320) and logs `D318W:` lines (first detection, confirmation after 600 ticks, periodic reminders while held). It used to re-seed the attack via `sub_GAME_7F025560`; that recovery was removed because D329 fixed the cause, so a hit now means a D329 regression. `GE_D318W=0` silences it. | **live** (D329 regression detector) |
+9 -2
View File
@@ -172,7 +172,7 @@ D290,FIXED,"Watch's in-game ""controller"" section shows no control-pad graphic
D291,CLOSED,"Dam: texture glitches when the truck drives past (M-141, Steam Deck user playtest).","CLOSED — duplicate of D306, faithful (M-201, 2026-09-26:"
D292,FIXED,Death animations are glitched —,"MOSTLY FIXED as a side effect of D243 (2026-09-18, user-confirmed alongside D173) —"
D293,FIXED,"QoL ask: no discoverable way to quit the game from the UI (M-141, Steam Deck user playtest —","FIXED (M-141) — ""Quit to desktop"" row added to the F10 overlay; build-verified Win+Linux, not yet live-confirmed.…"
D294,PARTIAL,"Statue: ground geometry near the large CCCP/hammer-and-sickle monument briefly renders solid black over a wide area, then self-recovers 10-30 seconds later… —",PARTIAL (2026-09-29 bookkeeping:
D294,PARTIAL,"Statue: ground geometry near the large CCCP/hammer-and-sickle monument briefly renders solid black over a wide area, then self-recovers 10-30 seconds later… —",PARTIAL -> fix landed pending by-eye check (2026-09-30:
D295,FIXED,"Campaign-halting crash at the end of Control Center on 00 Agent difficulty, then a crash on every subsequent relaunch/resume (GitHub #87, user campaign playtes…",FIXED (M-149) — M-148 root-caused + reproduced on HEAD; fix landed:
D296,CLOSED,"Control Center, 00 Agent:","CLOSED (2026-09-26, user-approved:"
D297,FIXED,v0.3.0 silently wipes ALL pre-fix player saves on first launch (D284 side effect; found during D295/M-148).,FIXED (M-150) — migration shim landed:
@@ -284,7 +284,7 @@ D404,RESOLVED,"""Aim style: Centred (PC)"" (Input.AimMode=1, D333/D337) has no e
D405,CLOSED,"fast3d/OpenGL: MSAA above the driver max black-screens; two glBindFramebuffer calls pass an index where a GL name is required (PR #106, italoarruda, 2026-09-25…","CLOSED (2026-09-28). Merged from PR #106 (code file only; security-reviewed clean - port-layer GL state fixes, no external input, no allocations)."
D406,RESOLVED,front-end PC Options screen:,"RESOLVED (2026-09-28, port-layer only, Steam Deck-validated):"
D407,CLOSED,Tanks cannot be boarded/exited on PC —,CLOSED (2026-09-28 bookkeeping:
D408,OPEN,Game.SkipIntro skips the post-mission failure dossier:,"OPEN (cosmetic, not root-caused past the handoff site)."
D408,FIXED,Game.SkipIntro skips the post-mission failure dossier:,FIXED 2026-09-30 (SkipIntro hook gated to first boot; live dossier check owed).
D415,FIXED,"Shooting a light fixture does not ""kill"" its flickering light (Bunker, Caverns):",FIXED (2026-09-29 bookkeeping:
D424,FIXED,WeaponStats.RecoilSpeed/b44[] byte order:,"FIXED in tree (WS_B44, port-guarded); user-verified vs GEPD/1964 2026-09-29."
D425,FIXED,Tank run-over death sound loops:,FIXED (2026-09-29 bookkeeping:
@@ -301,7 +301,14 @@ D440,FIXED,"""Original N64"" / ""Port defaults"" one-press presets in the F10 ov
D441,PARTIAL,64-bit pointer-width sweep (part 1):,"PARTIAL (mechanical widening landed, build/run verified on Bunker and Silo; census/provenance table for the remaining sites still owed)."
D442,FIXED,Game.AllUnlocked reworked into a pure RAM/query-time override:,FIXED (build and eep-diff verified; level-select/cheat-menu live check owed).
D443,FIXED,"D357 settings-display cleanup (mouse ×, whole-% deadzone and volume steps, FOV in horizontal degrees, MSAA 16×) plus a ""Restart game"" action (orderly D344 quit…",FIXED (port-only; reset probes pass; both-UI visual check and one Restart click owed; Linux relaunch path not compiled).
D454,FIXED,Knife slash crashed on Windows:,FIXED (verified by maintainer live:
D461,FIXED,Item-slot allocation overran into the next stage-bank block:,FIXED (maintainer-verified:
D462,FIXED,vtxstore.c sized its two record tables with the N64 stride 0x14 for a 24-byte struct unk_09B7A0_struct_parent on PC (Vertex* unk00 widened); both are indexed a…,FIXED (build + solo -level_09 and 4P GE_STARTMP=4 smoke runs exit clean; no crash; by-eye owed in solo).
D444,FIXED,Front-end PC Options:,FIXED (build-verified; human mouse test owed)
D455,FIXED,Binary-first census of exe-static addresses truncated to 32 bits (D454 class) (2026-09-30).,FIXED/CLEAN (no further REAL site beyond D454)
D447,FIXED,Pillarbox output mode (Video.AspectMode = Original) for the Original N64 preset; crop overscan no longer cancels Wide/Cinema.,"FIXED (build + 3 captures verified; Wide/Cinema, 16:9 Ratio, mouse and 1964 comparison owed to a human)"
D445,FIXED,Crouched-rifle firing animation (crouched_rifle_firing_animation_groupA) never resolved on PC -> SIGSEGV in modelConstrainOrWrapAnimFrame when an MP player cro…,"FIXED — layout only (§D5 linker adjacency, D164 class):"
D446,FIXED,Couch 2P watch-menu text (the MP pause page) differs by +-1-2 per channel between builds whose only difference is data layout (2026-09-29).,FIXED — port-only (fast3d):
D456,?,"Font-pointer / text-string-pointer slots retyped from s32 to real pointers (ABI/layout exception; front.c, options.c, language.c, bondview*, mpmenu.c, propobj.…",
D453,?,"memp allocator API made 64-bit clean (D441 step 0; ABI/layout exception, §A1).",2026-09-30
D451,FIXED,Automatic-fire gate at unsteady frame rates (D427 follow-up) and D173 residual.,"FIXED (RULE-2-SIGNOFF, maintainer 2026-09-30:"
1 # GENERATED by tools_pc/gen_findings_index.py -- do not hand-edit.
172 D291
173 D292
174 D293
175 D294
176 D295
177 D296
178 D297
284 D405
285 D406
286 D407
287 D408
288 D415
289 D424
290 D425
301 D441
302 D442
303 D443
304 D454
305 D461
306 D462
307 D444
308 D455
309 D447
310 D445
311 D446
312 D456
313 D453
314 D451
+119 -2
View File
@@ -580,7 +580,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
| D291 | **Dam: texture glitches when the truck drives past (M-141, Steam Deck user playtest).** — full `## D291` entry at file tail | CLOSED — duplicate of D306, faithful (M-201, 2026-09-26: user-captured, Dam intro camera 5 only). Earlier: OPEN — observed, not investigated. Needs a live repro with the truck's exact position/timing and, ideally, a screenshot. |
| D292 | **Death animations are glitched — Bond is often sunk halfway into the floor (M-141, Steam Deck user playtest).** — full `## D292` entry at file tail | **MOSTLY FIXED as a side effect of D243 (2026-09-18, user-confirmed alongside D173) — same fix, same outcome: right 9/10 times, small residual offset only when off, no more full sinking/glitching.** See D173 for the user's exact confirmation wording; not separately re-tested, reported together. |
| D293 | **QoL ask: no discoverable way to quit the game from the UI (M-141, Steam Deck user playtest — "couldn't see a way to exit the game. Maybe build this into the…** — full `## D293` entry at file tail | FIXED (M-141) — "Quit to desktop" row added to the F10 overlay; build-verified Win+Linux, not yet live-confirmed.… |
| D294 | **Statue: ground geometry near the large CCCP/hammer-and-sickle monument briefly renders solid black over a wide area, then self-recovers 10-30 seconds later…** — full `## D294` entry at file tail | PARTIAL (2026-09-29 bookkeeping: main symptom FIXED — room-model pool; user playtests 2026-09-22/25 no longer reproduce the black ground; the residual corner flicker at max FOV reported 2026-09-22 was never re-checked, tracked in docs/ROADMAP.md §2). Earlier: **FIXED, user playtest confirmed (2026-09-25: not reproduced)** — root cause: room-model POOL exhaustion (authored `-ma220`=220 KB < ~250 KB monument-plaza demand at FovScale 130 + max DD/Lod); `memaAlloc` NULL → silent skip → undrawn room shows the night-sky clear colour. Stochastic (wall-clock seed → load order). Fix: `portRoomPoolScale()` (video.c, identity at N64-faithful settings) applied in `bossMainloop`; verified headless (faithful→exact authored size; user settings→0 failures; `GE_ROOMPOOL=0.25`→exhaustion on demand). Playtest: main patch gone; residual corner flicker traced to full-level residency (487 KB = 2.2× authored) starving the 2× pool; the 3×/linear fix REGRESSED the renderer on the menu-driven path (D156 NaN anim state, not a bank overflow) → reverted to sqrt + 2.0 cap (ma=450560). Residual open; probes in place for next capture. High-DD horizon edge on night levels = setting-inherent, documented only.… |
| D294 | **Statue: ground geometry near the large CCCP/hammer-and-sickle monument briefly renders solid black over a wide area, then self-recovers 10-30 seconds later…** — full `## D294` entry at file tail | PARTIAL -> fix landed pending by-eye check (2026-09-30: pool cap 2.0 -> 2.5, DD/Lod terms powf 0.66, quarter-step round-up; ma 450560 -> 563200 at max settings; see D294 entry tail). Prior: PARTIAL (2026-09-29 bookkeeping: main symptom FIXED — room-model pool; user playtests 2026-09-22/25 no longer reproduce the black ground; the residual corner flicker at max FOV reported 2026-09-22 was never re-checked, tracked in docs/ROADMAP.md §2). Earlier: **FIXED, user playtest confirmed (2026-09-25: not reproduced)** — root cause: room-model POOL exhaustion (authored `-ma220`=220 KB < ~250 KB monument-plaza demand at FovScale 130 + max DD/Lod); `memaAlloc` NULL → silent skip → undrawn room shows the night-sky clear colour. Stochastic (wall-clock seed → load order). Fix: `portRoomPoolScale()` (video.c, identity at N64-faithful settings) applied in `bossMainloop`; verified headless (faithful→exact authored size; user settings→0 failures; `GE_ROOMPOOL=0.25`→exhaustion on demand). Playtest: main patch gone; residual corner flicker traced to full-level residency (487 KB = 2.2× authored) starving the 2× pool; the 3×/linear fix REGRESSED the renderer on the menu-driven path (D156 NaN anim state, not a bank overflow) → reverted to sqrt + 2.0 cap (ma=450560). Residual open; probes in place for next capture. High-DD horizon edge on night levels = setting-inherent, documented only.… |
| D295 | **Campaign-halting crash at the end of Control Center on 00 Agent difficulty, then a crash on every subsequent relaunch/resume (GitHub #87, user campaign playtest).** — full `## D295` entry at file tail | FIXED (M-149) — M-148 root-caused + reproduced on HEAD; fix landed: port-side JPN-cache seed (`port/src/main.c`) + `#ifdef PORT` buffer widen in `front.c` (D8 class). Verified: previously-crashing input now clean, 21/21 sweep PASS. Companion save-wipe regression → D297. History — M-148 fully root-caused + **reproduced on current HEAD**: `difficultytext[4]` (`front.c:2616`) overflow — `strcpy("00 Agent")`/`strcat("\n")` spills onto `textpos.p[0]` (y, ≥0x80 in normal geometries) → high-bit byte → `langGetJpnCharPixels` NULL deref (`language.c:320`, `g_JpnCacheCacheItems` NULL outside JPN mode — likewise unallocated on N64 non-JP builds, crash potential is decomp-native). The user's save is a *valid pre-D284* save (all 5 slot CRCs bit-exact under the old PRNG); post-fix builds wipe it at validation, which is why the unmodified artifact no longer triggers it — NOT a fix. No OOB exists (M-147 guess retracted: 007 difficulty has no stored times). Fix options flagged for sign-off (port-side JPN-cache init preferred; `difficultytext` enlarge = D8 class). Side finding: v0.3.0 silently wipes all pre-fix saves on first launch (D284 side effect — own D entry owed). Separate still-open issue found: unconditional `[EVTQ-DROP]` audio-pool-exhaustion logging, `src/libultra/audio/event.c:92`.… |
| D296 | **Control Center, 00 Agent: noticeable stutter, user's own read is "maybe from the drone guns" (M-146, same D295 investigation session).** — full `## D296` entry at file tail | CLOSED (2026-09-26, user-approved: stutter seen only under gdb (debug.ps1); D302, fixed two days later, removed a per-triangle uncached getenv costing ~15 fps; reopen if seen on a normal build). Earlier: OPEN — single live report, un-triaged; observed only under a gdb-attached debug-harness launch (`tools_pc/debug.ps1`), not the packaged release artifact — user flagged that harness overhead has caused false stutter before. Needs a plain-exe / real-release repro check before any perf tracing.… |
| D297 | **v0.3.0 silently wipes ALL pre-fix player saves on first launch (D284 side effect; found during D295/M-148).** — full `## D297` entry at file tail | FIXED (M-150) — migration shim landed: `port/src/legacycrc.c` (isolated, deliberately-wrong pre-D284 PRNG, reviewer-verified bit-exact against `0de99b4d^`) + eeprom-shim read-path hook in `libultra.c` (block 4 only; persists pristine+migrated bytes before the read-time-only AllUnlocked patch). Verified: user's #87 artifact → `D297-migrated 5`, all slots OK, idempotent on relaunch; native post-fix save untouched; corrupt slot left to the game's own wipe path; fresh eep untouched; 21/21 sweep PASS. Block-0 header needed no migration (factory seal, verified). |
@@ -692,7 +692,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
| D405 | **fast3d/OpenGL: MSAA above the driver max black-screens; two `glBindFramebuffer` calls pass an index where a GL name is required** (PR #106, italoarruda, 2026-09-25; label rebased from the PR's D323, which collides with our D323 LOD finding) — full `## D405` entry at file tail | **CLOSED (2026-09-28).** Merged from PR #106 (code file only; security-reviewed clean - port-layer GL state fixes, no external input, no allocations). `resolve_msaa_color_buffer` clamps `msaa_level` to `GL_MAX_SAMPLES`; the resolve/copy paths bind `current_framebuffer ? framebuffers[i].fbo : 0` (the default framebuffer is GL name 0). Both Windows (opengl32) and Linux use this backend. Post-merge sweep 21/21 PASS + MSAA=16 boot check render-verified; link ritual clean. |
| D406 | **front-end PC Options screen: long sections (INPUT, 17 visible rows) run off the bottom of the 440x330 paper, and rows past the silent 14-row page cap (Crouch mode, Reset to defaults, Bindings…) are unreachable from the front screen** (user report 2026-09-28, v0.4.0 pre-push review) — full `## D406` entry at file tail | RESOLVED (2026-09-28, port-layer only, Steam Deck-validated): in-section pagination in `frontoptions.c` (11 rows/page; the vertical stepper crosses page boundaries both ways; dim 'Page p/N' marker + bottom hint on every page; slider bars start past long labels; F10 overlay untouched). D406b/c/d same-cycle refinements (label/track overlap, hint visibility/wording, hint position). |
| D407 | **Tanks cannot be boarded/exited on PC — the v0.4.0 use/reload split (D378/D393) removed the B-button tap the engine's tank handlers in `bondview2.c` consume (user report 2026-09-28, Runway/Streets)** — full `## D407` entry at file tail | **CLOSED (2026-09-28 bookkeeping: tank board/exit accepted by the user; shipped in v0.4.0).** Earlier: PARTIAL (fix landed in v0.4.0, port-only): present `GE_CONT_B` on the use (E / pad A) rising edge only while `g_PlayerIsInTank == 1` or `g_BondCanEnterTank != 0`; E keeps its D378 no-reload-fallback semantics elsewhere, N64 layout unaffected. D407(b) same cycle: front PC Options page-edge highlight clamp + mouse-wheel / W-S paging (wheel queue consumed on the menu, D223); its wheel mapping shipped inverted and was fixed to match W/S (wheel up = step up). D407(c): board-animation lockout, menu-accept B gate in tank states, in-tank aim routed through the legacy velocity stick + `Input.TankAimScale` knob. RESOLVED 2026-09-28: all port-only (src/game zero-diff), 10 TANKDBG probes stripped, release binary verified clean, user live tank drive signed off (board + exit OK, aim feel good). |
| D408 | **`Game.SkipIntro` skips the post-mission failure dossier: with it on, entering a level then aborting (watch Z+A) or dying (KIA) returns straight to the menus — no REPORT / "Mission status: KILLED IN ACTION / ABORTED" screen** (user report 2026-09-28; save/AllUnlocked ruled out) — full `## D408` entry at file tail | OPEN (cosmetic, not root-caused past the handoff site). v0.4.0 decision: SkipIntro stays EXPERIMENTAL / not recommended for regular users — F10 row relabelled "Skip intro (EXPERIMENTAL)". |
| D408 | **`Game.SkipIntro` skips the post-mission failure dossier: with it on, entering a level then aborting (watch Z+A) or dying (KIA) returns straight to the menus — no REPORT / "Mission status: KILLED IN ACTION / ABORTED" screen** (user report 2026-09-28; save/AllUnlocked ruled out) — full `## D408` entry at file tail | FIXED 2026-09-30 (SkipIntro hook gated to first boot; live dossier check owed). |
| D415 | **Shooting a light fixture does not "kill" its flickering light (Bunker, Caverns): the fixture takes the hit but the flicker keeps going (community report, issue #87 comment by the reporter, 2026-09-28, 00 Agent playthrough of v0.4.0).** — full `## D415` entry at file tail | FIXED (2026-09-29 bookkeeping: root-caused and fixed as D430 + D431, user-verified vs GEPD/1964 on Caverns 2026-09-29; #119). Earlier: ROOT-CAUSED as D430 (see there); fix in tree, live check owed. |
| D424 | **`WeaponStats.RecoilSpeed`/`b44[]` byte order: no grenade-launcher recoil, shortened recoil on every gun (issue #118).** — full `## D424` entry at file tail | FIXED in tree (`WS_B44`, port-guarded); user-verified vs GEPD/1964 2026-09-29. |
| D425 | **Tank run-over death sound loops: `(chrflags << 7) >= 0` is always true on unsigned GCC enum (issue #115).** — full `## D425` entry at file tail | FIXED (2026-09-29 bookkeeping: use-site `(s32)` cast; user-verified vs GEPD/1964 2026-09-29, #115). Earlier: FIXED in tree (use-site `(s32)` cast); live check owed. |
@@ -709,10 +709,17 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
| D441 | **64-bit pointer-width sweep (part 1): 17 files widened `(u32)`/`(s32)` pointer casts to `uintptr_t` under `#ifdef PORT` (ABI/layout exception; ROADMAP §2 "Latent 64-bit ABI hazards").** — full `## D441` entry at file tail | PARTIAL (mechanical widening landed, build/run verified on Bunker and Silo; census/provenance table for the remaining sites still owed). |
| D442 | **`Game.AllUnlocked` reworked into a pure RAM/query-time override: #ifdef PORT hooks in `fileGetIsCheatUnlocked` and `fileIsStageUnlockedAtDifficulty` (every stage reports COMPLETED); the EEPROM patch and the D387 merge are removed (RULE-2-SIGNOFF, maintainer opt-in feature).** Full `## D442` entry at file tail | FIXED (build and eep-diff verified; level-select/cheat-menu live check owed). |
| D443 | **D357 settings-display cleanup (mouse ×, whole-% deadzone and volume steps, FOV in horizontal degrees, MSAA 16×) plus a "Restart game" action (orderly D344 quit, then relaunch).** Full `## D443` entry at file tail | FIXED (port-only; reset probes pass; both-UI visual check and one Restart click owed; Linux relaunch path not compiled). |
| D454 | **Knife slash crashed on Windows: `var_a0_2` in `gunTickHandState` was `u32`, truncating the exe-data address of `D_80034CA4/E0C` (PE image base 0x140000000) before `gunSample1PTransform` (ABI/layout exception, §A1).** Full `## D454` entry at file tail | FIXED (verified by maintainer live: knife slash no longer crashes). |
| D461 | **Item-slot allocation overran into the next stage-bank block: `alloc_additional_item_slots` used the N64 0x14 stride for a 32-byte `InvItem` (the code comment tags it D458b); `bondinvReinitInv` wrote `p_itemcur[i].type = -1` at the C stride, scribbling 0xffffffff into the first prop model file (TT33 root Child) and crashing 2P respawn / pickups later (ABI/layout, section A1).** Full `## D461` entry at file tail | FIXED (maintainer-verified: ~10 min of 2P on Caves, Bunker and Complex, no crash). |
| D462 | **`vtxstore.c` sized its two record tables with the N64 stride 0x14 for a 24-byte `struct unk_09B7A0_struct_parent` on PC (`Vertex* unk00` widened); both are indexed at the C stride, so each overran its block (up to ~2 KB at 500 entries) into the next stage-bank allocation. Same shape as D461; found by the local-Qwen literal-stride allocation audit (ABI/layout, section A1).** Full `## D462` entry at file tail | FIXED (build + solo `-level_09` and 4P `GE_STARTMP=4` smoke runs exit clean; no crash; by-eye owed in solo). |
| D444 | **Front-end PC Options: clickable Previous/Next page controls; hover no longer overrides the wheel/page selection; stale All-unlocked comments refreshed.** — full `## D444` entry at file tail | FIXED (build-verified; human mouse test owed) |
| D455 | **Binary-first census of exe-static addresses truncated to 32 bits (D454 class) (2026-09-30).** — full `## D455` entry at file tail | FIXED/CLEAN (no further REAL site beyond D454) |
| D447 | **Pillarbox output mode (`Video.AspectMode` = Original) for the Original N64 preset; crop overscan no longer cancels Wide/Cinema.** — full `## D447` entry at file tail | FIXED (build + 3 captures verified; Wide/Cinema, 16:9 Ratio, mouse and 1964 comparison owed to a human) |
| D445 | **Crouched-rifle firing animation (`crouched_rifle_firing_animation_groupA`) never resolved on PC -> SIGSEGV in modelConstrainOrWrapAnimFrame when an MP player crouch-fires a rifle (2026-09-28).** — full `## D445` entry at file tail | FIXED — layout only (§D5 linker adjacency, D164 class): `crouched_rifle_firing_animation_group1` has no zero terminator and on N64 falls through into `...groupA` (source-order .data); GCC places groupA first, so groupA[0] kept the raw anim offset 0xB84. `#ifdef PORT` one array {right, left, terminator} + `groupA` alias (chr.c/chr.h); N64 resolution restored. |
| D446 | **Couch 2P watch-menu text (the MP pause page) differs by +-1-2 per channel between builds whose only difference is data layout (2026-09-29).** — full `## D446` entry at file tail | FIXED — port-only (fast3d): `GL_TEXTURE_MAX_ANISOTROPY` is per-texture-object state that `gfx_opengl_set_sampler_parameters` set only when mipmapped and never reset; a GL texture id freed by `gfx_texture_cache_delete_range` (unordered_map order = texture address order) and reused for a non-mipmapped glyph kept 4x. Now always written (level or 1). Render-only; sim never affected. |
| D456 | **Font-pointer / text-string-pointer slots retyped from `s32` to real pointers (ABI/layout exception; front.c, options.c, language.c, bondview*, mpmenu.c, propobj.c:3909 alias replaced by named field).** `frontPrintText`/`display_aligned_white_text_to_screen` font params, `copy_1st/2ndfonttable`, `setFontTables`, ~26 options.c locals, `g_LangBanks[]` + `langGet` result, `player.ptr_text_*_mp_award`. front.c 186->2, options.c 139->0 int-conversion warnings. FIXED (static + 1 launch; not gameplay-verified). |
| D453 | **memp allocator API made 64-bit clean (D441 step 0; ABI/layout exception, §A1).** `mempCheckMemflagTokens` took the arena start as `s32`, `mempSetBankStarts` carried it in an `s32 mempStart`, and `mempAllocPackedBytesInBank` returned a pointer as `u32`; all truncate/sign-extend an arena at or above 0x80000000. Widened to `uintptr_t`/`void*` under `#ifdef PORT` (N64 arm unchanged). mema.c NOT changed (structural, see entry). | 2026-09-30 | fix/abi-memp-widen | [H] for memp, mema deferred |
| D451 | **Automatic-fire gate at unsteady frame rates (D427 follow-up) and D173 residual.** — full `## D451` entry at file tail | FIXED (**RULE-2-SIGNOFF**, maintainer 2026-09-30: covered by the D427 grant; tick-crossing gate, `gunfire.c` `d427Gate`); headless A/B at FpsCap 45 measured; maintainer hold-fire test at 60/30 fps showed no perceptible difference (expected: identical at steady rates). D173 not the same class (see entry). |
Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS
@@ -11980,6 +11987,8 @@ Superseded history below (the original OPEN investigation, root cause not found
**2026-09-25 playtest (user, Windows, one ~46-min session Silo → Caverns, `GE_D322=1 GE_D204=1`, build `7baf4900`):** walking past the CCCP monument did not reproduce the black ground. Confirmed fixed.
**2026-09-30 (fix/d294-room-pool-cap): cap raised 2.0 -> 2.5, residual corner flicker fix landed (port-only, awaiting by-eye check).** The 3x 'regression' above was D336 (uninitialised head-anim placeholder fields), not pool size, so the cap is revisitable. `portRoomPoolScale()` (`port/src/video.c`): final cap 2.0 -> 2.5 (and the `GE_ROOMPOOL` clamp 2.0 -> 2.5); DD/Lod terms changed from `sqrtf` to `powf(x,0.66)` (4^0.66 = 2.497) because with sqrt the max-DD term is exactly 2.0 and a raised cap alone changes nothing; result is rounded UP to a quarter step because `boss.c` truncates `(s64)(scale*4)` (raw 2.497 truncated to 2.25 -> ma=506880, only 4% over Statue's 487 KB set). Statue at FovScale 150 / DD 400 now gets `ma` = 2.5 x 225280 = 563200 (was 450560), i.e. 1.16x the full 27-room set (measured sum of `D104 LOAD used` over 27 rooms = 498752). Worst case `-ma350` row = 875 KB, well inside the STAGE bank (~3.8 MB free per D336; `boss.c` still clamps to bank-left minus 128 KB). Identity at N64-faithful settings unchanged. Headless check (max FOV, DD 400, scripted walk, `GE_D294`/`GE_D104`): no `FAIL alloc` before or after -- the scripted walk cannot reproduce the fast-strafe churn, so the exhaustion counter is NOT falsified either way; maintainer to check Statue corners at max FOV by eye. No `src/game` change.
## D211 — PC FOV slider — `Video.FovScale` (M-83 QoL wave, port-only, `WIDESCREEN-FOV-PLAN.md` Phase 4).
**PC FOV slider — `Video.FovScale` (M-83 QoL wave, port-only, `WIDESCREEN-FOV-PLAN.md` Phase 4).** fast3d applies a multiplicative half-angle scale to every *perspective* projection matrix as it is loaded (`gfx_apply_fov_scale()` in `gfx_pc.cpp gfx_sp_matrix`, on the `G_MTX_PROJECTION | G_MTX_LOAD` path only — GE always reloads its view projection via `guPerspectiveF` at `fr.c:725`, so a relative `G_MTX_MUL` onto an already-scaled matrix cannot compound). Columns 0 and 1 are scaled by the same factor `tan(half_orig)/tan(half_adj)` → H and V FOV widen uniformly, aspect ratio exactly preserved (no stretch), and because the game rewrites fovy every frame for aim-zoom the scale composes with zoom for free. Ortho matrices (`P[3][3]==1`) are left untouched so HUD/menu 2D is unaffected. `Video.FovScale` = percent of the original vertical FOV, 50–150, **default 100 ⇒ `g_fov_scale == 1.0f` ⇒ guarded early-return, zero FP ops on the matrix ⇒ golden dumps byte-identical**. F10 overlay row "FOV scale %". Math checked offline (`s=1.25 → fovy 60°→75°`, aspect held at 1.3333).
@@ -12905,6 +12914,8 @@ Verified: build clean (MSYS2 MINGW64/ninja); `tools_pc/verify.sh bunker1` — PA
## D322 — Long-session audio degradation: full campaign on v0.3.0, audio progressively worsens (Silo → Caverns/Cradle: OST inaudible, SFX "come and go"); restart restores it (issue #87, user report, 2026-09-20)
**Status (2026-09-30): CLOSED.** The 8-voice SFX cap is the decomp constant (`src/music.c:76`), so it is faithful and ACCEPTED by the maintainer. The #87 long-session decay was not reproduced in the maintainer's 2026-09-29 playtest ("our bugs are fixed for the most part with audio"). Reopen only with a repro.
### Report (verbatim, issue #87 comment, v0.3.0 build)
> I just did a full run on 00 agent including Aztec and Egyptian in one shot :) It definitely felt better overall but the audio really started to struggle in the final levels again, but i made it through to the end. Interestingly, exiting the game and starting it back up again let the audio go back to normal. Feels like something about long gaming sessions throws off the audio after a while. [...] i think Silo was the first instance i noticed some audio sync issues popping up (possibly from me spamming the KF7). But it wasn't until the end around caverns / cradle that it became very noticeable (like to the point you couldn't hear the background OST and the audio felt like it came and went)
@@ -15577,6 +15588,8 @@ included; D281 keeps the shipped mitigation + disclosure, zero-saves root cause
## D408 — `Game.SkipIntro` skips the post-mission failure dossier: failed/aborted missions return straight to the menus with no REPORT screen (user report 2026-09-28, during v0.4.0 wrap-up playtest)
**Status (2026-09-30): FIXED (boot-only gate).** The SkipIntro block in `src/game/lv.c` (`#ifdef PORT`, port feature code) ran on every title-stage load, including the return from a mission, and its `menu_update = MENU_FILE_SELECT` overwrote the `MENU_MISSION_FAILED` route. It is now gated on `is_first_time_on_main_menu`. Verified: build, plus one boot with `SkipIntro=1` that logs `booting to file-select` exactly once. **Owed (maintainer):** fail or abort a mission with SkipIntro on and check the dossier shows. The hook also re-set `maybe_is_in_menu` on every return, so D216's audio-break half may be fixed too; listen for it.
**Symptom:** with `Game.SkipIntro = 1`, entering a solo mission and then either aborting (watch menu Z+A) or dying (KIA) returns the player straight to the front-end menus — the post-mission failure dossier is never shown. On a normal boot that screen is `MENU_MISSION_FAILED` (`front.c` `constructor_menu0C_missionfailed`: "REPORT / Mission status: KILLED IN ACTION" or "ABORTED", with the objective list and next/prev tabs to the mission-complete report). Initial suspicion was the user's save or `Game.AllUnlocked` (D257/D387); **user confirmed 2026-09-28 that it is a SkipIntro quirk** — save state and AllUnlocked are not involved.
**Traced so far (handoff site, mechanism not fully pinned):** the stage-end handoff lives in `menu_init()`'s `MENU_RUN_STAGE` case (`front.c:~8909`, called from `lv.c:1322` only once `g_CurrentStageToLoad == LEVELID_TITLE`): non-replay solo stages fall through to `frontChangeMenu(MENU_MISSION_FAILED, 1)` (Cuba → cast display, multi → MP options, ramrom replay → legal screen). Under SkipIntro that handoff evidently never runs on the fail/abort return path — the exact point where the boot shortcut's state diverges (which flag/menu value the skipped intro stages would normally have left behind) is not yet identified. The abort path itself (`options.c:798`: `set_missionstate(MISSION_STATE_0); bossRunTitleStage(); mission_failed_or_aborted = TRUE; deleteCurrentSelectedFolder()`) and the KIA path (`bondview2.c bondviewKillCurrentPlayer`, `g_isBondKIA = 1`) are game logic and behave per the decomp; nothing in `port/` intercepts `MENU_MISSION_FAILED`.
@@ -15911,3 +15924,107 @@ About 55-922 pixels differ, a few glyphs only, each by +-1-2 (mostly green). Eve
- the free-id order is still address-dependent.
The general fix would reset the whole object on reuse, or sort the ids that `delete_range` frees. Neither is needed for this symptom. **Confidence: [H]** for the cause and the fix (A/B reproduced, fixed, bisected to the one GL parameter).
## D454 — knife slash crash: keyframe pointer held in `u32` (ABI/layout exception, 2026-09-30)
**Symptom.** Windows: equipping the knife and slashing crashed at once (EXCEPTION 0xc0000005, fault `0x40169764`, `Rcx=0x40169720`). Found by the read-side 64-bit census (`ABI-CENSUS-READSIDE.md`), confirmed by the maintainer.
**Cause.** `gunTickHandState` (`src/game/gunfire.c`) picks the keyframe table with `var_a0_2 = D_80034CA4;` (or `D_80034E0C`) and passes it to `gunSample1PTransform(Weapon1PTransformKeyframe *, ...)`. `var_a0_2` was declared `u32`, and the tables are `u32[]` in the exe's `.data`, which on Windows sits above 4 GiB (image base `0x140000000`, ASLR off, CMakeLists.txt ~305). The store truncated the address. Linux is unaffected (image base 0x20000000). Class: §A1 (pointer-width misread from 32→64 widening).
**Trap.** The declaration exists once per region arm (US, JP, EU). The first fix (`75559906`) patched only the EU arm and the US build still crashed with the identical fault; the disassembly showed `mov ecx,ecx` before the call. All three arms are now patched (`8ba7dbb7`). Lesson: a decomp-style function can repeat a local per region `#if` arm; grep the declaration in every arm and check the disassembly (`mov reg32,reg32` before a pointer argument).
**Fix.** `#ifdef PORT` declares `Weapon1PTransformKeyframe *var_a0_2` and casts the two table assignments; the `#else` arm is the original. No logic change.
**Verified.** Maintainer: knife slash no longer crashes (Windows, this build). Disassembly: no `mov ecx,ecx` before any `gunSample1PTransform` call. **Confidence: [H].**
## D456 - font/text pointer slots held in s32 (ABI/layout exception, 2026-09-30)
**Source.** Read-side census (ABI-CENSUS-READSIDE.md): 286 of 618 -Wint-conversion warnings were two slot kinds, font pointers (`struct font *` / `struct fontchar *`) and `langGet()` strings, parked in `s32`. Implicit ptr->int narrowing has no cast, so a cast census never sees it. Pointers come from arena memory; any arena >= 2 GiB sign-extends to wild pointers, >= 4 GiB truncates.
**Change (all `#ifdef PORT`, `#else` = original; no logic change).**
- front.c: `frontPrintText`, `display_aligned_white_text_to_screen` (+ forward decl) font params -> `struct fontchar *` / `struct font *`; `constructor_menu16_nocontrollers` `text` -> `char *`; `frontGetPlayersFavoriteWeaponInHand` returns `u16 *` (was `int`, also in `port/include/pc_protos.h`: the unprototyped `int` decl made mpmenu.c:1596 truncate the pointer).
- bondview.c/.h, bondview_internal.h, bondview2.c: `copy_1stfonttable`/`copy_2ndfonttable`, `setFontTables` params.
- options.c: 26 locals in 9 functions (pFontFile/pFontChars/*2, text locals, sp78/sp7C in draw_current_hand_item_and_ammo). NOTE the `spNN` names are per-function: `sp4C` is text in draw_abort_cancel_confirm but a colour in draw_text_mission_status, and sp50/sp4C are coordinates in draw_text_q_watch_v201_beta; retype by use, not by name.
- language.c: `g_LangBanks` -> `u32 *[45]`; `langGet` result built as `(u8 *)textbank_ptr + off` (was a `u32` accumulator, truncating >= 4 GiB). bondview.h: `player.ptr_text_first/second_mp_award` -> `char *` (live struct, never serialised; adds 4 bytes of pointer width, sizeof is taken from the type in player.c).
- mpmenu.c: `h2` (only receives langGet) -> `char *`; cast at the favourite-weapon assignment.
- propobj.c:3909: `!((s32)obj->model & 0x400)` -> `!(prop->chr->chrflags & CHRFLAG_HIDDEN)`. Prop type is VIEWER/CHR there and `obj`/`chr` share a union slot (bondtypes.h:2342-2344), so identical behaviour today (two layout shifts cancelled), now by name.
**Left (not a type/cast swap, or out of scope).** mpmenu.c `q` (dual-use: team colour, then `langGet` string; needs a separate local); front.c `ptr_menu_videobuffer` and `modelSetAnimation(animation_table_ptrs1)`; language.c:364/375 `romCopy` p2 (ROM address, tag R); `sp70 = bondinvGetTextbyInvIndex` and the options.c:34 `OS_PHYSICAL_TO_K0` macro warning; JP/EU `dword_CODE_bss_jp...` font arrays (not built on ntsc-final).
**Verified.** Falsifier held: front.c 186->2 (census predicted ~172 from the two params), options.c 139->0. Full ntsc-final build clean. Disassembly: font params read as QWORD stack slots, no `mov r32,r32` before pointer args; `langGet` adds at 64-bit. One default-boot launch (GE_PCDUMP 200-1400:200, GE_QUITFRAME=1450) ran rc=0, no crash log; frame 200 (legal screen, display_aligned_white_text_to_screen + textRender) renders all text correctly. No baseline-build frame diff was taken (budget); menu/watch/MP text paths not exercised at runtime. Confidence: [H] types/ABI, [M] runtime.
## D453 — memp allocator API 64-bit clean (D441 step 0, ABI/layout exception, cross-tag §A1)
**Scope.** `src/memp.c`, `src/memp.h` only. AGENTS.md rule-2 narrow exception (pointer-width; no logic or control-flow change). Every edit is under `#ifdef PORT`; the N64 arm is byte-identical.
**Sites.**
- `mempCheckMemflagTokens(s32 poolAreaStart, s32 poolAreaSize)` -> `(uintptr_t poolAreaStart, s32 poolAreaSize)`; the pool-0 `start`/`end` are now assigned through explicit `(u8 *)` casts. (memp.h prototype matches.)
- `mempSetBankStarts`: `s32 mempStart` -> `uintptr_t` (was a sign-extending round trip of the `u8*` pool start); the per-bank `start`/`end` assignments cast `(u8 *)`.
- `mempAllocPackedBytesInBank` returned the allocation as `u32` (truncates the pointer); now returns `void *`. It has no callers in the tree today.
- Left `s32` on purpose: `poolAreaSize`, `bankstarts[]`, `mempLen`, `poolSizes` (sizes, not addresses).
**Falsifier (written before the run).** The change altered behaviour if, on the spawn run, the startup log differs beyond version/config lines, OR a `-level_09` frame-400 `GE_PCDUMP` differs structurally (different geometry/textures/lighting, not just the intro-camera phase), OR the build gains a warning in memp.c.
**Evidence.** `-Wpointer-to-int-cast -Wint-to-pointer-cast -Wint-conversion` over memp.c: 6 real warnings (lines 46, 47, 133, 137, 139, 290) -> 0 (5 header-only warnings unchanged). Build clean, no new warnings. `-level_09`, GE_QUITFRAME=420, before vs after: startup logs identical (modulo hash/config/timing lines); frame-400 dump same scene/textures/lighting but the intro camera is at a slightly different phase (3072x1728, dmean 12.1 - wall-clock camera motion, not a memp effect; no noise-floor rerun was in budget). **Confidence [M-H] on no behaviour change** (types only; all pool values < 2 GiB here so the value path is identical by construction).
**Left, not fixed.**
- mema.c: `memaspace.addr` is `s32`, `g_MemaHeapStart` is `s32`, `memaReset` stores `(uintptr_t)heapaddr` into it, `memaAlloc` returns `(void *)` of an `s32`/`u32` addr (mema.c:317, 572), and `end1/end2.addr = 0xffffffff` plus `curr->addr == -1` sentinels. Widening needs `memaspace` layout + sentinel changes, which is more than a type/cast edit. Fine for an arena < 2 GiB; wrong above. Needs its own step (and a decision on the sentinels).
- boss.c:268 `(u32)tlbmanageGetTlbAllocatedBlock() - (u32)start` and `start` type: size diff stays correct only if both ends share the same 4 GiB window; boss.c was out of scope. memp.c callers that store `mempAllocBytesInBank` results into `s32` (bg.c `ptr_bg_data`, bondview_r.c `resolution`) are other census rows.
## D455 — census: exe-static addresses narrowed to 32 bits (D454 class) (ABI/layout exception, 2026-09-30)
**Question.** D454 (knife slash) was an exe-`.data` address (Windows image base `0x140000000`) held in a `u32`. Are there other sites?
**Method (binary-first).** `objdump -d -M intel` of the built exe, then a forward data-flow scan (`scratch` script, not committed): from every `lea/mov r64,[rip+...]` address load (incl. `.refptr.<sym>`), follow the register and its `mov r64,r64` copies for N instructions (following unconditional `jmp`, dropping the register on any overwrite or call), and flag (a) `mov r32,r32` same-register zero-extend, (b) `mov r32b,r32a` narrowing copy, (c) `mov DWORD PTR [..],r32` store. N=14 and N=60.
**Falsifier (done before fixing).** Run on the PRE-fix binary (release/v0.4.1 @ 48908d18 has no D454 fix): the scan re-detects D454 as 4 hits in `gunTickHandState` (`.refptr.D_80034CA4` / `D_80034E0C`, all joining at `mov ecx,ecx` before `call gunSample1PTransform`; the load precedes a `jmp` join, so the scan has to follow jumps; a naive adjacent-instruction scan misses it). Whole-exe hit counts: 29 (N=14) / 39 (N=60). Rebuilt with the two D454 commits cherry-picked (temp branch, dropped): 25 / 35, 0 in `gunTickHandState`. The remaining hits were triaged one by one.
**Triage of the remaining hits.** None is a REAL exe-static narrowing.
- OFFSET/ROM (benign): `_*SegmentEnd/RomStart/Start` are `.set` absolute constants in `port/src/romassets_u.s` (ROM addresses, not exe addresses): image_bank.c:251, initanitable.c:281, textrelated.c:123/146, music.c:686/709, romdata.c:268/275/372. `(s32)&ANIM_DATA_*` (title.c, initactorpropstuff.c) are D34 offsets.
- VALUE loads misflagged by the scan (register reloaded via a path the scan does not model, or a float/int value read from the global): bg.c:3232, bondview2.c:6512, chrprop.c:511, front.c:3424 (2 hits), image.c:2375, options.c:2049/4140, player.c:632, propobj.c:5376, spectrum.c:725, frontoptions.c:867, libultra.c:1457, fr.c:458, chr.c D_80030988/D_80030994, gfx_run_dl.
- Comparison-only narrowing (both sides truncated identically): chraction.c:2320/2324, zlib.c:312-441. Benign.
- Already guarded under `PORT`: bg.c:4238/4392, bondview2.c:10449, initplayergaitobject.c:16, model.c:1538 (in the `#else` arm), ramromreplay.c:225, initactorpropstuff.c ANIM_PTR, title.c:568.
- Not in the PC build: tlb_manage.c:134 (excluded; not in CMakeLists.txt).
- UNSURE (not exe-static, stack address): indy_comms.c:35 `(u32)&size`; dead on PC (`indy_ready`).
- Sites in the four files owned by another agent (front.c, options.c, propobj.c, language.c): only the value-load false positives above; no real site found. `language.c` produced no hit.
**Limits.** Flow-limited (N instructions, no full CFG, no loads through memory: an address stored to a 64-bit slot then re-read as `u32` later is not seen); heap/stack pointers truncation is out of scope (heap is mapped low). Link-time narrowing in static initialisers cannot occur (the linker rejects a truncated reloc). **No code change**: the only real site was D454. **Confidence: [M]** that no other straight-line narrowing remains; the scan is a filter, not a proof.
## D451 -- D427 follow-up: the auto-fire gate still counted frames, so an unsteady clock (1<->2 ticks/frame) gave the wrong cadence; D173 is not the same class
**Mechanism (code reading + measurement).** `gunTickHandState` (`gunfire.c` ~2902) does `field_890 += g_ClockTimer` (tick accumulator) and `field_88C += 1` (frame counter) each call; both reset together on state entry. The automatic-fire gate was `88C % AutomaticFiringRate` (N64: one shot per `rate` frames, i.e. 2-3x`rate` ticks at 20-30 fps; the port at 1 tick/frame fired 2-3x too fast = D427). The D427 fix multiplied the modulus by `(g_ClockTimer >= 2 ? 1 : 2)` using the CURRENT frame's clock. That is exact only at a steady 1 or 2 ticks/frame; at mixed pacing the modulus flips between `rate` and `2*rate` frames against a counter that is not rescaled (the D427 "known limit").
**Measured before (AK47 `GE_STARTWEAPON=8`, `-level_09`, `GE_INPUTSCRIPT=1000:ZHOLD`, `Video.FpsCap=45`, clock mix 1:307 / 2:153 frames):** 79 shots, every gap = 8 ticks (N64 rule at 2 ticks/frame = 6); i.e. the flip produces a systematic slow-down vs the console cadence, not just jitter.
**Fix (`#ifdef PORT`, `d427Gate`):** fire when a multiple of `P = 2*rate` ticks of `field_890` was crossed this call (`t1/P != (t1-clk)/P`), plus the existing `88C == 0` first-frame fire. Stateless, exact on average at any pacing; identical to the D427 result at steady 60 (P=6 ticks) and steady 30.
**Measured after (same run):** 90 shots, gaps 5/6/7 ticks (mean ~6 = 2*rate, quantised by the 1-2 tick frames). Residual +-1 tick jitter is inherent to frame quantisation. The probe log tick sum counts only gate-evaluated calls, so the 5-gaps are partly measurement alignment; the mean is the signal.
**Rule-2 bucket:** (c) game-logic, but the same gate the existing D427 sign-off covers. Quoted wording (D427 entry): "Fix (rule-2, user go-ahead 2026-09-29; `gunfire.c`, `#ifdef PORT`): the automatic-fire gate `88C % AutomaticFiringRate` now uses `rate * (g_ClockTimer >= 2 ? 1 : 2)` ... Only that gate changes." This edit replaces that one expression, same purpose, same gate, N64 branch byte-identical. Maintainer to confirm that wording extends to the tick-based formulation; if not, revert the `gunfire.c` commit.
**Grant (RULE-2-SIGNOFF).** Requested as "accept under the existing D427 sign-off, or treat as a new request"; scope = replace the one expression in the automatic-fire gate (same gate, same intent, identical result at steady 60/30 fps, N64 arm untouched). Maintainer reply 2026-09-30: "Proceed with option 1" (accept under the existing D427 sign-off). Resulting diff: `9d8b31b5` (`d427Gate`, `#ifdef PORT`). Still scoped to this one gate; a different behaviour in `gunfire.c` needs its own request.
**D173 (code analysis only):** NOT the same timing class as far as the evidence shows. Its measured defect is a constant ~324-329 unit rendered-root offset independent of animation and frame rate on the intro puppet (M-172/M-173), with an asset/transform hypothesis; its bulk was fixed by D243's `sizeof(Model)` layout fix and the user reports a ~1/10 small residual offset. No per-tick accumulator feeding a single floor/threshold was found on that path. Whether the residual shares the D13 class (`chrUpdateAnim` -> `modelTickAnim` -> one `modelSetAnimFrame2WithChrStuff` per frame, D329) is untested; the test is a `GE_D318B_CLK=1` vs `=2` A/B of the puppet's rendered offset. Low confidence on that negative.
**Confidence:** mechanism high; fix behaviour medium-high (one 45 fps run, headless); D173 negative low.
**By-eye test (maintainer):** AK47/Uzi held fire with `FpsCap` 60, 45 (or any unsteady load) and 30; empties the magazine in about the same wall time as 1964/GEPD at every setting; no stutter-bursts.
**Probes left in tree:** none (env-gated `GE_D427`/`GE_D427_OLD` probes stripped).
## D461 — `p_itemcur` allocated with the N64 stride; overran into the first prop model file (ABI/layout exception, 2026-09-30)
**Symptom.** 2P (split-screen branch) on Caves: a crash at the first or second respawn, or on a body-armor pickup, in `modelUpdateRelationsQuick` / `modelInitRwData`. The fault address `0xffffffff7022cb4c` repeated across runs: the TT33 pistol model's root Child slot had its high half overwritten with 0xffffffff.
**Cause.** `inititemslots.c` allocated `equipmaxitems * 0x14` bytes; `InvItem` is 32 bytes on PC (verified by a compile-time `_Static_assert`). `bondinvReinitInv` (`bondinv.c`) indexes `p_itemcur[i]` at the C stride and sets `.type = -1`, so the block was overrun by about 12 bytes per item (roughly 360-420 bytes at equipmaxitems 30-35) into the next MEMPOOL_STAGE allocation, which is the first prop model file loaded (TT33 on Caves). Solo levels run the same code, so this was likely corrupting something there too, silently. Not a converter or PROMOTE bug (all 340 P* models in `pcmodels.bin` were walked clean).
**Fix.** `#ifdef PORT` allocates `equipmaxitems * sizeof(InvItem)`; the `#else` arm is the original. Commit `0ebda7e8`. No logic change.
**Process.** The agent that found the corrupted node (analysis, D416-D448 branch) could not find the writer; a local Qwen read-only census of stage-bank allocations named `p_itemcur`, and the lead verified the 0x14 literal, `sizeof(InvItem) == 32`, and the write loop before applying. **Lesson (generalisable):** an allocation that multiplies a count by an N64 literal stride for a struct that grew on PC is invisible to a pointer-cast warning census; grep every `* 0x..` stride next to `mempAlloc*` and compare with `sizeof`.
**Verified.** Maintainer: no crash in about 10 minutes of 2P on Caves, Bunker and Complex (respawns including rocket-launcher kills, body-armor pickups). **Confidence: [H].** Still worth auditing: the other `mempAlloc*` sites with literal strides.
## D462 — `vtxstore` tables allocated with the N64 stride (ABI/layout exception, 2026-09-30)
**Cause.** `vtxstore.c` set `tmp = 0x14` and allocated `count * tmp` for two tables of `struct unk_09B7A0_struct_parent` (`Vertex* unk00; s32 unk04; s32 unk08; s16 unk0C, unk0E, unk10, unk12`). On PC the pointer widens it to 24 bytes (0x20 once D457(d) turns `unk04` into a pointer; that part is NOT merged). The init loop indexes the tables at the C stride (`...[i].unk0E = -1`), so each block was overrun by about 4 bytes per entry (about 2 KB at the 500-entry table size) into the next MEMPOOL_STAGE allocation. Silent corruption, the same class as D461. The Vertex arrays (`* 0x10`) are fine (Vertex is pinned to 16 bytes).
**Fix.** `#ifdef PORT` uses `sizeof(struct unk_09B7A0_struct_parent)`; the `#else` arm is the original. Commit `fix/abi-vtxstore-alloc`. No logic change. D457(d) (unmerged, `fix/abi-bg-model-slots`) contained the same sizeof change plus the `unk04` pointer retype; this lands only the allocation part.
**Found by.** The local-Qwen read-only audit of every `mempAlloc*` size expression (top suspects #1 and #2); the lead verified the literal at `vtxstore.c`, the C-stride indexing, and the D457(d) diff.
**Verified.** Build clean; solo `-level_09` to frame 420 and a 4-player `GE_STARTMP=4 GE_MPVIRT=3` run to frame 500 both exit rc=0 with no crash log. **Confidence: [H]** on the overrun, [M] on any visible effect (none expected). Remaining audit suspects: `size_item_buffer` weapon model buffers (`initBondDATA.c`), `model.c` rwdata word count vs converted data, `image_bank.c` +0x1000 slack: all UNKNOWN, need a converted-size check.