PC port: Phase 1.5 done, first frames render (D38-D42)

- D38: implicit function declarations truncated pointer returns on x86-64;
  added port/include/pc_protos.h prototype shim (398 decls, C-only, anchored
  in port/shim/PR/ucode.h) + pc_netorder.c for ntohl/ntohs; build warning-clean.
- D39: Globalimagetable rebasing - per-symbol offsets + GIMG_OFF() in
  image_bank.c (== N64 (u32)&sym incl. the 0x02000000 base); segment end
  markers extended to the true 0x13F8 size (CSV entry truncated by 0x930).
- D40: g_ModelHitEntries pool sized for the 40-byte PC ModelHitEntry stride
  (was a 12KB .bss overflow clobbering is_ramrom_flag -> demo-replay SIGSEGV).
- D41: release the GL context from the host main thread after videoInit so
  the game scheduler thread can bind it (WGL single-thread currency; this
  SDL2 build's MakeCurrent return value is an inverted ABI artifact).
- D42: rsp.c g_gfxTaskSettingsList XOR toggle -> explicit toggle on x86-64.

Boot now renders real frames ([NOTE] frame N rendered in ... us). Next
blocker D43 (model-file loading ABI mismatch) documented in PCPortResearch.md
section F/H; HANDOFF.md rewritten for it.
This commit is contained in:
James Dansereau committed 2026-08-22 04:07:07 -04:00
1 parent b3000825f2
commit 98412c281c
33 files changed
+2136 -198

No files matched your search

+2 -2
View File
@@ -47,7 +47,7 @@ Run `/linkcheck` for this sweep. Record new findings in `docs/PCPortResearch.md`
- **Phase 0 (scaffolding):** done, committed.
- **Compile+link milestone:** all ~235 TUs compile and `ge007.x86_64.exe` links clean (`ninja ge007 -k 0`, 236/236). Findings D7–D17 in `docs/PCPortResearch.md` §11; asset stubs in `port/src/assetstubs.c`, fast3d no-ops in `port/src/gfxstub.c` (delete when real fast3d lands).
- **Phase 1 (boot to window):** done — ROM loads/validates/maps at cart base 0x10000000 (`romdata.c`), SDL2 window + GL clear loop (`video.c`), absolute asset symbols from `scripts/gen_romassets.py` → `port/src/romassets_<r>.s` (findings D18–D23).
- **Phase 1.5 (boot to first frame):** IN PROGRESS — mainThread runs through `bossInitMainthreadData()`; D31 (langInit file-load SIGSEGV) fixed via real-zlib decompression (`port/src/rzdecomp.c`, replaces the excluded `src/game/decompress.c`+`zlib.c`). Current blocker is **D32** (ROM-struct pointer-width): Part A (u32 layout in `ModelAnimation`) done; Part B **root-caused (D33)** — the ROM stores structured fields big-endian (per-field load-time fixup needed) + an x86-64 loop-stride bug in `expand_ani_table_entries`; fix designed, implementation next. ROM dump verified against the No-Intro good dump (byte-identical, header CRCs re-checked — `tools_pc/romverify.c`). Recurring sub-task: ABI/layout fixes per the D32 procedure (§H) as more asset loading is reached.
- **Phase 2 (rendering):** bring in PD fast3d (replaces `port/src/gfxstub.c`), verify custom CC/RM modes against `gmain.s`, port the scheduler; replace asset stubs with ROM-backed data (D16). Assumes ABI-clean data loading from Phase 1.5.
- **Phase 1.5 (boot to first frame):** DONE — boot chain complete AND real frames render (`[NOTE] frame N rendered in … us`). D34–D42 closed the blockers on top of D31–D33: ANIM_DATA lvalues, music seq-table ABI+endianness, music-heap sizing, libaudio bank-tree re-layout, implicit-declaration prototype shim (D38), Globalimagetable rebasing + true segment size (D39), ModelHitEntry pool sizing (D40), cross-thread GL context release (D41), rsp task-settings toggle (D42). ROM dump verified against the No-Intro good dump (byte-identical, header CRCs re-checked — `tools_pc/romverify.c`).
- **Phase 2 (rendering):** IN PROGRESS — fast3d integrated + first frames render; current blocker is **D43** (model-file loading ABI mismatch: N64-layout ROM model files read as 8-byte-pointer PC structs → D37-style re-layout in `port/src/romdata.c`). Recurring sub-task: ABI/layout fixes per the D32 procedure (§H) as more asset types are first touched.
- **Phase 3 (audio + input):** libaudio → SDL; decide the ASP strategy (C2: `aspMain*` dummies in `ucode.c`).
- **Phase 4 (saves + polish):** file-backed EEPROM; PFS/motor already no-op stubs.
+6 -2
View File
@@ -391,9 +391,13 @@ list(PREPEND SRC_LIBULTRARE_LIBC "${CMAKE_SOURCE_DIR}")
# scripts/gen_romassets.py), backed by the ROM image that romdata.c maps at
# 0x10000000. The per-model data itself (chr/*/Model.c etc.) is gitignored
# ROM-derived and absent from this checkout. See docs/PCPortResearch.md §11.
# D34 (docs/PCPortResearch.md): animationtable_{data,entries}.c are NOT
# compiled on PC. Their arrays exist only so that &ANIM_DATA_x evaluates to
# the record's offset within the animation_data segment (N64 links it at VMA
# 0); on x86-64 assets/animationtable_data.h redefines them as lvalues at
# g_pc_animdata_base + offset instead. The data itself comes from ROM via
# romCopy, exactly like every other asset.
set(SRC_ASSETS
assets/animationtable_data.c
assets/animationtable_entries.c
assets/font_chardatae.c
assets/font_chardataj.c
assets/font_dl.c
+189
View File
@@ -197,6 +197,194 @@
// These are compile time extern constants (so not "really" const), but used the same
// as the defines above, which is to index into ptr_animation_table.
#if defined(__x86_64__)
/* D34 (docs/PCPortResearch.md): on N64 this segment links at VMA 0
* (ge007.ld), so &ANIM_DATA_x is the record's offset within the
* animation_data segment and game code re-bases it with
* `(s32)&ANIM_DATA_x + ptr_animation_table`. A PE image cannot be placed
* at a low address, so each ANIM_DATA_x is instead an lvalue at
* g_pc_animdata_base + offset (g_pc_animdata_base is the 4GiB-aligned PE
* image base, set by romdataInit); (s32)&ANIM_DATA_x then equals
* PTR_ANIM_x exactly. Address-only: the record data itself is romCopy'd
* from ROM into ptr_animation_table, and assets/animationtable_data.c is
* not compiled into the PC image. Never dereference these lvalues. */
extern unsigned char *g_pc_animdata_base;
#define ANIM_DATA_empty (g_pc_animdata_base[0x0u])
#define ANIM_DATA_idle (g_pc_animdata_base[0x1cu])
#define ANIM_DATA_fire_standing (g_pc_animdata_base[0x144u])
#define ANIM_DATA_fire_standing_fast (g_pc_animdata_base[0x214u])
#define ANIM_DATA_fire_hip (g_pc_animdata_base[0x318u])
#define ANIM_DATA_fire_shoulder_left (g_pc_animdata_base[0x3c4u])
#define ANIM_DATA_fire_turn_right1 (g_pc_animdata_base[0x610u])
#define ANIM_DATA_fire_turn_right2 (g_pc_animdata_base[0x814u])
#define ANIM_DATA_fire_kneel_right_leg (g_pc_animdata_base[0x990u])
#define ANIM_DATA_fire_kneel_left_leg (g_pc_animdata_base[0xb84u])
#define ANIM_DATA_fire_kneel_left (g_pc_animdata_base[0xdb4u])
#define ANIM_DATA_fire_kneel_right (g_pc_animdata_base[0x1028u])
#define ANIM_DATA_fire_roll_left (g_pc_animdata_base[0x1334u])
#define ANIM_DATA_fire_roll_right1 (g_pc_animdata_base[0x1578u])
#define ANIM_DATA_fire_roll_left_fast (g_pc_animdata_base[0x17b4u])
#define ANIM_DATA_hit_left_shoulder (g_pc_animdata_base[0x186cu])
#define ANIM_DATA_hit_right_shoulder (g_pc_animdata_base[0x1984u])
#define ANIM_DATA_hit_left_arm (g_pc_animdata_base[0x1a6cu])
#define ANIM_DATA_hit_right_arm (g_pc_animdata_base[0x1b54u])
#define ANIM_DATA_hit_left_hand (g_pc_animdata_base[0x1c9cu])
#define ANIM_DATA_hit_right_hand (g_pc_animdata_base[0x1e40u])
#define ANIM_DATA_hit_left_leg (g_pc_animdata_base[0x1f84u])
#define ANIM_DATA_hit_right_leg (g_pc_animdata_base[0x2134u])
#define ANIM_DATA_death_genitalia (g_pc_animdata_base[0x282cu])
#define ANIM_DATA_hit_neck (g_pc_animdata_base[0x299cu])
#define ANIM_DATA_death_neck (g_pc_animdata_base[0x2e64u])
#define ANIM_DATA_death_stagger_back_to_wall (g_pc_animdata_base[0x2f94u])
#define ANIM_DATA_death_forward_face_down (g_pc_animdata_base[0x30b8u])
#define ANIM_DATA_death_forward_spin_face_up (g_pc_animdata_base[0x31dcu])
#define ANIM_DATA_death_backward_fall_face_up1 (g_pc_animdata_base[0x32c8u])
#define ANIM_DATA_death_backward_spin_face_down_right (g_pc_animdata_base[0x33acu])
#define ANIM_DATA_death_backward_spin_face_up_right (g_pc_animdata_base[0x34d4u])
#define ANIM_DATA_death_backward_spin_face_down_left (g_pc_animdata_base[0x35c8u])
#define ANIM_DATA_death_backward_spin_face_up_left (g_pc_animdata_base[0x36d8u])
#define ANIM_DATA_death_forward_face_down_hard (g_pc_animdata_base[0x384cu])
#define ANIM_DATA_death_forward_face_down_soft (g_pc_animdata_base[0x39c0u])
#define ANIM_DATA_death_fetal_position_right (g_pc_animdata_base[0x3af0u])
#define ANIM_DATA_death_fetal_position_left (g_pc_animdata_base[0x3c10u])
#define ANIM_DATA_death_backward_fall_face_up2 (g_pc_animdata_base[0x3d04u])
#define ANIM_DATA_side_step_left (g_pc_animdata_base[0x3d9cu])
#define ANIM_DATA_fire_roll_right2 (g_pc_animdata_base[0x3fa0u])
#define ANIM_DATA_walking (g_pc_animdata_base[0x4018u])
#define ANIM_DATA_sprinting (g_pc_animdata_base[0x4070u])
#define ANIM_DATA_running (g_pc_animdata_base[0x40d4u])
#define ANIM_DATA_bond_eye_walk (g_pc_animdata_base[0x4144u])
#define ANIM_DATA_bond_eye_fire (g_pc_animdata_base[0x4298u])
#define ANIM_DATA_bond_watch (g_pc_animdata_base[0x42c8u])
#define ANIM_DATA_surrendering_armed (g_pc_animdata_base[0x4384u])
#define ANIM_DATA_surrendering_armed_drop_weapon (g_pc_animdata_base[0x4504u])
#define ANIM_DATA_fire_walking (g_pc_animdata_base[0x4574u])
#define ANIM_DATA_fire_running (g_pc_animdata_base[0x45ccu])
#define ANIM_DATA_fire_jump_to_side_left (g_pc_animdata_base[0x47bcu])
#define ANIM_DATA_fire_jump_to_side_right (g_pc_animdata_base[0x4a40u])
#define ANIM_DATA_hit_butt_long (g_pc_animdata_base[0x4ce0u])
#define ANIM_DATA_hit_butt_short (g_pc_animdata_base[0x4f14u])
#define ANIM_DATA_death_head (g_pc_animdata_base[0x51c4u])
#define ANIM_DATA_death_left_leg (g_pc_animdata_base[0x540cu])
#define ANIM_DATA_slide_right (g_pc_animdata_base[0x54a0u])
#define ANIM_DATA_slide_left (g_pc_animdata_base[0x5554u])
#define ANIM_DATA_jump_backwards (g_pc_animdata_base[0x5684u])
#define ANIM_DATA_extending_left_hand (g_pc_animdata_base[0x5744u])
#define ANIM_DATA_fire_throw_grenade (g_pc_animdata_base[0x5964u])
#define ANIM_DATA_spotting_bond (g_pc_animdata_base[0x5d10u])
#define ANIM_DATA_look_around (g_pc_animdata_base[0x5ef0u])
#define ANIM_DATA_fire_standing_one_handed_weapon (g_pc_animdata_base[0x60d4u])
#define ANIM_DATA_fire_standing_draw_one_handed_weapon_fast (g_pc_animdata_base[0x6254u])
#define ANIM_DATA_fire_standing_draw_one_handed_weapon_slow (g_pc_animdata_base[0x637cu])
#define ANIM_DATA_fire_hip_one_handed_weapon_fast (g_pc_animdata_base[0x6484u])
#define ANIM_DATA_fire_hip_one_handed_weapon_slow (g_pc_animdata_base[0x6554u])
#define ANIM_DATA_fire_hip_forward_one_handed_weapon (g_pc_animdata_base[0x6644u])
#define ANIM_DATA_fire_standing_right_one_handed_weapon (g_pc_animdata_base[0x6738u])
#define ANIM_DATA_fire_step_right_one_handed_weapon (g_pc_animdata_base[0x6808u])
#define ANIM_DATA_fire_standing_left_one_handed_weapon_slow (g_pc_animdata_base[0x694cu])
#define ANIM_DATA_fire_standing_left_one_handed_weapon_fast (g_pc_animdata_base[0x6a18u])
#define ANIM_DATA_fire_kneel_forward_one_handed_weapon_slow (g_pc_animdata_base[0x6c18u])
#define ANIM_DATA_fire_kneel_forward_one_handed_weapon_fast (g_pc_animdata_base[0x6d50u])
#define ANIM_DATA_fire_kneel_right_one_handed_weapon_slow (g_pc_animdata_base[0x6f08u])
#define ANIM_DATA_fire_kneel_right_one_handed_weapon_fast (g_pc_animdata_base[0x700cu])
#define ANIM_DATA_fire_kneel_left_one_handed_weapon_slow (g_pc_animdata_base[0x71d0u])
#define ANIM_DATA_fire_kneel_left_one_handed_weapon_fast (g_pc_animdata_base[0x7304u])
#define ANIM_DATA_fire_kneel_left_one_handed_weapon (g_pc_animdata_base[0x7430u])
#define ANIM_DATA_aim_walking_one_handed_weapon (g_pc_animdata_base[0x74a4u])
#define ANIM_DATA_aim_walking_left_one_handed_weapon (g_pc_animdata_base[0x7514u])
#define ANIM_DATA_aim_walking_right_one_handed_weapon (g_pc_animdata_base[0x7588u])
#define ANIM_DATA_aim_running_one_handed_weapon (g_pc_animdata_base[0x75ecu])
#define ANIM_DATA_aim_running_right_one_handed_weapon (g_pc_animdata_base[0x7650u])
#define ANIM_DATA_aim_running_left_one_handed_weapon (g_pc_animdata_base[0x76b8u])
#define ANIM_DATA_aim_sprinting_one_handed_weapon (g_pc_animdata_base[0x7714u])
#define ANIM_DATA_running_one_handed_weapon (g_pc_animdata_base[0x777cu])
#define ANIM_DATA_sprinting_one_handed_weapon (g_pc_animdata_base[0x77d4u])
#define ANIM_DATA_draw_one_handed_weapon_and_look_around (g_pc_animdata_base[0x78c8u])
#define ANIM_DATA_draw_one_handed_weapon_and_stand_up (g_pc_animdata_base[0x7aa8u])
#define ANIM_DATA_aim_one_handed_weapon_left_right (g_pc_animdata_base[0x7c4cu])
#define ANIM_DATA_cock_one_handed_weapon_and_turn_around (g_pc_animdata_base[0x7d04u])
#define ANIM_DATA_holster_one_handed_weapon_and_cross_arms (g_pc_animdata_base[0x7dd8u])
#define ANIM_DATA_cock_one_handed_weapon_turn_around_and_stand_up (g_pc_animdata_base[0x7f0cu])
#define ANIM_DATA_draw_one_handed_weapon_and_turn_around (g_pc_animdata_base[0x7fb4u])
#define ANIM_DATA_step_forward_and_hold_one_handed_weapon (g_pc_animdata_base[0x8080u])
#define ANIM_DATA_holster_one_handed_weapon_and_adjust_suit (g_pc_animdata_base[0x8164u])
#define ANIM_DATA_idle_unarmed (g_pc_animdata_base[0x8194u])
#define ANIM_DATA_walking_unarmed (g_pc_animdata_base[0x8204u])
#define ANIM_DATA_fire_walking_dual_wield (g_pc_animdata_base[0x8274u])
#define ANIM_DATA_fire_walking_dual_wield_hands_crossed (g_pc_animdata_base[0x82e0u])
#define ANIM_DATA_fire_running_dual_wield (g_pc_animdata_base[0x8340u])
#define ANIM_DATA_fire_running_dual_wield_hands_crossed (g_pc_animdata_base[0x83a4u])
#define ANIM_DATA_fire_sprinting_dual_wield (g_pc_animdata_base[0x8404u])
#define ANIM_DATA_fire_sprinting_dual_wield_hands_crossed (g_pc_animdata_base[0x845cu])
#define ANIM_DATA_walking_female (g_pc_animdata_base[0x84c4u])
#define ANIM_DATA_running_female (g_pc_animdata_base[0x8520u])
#define ANIM_DATA_fire_kneel_dual_wield (g_pc_animdata_base[0x8698u])
#define ANIM_DATA_fire_kneel_dual_wield_left (g_pc_animdata_base[0x8800u])
#define ANIM_DATA_fire_kneel_dual_wield_right (g_pc_animdata_base[0x8978u])
#define ANIM_DATA_fire_kneel_dual_wield_hands_crossed (g_pc_animdata_base[0x8aacu])
#define ANIM_DATA_fire_kneel_dual_wield_hands_crossed_left (g_pc_animdata_base[0x8bf0u])
#define ANIM_DATA_fire_kneel_dual_wield_hands_crossed_right (g_pc_animdata_base[0x8d28u])
#define ANIM_DATA_fire_standing_dual_wield (g_pc_animdata_base[0x8e1cu])
#define ANIM_DATA_fire_standing_dual_wield_left (g_pc_animdata_base[0x8f2cu])
#define ANIM_DATA_fire_standing_dual_wield_right (g_pc_animdata_base[0x9084u])
#define ANIM_DATA_fire_standing_dual_wield_hands_crossed_left (g_pc_animdata_base[0x9194u])
#define ANIM_DATA_fire_standing_dual_wield_hands_crossed_right (g_pc_animdata_base[0x92ecu])
#define ANIM_DATA_fire_standing_aiming_down_sights (g_pc_animdata_base[0x9444u])
#define ANIM_DATA_fire_kneel_aiming_down_sights (g_pc_animdata_base[0x95fcu])
#define ANIM_DATA_hit_taser (g_pc_animdata_base[0x97bcu])
#define ANIM_DATA_death_explosion_forward (g_pc_animdata_base[0x98c8u])
#define ANIM_DATA_death_explosion_left1 (g_pc_animdata_base[0x9a2cu])
#define ANIM_DATA_death_explosion_back_left (g_pc_animdata_base[0x9b48u])
#define ANIM_DATA_death_explosion_back1 (g_pc_animdata_base[0x9c4cu])
#define ANIM_DATA_death_explosion_right (g_pc_animdata_base[0x9d5cu])
#define ANIM_DATA_death_explosion_forward_right1 (g_pc_animdata_base[0x9e44u])
#define ANIM_DATA_death_explosion_back2 (g_pc_animdata_base[0x9f48u])
#define ANIM_DATA_death_explosion_forward_roll (g_pc_animdata_base[0xa094u])
#define ANIM_DATA_death_explosion_forward_face_down (g_pc_animdata_base[0xa1b8u])
#define ANIM_DATA_death_explosion_left2 (g_pc_animdata_base[0xa2f8u])
#define ANIM_DATA_death_explosion_forward_right2 (g_pc_animdata_base[0xa424u])
#define ANIM_DATA_death_explosion_forward_right2_alt (g_pc_animdata_base[0xa538u])
#define ANIM_DATA_death_explosion_forward_right3 (g_pc_animdata_base[0xa650u])
#define ANIM_DATA_running_hands_up (g_pc_animdata_base[0xa6b0u])
#define ANIM_DATA_sprinting_hands_up (g_pc_animdata_base[0xa704u])
#define ANIM_DATA_aim_and_blow_one_handed_weapon (g_pc_animdata_base[0xa8bcu])
#define ANIM_DATA_aim_one_handed_weapon_left (g_pc_animdata_base[0xa94cu])
#define ANIM_DATA_aim_one_handed_weapon_right (g_pc_animdata_base[0xa9dcu])
#define ANIM_DATA_conversation (g_pc_animdata_base[0xacacu])
#define ANIM_DATA_drop_weapon_and_show_fight_stance (g_pc_animdata_base[0xb174u])
#define ANIM_DATA_yawning (g_pc_animdata_base[0xb2acu])
#define ANIM_DATA_swatting_flies (g_pc_animdata_base[0xb528u])
#define ANIM_DATA_scratching_leg (g_pc_animdata_base[0xb6b0u])
#define ANIM_DATA_scratching_butt (g_pc_animdata_base[0xb7c8u])
#define ANIM_DATA_adjusting_crotch (g_pc_animdata_base[0xb854u])
#define ANIM_DATA_sneeze (g_pc_animdata_base[0xb9a8u])
#define ANIM_DATA_conversation_cleaned (g_pc_animdata_base[0xbc40u])
#define ANIM_DATA_conversation_listener (g_pc_animdata_base[0xbf80u])
#define ANIM_DATA_startled_and_looking_around (g_pc_animdata_base[0xc224u])
#define ANIM_DATA_laughing_in_disbelief (g_pc_animdata_base[0xc410u])
#define ANIM_DATA_surrendering_unarmed (g_pc_animdata_base[0xc544u])
#define ANIM_DATA_coughing_standing (g_pc_animdata_base[0xc838u])
#define ANIM_DATA_coughing_kneel1 (g_pc_animdata_base[0xcb78u])
#define ANIM_DATA_coughing_kneel2 (g_pc_animdata_base[0xce6cu])
#define ANIM_DATA_standing_up (g_pc_animdata_base[0xd0a8u])
#define ANIM_DATA_dancing (g_pc_animdata_base[0xd348u])
#define ANIM_DATA_dancing_one_handed_weapon (g_pc_animdata_base[0xd54cu])
#define ANIM_DATA_keyboard_right_hand1 (g_pc_animdata_base[0xd5e4u])
#define ANIM_DATA_keyboard_right_hand2 (g_pc_animdata_base[0xd668u])
#define ANIM_DATA_keyboard_left_hand (g_pc_animdata_base[0xd6f8u])
#define ANIM_DATA_keyboard_right_hand_tapping (g_pc_animdata_base[0xd728u])
#define ANIM_DATA_bond_eye_fire_alt (g_pc_animdata_base[0xd89cu])
#define ANIM_DATA_dam_jump (g_pc_animdata_base[0xdbe4u])
#define ANIM_DATA_surface_vent_jump (g_pc_animdata_base[0xdd20u])
#define ANIM_DATA_cradle_jump (g_pc_animdata_base[0xe05cu])
#define ANIM_DATA_cradle_fall (g_pc_animdata_base[0xe08cu])
#define ANIM_DATA_credits_bond_kissing (g_pc_animdata_base[0xe0bcu])
#define ANIM_DATA_credits_natalya_kissing (g_pc_animdata_base[0xe18cu])
#define ANIM_DATA_helicopter_cradle (g_pc_animdata_base[0xe470u])
#define ANIM_DATA_plane_runway (g_pc_animdata_base[0xe5f4u])
#define ANIM_DATA_helicopter_takeoff (g_pc_animdata_base[0xe7c0u])
#else
extern s32 ANIM_DATA_idle;
extern s32 ANIM_DATA_fire_standing;
extern s32 ANIM_DATA_fire_standing_fast;
@@ -384,6 +572,7 @@ extern s32 ANIM_DATA_credits_natalya_kissing;
extern s32 ANIM_DATA_helicopter_cradle;
extern s32 ANIM_DATA_plane_runway;
extern s32 ANIM_DATA_helicopter_takeoff;
#endif /* __x86_64__ (D34) */
+77 -87
View File
@@ -1,124 +1,114 @@
# Handoff brief — GoldenEye 007 PC port (Phase 1.5: boot to first frame)
# Handoff brief — GoldenEye 007 PC port (Phase 1.5 done, Phase 2: first frames render)
_Paste-ready briefing for the next agent session. The authoritative detail lives in
`AGENTS.md` and `docs/PCPortResearch.md` (§H handoff, §F/D31–D33 findings); this is
`AGENTS.md` and `docs/PCPortResearch.md` (§H handoff, §F/D31–D43 findings); this is
the summary + the immediate task._
## Your job
Get `ge007.x86_64.exe` booting to a **rendered first frame** on PC. You are mid-Phase-1.5:
the game boots, maps the ROM, opens the window, and runs mainThread through most of
`bossInitMainthreadData()`, but currently SIGSEGVs in weapon-animation init. Work
agentically — fix → build → verify under gdb → commit at each working milestone. The goal
when the user is back: a booting game.
Boot is complete AND **real frames render** (`[NOTE] frame N rendered in … us`). The
current blocker is **D43**: model-file loading ABI mismatch — stage object load faults
in `modelPromoteNodeOffsetsToPointers()` (src/game/model.c:5688) because ROM model files
are serialized with N64 layout (4-byte pointer fields) but read on PC as 8-byte-pointer
structs. Fix it with a D37-style re-layout in `port/src/romdata.c`, then keep pushing
rendering forward (more asset types will fault the same way). Work agentically — fix →
build → verify under gdb → commit at each working milestone.
## Read first (authoritative, in order)
1. `AGENTS.md` — non-negotiables, critical files, build, verification ritual, phase status.
2. `docs/PCPortResearch.md` §H (handoff + plan), then **§F/D33** (root cause of the
current blocker + complete fix design), D31–D32 for background.
2. `docs/PCPortResearch.md` §H (handoff + plan), then **§F/D43** (the open model-file
task) and **D37** (the libaudio bank-tree re-layout — the pattern to copy), D39–D42
for the first-render fixes, D31–D38 for background.
## Non-negotiables (the ones that bite)
- N64 build files (`Makefile`, `tools/`, `rsp/`, `ld/`) — never touch.
- **Game logic is unmodified.** Hardware deps go in `port/`. Narrow exception: mechanical,
semantics-preserving ABI/layout fixes forced by the 32→64-bit transition, each documented
semantics-preserving ABI/layout fixes forced by the 32→64-bit transition (embedded
pointers → u32 + cast at use; PC-guarded pool sizing / idiom branches), each documented
as a D3x finding. No behavior changes.
- Region macros in `CMakeLists.txt` must mirror the N64 Makefile exactly.
## Current state (verified this session)
- **ROM dump verified:** the No-Intro good dump `GoldenEye 007 (U) [!].z64` is
byte-identical to both dev ROM copies and its header CRCs re-compute clean
(`tools_pc/romverify.c`). Fingerprint + provenance in Environment below —
trust it, don't re-verify.
- **D31 FIXED:** langInit file-load crash gone (real-zlib `decompressdata` in
`port/src/rzdecomp.c`). mainThread boots into `bossInitMainthreadData()`.
- **D32 Part A DONE:** `ModelAnimation` pointer fields → `u32` (N64 layout, sizeof 64).
- **D32 Part B = ROOT-CAUSED (finding D33). Two independent bugs, both confirmed:**
1. **ROM endianness.** The `.z64` file stores structured multi-byte fields in
**big-endian byte order** (bit-packed streams are raw). The animation blob's
20-byte record headers and descriptor arrays must be converted per-field at load
time: u32 → bswap32, u16 → bswap16, u8 → identity. No uniform word transform exists.
Validated in Python over all 173 ROM entries: 0 bad ranges, 0 non-sequential
descriptor arrays; simulated root-motion frames show smooth per-frame deltas.
The exact garbage seen under gdb (0xC82BD8C0) is reproduced by LE-reading the BE
file bytes + rebase. Evidence: GE-specific ROM header, `tools/utils.h` "v64→z64"
convention, build pipeline with no byte-swap step, BE toolchain defaults.
2. **x86-64 stride bug.** `expand_ani_table_entries()` iterates with `s32** var_v0;
var_v0++` — +4 bytes/iter on N64, but +8 on x86-64 (verified: `add $0x8,%rdx` in the
compiled binary). Only even-indexed entries get rebased; odd ones keep raw small
offsets (fire_standing is index 1 → would stay 0x144 → SIGSEGV on use). The earlier
"zeros at odd indices" gdb observation was a display artifact — the compiled `.data`
array is dense (objdump-verified). This pattern occurs nowhere else in the set.
- **ROM dump verified:** byte-identical to the No-Intro good dump, header CRCs re-compute
clean (`tools_pc/romverify.c`). Fingerprint + provenance in Environment below.
- **Frames render.** The chain of fixes that got us here (all documented in §F):
- **D31–D37:** boot chain — real-zlib file decompression, animation-table ABI+endianness,
ANIM_DATA lvalues, music seq-table fixup, music-heap sizing, libaudio bank-tree
re-layout (`romdataFixupAudioBank`).
- **D38:** implicit function declarations truncated pointer returns on x86-64 →
`port/include/pc_protos.h` prototype shim (398 decls; generator `scripts/gen_pcprotos.py`;
anchored in `port/shim/PR/ucode.h`; C-only). Build is now warning-clean.
- **D39:** Globalimagetable rebasing — `texReset()`'s `globalbank_rdram_offset + (u32)&sym`
idiom needs the N64 `(u32)&sym == 0x02000000 + off` value; PC branch uses a per-symbol
offset enum + `GIMG_OFF()` in `src/game/image_bank.c`. Segment end markers extended to
the true 0x13F8 size (the CSV's `Globalimagetable.bin` entry is truncated by 0x930).
- **D40:** `g_ModelHitEntries` BSS pool was N64-sized (12 KB for 600×20 B entries); the
40-byte PC stride overflowed .bss and clobbered `is_ramrom_flag`. PC branch declares a
`600 * sizeof(ModelHitEntry)` pool.
- **D41:** WGL contexts are current on one thread only; the host main thread created +
held the context while the game thread renders. `videoInit()` now calls
`gfx_sdl_release_context()` after init; the game thread re-binds per frame. NOTE: this
SDL2 build's `SDL_GL_MakeCurrent` return value is an inverted ABI artifact (0 = success,
-1/255 = failure) — do NOT branch on it.
- **D42:** rsp.c's XOR toggle of `g_gfxTaskSettingsList` truncated exe pointers to u32 →
explicit toggle under `#if defined(__x86_64__)`.
## Immediate task — implement the D33 fix (design complete, ~3 small edits)
1. **`port/src/romdata.c` + `port/include/romdata.h`:** add
`void romdataFixupAnimationData(u8 *blob, u32 blobSize, const s32 *tableA, const s32 *tableB)`.
For each non-null (≠0 and ≠1 — sentinel is **1**) offset in both tables:
- record at `blob+off` (20 bytes): bswap32 fields at +0x00 (address), +0x08
(bitDescriptors), +0x10 (bitStream); bswap16 fields at +0x04 (frame count),
+0x0C (bitsPerFrame root motion), +0x0E (frame size bits); bytes +0x06/+0x07
(u8 angle width / loop flag) untouched.
- descriptor array at `[blob+bd, blob+bs)` step 6 (`ModelAnimBitField`): bswap16
words at +0 and +4 (bitOffset, valueOffset); bytes +2/+3 (bitCount, pad) untouched.
Guards: skip if bd==0 && bs==0; require `bd < bs <= blobSize` and `(bs-bd)%6==0`.
Full field semantics + layout rules in D33 (blob interleave: entry i's payload sits
at [PTR_ANIM_{i-1}+0x14, PTR_ANIM_i); all regions disjoint).
2. **`src/game/initanitable.c`, `alloc_load_expand_ani_table()` (~line 265):** call the
fixup **between** `romCopy(...)` and `expand_ani_table_entries(...)` — it must run
*before* expand (expand reads/writes those fields as LE after fixup). Add
`#include "romdata.h"` (`port/include` is already on the include path). One-line
mechanical ABI edit — document with a D32/D33 comment.
3. **`src/game/initanitable.c`, `expand_ani_table_entries()` (line 233):** change the loop
pointer from `s32** var_v0` to `s32 *var_v0` (cast at assignment: `var_v0 = (s32 *)arg0;`)
so every entry is visited on x86-64. Semantics-preserving on N64. Keep the rest of the
function body identical (`*var_v0 += base`, `((struct anim_entry *)(s32)*var_v0)->unk08/unk10`,
and loop 2's `*(s32 *)(s32)*var_v0 += entries_base`).
**Do NOT transform the entries segment** (per-frame joint angles, file 0x124AC0,
0x169EC0 bytes) — it is identity-encoded bit-packed data, not consumed at boot; verify
visually in Phase 2 when Bond first animates.
## Verify (gdb, launch mode only — attach fails with error 87)
- Break at `boss.c:233` (right after `alloc_load_expand_ani_table` returns). base =
`&ptr_animation_table->data`.
- For a sample of `animation_table_ptrs1/2[i]` entries (include odd indices!):
`*(u32*)(entry+8)` and `*(u32*)(entry+16)` must be in [0x70xxxxxx] (base + blob offset),
and the record's address field (+0x00) must be a cart address 0x10xxxxxx.
- Confirm boot advances past `init_weapon_animation_groups_maybe()` into the next init
step of `bossInitMainthreadData`. Expect more D32-class faults further along (models,
textures, other tables) — apply the standing procedure below each time.
## Immediate task — D43: model-file loading ABI mismatch
1. Reproduce: run; after a few rendered frames it SIGSEGVs in
`modelPromoteNodeOffsetsToPointers()` (model.c:5688) during stage object load via
`load_object_fill_header()` (src/game/objecthandler_2.c:89).
2. Understand the file format from `bondtypes.h` (`ModelFileHeader`, `ModelNode`,
`ModelRoData` union + per-opcode records) and `bondconstants.h`
(`MODELNODE_OPCODE_*`). N64 layout: header 24 B, node 20 B, all pointers 4 bytes.
3. Implement a D37-style two-pass re-layout in `port/src/romdata.c`: walk the node tree
(Child/Next/Parent links), place every sub-struct once (8-aligned) in a compact image,
rewrite each embedded pointer as a zero-extended offset from the image start, so the
existing `PROMOTE` rebase (`diff = fileramaddr - vma`, vma = 0x5000000) works
unmodified. Hook it between the romCopy in `load_resource()` and first use (or at the
`load_object_fill_header` call sites — choose the narrowest correct hook).
4. Validate one small model against ROM ground truth under gdb before generalizing
(opcode sequence, node links, a few Data records).
5. Expect follow-on faults in other asset types (BG/stan/propobj tables) — same procedure.
## Standing procedure (you will hit more of these)
Every ROM-serialized struct with a pointer field faults the same way once you reach more
asset loading. The D32 fix procedure (doc §H): at the fault run `ptype /o <Struct>`; if a
pointer field's offset/size diverges from its N64 offset comment, change it to `u32` + cast
at the use sites; verify the load-time rebase yields valid V1 addresses; **also check the
ROM bytes' endianness per-field (D33 rule: u32 bswap32 / u16 bswap16 / u8 identity) and any
fixup loop's pointer stride on x86-64**; rebuild; confirm boot advances. Log each as D3x in
§F and note it in AGENTS.md phase status.
at the use sites; verify the load-time rebase yields valid V1 addresses (< 0x80000000);
**also check the ROM bytes' endianness per-field (D33 rule: u32 bswap32 / u16 bswap16 / u8
identity) and any fixup loop's pointer stride on x86-64**; for packed struct trees use the
D37 re-layout pattern; for address-arithmetic idioms over exe symbols (D39/D42 class) guard
a PC branch that reproduces the N64 32-bit value exactly. Rebuild; confirm boot advances.
Log each as D3x in §F and note it in AGENTS.md phase status.
## Environment (do not rediscover these)
- MSYS2/MinGW tools are in `/c/msys64/mingw64/bin/` — **not** on PATH. Prefix every shell:
`export PATH="/c/msys64/mingw64/bin:$PATH"`.
- Build: `./build-pc.sh ntsc-final` (incremental; full rebuild only when `port/shim/`
changes). ROM is at `data/ge007.ntsc-final.z64` (= `baserom.u.z64`, byte-identical).
- ROM provenance (verified 2026-08): all three copies are byte-identical to the No-Intro
good dump `GoldenEye 007 (U) [!].z64` (kept in `data/`). MD5
- The ROM is **mapped at cart base 0x10000000** on PC (`romdataInit`), so `&<segment>Symbol`
values (e.g. `_sfxctlSegmentRomStart = 0x102EBDE0`) are directly dereferenceable host
pointers into the ROM image; `romCopy()` copies from those cart addresses. All game RAM
lives in the V1 view at 0x70xxxxxx (< 4 GiB) — s32 truncation of game-RAM pointers is
lossless (this is why D39 needed no changes outside image_bank.c).
- ROM provenance (verified 2026-08): byte-identical to the No-Intro good dump
`GoldenEye 007 (U) [!].z64` (kept in `data/`). MD5
`70c525880240c1e838b8b1be35666c3b`, SHA-256
`2cdcec8a9f0cb6e36337f3ee39d8ad105dc8afa6ba1c02d466e8f5b771f9a162`, 0xC00000 bytes,
country 'E' (US). Header CRCs DCBC50D1/09FD1AA3 re-verify with
`tools_pc/romverify.exe` (build line in its header comment).
country 'E' (US). Header CRCs DCBC50D1/09FD1AA3 re-verify with `tools_pc/romverify.exe`.
- gdb: **launch** mode only (`gdb -batch -ex "handle SIGSEGV stop" -ex run …`). Symbolicate
offline with `addr2line -e build-pc/ge007.x86_64.exe -f -C <0x140000000+rel>`. Image base
is `0x140000000` (re-verify with `info address` after a rebuild).
is `0x140000000` (re-verify with `info address` after a rebuild). Debug scripts live in
`build-pc/` (`d37_tree.py`, `d39_*.gdb`, `d41_probe.gdb`, …).
- `objdump -d --disassemble=<fn>` / `objdump -s -j .data` on build-pc/ge007.x86_64.exe is a
fast way to check what the compiler actually emitted (used this session to confirm the
stride bug and the dense table array).
fast way to check what the compiler actually emitted.
- Many init functions use a fake RBP — compute stack offsets from the **entry RSP**, not RBP.
- The D30 crash handler did **not** write `ge007.crash.log` for the game-thread fault;
attribute crashes via gdb. (Worth a separate look later.)
- The D30 crash handler did **not** write `ge007.crash.log` for game-thread faults;
attribute crashes via gdb (the in-process dump does print PC/registers to stdout).
- Python on this box: no f-strings with nested quotes (< 3.12) — use `%` formatting.
## Definition of done (this milestone)
mainThread reaches `bossMainloop()` and a frame is actually drawn on the GL surface (not just
the clear color). After that, Phase 2 (fast3d CC/RM correctness vs `gmain.s`) makes it look
right. Commit + push to `origin/master` at each working checkpoint using the message style
`PC port: <phase> — <what>`.
Stage objects load without faulting and the rendered scene contains actual model geometry
(not just the clear color / background), stable under a 30 s+ soak. After that, Phase 2
makes it look right (fast3d CC/RM correctness vs `gmain.s`) and Phase 3 adds sound
(libaudio → SDL). Commit + push to `origin/master` at each working checkpoint using the
message style `PC port: <phase> — <what>`.
+240 -37
View File
@@ -1053,6 +1053,191 @@ gameInit — and currently dies inside `langInit()` (D31).
boot (only during rendering). Verify visually in Phase 2 when Bond first
animates.
**D34** `ANIM_DATA_*` address placeholders break on x86-64. On N64 the
`animation_data` segment links at VMA 0, so `(s32)&ANIM_DATA_x` is a small
segment offset; on PC `&ANIM_DATA_x` is a high PE address and the game code's
ubiquitous s32 truncation yields garbage. Audit: all 82 uses are
address-taking only (`(s32)&…`, or through `ANIM_FRAC()` /
`ANIM_FRAC_MUL_FIRST()` in chraction.c/bondview2.c/title.c/
initBondDATAdefaults.c/initactorpropstuff.c) — no data dereferences, no
sizeof, no static initializers. Fix: on x86-64 each `ANIM_DATA_x` is
redefined as an lvalue at `g_pc_animdata_base + offset` where the base is the
PE image address with its low 32 bits zeroed (derived in `romdataInit()` from
a static probe; fail-fast if not 4 GiB-aligned), so
`(s32)&ANIM_DATA_x == PTR_ANIM_x` exactly. Files:
`assets/animationtable_data.h` (guarded macro branch, N64 externs kept under
`#else`), `port/src/romdata.c` (`g_pc_animdata_base` + init),
`CMakeLists.txt` (excludes `assets/animationtable_data.c` +
`animationtable_entries.c` on PC — the arrays are address-only placeholders;
the ROM is the data ground truth via romCopy). Verified under gdb:
`D_80030984 = ANIM_FRAC(ANIM_DATA_walking)` executes; the walking record at
`ptr_animation_table + 0x4018` has entry field `0x10177dcc`
(entries base `0x10124AC0` + `PTR_ANIM_ENTRY_walking`) and frame count 0x25;
after `initWeaponAnimGroups()` the derived globals hold sane floats
(4.59/11.25/16.71/16.19).
**D35** Music sequence table: pointer-width + endianness.
`musicSeqPlayerInit()` faulted in `romCopy()` with a NULL destination:
`tblSegmentSize = sizeof(RareALSeqData) * seqCount + 4` was garbage because
(1) `RareALSeqData.address` was `u8 *` — sizeof 16 on x86-64 vs the ROM's
8-byte records — and (2) the header `seqCount` and entry fields are stored
big-endian in ROM but read as LE (63 → 0x3F00 = 16128). Fix: `address` →
`u32` (`src/music.h`; sizeof 8 on both platforms) with `(void *)` casts at the
three use sites in `src/music.c`; new `romdataFixupMusicSeqTable()` in
`port/src/romdata.c` bswaps `seqCount` + per-entry `address`(u32)/
`uncompressed_len`(u16)/`len`(u16), called after both romCopys in
`musicSeqPlayerInit()`. Verified: the header-only (0x10-byte) copy logs the
expected `seqCount 63 exceeds blob capacity 1` clamp; boot passes the old
fault.
**D36** Music heap / PERMANENT pool sizing on x86-64. `alCSPNew()` SIGSEGV'd
because the music bump heap was oversubscribed: x86-64 libaudio runtime
structs are larger (ALVoiceState 88B, ALSeqPlayer 248B, ALCSPlayer 224B, …)
and the debug-token state (`tokenFind(1,"-level_")==NULL` →
`g_DebugAndUpdateStageFlag=1` → stale `-ml0 -me0` tokens) forces the
fixed-size mempool branch with PERMANENT at 296 KiB. Measured under gdb:
pre-music PERMANENT usage 0x1BCA0 left 189,280 B for music vs a measured init
demand of 0x31660 (shortfall 13,056). Fix (PC-only guards):
`MUSIC_ALLOCATION_BYTES` 0x2E000 → 0x32000 (`src/music.c`); PERMANENT fixed
branch 296/308 KiB → 320/340 KiB (`src/memp.c`, non-JP/JP). STAGE absorbs the
difference. Verified: boot reaches `bossMainloop()`.
**D37** libaudio bank trees must be re-laid out, not patched in place. The
audio thread SIGSEGV'd in `__initFromBank()` (`b=0xffffffff00000000`):
`ALBankFile/ALBank/ALInstrument/ALSound/ALWaveTable/…` are serialized in ROM
as big-endian scalars + 4-byte packed table-relative offsets, while the x86-64
C structs use 8-byte pointers — so BE values were LE-misread and 4-byte offset
arrays were walked with 8-byte stride, producing wild rebase pointers in
`alBnkfNew()`/`_bnkfPatch*()`. In-place conversion is impossible: each struct's
expanded tail overlaps whatever follows it in ROM (e.g. the ALWaveTable book
slot at +24 lands inside the ALADPCMBook that sits 12 bytes after the
wavetable). Fix: `romdataAudioBankPcSize()` + `romdataFixupAudioBank()` in
`port/src/romdata.c` — a two-pass DFS re-layout into a compact image where
every sub-struct is placed once (8-byte aligned, children before parents) and
each pointer slot stores the sub-struct's NEW offset from the image start,
zero-extended; `alBnkfNew()`/`_bnkfPatch*()` then rebase unmodified
(`ptr + (s32)file`). `ALWaveTable.base` stays an offset into the separate
wavetable data segment — `_bnkfPatchWaveTable()` adds its `table` argument,
verified at runtime (sfx: base+table = 0x102F19A0 =
`_sfxtblSegmentRomStart`). `music.c` PC branch allocates the re-layout size
and fixups after romCopy. Memory: bank images grow +0x1460 (sfx) / +0xA70
(instruments); init-time heap demand measures 0x33530, so
`MUSIC_ALLOCATION_BYTES` → 0x38000 and PERMANENT → 352/368 KiB (PC only).
Verified under gdb: runtime tree matches ROM ground truth (SFX instCount=1,
soundCount=261, bendRange=200, sampleRate=22050; INSTR instCount=75; all
env/keymap/wavetable/book/loop pointers valid heap addresses; book
order=2 npredictors=1); 25 s soak with every thread alive. Implementation
bugs caught during bring-up: a double bswap (afRd* already decodes BE),
afWr32 initially wrote BE, and the size pass used an uninitialized visited
array (undercount) — all fixed.
**D38** Implicit function declarations truncate pointer returns on x86-64.
~72 game TUs call ~400 functions with no visible prototype (missing `#include`
of the declaring header). Under C11 an implicit declaration assumes
`int f()`: harmless on N64 (32-bit pointers) but on x86-64 it silently
truncates every pointer return to 32 bits — e.g. `tokenFind()` in
`set_mt_tex_alloc()` returned a low-32-bit "pointer" that faulted in
`strtol()`. Fix: a port-layer prototype shim, `port/include/pc_protos.h`
(398 declarations; one-shot generator `scripts/gen_pcprotos.py` — the committed
header is the source of truth, hand-adjusted after generation), declaring each
function with its TRUE return type and an empty parameter list — purely
additive (only the returned value's width changes). Anchored in
`port/shim/PR/ucode.h`, the LAST include of `<ultra64.h>`: anchoring earlier
(e.g. `gbi.h`) poisons libaudio.h because the bondtypes chain reaches
`snd.h → <PR/libaudio.h>` while ultra64.h is still mid-parse. C-only guard:
C++ fast3d TUs reach this header via `SDL_stdinc.h → <stdarg.h> →
port/shim/stdarg.h`, and pulling the bondtypes/bondconstants chain into C++
brakes on `struct ALSoundState*` in `src/bondtypes.h`; no C++ TU needs a
fix here. C11 gotcha: an empty parameter name list `()` cannot match a true
prototype with default-promoted parameters (`s8`/`u8`/`s16`/…), so the 11
such functions get full prototypes with their TRUE parameter types
(substituting `int` for `s8` is NOT compatible). Also: `src/bondconstants.h`
defines function-like macros `ntohl`/`ntohs` over `CharArrayTo32/16`, which
collide with MinGW `<winsock.h>` in TUs that parse both — neutralized in the
port shim, real host functions provided in `port/src/pc_netorder.c` with
winsock-compatible signatures (`u_long` is 64-bit on LLP64!). No game-TU
edits. Result: build clean, zero warnings; the old `strtol()` crash is gone.
**D39** Globalimagetable rebasing idiom breaks on x86-64. `texReset()` /
`texLoadFromDisplayList()` compute ROM-layout pointers as
`globalbank_rdram_offset + (u32)&sym` with
`globalbank_rdram_offset = (u32)pGlobalimagetable + 0xFE000000`; on N64
`(u32)&sym` = `0x02000000 + off` and the 0x02 base cancels the 0xFE. On PC
`(u32)&sym` is a truncated PE address → garbage DL pointers. Fix (in
`src/game/image_bank.c`, PC branch): an enum of all 49 segment offsets (17
Gfx display lists + 32 sImageTableEntry tables) and
`#define GIMG_OFF(sym) (0x02000000u + g_pc_gimg_off_##sym)` — exactly the N64
`(u32)&sym`; all 49 texReset() sites replaced. No changes needed in
gunfire.c / texLoad / texSelect: all game RAM is s32-safe (V1 view,
0x70xxxxxx < 4 GiB), so the u32 math and pointer casts stay lossless.
Also fixed the segment markers: the CSV asset `Globalimagetable.bin`
(0xAC8) is TRUNCATED — the real linker segment (`ge007.ld`: oddtextures.o
`.data`) spans 0x13F8 = Gfx DLs (0xAC8) + ITE tables (0x930); the CSV's
`rarewarelogo` entry is mis-split (the logo actually starts at ROM
0x29E560). `_GlobalimagetableSegment{Rom}End` 0x1029DC28 → 0x1029E558 in
`port/src/romassets_u.s` + a size override in `scripts/gen_romassets.py`
(regeneration verified stable). All 49 offsets verified against the ROM:
the symbols tile `[0x29D160, 0x29E558)` exactly with no gaps; `gun.c`
ammo IconImage values cross-check. Verified under gdb: all 17
texLoadFromDisplayList diffs correct (0, 0x78, 0x120, …); boot passes
texReset. Known Phase-2 follow-up: `explosion.c`'s
`g_ExplosionDisplayLists[]` is a static table of EXE addresses to Gfx arrays
(16-byte entries on PC) — it must point at the ROM-layout copies in
pGlobalimagetable before explosions render.
**D40** N64-sized BSS placeholder pool overflows with pointer-stride structs.
`initModelHitEntryFreeList()` (initunk_005450.c:38, called from
lvlStageLoad) walks 600 ModelHitEntry records writing next/prev; the
decompiler-emitted BSS chain in objecthandler.c (`char g_ModelHitEntries[0xC];`
…dwords… `char g_ModelHitEntriesPenultimate[0x28]`) reproduces N64's
12 000-byte region (600 × 20 B), but PC ModelHitEntry is 40 bytes → the
free-list init writes 24 000 bytes, a 12 KB .bss overflow that clobbered
`is_ramrom_flag` (writer caught with a hardware watchpoint) → the
demo-replay path in bossMainloop ran with `address_demo_loaded == NULL` →
SIGSEGV at ramromreplay.c:341. Fix: PC branch declares a properly sized pool
(`char g_ModelHitEntries[600 * sizeof(ModelHitEntry)]`); the N64 chain stays
verbatim under `#else`; the sentinel assignment in initunk_005450.c is
computed directly on PC (`entries[LEN-1].prev = &entries[LEN-2]` — N64's
g_ModelHitEntriesPenultimate labels the start of entry 598). Verified: no
more SIGSEGV; boot progresses into GL rendering.
**D41** Cross-thread GL context binding. WGL allows a context to be current
on only ONE thread at a time. The window+context are created and made
current on the host main thread (`videoInit → gfx_sdl_init`), but all
rendering runs on the game's scheduler thread; while main still holds the
context, `wglMakeContextCurrent` from the game thread fails with "The
requested resource is in use" — and this SDL2 build (2.32.10, MSYS2)
swallows it: `SDL_GL_MakeCurrent` returns true on failure / 0 on success
(an inverted int-as-bool ABI artifact; confirmed by a minimal repro and
gdb `$1 = 255`). The silent failure left `wglGetCurrentContext() == NULL`
on the game thread → `glCreateShader` returned 0 → "Vertex shader
compilation failed" with an empty info log. Fix: new
`gfx_sdl_release_context()` (`SDL_GL_MakeCurrent(NULL, NULL)`) called at the
end of `videoInit()` after `set_swap_interval` (the last GL work on main);
the scheduler thread re-binds per frame via the existing
gfx_sdl_make_context_current() in videoStartFrame(). Do NOT branch on the
MakeCurrent return value (unreliable). Verified: frame 1 renders (69.7 ms),
several frames before the next (separate) fault.
**D42** rsp.c task-settings toggle idiom truncates pointers.
`g_gfxTaskSettingsList = (GfxInfo_s*)((u32)list ^ (u32)&g_gfxTaskSettings[0]
^ (u32)&g_gfxTaskSettings[1])` toggles between two adjacent settings structs
by XOR — fine on 32-bit, but on x86-64 the u32-truncated PE addresses XOR to
garbage → the next frame's `((GfxInfo_s*)g_gfxTaskSettingsList)->cfb = …`
(fr.c:458) SIGSEGV'd. Fix: explicit toggle under
`#if defined(__x86_64__)` (`list == &[0] ? &[1] : &[0]` — same semantics,
the list is always one of the two); N64 line verbatim under `#else`.
**D43 (OPEN)** Model-file loading ABI mismatch. Stage object load faults in
`modelPromoteNodeOffsetsToPointers()` (model.c:5688). Model files are
ROM-serialized with N64 layout (ModelFileHeader 24 B, ModelNode 20 B,
4-byte pointer fields) but on PC are read as 8-byte-pointer structs (48 B
each): `load_object_fill_header()` derives RootNode from `numtextures`
at the wrong offset, and the `PROMOTE` rebase (`(u32)var + diff`) then
operates on misaligned fields. Needs a D37-style re-layout of the model
file image (header + switches array + texture table + node tree +
ModelRoData records). Phase-2 project; see §H.
### G. Phase 2 status (current)
Done: PD fast3d integrated (`port/fast3d/`, replaces the deleted gfxstub.c);
@@ -1096,22 +1281,54 @@ shim expansion in compiled code.
### H. Handoff & plan (current session)
**State.** D31 (langInit file-load SIGSEGV) is fixed & verified — mainThread
boots past all language-file loading into `bossInitMainthreadData()`. The boot
dies in `init_weapon_animation_groups_maybe()` on the **D32** ROM-struct issue.
D32 Part A (struct layout) is done. **D32 Part B is now fully root-caused
(D33):** (1) the ROM file stores the animation segment's structured fields in
big-endian byte order — a per-field endianness fixup is required at load time;
(2) `expand_ani_table_entries` has an x86-64 loop-stride bug (`s32**` advances
8 bytes/iter, rebasing only even-indexed entries). The fix design is complete
and validated against all 173 ROM entries; **implementation is the immediate
task** (three small edits — see D33 "Fix design"). No frames rendered yet.
**State.** Boot is complete AND the first frames render: mainThread runs through
all of `bossInitMainthreadData()` + `bossEntry()`, enters `bossMainloop()`,
loads the stage, and fast3d renders real display lists — `[NOTE] frame N rendered
in … us` logs appear (frame 1 in ~70 ms). Findings **D34–D42** (§F) closed out
the boot + first-render blockers on top of D31–D33:
- **D34** `ANIM_DATA_*` → address-only lvalues at PE-image-base + ROM offset.
- **D35** music seq table: `RareALSeqData.address` → `u32` + BE fixup.
- **D36** music heap / PERMANENT pool grown for x86-64 libaudio struct bloat.
- **D37** libaudio bank trees re-laid out to PC-native form (`romdataFixupAudioBank`).
- **D38** implicit-declaration pointer truncation → `port/include/pc_protos.h`
prototype shim (398 decls, generator in build-pc/); build now warning-clean.
- **D39** Globalimagetable rebasing: per-symbol offsets + `GIMG_OFF()` macro
(== N64 `(u32)&sym`, incl. the 0x02000000 base) in image_bank.c; segment end
markers extended to the true 0x13F8 size (CSV truncation).
- **D40** ModelHitEntry BSS pool sized for the 40-byte PC stride (was 12 KB
overflow clobbering `is_ramrom_flag`).
- **D41** GL context released from the host main thread after init so the game
thread can bind it (WGL single-thread currency; SDL2 MakeCurrent return value
is an inverted ABI artifact — don't branch on it).
- **D42** rsp.c `g_gfxTaskSettingsList` XOR toggle → explicit toggle on x86-64.
**ROM provenance locked:** the working ROM is byte-identical to the No-Intro
good dump `GoldenEye 007 (U) [!].z64` (MD5 `70c525880240c1e838b8b1be35666c3b`,
0xC00000 bytes, country 'E'); embedded header CRCs DCBC50D1/09FD1AA3
re-computed and matched via `tools/n64cksum.c`'s CIC-6102 routine — see
`tools_pc/romverify.c` (one-shot re-check) and docs/HANDOFF.md Environment.
**Current blocker — D43: model-file loading ABI mismatch.** After a few frames,
stage object load faults in `modelPromoteNodeOffsetsToPointers()`
(model.c:5688). Model files are ROM-serialized with N64 layout (ModelFileHeader
24 B / ModelNode 20 B, 4-byte pointer fields) but read on PC as 8-byte-pointer
structs; `load_object_fill_header()` (objecthandler_2.c) derives RootNode from
`numtextures` at the wrong offset before any rebase. Plan: a D37-style two-pass
re-layout of the model file image in `port/src/romdata.c` — header, switches
array, texture table, ModelNode tree (walked via Child/Next/Parent), and the
per-opcode ModelRoData records — with every embedded pointer rewritten as a
zero-extended offset from the image start so the existing
`PROMOTE`/`diff = fileramaddr - vma` rebase works unmodified. Map the record
types first (bondtypes.h `ModelRoData` union, opcodes in bondconstants.h);
validate one small model against ROM ground truth under gdb before generalizing.
Expect more D32-class ABI fixes as other per-level asset types are first
touched — standing sub-task, not a one-off.
**Audio (Phase 3 prep).** Bank trees are correctly laid out at boot and the
libaudio manager + audio thread run without crashing; actual sound output still
needs an SDL backend for `alDriver`, and the ASP strategy question (C2) remains
open. No work required until rendering is stable.
**D32 repeatable fix procedure** (apply to any ROM-serialized struct that faults
on a pointer-field read):
1. At the fault, `ptype /o <Struct>` in gdb. If a pointer field's offset/size
@@ -1122,39 +1339,25 @@ on a pointer-field read):
3. Verify the load-time rebase/fixup writes valid **V1** DRAM addresses (< 0x80000000)
into those u32 fields; if not, debug the fixup (see Part B below).
4. Rebuild and confirm the boot advances past this struct to the next init step.
**Next concrete target — implement the D33 fix.** Three edits (full design in
D33):
1. `port/src/romdata.c` + `port/include/romdata.h`: add
`romdataFixupAnimationData(u8 *blob, u32 blobSize, const s32 *tableA,
const s32 *tableB)` — per-field BE→LE transform of the 20-byte records and
the [bd, bs) descriptor arrays (u32 → bswap32, u16 → bswap16, u8 identity).
2. `src/game/initanitable.c` `alloc_load_expand_ani_table()`: call the fixup
between `romCopy` and `expand_ani_table_entries` (+ `#include "romdata.h"`).
3. `src/game/initanitable.c` `expand_ani_table_entries()`: change the loop
pointer from `s32**` to `s32 *` so every entry is visited on x86-64.
Then: build (`./build-pc.sh ntsc-final`), verify under gdb (launch mode):
break at boss.c:233 after `alloc_load_expand_ani_table`; for a sample of
`animation_table_ptrs1/2[i]` entries confirm `*(u32*)(entry+8)` and
`*(u32*)(entry+16)` are in [0x70xxxxxx] (blob base + offset) and the record's
address field is a cart address (0x10xxxxxx); confirm boot advances past
`init_weapon_animation_groups_maybe`. Commit at the milestone.
5. If the struct is a *tree* of packed sub-structs (like the libaudio banks, D37,
or model files, D43), an in-place BE→LE patch will not fit — the expanded
8-byte pointer slots overrun the following ROM data. Re-lay it out into a
fresh compact image and rewrite each pointer slot as the sub-struct's new
offset (zero-extended), so the existing `ptr + (s32)base` rebase still works.
6. If the fault is NOT in ROM-loaded data but in an address arithmetic idiom over
exe-resident symbols (`(u32)&sym`, XOR toggles, pointer-delta math — D39/D42),
guard a PC branch that reproduces the N64 32-bit value exactly (e.g. keep the
0x02000000 base) or replace the idiom with an explicit equivalent; N64 line
stays verbatim under `#else`.
**Non-negotiable #2 refinement (applied to AGENTS.md).** The original "game code
compiles unmodified / fix belongs in port/" is too absolute: pointer-width layout
cannot be isolated in `port/` (no hook between the romCopy and the first read).
Refined to "game **logic** is unmodified" with a narrow, documented exception for
mechanical, semantics-preserving ABI/layout changes forced by the 32→64-bit
transition (embedded pointers → u32 + cast at use), following PD ground truth. No
logic/behavior changes; each such edit is logged in §F/D3x.
**Reassessed plan.** The frontier is now **Phase 1.5 — boot to first frame**:
drive mainThread through the rest of `bossInitMainthreadData` + `bossEntry` to
`bossMainloop()` and render frame 1, using the D32 procedure each time a
ROM-data struct faults. Phase 2 (fast3d/CC-RM/scheduler) then makes those frames
correct. Expect several more D3x ABI fixes (models, textures, other tables) as
more asset loading is reached — this is now a standing sub-task, not a one-off.
transition (embedded pointers → u32 + cast at use; PC-guarded pool sizing),
following PD ground truth. No logic/behavior changes; each such edit is logged in
§F/D3x.
**Environment reminders.** MSYS2 tools in `/c/msys64/mingw64/bin/` (not on PATH —
prefix `export PATH=…`). Build: `./build-pc.sh ntsc-final`. gdb **launch** mode
+4
View File
@@ -13,6 +13,10 @@ extern struct GfxWindowManagerAPI gfx_sdl;
* runs on the game's scheduler thread, not the thread that created it). */
void gfx_sdl_make_context_current(void);
/* Release the GL context from the calling (host main) thread after init, so
* the game's scheduler thread can bind it per frame (see gfx_sdl2.cpp). */
void gfx_sdl_release_context(void);
/* Refresh the cached drawable size after a window resize (called by the
* host thread's event pump). */
void gfx_sdl_update_cached_size(void);
+15
View File
@@ -330,10 +330,25 @@ static void gfx_sdl_handle_events(void) {
* there before each frame. extern "C": called from C (port/src/video.c). */
extern "C" void gfx_sdl_make_context_current(void) {
if (wnd && ctx) {
/* Note: SDL2's MakeCurrent return value is unreliable on this MinGW
* toolchain (ABI artifact), so we don't branch on it. A genuine bind
* failure will surface later as a GL call returning nothing / shader
* compile failure, which is far louder. */
SDL_GL_MakeCurrent(wnd, ctx);
}
}
/* Release the GL context from the calling (host main) thread. WGL only allows
* a context to be current on one thread at a time, and while another thread
* still holds it, wglMakeContextCurrent fails with "The requested resource is
* in use" (SDL2 reports this via SDL_GetError). The host thread must release
* after init so the game's scheduler thread can bind it per frame. */
extern "C" void gfx_sdl_release_context(void) {
if (ctx) {
SDL_GL_MakeCurrent(NULL, NULL);
}
}
/* The host thread owns SDL event pumping (see videoPumpEvents); when it sees
* a window resize it calls this to refresh the cached drawable size that
* fast3d uses for scaling. */
+5
View File
@@ -39,6 +39,11 @@
#include <string.h>
#include "glad.h"
/* D38: on PC <stdlib.h> resolves to the decomp's stub (empty body), so the
* host CRT functions used below need explicit declarations. */
extern void *malloc(unsigned long long size);
extern void free(void *ptr);
struct gladGLversionStruct GLVersion = { 0, 0 };
#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)
+473
View File
@@ -0,0 +1,473 @@
/*
* pc_protos.h - PC-port-only prototypes for implicitly declared functions.
*
* D38 (docs/PCPortResearch.md section F): the decomp has ~72 translation units
* that call ~400 functions without any visible prototype (missing #include of
* the declaring header). Under C11 an implicit declaration assumes `int f()`,
* which on N64 (MIPS, 32-bit pointers) is harmless, but on x86-64 it silently
* truncates every pointer (and 64-bit scalar) return value to 32 bits - e.g.
* tokenFind() in set_mt_tex_alloc() returned a low-32-bit "pointer" that then
* faulted in strtol().
*
* This header declares each of those functions with its TRUE return type and
* an empty parameter list (no argument checking, no dependency on the
* parameter types' headers). Empty-paren declarations are compatible with the
* real prototypes elsewhere, so this is purely additive: it changes only the
* width of the returned value, restoring N64-correct semantics on x86-64.
*
* Anchored in port/shim/PR/ucode.h (PC-only shim): ucode.h is the LAST include
* of <ultra64.h>, so by the time this header runs every PR type libaudio.h /
* gbi.h needs is already defined. (Anchoring earlier, e.g. in gbi.h, poisons
* libaudio.h: the bondtypes chain reaches snd.h -> <PR/libaudio.h> while
* ultra64.h is still mid-parse.) Inert in the N64 build (no -DPORT).
*
* C TUs only: C++ translation units (port/fast3d/*.cpp) also reach this header
* (SDL_stdinc.h -> <stdarg.h> -> port/shim/stdarg.h -> include/stdarg.h ->
* <ultra64.h>), but pulling the bondtypes/bondconstants chain into C++ breaks
* on `struct ALSoundState*` in src/bondtypes.h (C forbids nothing, C++ does:
* "using typedef-name after 'struct'"), and no C++ TU has an implicit
* declaration needing a fix here. C++ TUs keep their pre-D38 header exposure.
*/
#ifndef _PC_PROTOS_H_
#define _PC_PROTOS_H_
#if defined(PORT) && defined(__x86_64__) && !defined(__cplusplus)
#include <PR/ultratypes.h> /* u8..s32, f32, size_t (host on PC) */
#include <PR/gbi.h> /* Gfx, Mtx, Vtx, Light (shimmed on PC) */
#include "bondtypes.h" /* coord3d, PropRecord, ObjectRecord, ModelFileHeader, bool, ITEM_IDS */
#include "bondconstants.h" /* MPSCENARIOS, OBJECTIVESTATUS, PROP, DIFFICULTY, TICKOP */
#include "game/file.h" /* save_data */
#include "game/ob.h" /* FILELOADMETHOD (for _fileNameLoadToBank below) */
#pragma push_macro("assert")
#undef assert
void assert();
#pragma pop_macro("assert")
/* D38: host byte-order functions replacing the CharArrayTo16/32 macros that
* src/bondconstants.h used to define (neutralized in port/shim/bondconstants.h
* because they break <winsock.h> parsing; see D38). Declared only when
* winsock has not already declared them, so TUs that include <windows.h>
* before <ultra64.h> don't get a dllimport redeclaration warning. Defined in
* port/src/pc_netorder.c (little-endian byte swap == CharArrayTo16/32). */
#if !defined(__WINSOCK_H) && !defined(_WINSOCK2_H)
/* Match winsock's own signatures (u_short/u_long) so TUs that parse
* <windows.h> later redeclare compatibly; on LLP64 u_long is 64-bit and the
* high bits are unused for the 16/32-bit values game code passes. */
unsigned short ntohs(unsigned short);
unsigned long ntohl(unsigned long);
#endif
int GetCurrentThreadStackLimits();
int _Printf();
s32 __scSchedule();
s32 __scTaskComplete();
void * _fileNameLoadToBank(char *filename, FILELOADMETHOD loadMethod, s32 size, u8 bank);
void abort();
void add_ammo_to_inventory();
void ai();
void alloc_false_GUARDdata_to_exec_global_action();
void alloc_init_GUARDdata_entries();
void alloc_load_expand_ani_table();
void assert();
char * bgDebPrintROOMID();
void bgFindRoomsAlongSegment();
s32 bgGetConnectedRooms();
bool bgProjectRoomCoordToScreen();
Gfx* bgScissorCurrentPlayerViewDefault();
bool bgTestRayIntersectsBbox();
int bondinvAddPropToInv();
void bondinvAddTextOverride();
int bondinvAddWeaponByProp();
bool bondinvCheckHasKeyFlags();
s32 bondinvCountTotalItemsInInv();
void bondinvDetermineEquippedItem();
u8 * bondinvGetActivatedTextObject();
u8 * bondinvGetActivatedTextWeapon();
s32 bondinvGetAllGunsFlag();
int bondinvGetCurEquippedItem();
u16 * bondinvGetFirstTitlebyIndex();
u16 * bondinvGetLongNameByIndex();
u16 * bondinvGetSecondTitlebyIndex();
s32 bondinvGetTextbyInvIndex();
s32 bondinvGetWeaponOfChoice();
bool bondinvHasGEKey();
bool bondinvHasPropInInv();
void bondinvIncrementHeldTime();
void bondinvSetAllGunsFlag();
void bondinvSetCurEquippedItem();
u32 bondviewGetCameraMode();
coord3d * bondviewGetCurrentPlayersPosition3();
s32 bondviewGetVisibleToGuardsFlag();
void bondviewKillCurrentPlayer();
void bondviewRemovePlayerBody();
void bondviewSetCameraMode();
void bondviewSetCurrentPlayerPosition();
void bondviewSetVisibleToGuardsFlag();
void bondviewUpdateCameraMatrices();
void bondviewUpdateFrustumPlanes();
void bossEntry();
void bullet_path_from_screen_center();
void * calloc();
void casingsInit();
bool cheatIsActive();
s32 check_cur_player_ammo_amount_in_inventory();
bool check_if_imageID_is_light();
u32 check_ramrom_flags();
s32 chrGetNumFree();
bool chrHasStageFlag();
void chrObjRandomSetSeed();
void chrSetWeaponFlag4();
s32 chrTick();
void chrobjSndCreatePostEventDefault();
void chrpropDeregisterRoom(PropRecord *, s16);
void chrpropDetach();
PropRecord * chrpropGetActiveTail();
void cleanupAlarms();
void cleanupExplosions();
void cleanupGuardData();
void cleanupObjectSounds();
void cleanupObjectives();
void cleanupSFXRelated();
void cleanup_REMOVED_();
void cleanup_rooms();
void cleanup_window_pieces();
void cleanupplayersoundrelated();
void clearChrGunModelInstances();
void clear_light_fixturetable_in_room();
void clear_ramrom_block_buffer_heading_ptrs();
void crashDumpThreads();
void cur_player_set_aim_control();
void cur_player_set_ammo_onscreen_setting();
void cur_player_set_lookahead();
void cur_player_set_screen_setting();
void cur_player_set_sight_onscreen_control();
void currentPlayerCreateRocket();
s32 currentPlayerGetCrouchPos();
Mtx * currentPlayerGetMatrix10C8();
bool currentPlayerGetXAutoAimEnabled();
bool currentPlayerGetYAutoAimEnabled();
void currentPlayerSetCameraMode();
void currentPlayerSetMatrix10C4();
void currentPlayerSetMatrix10C8();
void * currentPlayerSetMatrix10CC();
void currentPlayerSetProjectionMatrix();
void currentPlayerSetProjectionMatrixF();
void currentPlayerSetViewToWorldMtxf();
void currentPlayerSetXAutoAimEnabled();
void currentPlayerSetYAutoAimEnabled();
void currentPlayerUnEquipWeaponWrapper();
s32 darkened_light_table_contains_vertex();
void debTryAdd();
void debmenuPrintString();
void debmenuResetBuffer();
void debmenuSetFgColour();
void debmenuSetMenu();
void debmenuSetPos();
void debug_object_load_all_models();
void debug_weapon_load_table();
void default_player_perspective_and_height();
Gfx * display_red_blue_on_radar();
void display_text_for_weapon_in_lower_left_corner();
void doorDeactivatePortal();
PropRecord* doorInit();
bool doorIsClosed();
bool doorIsPadlockFree();
bool doorTestForInteract();
void * dynAllocate();
Light * dynAllocateLights();
Mtx * dynAllocateMatrix();
Vtx * dynAllocateVertices();
s32 dynGetFreeGfx();
s32 dynGetFreeVtx();
void explosionClearBulletImpactRoom();
void explosionClearBulletImpactRoomByFlag(PropRecord *, s8);
void fileClearSavefileForFolder();
void fileCopyDemoSaveToRamRomSave();
void fileCopyFolderToFirstFree();
void fileCopySave();
void fileGenerateCRC();
save_data * fileGetSaveForFoldernum();
s32 fileGetSaveStageDifficultyTime();
s32 fileIs007ModeUnlocked();
bool fileIsAztecCompletedOnSecretOr00ForFolder();
bool fileIsCradleCompletedAnyFolder();
bool fileIsCradleCompletedForFolder();
bool fileIsEgyptCompletedOn00ForFolder();
void fileLoad();
void fileResetRamRomSave();
void fileUpdateSelectedBondInSave();
s32 fogGetPropDistColor();
void fogLoadLevelEnvironment();
void free();
int frontGetPlayersFavoriteWeaponInHand();
void generate_player_thrown_grenade();
void generate_player_thrown_knife();
void generate_player_thrown_object();
PropRecord* getCurrentPlayerProp();
ITEM_IDS getCurrentPlayerWeaponId();
int getCurrentWeaponOrItem();
s32 getIndexOfPORTALID();
s32 getMPWeaponSet();
s32 getMaxNumRooms();
s32 getMissiontimer();
s32 getPlayerCount();
u8 * getPlayerWeaponBufferForHand();
PROP getPropForHeldItem();
void getRoomPositionScaledByIndex();
u32 getSizeBufferWeaponInHand();
s32 get_ammo_type_for_weapon();
u8 get_bondata_invincible_flag();
s32 get_cur_playernum();
s32 get_curplay_killcount();
s32 get_curplayer_shot_register();
s32 get_debug_007_unlock_flag();
s32 get_debug_all_obj_complete_flag();
s32 get_debug_explosioninfo_flag();
s32 get_debug_gunwatchpos_flag();
f32 get_depth_offset_solo_watch_menu_inventory_page_for_item();
f32 get_depth_on_solo_watch_menu_page_for_item();
s32 get_difficulty_for_objective();
s32 get_highlighted_debug_option();
f32 get_horizontal_offset_on_solo_watch_menu_for_item();
s32 get_is_ramrom_flag();
s32 get_itemtype_in_hand();
s32 get_keyanalyzer_flag();
f32 get_lateral_position_solo_watch_menu_main_page_for_item();
s32 get_obj_collision_flag();
s32 get_pc_buffer_remaining_value();
u32 get_player_control_style();
u16 get_player_mp_char_body();
u16 get_player_mp_char_head();
s32 get_players_team_or_scenario_item_flag();
u16 * get_ptr_first_title_line_item();
ModelFileHeader * get_ptr_itemheader_in_hand();
u16 * get_ptr_long_watch_text_for_item();
u16 * get_ptr_second_title_line_item();
u8 * get_ptr_text_for_watch_breifing_page();
s32 get_recording_ramrom_flag();
MPSCENARIOS get_scenario();
s32 get_show_patrols_flag();
OBJECTIVESTATUS get_status_of_objective();
u8 * get_text_for_objective();
f32 get_vertical_offset_on_solo_watch_menu_for_item();
f32 get_vertical_position_solo_watch_menu_main_page_for_item();
f32 get_xrotation_solo_watch_menu_for_item();
f32 get_yrotation_solo_watch_menu_for_item();
s32 getmusictrack_or_randomtrack();
void gotoAboveDebugOption();
void gotoBelowDebugOption();
void gotoLeftDebugOption();
void gotoRightDebugOption();
Gfx * gunDrawWatchAmmoDisplay();
void gunSpawnGLGrenade();
void gunUpdateAttachedRocket();
void handle_alarm_gas_timer_calldamage();
void hatAssignToChr();
PropRecord * hatCreateForChr();
void image_entries_load();
void increment_num_deaths();
void increment_num_suicides_display_MP();
void increment_num_times_killed_MwtGC();
u8 * indycommHostCheckFileExists();
void indycommHostRamRomLoad();
void indycommHostSaveFile();
void initAnimationsBuffer();
void initGameData();
void initWeaponAnimGroups();
void init_player_gait_object();
void init_watch_at_start_of_stage();
void init_weapon_animation_groups_maybe();
void initializeDebugCameraPosition();
void initializeGunBarrelIntro();
void initializeRoomData();
s32 interface_menu0B_runstage();
bool intersectRayTriangle();
s32 isGunBarrelInMode2();
s32 isGunBarrelInMode9();
bool is_clock_drawn_onscreen();
/* Functions whose true prototype has default-promoted (s8/u16/char) params
* CANNOT be declared with empty parentheses (C11 6.7.6.3p16: a promoted arg
* type cannot match an empty parameter name list). For those, emit the full
* prototype with the TRUE parameter types (int would be an incompatible
* adjusted type, not a compatible one). */
s8 joy7000C174(s8);
s8 joy7000C284(s8);
void joyConsumeSamplesWrapper();
u16 joyGetButtons(s8, u16);
u16 joyGetButtonsPressedThisFrame(s8, u16);
s8 joyGetControllerCount();
s8 joyGetStickX(s8);
s32 joyGetStickXInRange(s8, s32, s32);
s8 joyGetStickY(s8);
s32 joyGetStickYInRange(s8, s32, s32);
void langClearBank();
u8 * langGet();
void langInit();
void lightFixtureBreak();
void lightFixtureInitTables();
void load_ramrom_from_devtool();
void loop_set_sound_effect_all_slots();
s32 lvlGetCurrentStageToLoad();
DIFFICULTY lvlGetSelectedDifficulty();
void * malloc();
void matrix_4x4_7F058C64();
void matrix_4x4_7F058C88();
void matrix_4x4_invert_affine();
void * memaAlloc();
void memaFree();
s32 memaGetLongestFree();
int memcmp();
void * memcpy();
u32 modelFindNextProjectileHitCandidate();
void modelGetXYExtents();
s32 modelLoad();
bool modelTestRayIntersectsNodeBBox();
void modelmgrAllocateAnimModelSlots();
void modelmgrAllocateModelSlots();
void mpwatchMenuTick();
void mtxLoadRandomRotation();
void null_init_main_1();
void null_init_main_2();
void null_init_main_3();
void obInit();
void obLoadBGFileBytesAtOffset();
bool objGetOnscreenRenderBounds();
void objHit();
bool objIsHealthy();
bool objTestForInteract();
s32 objTick();
s32 objectiveGetCount();
bool objectiveIsAllComplete();
void objectivestatusCheckRoomEntered();
void piCreateManager();
s32 playerGetCrouchPos();
s32 playerTick();
Gfx * process_monitor_animation_microcode();
s32 propDoorGetCdTypes();
TICKOP propdoorInteract();
bool propobjFindHit();
u32 randomGetNext();
void redarken_lights_in_room();
void removed_debug_roomblocks_feature();
Gfx * renderGunbarrelEyeIntroSequence();
void replay_recorded_ramrom_from_indy();
void resetDebugCameraToPlayerPosition();
void reset_counter_rand_body_head();
Gfx * retrieve_display_rareware_logo();
void rle_expand_8bit();
s32 rmonStatus();
void romCopy();
void save_img_index_to_obj_ani_slot();
void save_ramrom_to_devtool();
void select_ramrom_to_play();
void setMPWeaponSet();
void setRamRomRecordSlot();
void * setSPToEnd();
void setSixExplosionAndSmokeEntries();
void setTextOrientation();
void set_BONDdata_field_10E0();
void set_BONDdata_outside_watch_menu_flag();
void set_bondata_invincible_flag();
void set_cur_player();
void set_cur_player_look_vertical_inverted();
Gfx * set_enviro_fog_for_items_in_solo_watch_menu();
void set_favorite_weapon_for_every_player();
void set_gu_scale();
void set_max_ammo_for_cur_player();
void set_missionstate();
void set_missionstate_zero();
void set_obj_collision_flag();
void set_players_team_or_scenario_item_flag();
void set_show_patrols_flag();
void set_sound_effect_for_weapontype_collection();
ObjectRecord * setupFindObjForReuse();
void setupRarewareLogoData();
void setupUpdateObjectRoomPosition();
s32 sizepropdef();
Gfx * skyRender();
void skySetStageNum();
void skyTick();
/* Exact match of GCC's built-in (and MSVCRT) prototype; size_t is
* `unsigned long long` on this MinGW toolchain. */
int snprintf(char *, unsigned long long, const char *, ...);
PropRecord * something_with_generating_object();
void speedgraphInit();
void speedgraphMarkerUpdate();
void stanDetermineEOF();
void stanLoadFile();
bool stanTileHasZeroArea();
void stop_recording_ramrom();
void store_favorite_weapon_current_player();
void store_osgetcount();
char * strcat();
int strcmp();
char * strcpy();
size_t strlen();
char * strncpy();
long int strtol();
void sub_GAME_7F008DE4();
s32 sub_GAME_7F03DB70();
void sub_GAME_7F04E9BC();
void sub_GAME_7F057DF8();
void sub_GAME_7F059334();
void sub_GAME_7F05C614();
void sub_GAME_7F05D690();
void sub_GAME_7F05E6B4();
void sub_GAME_7F05E83C();
void sub_GAME_7F05E978();
Gfx * sub_GAME_7F061E18();
u16 sub_GAME_7F06D2E4();
s32 sub_GAME_7F074CAC();
void sub_GAME_7F078464();
Gfx* sub_GAME_7F09343C();
Gfx* sub_GAME_7F09365C();
void sub_GAME_7F0A6A80();
int sub_GAME_7F0AC0E8();
/* Region-dependent signature (bg.c): EU is void/4-arg, US/JP is s32/5-arg. */
#if defined(VERSION_EU)
void sub_GAME_7F0B7F84();
#else
s32 sub_GAME_7F0B7F84();
#endif
s32 sub_GAME_7F0B993C();
s32 sub_GAME_7F0B9F14();
void sub_GAME_7F0BD8FC();
void sub_GAME_7F0C1310();
s32 texBuildLookup();
s32 texChannelsToPixels();
void texCopyGdls();
s32 texGetWidthAtLod();
s32 texInflateLookup();
s32 texInflateLookupFromBuffer();
void texLoadFromDisplayList();
s32 texLoadFromGdl();
s32 texReadUncompressed();
void texSelect();
s32 texShrinkNonPaletted();
long long time();
const char * tokenFind();
s32 tokenReadIo();
void transform3Dto2DWithZScaling();
void updateDebugCameraWorldPosition();
void updateFrameCounters();
void used_to_load_1st_person_model_on_demand();
void viDebugRemoved();
u16 viGetPerspNorm();
s16 viGetViewHeight();
s16 viGetViewLeft();
s16 viGetViewTop();
s16 viGetViewWidth();
s16 viGetX();
s16 viGetY();
void viSetColorMode16Bit();
void viSetColorMode32Bit();
s32 vtxstore_allocate();
Gfx* watchRenderController();
Gfx * watchRenderControllerOpaque();
u32 weaponLoadProjectileModels();
void zbufSetBuffer();
#endif /* PORT && __x86_64__ && !__cplusplus */
#endif /* _PC_PROTOS_H_ */
+57
View File
@@ -36,6 +36,63 @@ u32 romdataGetRomSize(void);
*/
int romdataCartAddrValid(u32 addr, u32 size);
/*
* D33 (docs/PCPortResearch.md): the .z64 file stores structured multi-byte
* fields in big-endian byte order (bit-packed streams are raw). Per-field
* BE->LE transform of the animation data segment after romCopy() and before
* expand_ani_table_entries(): u32 -> bswap32, u16 -> bswap16, u8 -> identity.
*
* blob/blobSize: the animation_data segment as copied into DRAM by
* alloc_load_expand_ani_table(). tableA/tableB: the null-terminated offset
* arrays (animation_table_ptrs1/2); an entry of 1 is the null sentinel and
* is skipped. Transforms each 20-byte record header at blob+off and its
* [bd, bs) descriptor array (6-byte ModelAnimBitField records).
*/
void romdataFixupAnimationData(u8 *blob, u32 blobSize,
const s32 *tableA, const s32 *tableB);
/*
* D35 (docs/PCPortResearch.md): the music sequence table segment ( RareALSeqBankFile )
* stores its fields big-endian: +0 u16 seqCount, then 8-byte entries at +4:
* u32 address offset, u16 uncompressed_len, u16 len. Per-field BE->LE
* transform after romCopy(). Entries are decoded only as far as blobSize
* allows (the header-only 0x10-byte copy decodes the header plus entry 0;
* the caller re-copies fresh BE bytes before the full-size call).
*/
void romdataFixupMusicSeqTable(u8 *blob, u32 blobSize);
/*
* D37 (docs/PCPortResearch.md): the libaudio SFX/instrument bank segments
* (ALBankFile trees: ALBank -> ALInstrument -> ALSound -> ALWaveTable /
* ALEnvelope / ALKeyMap / ALADPCMloop / ALADPCMBook) are serialized in ROM
* as big-endian scalars with 4-byte packed table-relative offsets where the
* C structs have pointers. On N64 (4-byte pointers, BE MIPS) the ROM layout
* and the C layout coincide; on x86-64 they diverge both in stride and
* endianness, so alBnkfNew() misparses the tree (bankCount read as 256,
* wild pointer walks).
*
* The PC image is larger than the ROM segment (each 4-byte packed offset
* becomes an 8-byte pointer slot), and structs are packed back-to-back in
* ROM, so the tree cannot be converted in place: it is re-laid out into a
* compact image where every sub-struct sits once, 8-byte aligned, and each
* pointer slot stores the sub-struct's new offset from the image start
* (zero-extended). alBnkfNew()/_bnkfPatch*() then rebase those offsets to
* absolute pointers unmodified (ptr + (s32)file). Wavetable `base` fields
* are offsets into the separate wavetable data segment and stay offsets;
* _bnkfPatchWaveTable() rebases them with the `table` argument.
*
* romdataAudioBankPcSize(): size in bytes of the PC-native image for a
* ROM-layout bank blob (src/srcSize), 16-aligned. The caller must allocate
* at least this much (music.c grows the alHeapAlloc accordingly).
*
* romdataFixupAudioBank(): converts in place; blob must hold srcSize bytes
* of pristine ROM layout and allocSize >= romdataAudioBankPcSize() bytes.
* Call after romCopy() (which copies exactly srcSize bytes) and before
* alBnkfNew().
*/
u32 romdataAudioBankPcSize(const u8 *src, u32 srcSize);
void romdataFixupAudioBank(u8 *blob, u32 srcSize, u32 allocSize);
/*
* Map an N64 virtual address (0xA0000000 RDRAM space) to the corresponding
* pointer in the loaded image. Used to resolve asset/segment references.
+21
View File
@@ -0,0 +1,21 @@
/*
* PC port shim for PR/ucode.h.
*
* Pass-through to the real header, plus — because ucode.h is the LAST include
* in <ultra64.h> (after libaudio.h, whose partial-parse poisoning is the
* reason pc_protos.h cannot anchor earlier, e.g. in gbi.h) — the D38
* prototype header for implicitly declared game functions.
*
* Inert in the N64 build (port/shim is not on its include path).
*/
#ifndef _PORT_SHIM_UCODE_H_
#define _PORT_SHIM_UCODE_H_
#if defined(PORT)
# include "include/PR/ucode.h"
# include "pc_protos.h"
#else
# include <PR/ucode.h>
#endif
#endif /* _PORT_SHIM_UCODE_H_ */
+14
View File
@@ -23,6 +23,20 @@
#if defined(PORT)
#include "src/bondconstants.h"
/*
* D38: src/bondconstants.h defines ntohl()/ntohs() as function-like macros
* (byte-order helpers over CharArrayTo16/32). In any TU that also parses
* <winsock.h> (port files that include <windows.h>, or C++ TUs whose host
* headers pull in winsock), those macros expand winsock's own
* `u_long WSAAPI ntohl(u_long)` declarations into garbage and the build
* breaks. On little-endian x86-64 the host functions have identical
* semantics (a byte swap), so neutralize the macros on PC and let calls bind
* to real functions: declared in port/include/pc_protos.h, defined in
* port/src/pc_netorder.c. Game logic is unchanged (same computed values).
*/
#undef ntohl
#undef ntohs
#ifndef _PORT_BITFLAG_ENUMS_DEFINED
#define _PORT_BITFLAG_ENUMS_DEFINED
+4 -1
View File
@@ -36,6 +36,9 @@
#include <inttypes.h>
#include <excpt.h>
/* D38: MinGW's <windows.h> does not declare this; add it (NT4+ API). */
extern int GetCurrentThreadStackLimits(PVOID *lpLowAddress, PVOID *lpHighAddress);
static LPTOP_LEVEL_EXCEPTION_FILTER prevExFilter;
static void *crashGetModuleBase(const void *addr)
@@ -69,7 +72,7 @@ static void crashStackTraceRaw(char *msg, PEXCEPTION_POINTERS exinfo)
(unsigned)(context.FloatSave.ControlWord & 0xffff),
(unsigned)(context.FloatSave.StatusWord & 0xffff));
PVOID low = NULL, high = NULL;
if (GetCurrentThreadStackLimits(low, high)) {
if (GetCurrentThreadStackLimits(&low, &high)) {
CRASH_MSG("thread stack: %p..%p Rsp=%p used=%llu bytes\n",
low, high, (void *)context.Rsp,
(unsigned long long)((uintptr_t)high - (uintptr_t)context.Rsp));
+5
View File
@@ -13,6 +13,11 @@
#include <stdlib.h>
#include <string.h>
/* D38: <stdlib.h> resolves to the decomp's N64 stub (body #if 0'd) via the
* include path, so declare the host functions this file uses. */
extern void *calloc(unsigned long long count, unsigned long long size);
extern void free(void *ptr);
#include "platform.h"
#include "fs.h"
+1
View File
@@ -43,6 +43,7 @@
#include "platform.h"
#include "system.h"
#include "crash.h" /* D38: crashDumpThreads() */
#include "video.h"
#include "audio.h"
#include "input.h"
+32
View File
@@ -0,0 +1,32 @@
/*
* pc_netorder.c - D38: host byte-order helpers (see docs/PCPortResearch.md).
*
* src/bondconstants.h implements ntohl()/ntohs() as function-like macros over
* CharArrayTo16/32 ("rewrite these to use char array as system provided funcs
* do not"). Those macros expand <winsock.h>'s own `u_long WSAAPI ntohl(u_long)`
* declarations into garbage in any TU that parses both, so
* port/shim/bondconstants.h neutralizes them on PC. This file provides the
* real functions those calls now bind to (declared in pc_protos.h).
*
* On little-endian x86-64 a byte swap is exactly what CharArrayTo16/32
* computed:
* CharArrayTo16(v,0) = v[1] | v[0]<<8 == ntohs(v) on LE
* CharArrayTo32(v,0) = v[1]<<16|v[2]<<8|v[3]|v[0]<<24 == ntohl(v) on LE
* so game code (e.g. the ntohs() calls in src/game/chrai.c) keeps its
* N64-correct semantics without touching winsock or linking ws2_32.
*/
#include <PR/ultratypes.h>
/* Signatures match winsock's (u_short/u_long) so TUs that also parse
* <winsock.h> see one consistent declaration; game code only ever passes
* 16/32-bit values, for which the byte swap below is exact. */
unsigned short ntohs(unsigned short v)
{
return (unsigned short)((v >> 8) | (v << 8));
}
unsigned long ntohl(unsigned long v)
{
return __builtin_bswap32((unsigned int)v);
}
+2 -2
View File
@@ -3318,11 +3318,11 @@
.global _GlobalimagetableSegmentRomStart
.set _GlobalimagetableSegmentRomStart, 0x1029D160
.global _GlobalimagetableSegmentRomEnd
.set _GlobalimagetableSegmentRomEnd, 0x1029DC28
.set _GlobalimagetableSegmentRomEnd, 0x1029E558
.global _GlobalimagetableSegmentStart
.set _GlobalimagetableSegmentStart, 0x1029D160
.global _GlobalimagetableSegmentEnd
.set _GlobalimagetableSegmentEnd, 0x1029DC28
.set _GlobalimagetableSegmentEnd, 0x1029E558
.global _rarewarelogoSegmentRomStart
.set _rarewarelogoSegmentRomStart, 0x1029E560
.global _rarewarelogoSegmentRomEnd
+634
View File
@@ -20,6 +20,11 @@
#include <stdlib.h>
#include <string.h>
/* D38: <stdio.h>/<stdlib.h> resolve to the decomp's N64 stubs via the include
* path; declare the host functions this file uses. */
extern void *malloc(unsigned long long size);
extern int snprintf(char *str, unsigned long long maxsize, const char *format, ...);
#include "platform.h"
#if defined(PLATFORM_WINDOWS)
@@ -39,6 +44,16 @@ static u8 *rom = NULL; /* heap copy (fallback path) */
static u32 romSize = 0;
static int mappedAtCartBase = 0;
/* D34 (docs/PCPortResearch.md): PE image base, low 32 bits zero. On N64 the
* animation_data segment links at VMA 0, so game code computes record
* addresses as `(s32)&ANIM_DATA_x + ptr_animation_table` where &ANIM_DATA_x
* is a small segment offset. A PE image cannot be placed at a low address,
* so assets/animationtable_data.h (PC branch) defines ANIM_DATA_x as an
* lvalue at g_pc_animdata_base + offset, making (s32)&ANIM_DATA_x equal the
* offset exactly. Set once in romdataInit(); see also the D34 note in that
* header. */
unsigned char *g_pc_animdata_base = NULL;
/* Candidate ROM file names, in priority order. The decomp repo ships the
* base ROM at the repo root (baserom.<r>.z64); a user-supplied final ROM in
* data/ takes precedence. */
@@ -88,6 +103,22 @@ int romdataInit(void)
char listBuf[512];
strcpy(listBuf, list);
/* D34: derive the PE image base (any symbol's address with its low 32
* bits cleared) for the ANIM_DATA_* lvalues. Requires a 4GiB-aligned
* image base — always true for the default x64 PE base 0x140000000. */
{
static char probe;
uintptr_t p = (uintptr_t)&probe;
uintptr_t hi = p & ~(uintptr_t)0xFFFFFFFFu;
if (hi == 0) {
sysLogPrintf(LOG_ERROR, "romdataInit: image base 0x%llX is not "
"4GiB-aligned; ANIM_DATA_* offsets would be wrong",
(unsigned long long)p);
return -1;
}
g_pc_animdata_base = (u8 *)hi;
}
/* Try each candidate in the data dir, next to the exe, and in the CWD
* (the decomp repo ships baserom.<r>.z64 at the repo root). */
for (char *tok = strtok(listBuf, "|"); tok;
@@ -195,3 +226,606 @@ const void *romdataMapVa(u32 va)
(void)va;
return NULL;
}
/*
* D33 (docs/PCPortResearch.md): the .z64 file stores structured multi-byte
* fields in big-endian byte order; bit-packed data streams are raw. No single
* uniform word transform exists (mixed field widths), so the animation_data
* segment is converted per field at load time, after romCopy() and before
* expand_ani_table_entries() reads/writes the fields as LE:
*
* 20-byte record at blob+off:
* +0x00 u32 entries-segment offset -> bswap32
* +0x04 u16 frame count -> bswap16
* +0x06 u8 angle bit width -> identity
* +0x07 u8 loop flag -> identity
* +0x08 u32 bitDescriptors blob offset -> bswap32
* +0x0C u16 bitsPerFrame root motion -> bswap16
* +0x0E u16 frame size in bits -> bswap16
* +0x10 u32 bitStream blob offset -> bswap32
* descriptor array at [bd, bs), 6-byte ModelAnimBitField records:
* +0 u16 bitOffset -> bswap16
* +2 u8 bitCount -> identity
* +3 u8 pad -> identity
* +4 u16 valueOffset -> bswap16
*
* The entries segment (per-frame joint angles) is left identity — it is
* identity-encoded bit-packed data, not consumed at boot.
*/
static u32 romdataBswap32(u32 v)
{
return ((v & 0x000000FFu) << 24) | ((v & 0x0000FF00u) << 8) |
((v & 0x00FF0000u) >> 8) | ((v & 0xFF000000u) >> 24);
}
static u16 romdataBswap16(u16 v)
{
return (u16)((v << 8) | (v >> 8));
}
static void romdataFixupAnimTable(const s32 *table, u8 *blob, u32 blobSize)
{
while (*table != 0) {
s32 off = *table++;
if (off == 1)
continue; /* null sentinel */
if (off < 0 || (u32)off + 20 > blobSize) {
sysLogPrintf(LOG_ERROR, "romdataFixupAnimationData: bad record offset 0x%X",
off);
continue;
}
u8 *rec = blob + off;
*(u32 *)(rec + 0x00) = romdataBswap32(*(const u32 *)(rec + 0x00));
*(u16 *)(rec + 0x04) = romdataBswap16(*(const u16 *)(rec + 0x04));
/* +0x06/+0x07 are u8: identity */
*(u32 *)(rec + 0x08) = romdataBswap32(*(const u32 *)(rec + 0x08));
*(u16 *)(rec + 0x0C) = romdataBswap16(*(const u16 *)(rec + 0x0C));
*(u16 *)(rec + 0x0E) = romdataBswap16(*(const u16 *)(rec + 0x0E));
*(u32 *)(rec + 0x10) = romdataBswap32(*(const u32 *)(rec + 0x10));
u32 bd = *(const u32 *)(rec + 0x08); /* descriptor array start */
u32 bs = *(const u32 *)(rec + 0x10); /* bitStream start */
if (bd == 0 && bs == 0)
continue; /* no descriptors/stream */
if (!(bd < bs && bs <= blobSize && ((bs - bd) % 6u) == 0u)) {
sysLogPrintf(LOG_ERROR, "romdataFixupAnimationData: bad descriptor range [0x%X, 0x%X)",
bd, bs);
continue;
}
for (u32 p = bd; p < bs; p += 6) {
*(u16 *)(blob + p) = romdataBswap16(*(const u16 *)(blob + p));
/* +2 bitCount, +3 pad are u8: identity */
*(u16 *)(blob + p + 4) = romdataBswap16(*(const u16 *)(blob + p + 4));
}
}
}
void romdataFixupAnimationData(u8 *blob, u32 blobSize,
const s32 *tableA, const s32 *tableB)
{
romdataFixupAnimTable(tableA, blob, blobSize);
if (tableB != tableA)
romdataFixupAnimTable(tableB, blob, blobSize);
}
/*
* D35 (docs/PCPortResearch.md): the music sequence table segment stores its
* fields big-endian:
*
* header at blob+0 (RareALSeqBankFile):
* +0x0 u16 seqCount -> bswap16
* +0x2 u16 unk -> identity (unused)
* entries at blob+4, 8-byte RareALSeqData records:
* +0 u32 address offset -> bswap32
* +4 u16 uncompressed_len -> bswap16
* +6 u16 len -> bswap16
*
* Entries are decoded only as far as blobSize allows: the 0x10-byte header
* copy decodes the header plus entry 0, and the caller re-copies fresh BE
* bytes before the full-size call.
*/
void romdataFixupMusicSeqTable(u8 *blob, u32 blobSize)
{
if (blobSize < 4) {
sysLogPrintf(LOG_ERROR, "romdataFixupMusicSeqTable: blob too small (%u)",
blobSize);
return;
}
*(u16 *)(blob + 0) = romdataBswap16(*(const u16 *)(blob + 0)); /* seqCount */
u32 count = *(const u16 *)(blob + 0);
u32 maxEntries = (blobSize - 4) / 8;
if (count > maxEntries) {
sysLogPrintf(LOG_ERROR,
"romdataFixupMusicSeqTable: seqCount %u exceeds blob capacity %u",
count, maxEntries);
count = maxEntries;
}
for (u32 i = 0; i < count; i++) {
u8 *e = blob + 4 + 8 * i;
*(u32 *)(e + 0) = romdataBswap32(*(const u32 *)(e + 0));
*(u16 *)(e + 4) = romdataBswap16(*(const u16 *)(e + 4));
*(u16 *)(e + 6) = romdataBswap16(*(const u16 *)(e + 6));
}
}
/*
* D37 (docs/PCPortResearch.md): convert a ROM-serialized libaudio bank tree
* (ALBankFile -> ALBank -> ALInstrument -> ALSound -> ALWaveTable /
* ALEnvelope / ALKeyMap / ALADPCMloop / ALRawLoop / ALADPCMBook) to
* PC-native form. See the header for the contract.
*
* Why a re-layout rather than an in-place transform: on N64 (4-byte
* pointers, BE MIPS) the ROM layout and the C struct layout coincide. On
* x86-64 every pointer field doubles to 8 bytes, so each struct's PC image
* is larger than its ROM form — and structs are packed back-to-back in ROM,
* so a struct's expanded tail overlaps whatever follows it (e.g. the
* ALWaveTable book slot at +24 lands inside the ALADPCMBook that sits 12
* bytes after the wavetable in ROM). The tree is therefore re-laid out into
* a fresh compact image: every sub-struct is placed once, 8-byte aligned,
* and each pointer slot stores the sub-struct's NEW offset from the image
* start (zero-extended to 64 bits). alBnkfNew()/_bnkfPatch*() then rebase
* those offsets to absolute pointers unmodified (ptr + (s32)file). The
* caller must allocate romdataAudioBankPcSize() bytes for the image.
*
* Wavetable `base` fields are offsets into the separate wavetable data
* segment, not into this blob: they are byte-swapped and zero-extended but
* left as offsets — _bnkfPatchWaveTable() rebases them with its `table`
* argument (the cart address of that segment on PC).
*
* ROM layout (offsets within each struct; multi-byte fields big-endian):
* ALBankFile +0 s16 revision, +2 s16 bankCount, +4 N x u32 bank offsets
* ALBank +0 s16 instCount, +2 u8 flags, +3 u8 pad,
* +4 s32 sampleRate, +8 u32 percussion offset,
* +12 M x u32 instrument offsets
* ALInstrument +0..+11 12 x u8, +12 s16 bendRange, +14 s16 soundCount,
* +16 K x u32 sound offsets
* ALSound +0 u32 envelope, +4 u32 keyMap, +8 u32 wavetable,
* +12 u8 pan, +13 u8 volume, +14 u8 flags
* ALWaveTable +0 u32 base (offset into the wavetable data segment),
* +4 s32 len, +8 u8 type, +9 u8 flags,
* adpcm: +12 u32 loop, +16 u32 book; raw: +12 u32 loop
* ALEnvelope +0/+4/+8 s32 times, +12/+13 u8 volumes
* ALKeyMap 6 x u8 (identity)
* ALADPCMloop +0/+4/+8 u32 start/end/count, +12 s16[16] state
* ALRawLoop +0/+4/+8 u32 start/end/count
* ALADPCMBook +0 s32 order, +4 s32 npredictors,
* +8 2*order*npredictors*4 x s16 coefficients
*/
#define AF_UNSET 0xFFFFFFFFu
struct afCtx {
const u8 *src; /* pristine ROM-layout bytes; first srcSize valid */
u32 srcSize;
u8 *dst; /* target image buffer (allocSize bytes) */
u32 allocSize;
int write; /* 0 = measure pass, 1 = write pass */
u32 pos; /* write cursor (kept 8-aligned by afReserve) */
u8 *visited; /* [srcSize] per-struct visited marker */
u32 *newOff; /* [srcSize] ROM offset -> image offset (write pass) */
s16 revision;
s16 bankCount;
int errors;
};
/* ROM-layout reads are big-endian. */
static u16 afRd16(const struct afCtx *c, u32 off)
{
return (u16)((c->src[off] << 8) | c->src[off + 1]);
}
static u32 afRd32(const struct afCtx *c, u32 off)
{
return ((u32)c->src[off] << 24) | ((u32)c->src[off + 1] << 16) |
((u32)c->src[off + 2] << 8) | (u32)c->src[off + 3];
}
static void afWr16(struct afCtx *c, u32 off, u16 v)
{
c->dst[off] = (u8)v;
c->dst[off + 1] = (u8)(v >> 8);
}
static void afWr32(struct afCtx *c, u32 off, u32 v)
{
/* Host byte order (LE on x86-64): libaudio reads these with plain loads. */
c->dst[off] = (u8)v;
c->dst[off + 1] = (u8)(v >> 8);
c->dst[off + 2] = (u8)(v >> 16);
c->dst[off + 3] = (u8)(v >> 24);
}
/* Zero-extended image-relative offset into an 8-byte pointer slot. */
static void afWrPtr(struct afCtx *c, u32 off, u32 value)
{
afWr32(c, off, value);
c->dst[off + 4] = c->dst[off + 5] = c->dst[off + 6] = c->dst[off + 7] = 0;
}
/* -1: invalid offset, 0: first visit, 1: already placed. */
static int afEnter(struct afCtx *c, u32 off)
{
if (off == 0 || off >= c->srcSize)
return -1;
if (c->visited[off])
return 1;
c->visited[off] = 1;
return 0;
}
/* Align the cursor, advance it by size, and return the reserved start
* (write pass) or 0 (measure pass). */
static u32 afReserve(struct afCtx *c, u32 size)
{
u32 n = ((c->pos + 7u) & ~7u);
c->pos = n + size;
return c->write ? n : 0;
}
/* New image offset of a referenced sub-struct, or 0 if absent/invalid. */
static u32 afPtr(struct afCtx *c, u32 off)
{
if (off == 0 || off >= c->srcSize)
return 0;
return (c->newOff[off] != AF_UNSET) ? c->newOff[off] : 0;
}
static void afFixupBook(struct afCtx *c, u32 o)
{
int r = afEnter(c, o);
if (r == 1)
return;
if (r < 0 || o + 8 > c->srcSize) { c->errors++; return; }
s32 order = (s32)afRd32(c, o);
s32 npred = (s32)afRd32(c, o + 4);
u32 size = 8;
if (order > 0 && npred > 0) {
/* Coefficient array is 2*order*npredictors*ADPCMVSIZE(8) bytes on
* disk (see load.c bookSize): that many s16 values. */
size += 2u * (u32)order * (u32)npred * 8u;
} else {
sysLogPrintf(LOG_ERROR,
"romdataFixupAudioBank: bad ADPCM book @0x%X (order %d, npredictors %d)",
o, order, npred);
}
u32 n = afReserve(c, size);
if (!c->write)
return;
c->newOff[o] = n;
afWr32(c, n, (u32)order);
afWr32(c, n + 4, (u32)npred);
if (order > 0 && npred > 0 && o + size <= c->srcSize) {
u32 ncoeff = 2u * (u32)order * (u32)npred * 4u;
for (u32 i = 0; i < ncoeff; i++)
afWr16(c, n + 8 + 2 * i, afRd16(c, o + 8 + 2 * i));
}
}
static void afFixupAdpcmLoop(struct afCtx *c, u32 o)
{
int r = afEnter(c, o);
if (r == 1)
return;
if (r < 0 || o + 44 > c->srcSize) { c->errors++; return; }
u32 n = afReserve(c, 48);
if (!c->write)
return;
c->newOff[o] = n;
for (u32 i = 0; i < 3; i++) /* start/end/count */
afWr32(c, n + 4 * i, afRd32(c, o + 4 * i));
for (u32 i = 0; i < 16; i++) /* ADPCM_STATE = s16[16] */
afWr16(c, n + 12 + 2 * i, afRd16(c, o + 12 + 2 * i));
}
static void afFixupRawLoop(struct afCtx *c, u32 o)
{
int r = afEnter(c, o);
if (r == 1)
return;
if (r < 0 || o + 12 > c->srcSize) { c->errors++; return; }
u32 n = afReserve(c, 16);
if (!c->write)
return;
c->newOff[o] = n;
for (u32 i = 0; i < 3; i++) /* start/end/count */
afWr32(c, n + 4 * i, afRd32(c, o + 4 * i));
}
static void afFixupEnvelope(struct afCtx *c, u32 o)
{
int r = afEnter(c, o);
if (r == 1)
return;
if (r < 0 || o + 14 > c->srcSize) { c->errors++; return; }
u32 n = afReserve(c, 16);
if (!c->write)
return;
c->newOff[o] = n;
for (u32 i = 0; i < 3; i++) /* ALMicroTime x3 */
afWr32(c, n + 4 * i, afRd32(c, o + 4 * i));
c->dst[n + 12] = c->src[o + 12]; /* u8 volumes: identity */
c->dst[n + 13] = c->src[o + 13];
}
static void afFixupWaveTable(struct afCtx *c, u32 o)
{
int r = afEnter(c, o);
if (r == 1)
return;
if (r < 0 || o + 20 > c->srcSize) { c->errors++; return; }
u32 base = afRd32(c, o); /* offset into wavetable data */
s32 len = (s32)afRd32(c, o + 4);
u8 type = c->src[o + 8];
u32 loop, book;
if (type == 0 /* AL_ADPCM_WAVE */) {
loop = afRd32(c, o + 12);
book = afRd32(c, o + 16);
if (loop) afFixupAdpcmLoop(c, loop); /* children first */
if (book) afFixupBook(c, book);
} else if (type == 1 /* AL_RAW16_WAVE */) {
loop = afRd32(c, o + 12);
book = 0;
if (loop) afFixupRawLoop(c, loop);
} else {
sysLogPrintf(LOG_ERROR,
"romdataFixupAudioBank: unknown wavetable type %d @0x%X",
type, o);
loop = book = 0;
}
u32 n = afReserve(c, 32);
if (!c->write)
return;
c->newOff[o] = n;
afWrPtr(c, n, base); /* stays an offset; rebased with `table` */
afWr32(c, n + 8, (u32)len);
c->dst[n + 12] = type;
c->dst[n + 13] = c->src[o + 9]; /* flags: identity */
if (type == 0) {
afWrPtr(c, n + 16, afPtr(c, loop));
afWrPtr(c, n + 24, afPtr(c, book));
} else {
afWrPtr(c, n + 16, afPtr(c, loop));
}
}
static void afFixupSound(struct afCtx *c, u32 o)
{
int r = afEnter(c, o);
if (r == 1)
return;
if (r < 0 || o + 15 > c->srcSize) { c->errors++; return; }
u32 env = afRd32(c, o);
u32 km = afRd32(c, o + 4);
u32 wt = afRd32(c, o + 8);
if (env) afFixupEnvelope(c, env); /* children first */
if (km) {
int kr = afEnter(c, km);
if (kr == -1 || km + 6 > c->srcSize)
c->errors++;
else if (kr == 0) {
u32 kn = afReserve(c, 8);
if (c->write) {
c->newOff[km] = kn;
memcpy(c->dst + kn, c->src + km, 6); /* ALKeyMap: all u8 */
}
}
}
if (wt) afFixupWaveTable(c, wt);
u32 n = afReserve(c, 32);
if (!c->write)
return;
c->newOff[o] = n;
afWrPtr(c, n, afPtr(c, env));
afWrPtr(c, n + 8, afPtr(c, km));
afWrPtr(c, n + 16, afPtr(c, wt));
c->dst[n + 24] = c->src[o + 12]; /* pan/volume/flags: identity */
c->dst[n + 25] = c->src[o + 13];
c->dst[n + 26] = c->src[o + 14];
}
static void afFixupInst(struct afCtx *c, u32 o)
{
int r = afEnter(c, o);
if (r == 1)
return;
if (r < 0 || o + 16 > c->srcSize) { c->errors++; return; }
s16 scount = (s16)afRd16(c, o + 14); /* soundCount */
u32 maxSounds = (c->srcSize - (o + 16)) / 4;
if ((u32)scount > maxSounds) {
sysLogPrintf(LOG_ERROR,
"romdataFixupAudioBank: soundCount %d @0x%X exceeds blob capacity %u",
scount, o, maxSounds);
scount = (s16)maxSounds;
}
for (u32 i = 0; i < (u32)scount; i++) { /* children first */
u32 soff = afRd32(c, o + 16 + 4 * i);
if (soff)
afFixupSound(c, soff);
}
u32 n = afReserve(c, 16 + 8u * (u32)scount);
if (!c->write)
return;
c->newOff[o] = n;
memcpy(c->dst + n, c->src + o, 12); /* 12 x u8: identity */
afWr16(c, n + 12, afRd16(c, o + 12)); /* bendRange */
afWr16(c, n + 14, (u16)scount);
for (u32 i = 0; i < (u32)scount; i++) {
u32 soff = afRd32(c, o + 16 + 4 * i);
afWrPtr(c, n + 16 + 8 * i, afPtr(c, soff));
}
}
static void afFixupBank(struct afCtx *c, u32 o)
{
int r = afEnter(c, o);
if (r == 1)
return;
if (r < 0 || o + 12 > c->srcSize) { c->errors++; return; }
s16 icount = (s16)afRd16(c, o); /* instCount */
u32 perc = afRd32(c, o + 8);
u32 maxInsts = (c->srcSize - (o + 12)) / 4;
if ((u32)icount > maxInsts) {
sysLogPrintf(LOG_ERROR,
"romdataFixupAudioBank: instCount %d @0x%X exceeds blob capacity %u",
icount, o, maxInsts);
icount = (s16)maxInsts;
}
if (perc) afFixupInst(c, perc); /* children first */
for (u32 i = 0; i < (u32)icount; i++) {
u32 ioff = afRd32(c, o + 12 + 4 * i);
if (ioff)
afFixupInst(c, ioff);
}
u32 n = afReserve(c, 16 + 8u * (u32)icount);
if (!c->write)
return;
c->newOff[o] = n;
afWr16(c, n, (u16)icount);
c->dst[n + 2] = c->src[o + 2]; /* flags: identity */
c->dst[n + 3] = c->src[o + 3]; /* pad : identity */
afWr32(c, n + 4, afRd32(c, o + 4)); /* sampleRate */
afWrPtr(c, n + 8, afPtr(c, perc));
for (u32 i = 0; i < (u32)icount; i++) {
u32 ioff = afRd32(c, o + 12 + 4 * i);
afWrPtr(c, n + 16 + 8 * i, afPtr(c, ioff));
}
}
/* One pass over the tree. Returns the image size (16-aligned). */
static u32 afWalk(struct afCtx *c)
{
c->revision = (s16)afRd16(c, 0);
c->bankCount = (s16)afRd16(c, 2);
if (c->revision != 0x4231 /* AL_BANK_VERSION 'B1' */) {
sysLogPrintf(LOG_ERROR,
"romdataFixupAudioBank: bad bank revision 0x%04X",
(u32)c->revision);
}
u32 maxBanks = (c->srcSize - 4) / 4;
if ((u32)c->bankCount > maxBanks) {
sysLogPrintf(LOG_ERROR,
"romdataFixupAudioBank: bankCount %d exceeds blob capacity %u",
c->bankCount, maxBanks);
c->bankCount = (s16)maxBanks;
}
/* The ALBankFile header must sit at image offset 0 (alBnkfNew rebases
* bankArray entries relative to the blob start), so reserve it first. */
afReserve(c, 8 + 8u * (u32)c->bankCount);
for (s16 i = 0; i < c->bankCount; i++) {
u32 off = afRd32(c, 4 + 4 * (u32)i);
if (off)
afFixupBank(c, off);
}
if (c->write) {
afWr16(c, 0, (u16)c->revision);
afWr16(c, 2, (u16)c->bankCount);
for (s16 i = 0; i < c->bankCount; i++) {
u32 off = afRd32(c, 4 + 4 * (u32)i);
afWrPtr(c, 8 + 8 * (u32)i, afPtr(c, off));
}
}
return ((c->pos + 15u) & ~15u);
}
static void afPass(struct afCtx *c, int write)
{
c->write = write;
c->pos = 0;
memset(c->visited, 0, c->srcSize);
if (c->newOff)
for (u32 i = 0; i < c->srcSize; i++)
c->newOff[i] = AF_UNSET;
afWalk(c);
}
u32 romdataAudioBankPcSize(const u8 *src, u32 srcSize)
{
if (!src || srcSize < 4)
return 0;
struct afCtx c = { .src = src, .srcSize = srcSize, .dst = NULL,
.allocSize = 0, .write = 0, .pos = 0,
.visited = NULL, .newOff = NULL, .errors = 0 };
c.visited = (u8 *)malloc(srcSize);
if (!c.visited)
return 0;
memset(c.visited, 0, srcSize);
u32 n = afWalk(&c);
free(c.visited);
return n;
}
void romdataFixupAudioBank(u8 *blob, u32 srcSize, u32 allocSize)
{
if (!blob || srcSize < 4) {
sysLogPrintf(LOG_ERROR, "romdataFixupAudioBank: blob too small (%u)",
srcSize);
return;
}
/* The write pass lays the image out over the pristine bytes (dst ==
* blob), so both passes must read from an untouched copy. */
struct afCtx c = { .src = NULL, .srcSize = srcSize, .dst = blob,
.allocSize = allocSize, .write = 0, .pos = 0,
.visited = NULL, .newOff = NULL, .errors = 0 };
c.src = (const u8 *)malloc(srcSize);
c.visited = (u8 *)malloc(srcSize);
c.newOff = (u32 *)malloc(4 * (size_t)srcSize);
if (!c.src || !c.visited || !c.newOff) {
free((void *)c.src);
free(c.visited);
free(c.newOff);
sysLogPrintf(LOG_ERROR, "romdataFixupAudioBank: OOM");
return;
}
memcpy((void *)c.src, blob, srcSize);
/* Measure pass, then write pass over the same arrays. */
afPass(&c, 0);
if (c.pos > allocSize) {
sysLogPrintf(LOG_ERROR,
"romdataFixupAudioBank: image 0x%X exceeds allocation 0x%X",
c.pos, allocSize);
free((void *)c.src);
free(c.visited);
free(c.newOff);
return;
}
afPass(&c, 1);
free((void *)c.src);
free(c.visited);
free(c.newOff);
if (c.errors) {
sysLogPrintf(LOG_ERROR,
"romdataFixupAudioBank: %d out-of-range sub-struct(s)",
c.errors);
}
}
+5
View File
@@ -17,6 +17,11 @@
#endif
#include <windows.h>
#include <io.h>
/* D38: <stdlib.h>/<time.h> are shadowed by the decomp's N64 stubs on this
* include path; declare the host functions this file uses. */
extern long long time(long long *t);
extern void abort(void);
extern void exit(int status);
#else
#define _POSIX_C_SOURCE 199309L
#include <unistd.h>
+8
View File
@@ -79,6 +79,14 @@ int videoInit(void)
gfx_set_texture_filter(FILTER_LINEAR);
gfx_set_mipmap_filter(MIPMAP_LINEAR);
/* The GL context is currently current on this (host main) thread, but all
* rendering happens on the game's scheduler thread. WGL only allows a
* context to be current on one thread at a time, so release it here; the
* scheduler thread re-binds it per frame via gfx_sdl_make_context_current()
* (see videoStartFrame). Must come after set_swap_interval above, which
* still needs a current context on this thread. */
gfx_sdl_release_context();
initDone = 1;
sysLogPrintf(LOG_INFO, "video: %dx%d window (native %dx%d)",
(int)gfx_current_dimensions.width, (int)gfx_current_dimensions.height,
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env python3
"""One-shot D38 generator for port/include/pc_protos.h (see docs/PCPortResearch.md §F/D38).
Pipeline (all inputs were captured from a PC build of the decomp):
1. Compile the game TUs and collect `implicit function declaration` warnings
-> build-pc/implicit_funcs.txt (one name per line).
2. Scan the tree for each name's true definition signature
-> build-pc/protomap.json (name -> [return type, params...]).
3. Run this script to emit port/include/pc_protos.h.
NOTE: the committed pc_protos.h is the source of truth and was hand-adjusted
after generation (C-only guard; full prototypes for the 11 functions whose true
signatures use default-promoted parameter types, which C11 forbids matching
with an empty parameter list). Re-running this script overwrites those tweaks —
regenerate only if new implicit declarations appear, then re-apply the tweaks.
"""
import json, re
names = [l.strip() for l in open('build-pc/implicit_funcs.txt') if l.strip()]
pm = json.load(open('build-pc/protomap.json'))
# MSVCRT / not-in-tree: fixed declarations
specials = {
'assert': 'void',
'memcmp': 'int',
'snprintf': 'int',
'time': 'long long',
'GetCurrentThreadStackLimits': 'int',
}
def clean(rtype):
rtype = re.sub(r'\s+', ' ', rtype).strip()
for kw in ('extern ', 'static ', 'inline '):
if rtype.startswith(kw):
rtype = rtype[len(kw):].strip()
return rtype
lines = []
for n in sorted(names):
if n in specials:
rt = specials[n]
elif n in pm:
rt = clean(pm[n][0])
assert rt, (n, pm[n])
else:
raise SystemExit('no type for %s' % n)
lines.append('%s %s();' % (rt, n))
hdr = '''/*
* pc_protos.h - PC-port-only prototypes for implicitly declared functions.
*
* D38 (docs/PCPortResearch.md section F): the decomp has ~72 translation units
* that call ~400 functions without any visible prototype (missing #include of
* the declaring header). Under C11 an implicit declaration assumes `int f()`,
* which on N64 (MIPS, 32-bit pointers) is harmless, but on x86-64 it silently
* truncates every pointer (and 64-bit scalar) return value to 32 bits - e.g.
* tokenFind() in set_mt_tex_alloc() returned a low-32-bit "pointer" that then
* faulted in strtol().
*
* This header declares each of those functions with its TRUE return type and
* an empty parameter list (no argument checking, no dependency on the
* parameter types' headers). Empty-paren declarations are compatible with the
* real prototypes elsewhere, so this is purely additive: it changes only the
* width of the returned value, restoring N64-correct semantics on x86-64.
*
* Anchored at the end of port/shim/PR/gbi.h (PC-only shim), which every game
* TU parses via <ultra64.h>. Inert in the N64 build (no -DPORT).
*/
#ifndef _PC_PROTOS_H_
#define _PC_PROTOS_H_
#if defined(PORT) && defined(__x86_64__)
#include <stddef.h> /* size_t */
#include <PR/gbi.h> /* Gfx, Mtx, Vtx, Light (shimmed on PC) */
#include "bondtypes.h" /* coord3d, PropRecord, ObjectRecord, ModelFileHeader, bool, ITEM_IDS */
#include "bondconstants.h" /* MPSCENARIOS, OBJECTIVESTATUS, PROP, DIFFICULTY, TICKOP */
#include "game/file.h" /* save_data */
#pragma push_macro("assert")
#undef assert
void assert();
#pragma pop_macro("assert")
''' + '\n'.join(lines) + '''
#endif /* PORT && __x86_64__ */
#endif /* _PC_PROTOS_H_ */
'''
open('port/include/pc_protos.h', 'w').write(hdr)
print("wrote port/include/pc_protos.h with %d declarations" % len(lines))
+7 -1
View File
@@ -362,7 +362,13 @@ def build_segments(region, csv, obseg_resolved, ramrom_files, music_tracks,
off, size = csv1("animationtable_data.bin")
seg["animation_data"] = (off, size, True)
off, size = csv1("Globalimagetable.bin")
seg["Globalimagetable"] = (off, size, True)
# D39 (docs/PCPortResearch.md): the CSV asset is truncated to the 17 Gfx
# display lists (0xAC8). The N64 linker segment (ge007.ld) places ALL of
# oddtextures.o (.data) here — Gfx DLs + 32 sImageTableEntry tables =
# 0x13F8 bytes. texReset() copies End-Start, so the marker must span the
# full linked .data or the image tables are never loaded. Verified by
# byte-tiling all 49 symbols against ROM [off, off+0x13F8).
seg["Globalimagetable"] = (off, 0x13F8, True)
off, size = csv1("rarewarelogo.bin")
seg["rarewarelogo"] = (off, size, True)
off, size = csv1("usedby7F008DE4.bin") # assets/romfiles2.s blob
+2 -1
View File
@@ -1083,7 +1083,8 @@ void ai(PropDefHeaderRecord *Entityp, PROP_TYPE EntityType)
else if (AircraftEntityp)
{
zero = 0; // debug value maybe?
modelSetAnimation(AircraftEntityp->model, animation_table_ptrs2[anim_id], zero, startframe, 0.5f, (s32)ai->INTERPOL_TIME60);
/* D32/D33: table holds s32 offsets; cast at use site. */
modelSetAnimation(AircraftEntityp->model, (ModelAnimation *)animation_table_ptrs2[anim_id], zero, startframe, 0.5f, (s32)ai->INTERPOL_TIME60);
if (endframe >= 0)
{
modelSetAnimEndFrame(AircraftEntityp->model, endframe);
+117 -49
View File
@@ -164,6 +164,74 @@ extern Gfx* globalDL_0x900;
extern Gfx* globalDL_0x9a8;
extern Gfx* globalDL_0xa50;
#if defined(__x86_64__)
/* D39 (docs/PCPortResearch.md): on N64 all 49 symbols above link inside the
* Globalimagetable segment at physical 0x02xxxxxx (ge007.ld), so
* `globalbank_rdram_offset + (u32)&sym` rebases each onto pGlobalimagetable.
* A PE image cannot sit at 0x02xxxxxx, so (u32)&sym is the low 32 bits of an
* exe address — garbage. Use each symbol's offset within the segment instead
* (verified by byte-tiling all 49 arrays against ROM [0x1029D160, 0x1029E558),
* NTSC; offsets are region-independent). pGlobalimagetable is s32-safe (V1
* @ 0x70xxxxxx, see port/src/dram.c), so the same u32 math yields the correct
* 64-bit pointer. NOTE: this also requires _GlobalimagetableSegmentEnd to span
* the full linked .data (Gfx DLs + sImageTableEntry tables = 0x13F8), not just
* the 0xAC8-byte CSV asset — see port/src/romassets_u.s. */
enum {
g_pc_gimg_off_s_genericimage = 0xAC8,
g_pc_gimg_off_s_impactimages = 0xAD4,
g_pc_gimg_off_s_explosion_smokeimages = 0xBC4,
g_pc_gimg_off_s_scattered_explosions = 0xC0C,
g_pc_gimg_off_s_flareimage1 = 0xC48,
g_pc_gimg_off_s_flareimage2 = 0xC54,
g_pc_gimg_off_s_flareimage3 = 0xC60,
g_pc_gimg_off_s_flareimage4 = 0xC6C,
g_pc_gimg_off_s_flareimage5 = 0xC78,
g_pc_gimg_off_s_ammo9mmimage = 0xC84,
g_pc_gimg_off_s_rifleammoimage = 0xC90,
g_pc_gimg_off_s_shotgunammoimage = 0xC9C,
g_pc_gimg_off_s_knifeammoimage = 0xCA8,
g_pc_gimg_off_s_glammoimage = 0xCB4,
g_pc_gimg_off_s_rocketammoimage = 0xCC0,
g_pc_gimg_off_s_genericmineammoimage = 0xCCC,
g_pc_gimg_off_s_grenadeammoimage = 0xCD8,
g_pc_gimg_off_s_magnumammoimage = 0xCE4,
g_pc_gimg_off_s_goldengunammoimage = 0xCF0,
g_pc_gimg_off_s_remotemineammoimage = 0xCFC,
g_pc_gimg_off_s_timedmineammoimage = 0xD08,
g_pc_gimg_off_s_proxmineammoimage = 0xD14,
g_pc_gimg_off_s_tankammoimage = 0xD20,
g_pc_gimg_off_s_crosshairimage = 0xD2C,
g_pc_gimg_off_s_betacrosshairimage = 0xD38,
g_pc_gimg_off_s_glassoverlayimage = 0xD44,
g_pc_gimg_off_s_monitorimages = 0xD5C,
g_pc_gimg_off_s_skywaterimages = 0xFB4,
g_pc_gimg_off_s_mainfolderimages = 0xFD8,
g_pc_gimg_off_s_mpradarimages = 0x1020,
g_pc_gimg_off_s_mpcharselimages = 0x102C,
g_pc_gimg_off_s_mpstageselimages = 0x132C,
g_pc_gimg_off_globalDL_0x000 = 0x0,
g_pc_gimg_off_globalDL_0x078 = 0x78,
g_pc_gimg_off_globalDL_0x120 = 0x120,
g_pc_gimg_off_globalDL_0x1c8 = 0x1C8,
g_pc_gimg_off_globalDL_0x270 = 0x270,
g_pc_gimg_off_globalDL_0x318 = 0x318,
g_pc_gimg_off_globalDL_0x3c0 = 0x3C0,
g_pc_gimg_off_globalDL_0x468 = 0x468,
g_pc_gimg_off_globalDL_0x510 = 0x510,
g_pc_gimg_off_globalDL_0x5b8 = 0x5B8,
g_pc_gimg_off_globalDL_0x660 = 0x660,
g_pc_gimg_off_globalDL_0x708 = 0x708,
g_pc_gimg_off_globalDL_0x7b0 = 0x7B0,
g_pc_gimg_off_globalDL_0x858 = 0x858,
g_pc_gimg_off_globalDL_0x900 = 0x900,
g_pc_gimg_off_globalDL_0x9a8 = 0x9A8,
g_pc_gimg_off_globalDL_0xa50 = 0xA50
};
#define GIMG_OFF(sym) (0x02000000u + g_pc_gimg_off_##sym) /* == N64 (u32)&sym */
#else
#define GIMG_OFF(sym) ((u32)&(sym))
#endif
void texReset(void)
{
u32 size;
@@ -176,56 +244,56 @@ void texReset(void)
romCopy(pGlobalimagetable, &_GlobalimagetableSegmentRomStart, size);
globalbank_rdram_offset = (u32)pGlobalimagetable + 0xFE000000;
genericimage = (void *) (globalbank_rdram_offset + (u32)&s_genericimage);
impactimages = (void *) (globalbank_rdram_offset + (u32)&s_impactimages);
explosion_smokeimages = (void *) (globalbank_rdram_offset + (u32)&s_explosion_smokeimages);
scattered_explosions = (void *) (globalbank_rdram_offset + (u32)&s_scattered_explosions);
flareimage1 = (void *) (globalbank_rdram_offset + (u32)&s_flareimage1);
flareimage2 = (void *) (globalbank_rdram_offset + (u32)&s_flareimage2);
flareimage3 = (void *) (globalbank_rdram_offset + (u32)&s_flareimage3);
flareimage4 = (void *) (globalbank_rdram_offset + (u32)&s_flareimage4);
flareimage5 = (void *) (globalbank_rdram_offset + (u32)&s_flareimage5);
ammo9mmimage = (void *) (globalbank_rdram_offset + (u32)&s_ammo9mmimage);
rifleammoimage = (void *) (globalbank_rdram_offset + (u32)&s_rifleammoimage);
shotgunammoimage = (void *) (globalbank_rdram_offset + (u32)&s_shotgunammoimage);
knifeammoimage = (void *) (globalbank_rdram_offset + (u32)&s_knifeammoimage);
glaunchammoimage = (void *) (globalbank_rdram_offset + (u32)&s_glammoimage);
rocketammoimage = (void *) (globalbank_rdram_offset + (u32)&s_rocketammoimage);
genericmineammoimage = (void *) (globalbank_rdram_offset + (u32)&s_genericmineammoimage);
grenadeammoimage = (void *) (globalbank_rdram_offset + (u32)&s_grenadeammoimage);
magnumammoimage = (void *) (globalbank_rdram_offset + (u32)&s_magnumammoimage);
goldengunammoimage = (void *) (globalbank_rdram_offset + (u32)&s_goldengunammoimage);
remotemineammoimage = (void *) (globalbank_rdram_offset + (u32)&s_remotemineammoimage);
timedmineammoimage = (void *) (globalbank_rdram_offset + (u32)&s_timedmineammoimage);
proxmineammoimage = (void *) (globalbank_rdram_offset + (u32)&s_proxmineammoimage);
tankammoimage = (void *) (globalbank_rdram_offset + (u32)&s_tankammoimage);
crosshairimage = (void *) (globalbank_rdram_offset + (u32)&s_crosshairimage);
betacrosshairimage = (void *) (globalbank_rdram_offset + (u32)&s_betacrosshairimage);
glassoverlayimage = (void *) (globalbank_rdram_offset + (u32)&s_glassoverlayimage);
monitorimages = (void *) (globalbank_rdram_offset + (u32)&s_monitorimages);
skywaterimages = (void *) (globalbank_rdram_offset + (u32)&s_skywaterimages);
mainfolderimages = (void *) (globalbank_rdram_offset + (u32)&s_mainfolderimages);
mpradarimages = (void *) (globalbank_rdram_offset + (u32)&s_mpradarimages);
mpcharselimages = (void *) (globalbank_rdram_offset + (u32)&s_mpcharselimages);
mpstageselimages = (void *) (globalbank_rdram_offset + (u32)&s_mpstageselimages);
genericimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_genericimage));
impactimages = (void *) (globalbank_rdram_offset + GIMG_OFF(s_impactimages));
explosion_smokeimages = (void *) (globalbank_rdram_offset + GIMG_OFF(s_explosion_smokeimages));
scattered_explosions = (void *) (globalbank_rdram_offset + GIMG_OFF(s_scattered_explosions));
flareimage1 = (void *) (globalbank_rdram_offset + GIMG_OFF(s_flareimage1));
flareimage2 = (void *) (globalbank_rdram_offset + GIMG_OFF(s_flareimage2));
flareimage3 = (void *) (globalbank_rdram_offset + GIMG_OFF(s_flareimage3));
flareimage4 = (void *) (globalbank_rdram_offset + GIMG_OFF(s_flareimage4));
flareimage5 = (void *) (globalbank_rdram_offset + GIMG_OFF(s_flareimage5));
ammo9mmimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_ammo9mmimage));
rifleammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_rifleammoimage));
shotgunammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_shotgunammoimage));
knifeammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_knifeammoimage));
glaunchammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_glammoimage));
rocketammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_rocketammoimage));
genericmineammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_genericmineammoimage));
grenadeammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_grenadeammoimage));
magnumammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_magnumammoimage));
goldengunammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_goldengunammoimage));
remotemineammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_remotemineammoimage));
timedmineammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_timedmineammoimage));
proxmineammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_proxmineammoimage));
tankammoimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_tankammoimage));
crosshairimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_crosshairimage));
betacrosshairimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_betacrosshairimage));
glassoverlayimage = (void *) (globalbank_rdram_offset + GIMG_OFF(s_glassoverlayimage));
monitorimages = (void *) (globalbank_rdram_offset + GIMG_OFF(s_monitorimages));
skywaterimages = (void *) (globalbank_rdram_offset + GIMG_OFF(s_skywaterimages));
mainfolderimages = (void *) (globalbank_rdram_offset + GIMG_OFF(s_mainfolderimages));
mpradarimages = (void *) (globalbank_rdram_offset + GIMG_OFF(s_mpradarimages));
mpcharselimages = (void *) (globalbank_rdram_offset + GIMG_OFF(s_mpcharselimages));
mpstageselimages = (void *) (globalbank_rdram_offset + GIMG_OFF(s_mpstageselimages));
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x000, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x078, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x120, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x1c8, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x270, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x318, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x3c0, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x468, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x510, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x5b8, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x660, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x708, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x7b0, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x858, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x900, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0x9a8, 0);
texLoadFromDisplayList(globalbank_rdram_offset + (u32)&globalDL_0xa50, 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x000), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x078), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x120), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x1c8), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x270), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x318), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x3c0), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x468), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x510), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x5b8), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x660), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x708), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x7b0), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x858), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x900), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0x9a8), 0);
texLoadFromDisplayList(globalbank_rdram_offset + GIMG_OFF(globalDL_0xa50), 0);
texLoad(genericimage, 0);
+21 -7
View File
@@ -4,6 +4,9 @@
#include "objecthandler.h"
#include "bondgame.h"
/* D33: ROM-file endianness fixup for the animation data segment (port layer). */
#include "romdata.h"
//bss
// Where animation frames are saved. Can possibly hold as much as nine, but the game will ever store four at maximum.
@@ -211,7 +214,11 @@ s32 animation_table_ptrs1[] = {
0
};
struct ModelAnimation *animation_table_ptrs2[] = {
/* D32/D33: stored as s32 offsets (N64 layout) so the array is dense on
* x86-64; cast to ModelAnimation * at the use sites. As ModelAnimation*[]
* the elements would be 8 bytes wide and expand_ani_table_entries() would
* stop at the first zero high word. */
s32 animation_table_ptrs2[] = {
PTR_ANIM_helicopter_cradle,
PTR_ANIM_plane_runway,
PTR_ANIM_helicopter_takeoff,
@@ -232,11 +239,14 @@ struct anim_entry
void expand_ani_table_entries(s32** arg0)
{
s32** var_v0;
/* D33: iterate as s32 * (4 bytes/iter). As s32 ** the loop advanced 8
* bytes/iter on x86-64, rebasing only even-indexed entries. Identical
* semantics to the N64 original, where both types were 4 bytes wide. */
s32 *var_v0;
var_v0 = arg0;
var_v0 = (s32 *)arg0;
while (*var_v0 != 0) {
if (*var_v0 != (s32*)1) {
if (*var_v0 != 1) {
*var_v0 = (s32)((s32)*var_v0 + (s32)(&ptr_animation_table->data));
((struct anim_entry *)*var_v0)->unk08 += (s32)&ptr_animation_table->data;
((struct anim_entry *)*var_v0)->unk10 += (s32)&ptr_animation_table->data;
@@ -244,9 +254,9 @@ void expand_ani_table_entries(s32** arg0)
var_v0++;
}
for (var_v0 = arg0; *var_v0 != 0; var_v0++) {
if (*var_v0 != (s32*)1) {
**var_v0 += (s32)&_animation_entriesSegmentRomStart;
for (var_v0 = (s32 *)arg0; *var_v0 != 0; var_v0++) {
if (*var_v0 != 1) {
*(s32 *)*var_v0 += (s32)&_animation_entriesSegmentRomStart;
}
}
}
@@ -263,6 +273,10 @@ void alloc_load_expand_ani_table(void)
ptr_animation_table = mempAllocBytesInBank(animsDataSegmentSize, MEMPOOL_PERMANENT);
romCopy(ptr_animation_table, &_animation_dataSegmentRomStart, animsDataSegmentSize);
/* D33: the ROM file stores the record headers and descriptor arrays
* big-endian; convert per field before expand reads/writes them as LE. */
romdataFixupAnimationData((u8 *)ptr_animation_table, (u32)animsDataSegmentSize,
animation_table_ptrs1, animation_table_ptrs2);
expand_ani_table_entries((s32*)&animation_table_ptrs1);
expand_ani_table_entries((s32*)&animation_table_ptrs2);
}
+4 -1
View File
@@ -34,7 +34,10 @@ extern s32 animation_table_ptrs1[];
* Contains offsets into ptr_animation_table for object/vehicle animations.
* The index of each value corresponds to `enum AIRCRAFT_ANIMATION`.
* The value corresponds to (e.g. index=0) PTR_ANIM_helicopter_cradle (same as ANIM_DATA_helicopter_cradle)
*
* D32/D33: s32 offsets (N64 layout — 4-byte elements on both targets);
* cast to ModelAnimation * at the use sites.
*/
extern struct ModelAnimation * animation_table_ptrs2[];
extern s32 animation_table_ptrs2[];
#endif
+6
View File
@@ -38,5 +38,11 @@ void initModelHitEntryFreeList(void)
entries[i].prev = &entries[i - 1];
}
#if defined(__x86_64__)
// D40: N64 g_ModelHitEntriesPenultimate labels entry 598 of the pool; on PC the
// placeholder symbol is not emitted, so take the address directly.
((ModelHitEntry *)g_ModelHitEntries)[MODELHITENTRIES_LEN - 1].prev = &entries[MODELHITENTRIES_LEN - 2];
#else
((ModelHitEntry *)g_ModelHitEntries)[MODELHITENTRIES_LEN - 1].prev = (ModelHitEntry *)g_ModelHitEntriesPenultimate;
#endif
}
+10
View File
@@ -4,6 +4,15 @@
#include "model.h"
// bss
#if defined(__x86_64__)
// D40: the N64 placeholder chain below (g_ModelHitEntries .. g_ModelHitEntriesPenultimate)
// forms one contiguous 600 x 20-byte ModelHitEntry pool in CODE.bss. On x86-64
// ModelHitEntry is 40 bytes (two pointer pairs), so initModelHitEntryFreeList()
// writes 600*40 bytes from g_ModelHitEntries and overruns the N64-sized placeholders
// by 12KB into adjacent .bss globals. Size the pool for the real struct on PC; keep
// the original placeholder chain verbatim on N64.
char g_ModelHitEntries[600 * sizeof(ModelHitEntry)];
#else
//CODE.bss:80076A50
char g_ModelHitEntries[0xC];
//CODE.bss:80076A5C
@@ -50,6 +59,7 @@ char dword_CODE_bss_80076ADF;
char dword_CODE_bss_80076AE0[0x2E28];
//CODE.bss:80079908
char g_ModelHitEntriesPenultimate[0x28];
#endif
//CODE.bss:80079930
struct AnimModelSlot *g_AnimModelSlots;
+3 -2
View File
@@ -188,7 +188,8 @@
*/
f32 g_SoloAmmoMultiplier = 1.0;
extern struct ModelAnimation *animation_table_ptrs2[];
/* D32/D33: s32 offsets (N64 layout); cast to ModelAnimation * at use sites. */
extern s32 animation_table_ptrs2[];
struct tvcmd {
u32 type;
@@ -5718,7 +5719,7 @@ s32 objTick(struct PropRecord *prop)
#endif
temp_s0_6 = render_pad2F4->model;
if (temp_s0_6->anim == animation_table_ptrs2[1])
if (temp_s0_6->anim == (ModelAnimation *)animation_table_ptrs2[1]) /* D32/D33 */
{
modelSetAnimTranslationScale(temp_s0_6, 10.438f);
setsubroty(render_pad2F4->model, M_PI_F);
+6
View File
@@ -273,6 +273,12 @@ void rspGfxTaskStart(Gfx *firstGdl, Gfx *gdl, s32 arg2, OSMesg rspReplyMsg)
// Toggles the g_gfxTaskSettingsList to point to altnerate tasks.
// Depends on first assignment taking place (g_gfxTaskSettingsList = g_gfxTaskSettings[0]).
// Not a typo, but probably not best practice.
#if defined(__x86_64__)
// D32-class: the XOR idiom below truncates exe pointers to u32 on x86-64 and
// yields garbage; toggle explicitly (same semantics: list is always &[0] or &[1]).
g_gfxTaskSettingsList = (g_gfxTaskSettingsList == &g_gfxTaskSettings[0]) ? &g_gfxTaskSettings[1] : &g_gfxTaskSettings[0];
#else
g_gfxTaskSettingsList = (struct GfxInfo_s *)((u32)g_gfxTaskSettingsList ^ (u32) &g_gfxTaskSettings[0] ^ (u32) &g_gfxTaskSettings[1]);
#endif
}
+15
View File
@@ -63,7 +63,22 @@ void mempCheckMemflagTokens(s32 poolAreaStart, s32 poolAreaSize)
if (poolSizes.me == 0)
{
poolSizes.mf = 0;
#if defined(__x86_64__)
/* D36 (PC port): the PERMANENT bank must also hold the enlarged
* music heap (MUSIC_ALLOCATION_BYTES in src/music.c, 0x2E000 ->
* 0x32000 on x86-64 due to libaudio pointer bloat). Grow the bank on
* PC only; STAGE absorbs the difference. N64 value unchanged.
* See docs/PCPortResearch.md D36.
*
* D37 (PC port): MUSIC_ALLOCATION_BYTES grows again to 0x38000 (the
* re-laid-out bank images live in the music heap; init demand
* measures 0x33530). Pre-music PERMANENT usage is ~0x1BCA0, so the
* bank must be at least 0x1BCA0 + 0x38000 plus post-music headroom;
* 352/368 KiB leaves ~17-19 KiB. STAGE absorbs the difference. */
poolSizes.me = ((j_text_trigger ? 368 : 352) * 1024);
#else
poolSizes.me = ((j_text_trigger ? 308 : 296) * 1024);
#endif
poolSizes.ml = poolAreaSize - poolSizes.me;
}
+54 -4
View File
@@ -8,6 +8,7 @@
#include "memp.h"
#include "music.h"
#include "ramrom.h"
#include "romdata.h"
#include "snd.h"
#include <macro.h>
@@ -36,8 +37,26 @@
/**
* Bytes allocated by call to mempAllocBytesInBank. This sets up a local heap,
* the rest of the memory allocations in this file come from this heap.
*
* D36 (PC port): on x86-64 the libaudio runtime structs (ALVoiceState,
* ALChanState, ALEventListItem, ALCSPlayer, ALSeqPlayer, ALSoundState_s) are
* larger than their N64 32-bit counterparts because their internal pointers
* are 8 bytes. The full init-time demand of this bump heap grows from the
* N64 ~0x2E000 budget to 0x31660 (measured under gdb). Bump the budget on PC
* only; the N64 value is unchanged. See docs/PCPortResearch.md D36.
*
* D37 (PC port): the SFX/instrument bank blobs are re-laid out to PC-native
* form in this same heap (romdataFixupAudioBank); their images are larger
* than the ROM segments (+0x1460 sfx, +0xA70 instruments), and the full
* init-time demand measures 0x33530 under gdb. Per-track seq file buffers
* allocated later from this heap need N64-equivalent headroom on top, so
* budget 0x38000 (init headroom ~19 KiB). PC only.
*/
#if defined(__x86_64__)
#define MUSIC_ALLOCATION_BYTES 0x38000
#else
#define MUSIC_ALLOCATION_BYTES 0x2E000
#endif
#define MUSIC_MEMP_BANK 6
/**
@@ -614,7 +633,9 @@ void musicSeqFileNew(RareALSeqBankFile *file, u8 *base)
* patch the file so that offsets are pointers
*/
for (i = 0; i < file->seqCount; i++) {
file->seqArray[i].address = (u8 *)((u8 *)file->seqArray[i].address + offset);
// D35: address is a u32 embedded value (was pointer-typed); the
// rebase math is unchanged, result stays a 32-bit cart address.
file->seqArray[i].address += (u32)offset;
}
}
@@ -664,8 +685,21 @@ void musicSeqPlayerInit(void)
{
size = (u32)&_sfxtblSegmentRomStart - (u32)&_sfxctlSegmentRomStart;
#if defined(__x86_64__)
// D37: the PC-native bank image is larger than the ROM segment
// (4-byte packed offsets become 8-byte pointer slots); allocate the
// re-laid-out size, copy only the ROM bytes, then convert.
{
u32 romSize = size;
size = romdataAudioBankPcSize((const u8 *)&_sfxctlSegmentRomStart, size);
sfxBank = alHeapAlloc(&g_musicHeap, 1, size);
romCopy(sfxBank, &_sfxctlSegmentRomStart, romSize);
romdataFixupAudioBank(sfxBank, romSize, size);
}
#else
sfxBank = alHeapAlloc(&g_musicHeap, 1, size);
romCopy(sfxBank, &_sfxctlSegmentRomStart, size);
#endif
alBnkfNew(sfxBank, (u8 *)&_sfxtblSegmentRomStart);
g_musicSfxBufferPtr = sfxBank->bankArray[0];
}
@@ -674,8 +708,19 @@ void musicSeqPlayerInit(void)
{
size = (u32)&_instrumentstblSegmentRomStart - (u32)&_instrumentsctlSegmentRomStart;
#if defined(__x86_64__)
// D37: as above.
{
u32 romSize = size;
size = romdataAudioBankPcSize((const u8 *)&_instrumentsctlSegmentRomStart, size);
instrumentBank = alHeapAlloc(&g_musicHeap, 1, size);
romCopy(instrumentBank, &_instrumentsctlSegmentRomStart, romSize);
romdataFixupAudioBank(instrumentBank, romSize, size);
}
#else
instrumentBank = alHeapAlloc(&g_musicHeap, 1, size);
romCopy(instrumentBank, &_instrumentsctlSegmentRomStart, size);
#endif
alBnkfNew(instrumentBank, (u8 *)&_instrumentstblSegmentRomStart);
g_musicInstrumentBufferPtr = instrumentBank->bankArray[0];
}
@@ -686,10 +731,15 @@ void musicSeqPlayerInit(void)
size = 0x10;
g_musicDataTable = alHeapAlloc(&g_musicHeap, 1, size);
romCopy(g_musicDataTable, (void *)tblSegmentRomStartAddress, size);
// D35: decode the BE header (+0 seqCount) and any entries that fit,
// before seqCount is read below.
romdataFixupMusicSeqTable(g_musicDataTable, size);
tblSegmentSize = (sizeof(RareALSeqData) * g_musicDataTable->seqCount) + 4;
g_musicDataTable = alHeapAlloc(&g_musicHeap, 1, tblSegmentSize);
romCopy(g_musicDataTable, (void *)tblSegmentRomStartAddress, ALIGN16_a(tblSegmentSize));
// D35: the copy above reloaded raw BE bytes; decode the full table.
romdataFixupMusicSeqTable(g_musicDataTable, ALIGN16_a(tblSegmentSize));
// end auReadSeqFileHeader
@@ -813,7 +863,7 @@ void musicTrack1Play(s32 track)
while (alCSPGetState(g_musicXTrack1SeqPlayer))
;
romAddress = g_musicDataTable->seqArray[g_musicXTrack1CurrentTrackNum].address;
romAddress = (void *)g_musicDataTable->seqArray[g_musicXTrack1CurrentTrackNum].address;
if (romAddress < (void*)ROM_MUSIC_START_OFFSET)
{
@@ -1002,7 +1052,7 @@ void musicTrack2Play(s32 track)
while (alCSPGetState(g_musicXTrack2SeqPlayer))
;
romAddress = g_musicDataTable->seqArray[g_musicXTrack2CurrentTrackNum].address;
romAddress = (void *)g_musicDataTable->seqArray[g_musicXTrack2CurrentTrackNum].address;
if (romAddress < (void*)ROM_MUSIC_START_OFFSET)
{
@@ -1191,7 +1241,7 @@ void musicTrack3Play(s32 track)
while (alCSPGetState(g_musicXTrack3SeqPlayer))
;
romAddress = g_musicDataTable->seqArray[g_musicXTrack3CurrentTrackNum].address;
romAddress = (void *)g_musicDataTable->seqArray[g_musicXTrack3CurrentTrackNum].address;
if (romAddress < (void*)ROM_MUSIC_START_OFFSET)
{
+5 -2
View File
@@ -18,8 +18,11 @@
*/
typedef struct
{
// address is offset from the start of .sbk file
u8 *address;
// address is offset from the start of .sbk file.
// D35 (docs/PCPortResearch.md): stored as the 32-bit embedded value, not a
// host pointer. As `u8 *` this struct is 16 bytes on x86-64 while the ROM
// records are 8 (u32 + 2x u16); cast to a real pointer at the use sites.
u32 address;
// seq length after uncompressed.
u16 uncompressed_len;