From 8e5d4bcd5ad5e930178adc0cc4a87bf19e467e61 Mon Sep 17 00:00:00 2001 From: dolent Date: Mon, 28 Sep 2026 20:30:44 -0700 Subject: [PATCH] fix: D420 -- crouched-rifle firing animation never resolved on PC (linker adjacency) -> SIGSEGV in MP matches crouched_rifle_firing_animation_group1 (chr.c) has one entry and no terminator; on the N64, .data is in source order, so crouched_rifle_firing_animation_groupA follows it and the single initResolveAnimGroupTable pass over group1 also resolves groupA (len 2). GCC places groupA below group1, so groupA[0] kept the raw anim offset 0xB84 (PTR_ANIM_fire_kneel_left_leg). playerTick (bondview2.c:11021) passes it to modelSetAnimation for a player's crouched rifle fire (firing_animation_groups[1][5]) -> SIGSEGV in modelConstrainOrWrapAnimFrame (fault 0xb88). Crouching rifle guards also saw len 1 instead of 2. #ifdef PORT: one array {right_leg, left_leg, terminator} (the N64 layout), groupA = &group1[1] (chr.h). ABI/layout exception, porting-notes D5 class (D164 precedent). Verified: 4-seat repro (M16, Complex, frame 5030) runs 5 min x3 SH-identical, no crash; gdb scan of all firing_animation_groups entries: 1 bad pointer before, 0 after; P1 corpus (1324 recordings) verdicts identical; solo/couch frames byte-identical; netplay 2P/4P identical. Co-Authored-By: Claude Opus 5.5 (cherry picked from commit 8595a06edd40ca22ac9ba4955b13a9ce61064475) --- src/game/chr.c | 20 ++++++++++++++++++++ src/game/chr.h | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/src/game/chr.c b/src/game/chr.c index f0c6f819..43a37a10 100644 --- a/src/game/chr.c +++ b/src/game/chr.c @@ -884,6 +884,25 @@ struct anim_group_info *ptr_doubles_firing_animation_groups[] = { &ptr_doubles_firing_animation_group1 }; +#ifdef PORT +/* D420 (ABI/layout, porting-notes §D5 linker adjacency): on the N64, + * crouched_rifle_firing_animation_group1 has NO terminator and the linker + * places crouched_rifle_firing_animation_groupA right after it, so the one + * resolver pass over group1 (initResolveAnimGroupTable, via + * ptr_crouched_rifle_firing_animation_groups) walks into groupA and turns its + * anim offset into a pointer too; the table len is 2. GCC does not keep the + * two arrays adjacent (x86-64: groupA BELOW group1), so groupA[0] kept the raw + * offset 0xB84 (PTR_ANIM_fire_kneel_left_leg) and playerTick handed it to + * modelSetAnimation as a ModelAnimation* (SIGSEGV, crouched rifle fire), and + * crouching rifle guards saw len 1 instead of 2. One array restores the N64 + * layout; groupA is its second entry (chr.h). Same data, same order. */ +struct weapon_firing_animation_table crouched_rifle_firing_animation_group1[] = { + { PTR_ANIM_fire_kneel_right_leg, 27.0, 0, 0, 0, -1.0, 35.0, 75.0, -1.0, -1.0, 31.0, 75.0, 0.87266463, -0.69813174, 0.90757126, -0.69813174, 1.5, 1.5 }, + /* = crouched_rifle_firing_animation_groupA[0] */ + { PTR_ANIM_fire_kneel_left_leg, 24.0, 0, 0, 0, -1.0, 46.0, 98.0, -1.0, -1.0, 41.0, 98.0, 0.87266463, -0.52359879, 1.134464, -0.69813174, 1.6, 1.6 }, + {0, 0.0, 0, 0, 0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0} +}; +#else struct weapon_firing_animation_table crouched_rifle_firing_animation_group1[] = { { PTR_ANIM_fire_kneel_right_leg, 27.0, 0, 0, 0, -1.0, 35.0, 75.0, -1.0, -1.0, 31.0, 75.0, 0.87266463, -0.69813174, 0.90757126, -0.69813174, 1.5, 1.5 }, }; @@ -892,6 +911,7 @@ struct weapon_firing_animation_table crouched_rifle_firing_animation_groupA[] = { PTR_ANIM_fire_kneel_left_leg, 24.0, 0, 0, 0, -1.0, 46.0, 98.0, -1.0, -1.0, 41.0, 98.0, 0.87266463, -0.52359879, 1.134464, -0.69813174, 1.6, 1.6 }, {0, 0.0, 0, 0, 0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0} }; +#endif struct anim_group_info ptr_crouched_rifle_firing_animation_group1 = { &crouched_rifle_firing_animation_group1, -1 }; diff --git a/src/game/chr.h b/src/game/chr.h index 03abfe61..2617ff1e 100644 --- a/src/game/chr.h +++ b/src/game/chr.h @@ -181,7 +181,12 @@ extern struct weapon_firing_animation_table doubles_firing_animation_group1[]; extern struct weapon_firing_animation_table doubles_firing_animation_group2[]; extern struct weapon_firing_animation_table doubles_firing_animation_group3[]; extern struct weapon_firing_animation_table crouched_rifle_firing_animation_group1[]; +#ifdef PORT +/* D420: groupA is the second entry of group1 (N64 adjacency, see chr.c). */ +#define crouched_rifle_firing_animation_groupA (&crouched_rifle_firing_animation_group1[1]) +#else extern struct weapon_firing_animation_table crouched_rifle_firing_animation_groupA[]; +#endif extern struct weapon_firing_animation_table crouched_rifle_firing_animation_group2[]; extern struct weapon_firing_animation_table crouched_rifle_firing_animation_group3[]; extern struct weapon_firing_animation_table crouched_pistol_firing_animation_group1[];