forked from Ports/goldeneye-pc-port
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:
1 parent
b3000825f2
commit
98412c281c
33 files changed
+2136
-198
No files matched your search
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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.
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
#include "crash.h" /* D38: crashDumpThreads() */
|
||||
#include "video.h"
|
||||
#include "audio.h"
|
||||
#include "input.h"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user