fix(audio): D208 — player fire sound goes permanently silent (field_A48 ptr width)

Root cause of the "doesn't recover after the alarm stops" half of the user's
D207 report (distinct bug from D207's voice-pool question).

struct field bondview.h:200 `s32 field_A48` is used as a SECOND
ALSoundState* sound-handle slot for the player's double-buffered per-hand
fire sound (gunfire.c:3186-3200). sndPlaySfx stores the new state through
`&field_A48` as an 8-byte `pendingState->link.next = nextState`; as s32 that
write tears into the adjacent field_A4C (the auto-fire cadence timer, rewritten
every frame). After enough double-buffered fire cycles field_A48 holds a value
that is neither 0 nor a valid pointer, so the `else if (field_A48 == 0)`
re-arm gate never fires again and no further player gunshot is issued.

Evidence: user Bunker audiotrace.log — silenced-PP7 idx-46 sndPlaySfx requests
stop dead at log line 18930/25086 while all other SFX continue; the player-gun
slot 0x70076630 is then caught holding a torn value 0x000001db00000000
(field_A48=0 + field_A4C=0x1db read as one word).

Fix (D3x ABI/layout, #ifdef PORT): declare field_A48 as ALSoundState* under
PORT, s32 verbatim under #else. hands[] is runtime-only state (g_CurrentPlayer),
never ROM-serialized, and all downstream fields are accessed by member name —
widening 4 bytes is layout-safe. The (struct ALSoundState *) casts at the use
sites become no-ops and stay for a minimal diff.

Build clean (gunfire.c + full link). Runtime verification owed: replay the
Bunker firefight, confirm idx-46 requests continue for its full duration.

Claude-Session: https://claude.ai/code/session_01U7gyYC3pcatXHhD1kPCgsR
This commit is contained in:
James Dansereau committed 2026-09-08 08:06:28 -04:00
1 parent 0f7fbcecdf
commit 76910cbf19
3 files changed
+12

No files matched your search

+10
View File
@@ -197,7 +197,17 @@ struct hand
f32 field_A3C;
f32 field_A40;
ALSoundState * audioHandle;
#ifdef PORT
/* D208 (D3x ABI/layout): field_A48 is used as a second ALSoundState* sound-
* handle slot (gunfire.c:3186-3200 pass &field_A48 to sndPlaySfx, which
* stores an 8-byte pointer through it as pendingState->link.next). As s32
* that 8-byte write tears into field_A4C and the player fire sound goes
* permanently silent mid-firefight. hands[] is runtime-only (never ROM-
* serialized), so widening to pointer width is layout-safe. */
ALSoundState * field_A48;
#else
s32 field_A48;
#endif
s32 field_A4C;
s32 field_A50;
BeamRecord weapon_beam;