forked from Ports/goldeneye-pc-port
M-27: front-end bucket — D140/D141/D118d pause+watch menu fixes
D140 (src/game/bondview.h, bondview2.c): pause-menu crash in bondviewRenderWatch -> bondviewTransformManyPosToViewMatrix(field_23C=NULL). player +0x230 is a struct Model + RW pool punned into a 0x184-byte field run; field_23C/watch_scale_destination/pause_watch_related_adjust alias .render_pos/.scale/.animframe1. PC sizeof(struct Model) grows -> aliases break -> NULL render_pos. Fix: real inline struct Model + u32 watchRwPool[192] under #ifdef PORT; named reads redirect via GE_WATCH_* macros (N64 #else verbatim). D141 (src/game/gunfire.c): crash D140 exposed. set_enviro_fog_for_items_in_solo_watch_menu walks bodymodel->Switches[] (ModelNode* array) with raw byte offsets +0x48/+0x5c, j+=4 -> PC 8-byte stride reads garbage -> AV in modelGetNodeRwData. #ifdef PORT uses Switches[18 + (j>>2)] / Switches[23 + (j>>2)]. D118d (src/game/options.c): watch inventory list over-scrolls with keyboard W/S (one press skips several items). GE fast-scroll is a raw per-frame level check (joyGetStickY < -0x46); digital pads sit at max every frame. #ifdef PORT drops the stick term -> nav via the latched single-step path. Feel unverified headlessly. Verified: build green (ntsc-final); -level_09 framediff 3/3, -level_20 crash-free (unregressed).
This commit is contained in:
1 parent
326564736d
commit
f98414f8c6
7 files changed
+167
-30
No files matched your search
+51
-3
@@ -335,6 +335,52 @@ prior behaviour. Full review: **`docs/M-26-QOL-REVIEW.md`**.
|
||||
boot crash-free 6/6 GE_PCDUMP frames (unregressed); `ge007.ini` gains a
|
||||
populated `[Window]` block on exit.
|
||||
|
||||
## Done this session (M-27) — front-end/pause bucket: D140 + D141 FIXED
|
||||
|
||||
Started the parked "front-end / transitions / pause / watch" bucket.
|
||||
**Pressing Start in-level no longer crashes** — the pause / watch menu
|
||||
renders (weapon page verified via a temp `GE_AUTOPAUSE` input probe,
|
||||
since removed). Cosmetics unchanged and still parked: mirrored watch text
|
||||
(D114/D116), dark/faint weapon model (D75).
|
||||
|
||||
- **D140 (FIXED)** `src/game/bondview.h`, `src/game/bondview2.c` —
|
||||
pause-menu crash `bondviewRenderWatch` → `bondviewTransformManyPos-
|
||||
ToViewMatrix(field_23C=NULL)`. `something_with_watch_object_instance`
|
||||
(N64 player +0x230) is a **`struct Model` + RW-pool punned into a
|
||||
0x184-byte field-run**; `field_23C`/`watch_scale_destination`/
|
||||
`pause_watch_related_adjust` are actually `.render_pos`/`.scale`/
|
||||
`.animframe1`. PC `sizeof(struct Model)` grows → the aliases break →
|
||||
NULL `render_pos`. Fix: real inline `struct Model` +
|
||||
`u32 watchRwPool[192]` under `#ifdef PORT`; the 3 named reads redirect to
|
||||
the member via `GE_WATCH_{ANIMFRAME,SCALE,RENDERPOS}` macros (N64 `#else`
|
||||
= verbatim field). D56 branch of `sub_GAME_7F07E7CC` now uses the inline
|
||||
pool. D100/D102 pattern. §F/§H **D140**, PORT-LEARNINGS §A.
|
||||
- **D118d (FIXED, feel unconfirmed)** `src/game/options.c` — user bug report:
|
||||
watch inventory list over-scrolls with keyboard W/S (one press skips several
|
||||
items). GE's "slam the stick" fast-scroll is a raw per-frame level check
|
||||
(`joyGetStickY < -0x46`); keyboard/digital pads sit at max every frame.
|
||||
`#ifdef PORT` drops the stick term → list-nav goes through the latched
|
||||
single-step path (one step per press). Build green, `-level_09` unregressed;
|
||||
couldn't verify the feel headlessly (INVENTORY watch page unreachable without
|
||||
page-cycling input) — **needs an in-game check**. Latent sibling in `front.c`
|
||||
menu nav (same `joyGetStick*InRange` level-check pattern). §F **D118d**.
|
||||
- **D141 (FIXED)** `src/game/gunfire.c` — the crash D140 exposed.
|
||||
`set_enviro_fog_for_items_in_solo_watch_menu` walks `bodymodel->Switches[]`
|
||||
(a `ModelNode*` array) with raw byte offsets `+0x48`/`+0x5c`, `j += 4`
|
||||
(4-byte-pointer constants) → PC 8-byte stride reads garbage → bogus node
|
||||
→ AV in `modelGetNodeRwData`. Fix: `#ifdef PORT` uses
|
||||
`Switches[18 + (j>>2)]` / `Switches[23 + (j>>2)]`. §F **D141**,
|
||||
PORT-LEARNINGS §B (cf. D128).
|
||||
- **Verified:** build green (`ntsc-final`); `-level_09` framediff 3/3,
|
||||
`-level_20` crash-free (both unregressed); autopause probe → watch page
|
||||
renders 400+ frames, no `ge007.crash.log`.
|
||||
- **D139** (stage-unload, M-23, still UNVERIFIED) not exercised — it needs
|
||||
a real level-exit teardown, not a pause. Next front-end item.
|
||||
- **Still in the bucket:** D139 verify; unpause / watch-page navigation
|
||||
(the temp probe didn't test exiting the watch); level exit → MISSION
|
||||
COMPLETE → debrief → auto-advance; main-menu / mission-select walkthrough
|
||||
(WS2); watch objectives page + gadgets.
|
||||
|
||||
## Next task (M-24 — Opus 5)
|
||||
|
||||
**Scope call (user, M-23):** D139 (stage unload) + D140 (pause menu / watch)
|
||||
@@ -366,9 +412,11 @@ during normal `-level_XX` play.
|
||||
parser to D135, `~3373-3646`) — do it before a level where wall-shooting
|
||||
is heavy (i.e. soon).
|
||||
|
||||
**Parked bucket — front-end / transitions (one focused session):**
|
||||
- D139 verify (stage unload / `cleanupObjects`)
|
||||
- D140 (pause menu → `bondviewRenderWatch` → NULL `field_23C` matrix)
|
||||
**Parked bucket — front-end / transitions (STARTED M-27):**
|
||||
- ~~D140 (pause menu → `bondviewRenderWatch` → NULL `field_23C`)~~ FIXED M-27
|
||||
(+ D141, the watch-page crash it exposed). Pause now renders.
|
||||
- D139 verify (stage unload / `cleanupObjects`) — still needs a real exit
|
||||
- unpause / watch-page navigation (M-27 probe only tested opening it)
|
||||
- level exit → MISSION COMPLETE → debrief → auto-advance to next briefing
|
||||
- main menu / mission-select / difficulty-select playtest (WS2)
|
||||
- the watch (objectives page, gadgets) — needed for the WS6 exit/objective
|
||||
|
||||
@@ -829,7 +829,9 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D135 | **Firefight crash — `bgTestHitOnObj` (`propobj.c:8446`) is an unported N64 GBI parser (M-22, WS6 playtest).** The object bullet-hit test walks a model's display list command stream with hardcoded 8-byte-N64-`Gfx` byte/word indices (`*(s8*)gdl`, `((u32*)gdl)[1]`, `((u8*)gdl)[5..7]`, the G_TRI4 nibble reads, the backward `G_SETTIMG` scan). On PC the model GDL is the 16-byte `Gfx` layout (`d43_emit.py`, `{u64 w0; u64 w1}`), so every access reads the wrong bytes → the walk desyncs on the first `G_VTX`/`G_TRI1` → `v->coord.x` deref off the end of the DL → AV (`0x709ae02a`, a near-miss past a live DRAM ptr = the §B signature). Triggered by `propobjFindHit` every time a shot ray resolves a hit on an object model (guards' dropped weapons, attached objects, shootable props) — invisible to `level_sweep.sh` (no weapon fire). Two sibling faults seen the same session (`texSelect`/`texSetRenderMode` wild `gdl`, `objFreeEmbedmentOrProjectile` `prop->obj`) are likely the same corrupted-object cascade. PD `bgTestHitOnObj` (`pd_port/src/game/bg.c:3635`) is the fully-ported ground truth. | FIXED (`src/game/propobj.c`, `#ifdef PORT` — reads `gdl->words.w0/.w1` shifts). **Second fault, same walker:** after the parser fix the Facility firefight then hit `0xc0000094` (int div-by-zero) at `objHit` `propobj.c:9717` `randomGetNext() % impact_sounds->thing2_len` — the ported texnum-recovery (`*(s16*)(phys(w1) − 8)`) returned a bogus positive `texturenum` (`(u32)` cast truncated a 64-bit `words.w1`; the N64 "texnum 8 bytes before the texture data" layout isn't guaranteed for the port's converted/replaced GDLs) → `g_HitTypeSounds[texnum & 0xf]` picked an entry with `thing2_len == 0`. Fixed: the PORT texnum branch now always returns `-1` (→ `g_HitTypeSounds[0]` = `isnd_default`, `thing2_len == 1`, safe). `texturenum` only flavours the bullet-impact sound/decal, so generic-hit is an acceptable degradation (park with D77). **Follow-up:** the BG room-geometry sibling `sub_GAME_…` (`bg.c:~3373-3646`, walks the D85-widened `ptr_expanded_mapping_info`) has the identical unported parser pattern — latent, fires on shooting walls/floor; not yet ported. |
|
||||
| D136 | **Stage-unload crash — `objFree` `obj->prop` reads float garbage (M-22, WS6 Facility playthrough).** After D135 cleared the firefight crashes the level is completable; then on teardown `lvlUnloadStageTextData` → `cleanupObjects` (`cleanup_objects.c:42`) → `objFreePermanently` → `objFree` (`propobj.c:994`) → `objFreeEmbedmentOrProjectile(obj->prop)` faults at `:888` (`prop->obj` deref). Real gdb `bt`: `obj` = `0x701bd730` (valid) but `obj->prop` = `0x3e567400405c30c0` — two packed `f32` (~0.21, ~3.44), i.e. a `coord3d` read as a pointer → `ObjectRecord.prop` (N64 `/*0x10*/`, after `inherits PropDefHeaderRecord` + `s16 obj; s16 pad; u32 flags; u32 flags2;`) lands at the wrong PC offset for this object's propDef type, OR the record is undersized and setup's `->prop` store never happened / was clobbered. Same family as D122/D132 (`tools_pc/d88_propdefs.py` per-type record sizing vs the compiled `ObjectRecord`/subtype layout). Type not yet identified — needs a gdb run with a breakpoint on `objFree` to read `obj->type` (`cleanupObjects` frees WEAPON/AMMO/MAGAZINE/COLLECTABLE/MONITOR/RACK/AUTOGUN/HAT/ARMOUR/GAS_RELEASING/VEHICHLE/AIRCRAFT/GLASS/SAFE/TANK/TINTED_GLASS via this path). Mission completes; crash is before the next briefing loads. | ROOT-CAUSED → **D139** (it was NOT a layout bug — `cleanupObjects` read the type from the wrong byte on LE and walked off the propDef blob). |
|
||||
| D139 | **Stage-unload crash root cause + fix (M-23).** `cleanupObjects` (`cleanup_objects.c`) walks `g_CurrentSetup.propDefs` with `(u8)obj[0]` as the record type — correct only because the header word `[u16 extrascale][u8 state][u8 type]` is **big-endian** on N64, so `type` is the low byte. On little-endian PC the low byte is `extrascale`, so `(u8)obj[0]` is never `PROPDEF_END` → the `while` runs off the end of the blob, and the `switch` dispatches on garbage → `objFreePermanently()` on records that aren't `ObjectRecord`s → `obj->prop`/`obj->model` read adjacent float data (`0x3e567400405c30c0`) → AV in `objFreeEmbedmentOrProjectile` (`propobj.c:888`). `sizepropdef()` and every other propDef consumer already use `pdef->type` (struct member, offset 3) — only this one spot used the raw byte cast. Not a `d88_propdefs.py` layout issue at all. | FIXED (`src/game/cleanup_objects.c`, `#ifdef PORT` — `CLEANUP_PDTYPE(o)` = `((PropDefHeaderRecord*)o)->type`). **UNVERIFIED** — the fast teardown test (pause→abort) hit D140 first; `-level_09` unregressed. PORT-LEARNINGS §C. |
|
||||
| D140 | **Pause-menu crash (M-23, OPEN).** `maybe_mp_interface` → `bondviewRenderWatch` (`bondview2.c:8604`) → `bondviewTransformManyPosToViewMatrix(g_CurrentPlayer->field_23C, objheader->numMatrices)` with `field_23C == NULL` (`arg1 == 9`) → `matrix_4x4_copy(src=0x0, …)` (`matrixmath.c:130`) AV. `struct player.field_23C` is the watch model's per-node view-matrix cache — never allocated on PC, or `field_23C` is at the wrong `struct player` offset (`docs/AUDIT-M6-player-offsets.md`). Blocks the WS6 pause-menu / `objective_status` checks + watch gadgets. D75 3D-model-transform family. Files: `src/game/bondview2.c` (`bondviewRenderWatch` ~8540-8608), `src/bondtypes.h` (`struct player`). | OPEN |
|
||||
| D140 | **Pause-menu crash (M-23 open → M-27 fixed).** `bondviewRenderWatch` (`bondview2.c:8604`) → `bondviewTransformManyPosToViewMatrix(g_CurrentPlayer->field_23C, objheader->numMatrices)` with `field_23C == NULL` → `matrix_4x4_copy(src=0x0)` AV. Root cause (NOT "cache never allocated"): `something_with_watch_object_instance` (N64 player +0x230) is a **`struct Model` (0xBC) + its RW-data pool (0xC8) punned into a 0x184-byte hole** (`field_234..field_3B0`); three of those "fields" are Model members the game reads by name — `watch_scale_destination`==`.scale`, `pause_watch_related_adjust`==`.animframe1`, **`field_23C`==`.render_pos`**. `render_pos` IS populated (by `instcalcmatrices` via `subcalcmatrices` at `:8524`), but on x86-64 `sizeof(struct Model)` grows (4→8B ptrs) so `field_23C` no longer overlaps `.render_pos` → reads Model padding (~0) → NULL. Same class as D100 (gait Model) / D102 (weapon Model) / D56 (this very watch Model's pool). Fix: `#ifdef PORT` makes `something_with_watch_object_instance` a real inline `struct Model` + `u32 watchRwPool[192]` in `struct player` (`bondview.h`); `bondview2.c` redirects the 3 named reads to the member via `GE_WATCH_{ANIMFRAME,SCALE,RENDERPOS}` macros (N64 `#else` = the verbatim field). The D56 branch of `sub_GAME_7F07E7CC` drops its `static u8 watchRwPool[0xC8]` for the inline pool. | FIXED (`src/game/bondview.h`, `src/game/bondview2.c`; `#ifdef PORT`). Exposed the next watch-render crash → **D141**. `-level_09`/`-level_20` unregressed (3/3). PORT-LEARNINGS §A. |
|
||||
| D118d | **Watch-menu list over-scrolls with keyboard (M-27, user bug report).** `game_options_inventory_navigation` / `sub_GAME_7F0A611C` (`options.c`) have a "slam the stick" fast-scroll: a raw *level* check `joyGetStickY(PLAYER_1) < -0x46` / `>= 0x47` that steps `watch_inventory_cursor_pos` by 1 **every frame** it's held. An N64 stick rarely sustains past ~0x46 on-axis so a human taps it; keyboard W/S (and SDL pads pegged at 0x80) sit there every frame → one keypress skips several items (breaks item selection needed to finish the game). Fix: `#ifdef PORT` macro `GE_WATCH_STICK_FAST{UP,DOWN}` → `0` for the port — stick list-nav then goes only through the latched single-step path (`watch_stick_y_pressed_*`, one step per press, existing game mechanism) and the -0x1e..-0x45 smooth-scroll band; D-pad/C-buttons keep edge-repeat. N64 `#else` = verbatim. | FIXED (`src/game/options.c`, `#ifdef PORT`). Build green, `-level_09` unregressed. **Feel to be confirmed in playtest** (probe couldn't reach the INVENTORY watch page headlessly). D118 input family. Latent sibling: `front.c` menu nav uses `joyGetStick*InRange` level checks the same way. |
|
||||
| D141 | **Watch-menu item-model crash (M-27), exposed by D140.** Pressing Start → watch page renders → `set_enviro_fog_for_items_in_solo_watch_menu` (`gunfire.c:1620`) → `modelGetNodeRwData(&model, root)` with `root` garbage → AV reading `root->Opcode` (`model.c:478`). `gunfire.c:1720-1743` walks `bodymodel->Switches[]` (a `ModelNode*` array) with **raw byte offsets** `((u8*)Switches) + j + 0x48` / `+ 0x5c` and `j += 4` — assumes 4-byte N64 pointers. `0x48/4=18`, `0x5c/4=23`, `k=j>>2=0..4` → intent is `Switches[18+k]` and `Switches[23+k]`. PC's 8-byte stride makes the raw math read misaligned garbage → bogus non-NULL `ModelNode*`. PORT-LEARNINGS §B (raw hardcoded stride into a pointer array, cf. D128). Fix: `#ifdef PORT` uses `bodymodel->Switches[18 + (j>>2)]` / `[23 + (j>>2)]`. | FIXED (`src/game/gunfire.c`, `#ifdef PORT`). Watch page now renders (weapon page verified — mirrored text D114/D116, dark weapon model D75, both parked cosmetic). `-level_09`/`-level_20` unregressed. |
|
||||
| D130 | crash class C2 (Facility `-level_34`, Runway `-level_35`): `import_texture_i8` AV on a wild texture pointer (`0x72181ee8`), baked into the HUD glyph DL by `gDPLoadTextureBlock(gdl, curchar->pixeldata, …)` when the level-title string ("Chemical Warfare Facility #2") renders `#`/`"`. Root cause: `romdataFixupFont` (`port/src/romdata.c`) converts the N64 24B fontchar array to the PC 32B array **in place**, forward-field, backward-glyph. PC−N64 stride = 8, so for glyphs 0/1/2 `d` overlaps `s` by less than the 24B read span and an early field write clobbers a later field's source mid-`for k<5` loop → glyph 1's `width` = `bswap(index)` = `0x01000000`, glyph 2's `pixeldata` = `pcPixOff+(index−pixStart)` ≈ `0x020002e8` → `+= font_base` → wild → fast3d AV. On N64 the array is read-only rodata, never re-laid-out, so no bug there. NOT the model-GDL relocation (`sub_GAME_7F0762E0` / `texLoadFromGdl`) the D124-Facility addendum suspected — `gdl` there is still a segmented `0x05xxxxxx` value so the `& 0x00ffffff` masks are correct, and `texLoadFromGdl` never copies a `G_SETTIMG` for these levels. | resolved — stage all 6 N64 fields in a local `f[6]` before writing any. Facility + Runway now PASS (16→18/21). Probes reverted. |
|
||||
|
||||
Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS
|
||||
|
||||
@@ -21,6 +21,11 @@ state.
|
||||
(rwdata record count), D67 (image_entry), D79 (bg_room_data), D98
|
||||
(struct player alloc), D100 (player.model inline Model), D101/D102
|
||||
(ModelNode*/weapon Model puns), D115 (gunfire THROW* raw offsets),
|
||||
D140 (watch Model punned into a `struct player` field-run: PC
|
||||
`sizeof(struct Model)` grows so the "fields" that aliased `.render_pos` /
|
||||
`.scale` / `.animframe1` on N64 no longer overlap → NULL `render_pos` →
|
||||
pause-menu crash; fix = real inline `struct Model` + pool, redirect the
|
||||
named reads to the member — same as D100/D102),
|
||||
D119 (`weapons_held[]->chr` punned as `ChrRecord*` to read
|
||||
`.act_*.attack_item` — aliased `WeaponObjRecord.weaponnum` at 0x80 on
|
||||
N64 via act-union@0x2C+84; act union moves to ~0x38 on PC).
|
||||
@@ -127,6 +132,14 @@ expressed in N64 `Gfx`/`Vtx` units is **half-size** on PC.
|
||||
`g_BgPortals[portal].controlbytes1 |= PORTALFLAG_SPECIAL`). Grep for
|
||||
`<< 3` / `* 8` / `+ 6` style raw offsets into any struct that gained a
|
||||
pointer field.
|
||||
D141: `set_enviro_fog_for_items_in_solo_watch_menu` (`gunfire.c:1720`)
|
||||
indexed a `ModelNode*` array (`ModelFileHeader.Switches`) as
|
||||
`*(ModelNode**)((u8*)Switches + j + 0x48)` with `j += 4` — the `0x48`/
|
||||
`0x5c` and the step are 4-byte-pointer constants. PC 8-byte stride → wrong
|
||||
+ misaligned slot → bogus non-NULL node → crash in `modelGetNodeRwData`.
|
||||
Fix: `Switches[18 + (j>>2)]` / `Switches[23 + (j>>2)]` under `#ifdef PORT`
|
||||
(`0x48/4=18`, `0x5c/4=23`). Grep every `(TYPE**)((u8*)arr + <const>)` and
|
||||
`arr[i << k]` where the array element is a pointer.
|
||||
|
||||
## C. Big-endian rodata / ROM data read on little-endian PC
|
||||
|
||||
|
||||
@@ -708,6 +708,23 @@ struct player
|
||||
*/
|
||||
f32 pause_watch_related_scaled;
|
||||
|
||||
#ifdef PORT
|
||||
/* D140 / D56 / D53.2: N64 puns a `struct Model` (0xBC) followed immediately
|
||||
* by its RW-data pool (0xC8) into this 0x184-byte hole (player +0x230 ..
|
||||
* +0x3B4). The decomp splits it as this field + `s32 field_234..field_3B0`;
|
||||
* three of those "fields" are actually watch-Model members read by name:
|
||||
* watch_scale_destination == .scale (Model +0x14)
|
||||
* pause_watch_related_adjust == .animframe1 (Model +0x28)
|
||||
* field_23C == .render_pos (Model +0x0c)
|
||||
* On x86-64 `sizeof(struct Model)` grows (4->8B pointers) so the punned
|
||||
* layout desyncs: field_23C stops overlapping render_pos -> NULL -> crash in
|
||||
* bondviewTransformManyPosToViewMatrix on pause (D140), and the grown Model
|
||||
* overruns the live fields below. Give the Model + pool real inline storage
|
||||
* (D100 gait-model / D102 weapon-model pattern); the three named reads are
|
||||
* redirected to the member at their bondview2.c use sites. */
|
||||
struct Model something_with_watch_object_instance;
|
||||
u32 watchRwPool[192]; /* N64 pool 0xC8 B; PC RW records ~2x (cf. D102) */
|
||||
#else
|
||||
s32 something_with_watch_object_instance;
|
||||
|
||||
s32 field_234;
|
||||
@@ -811,6 +828,7 @@ struct player
|
||||
s32 field_3A8;
|
||||
s32 field_3AC;
|
||||
s32 field_3B0;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Copy of buttons pressed.
|
||||
|
||||
+31
-22
@@ -168,6 +168,19 @@
|
||||
#define aZ4_0f "z %4.0f"
|
||||
#define aS3d "%s %3d"
|
||||
|
||||
#ifdef PORT
|
||||
/* D140: on N64 these three struct-player "fields" alias members of the watch
|
||||
* Model punned at `something_with_watch_object_instance` (see bondview.h).
|
||||
* The pun grows on PC, so the alias breaks; redirect to the real member. */
|
||||
#define GE_WATCH_ANIMFRAME (g_CurrentPlayer->something_with_watch_object_instance.animframe1)
|
||||
#define GE_WATCH_SCALE (g_CurrentPlayer->something_with_watch_object_instance.scale)
|
||||
#define GE_WATCH_RENDERPOS (g_CurrentPlayer->something_with_watch_object_instance.render_pos)
|
||||
#else
|
||||
#define GE_WATCH_ANIMFRAME (g_CurrentPlayer->pause_watch_related_adjust)
|
||||
#define GE_WATCH_SCALE (g_CurrentPlayer->watch_scale_destination)
|
||||
#define GE_WATCH_RENDERPOS (g_CurrentPlayer->field_23C)
|
||||
#endif
|
||||
|
||||
|
||||
vec3d g_ForceBondMoveOffset;
|
||||
|
||||
@@ -3143,20 +3156,16 @@ void sub_GAME_7F07E7CC(void) {
|
||||
}
|
||||
#endif
|
||||
#ifdef PORT
|
||||
/* PC port (D56, docs/PCPortResearch.md): the watch Model is embedded in
|
||||
* struct player at N64 +0x230 and its RW-data pool at +0x2EC. On x86-64
|
||||
* the pointer fields before 0x230 shift the region to +0x24C (probe:
|
||||
* vsize.c, not 8-aligned — unaligned pointer stores are fine on x86-64,
|
||||
* D53.2), and the grown Model (0xE8 vs 0xBC) leaves only 0x9C of the N64
|
||||
* 0xC8-byte pool capacity in-struct — so take the Model by field name and
|
||||
* host the pool in a static buffer of the N64 capacity. All rwdata access
|
||||
* goes through Model.datas (set by modelInit), so nothing else references
|
||||
* the old raw offsets. */
|
||||
/* PC port (D56/D140): the watch Model was punned at N64 player+0x230 with
|
||||
* its RW-data pool at +0x2EC. On x86-64 `sizeof(struct Model)` grows so the
|
||||
* pun overruns the live watch fields below it — D140 gave the Model and its
|
||||
* pool real inline storage in `struct player` (`something_with_watch_object_
|
||||
* instance` is now a `struct Model`, `watchRwPool` follows it). All rwdata
|
||||
* access goes through Model.datas (set by animInit). */
|
||||
{
|
||||
static u8 watchRwPool[0xC8]; /* N64: embedded at player+0x2EC */
|
||||
Model *watch = (Model *)&g_CurrentPlayer->something_with_watch_object_instance;
|
||||
Model *watch = &g_CurrentPlayer->something_with_watch_object_instance;
|
||||
|
||||
animInit(watch, itemheader, (u32 *)watchRwPool);
|
||||
animInit(watch, itemheader, g_CurrentPlayer->watchRwPool);
|
||||
modelSetScale(watch, c_item_entries[41].scale * 0.10000001f);
|
||||
modelSetAnimation(watch, (ModelAnimation *)&ptr_animation_table->data[(s32)&ANIM_DATA_bond_watch], 0, 0.0f, 0.5f * watch_transition_time, 0.0f);
|
||||
g_CurrentPlayer->step_in_view_watch_animation = 0; /* N64: *(s32*)(player+0x220) */
|
||||
@@ -3184,7 +3193,7 @@ void bondviewSetPauseWatchRelated(f32 arg0)
|
||||
}
|
||||
else
|
||||
{
|
||||
g_CurrentPlayer->pause_watch_related_scaled = (20.0f - g_CurrentPlayer->pause_watch_related_adjust) / arg0;
|
||||
g_CurrentPlayer->pause_watch_related_scaled = (20.0f - GE_WATCH_ANIMFRAME) / arg0;
|
||||
}
|
||||
|
||||
g_CurrentPlayer->step_in_view_watch_animation = 1;
|
||||
@@ -3205,7 +3214,7 @@ void bondviewSetPauseWatchRelatedAlt(f32 arg0)
|
||||
}
|
||||
else
|
||||
{
|
||||
g_CurrentPlayer->pause_watch_related_scaled = g_CurrentPlayer->pause_watch_related_adjust / arg0;
|
||||
g_CurrentPlayer->pause_watch_related_scaled = GE_WATCH_ANIMFRAME / arg0;
|
||||
}
|
||||
|
||||
g_CurrentPlayer->step_in_view_watch_animation = 2;
|
||||
@@ -3463,7 +3472,7 @@ void bondviewWatchAnimationTick(void)
|
||||
|
||||
if ((g_CurrentPlayer->step_in_view_watch_animation != 0) && (g_CurrentPlayer->step_in_view_watch_animation != 3))
|
||||
{
|
||||
sp30 = ((20.0f - g_CurrentPlayer->pause_watch_related_adjust) * 40.0f) / 20.0f;
|
||||
sp30 = ((20.0f - GE_WATCH_ANIMFRAME) * 40.0f) / 20.0f;
|
||||
}
|
||||
|
||||
if ((g_CurrentPlayer->pause_state == 0) || (g_CurrentPlayer->pause_state == 2) || (g_CurrentPlayer->pause_state == 3))
|
||||
@@ -3587,7 +3596,7 @@ void bondviewWatchAnimationTick(void)
|
||||
|
||||
if ((g_CurrentPlayer->step_in_view_watch_animation != 0) && (g_CurrentPlayer->step_in_view_watch_animation != 3))
|
||||
{
|
||||
sp28 = (g_CurrentPlayer->pause_watch_related_adjust * 40.0f) / 20.0f;
|
||||
sp28 = (GE_WATCH_ANIMFRAME * 40.0f) / 20.0f;
|
||||
}
|
||||
|
||||
sp20 = sp28 + 20.0f;
|
||||
@@ -8524,16 +8533,16 @@ Gfx *bondviewRenderWatch(Gfx *gdl)
|
||||
subcalcmatrices(&renderdata, (Model *) (&g_CurrentPlayer->something_with_watch_object_instance));
|
||||
nodepos = (f32 *) objheader->Switches[0]->Data;
|
||||
time = watch_time_0;
|
||||
t = g_CurrentPlayer->pause_watch_related_adjust / 20.0f;
|
||||
t = GE_WATCH_ANIMFRAME / 20.0f;
|
||||
|
||||
if (t > 1.0f)
|
||||
{
|
||||
t = 1.0f;
|
||||
}
|
||||
|
||||
targetpos.x = matrices->m[3][0] + (((g_CurrentPlayer->field_1D4 - (nodepos[0] * g_CurrentPlayer->watch_scale_destination)) - matrices->m[3][0]) * t);
|
||||
targetpos.y = matrices->m[3][1] + (((g_CurrentPlayer->field_1D8 + (nodepos[2] * g_CurrentPlayer->watch_scale_destination)) - matrices->m[3][1]) * t);
|
||||
targetpos.z = matrices->m[3][2] + (((g_CurrentPlayer->pause_watch_position - (nodepos[1] * g_CurrentPlayer->watch_scale_destination)) - matrices->m[3][2]) * t);
|
||||
targetpos.x = matrices->m[3][0] + (((g_CurrentPlayer->field_1D4 - (nodepos[0] * GE_WATCH_SCALE)) - matrices->m[3][0]) * t);
|
||||
targetpos.y = matrices->m[3][1] + (((g_CurrentPlayer->field_1D8 + (nodepos[2] * GE_WATCH_SCALE)) - matrices->m[3][1]) * t);
|
||||
targetpos.z = matrices->m[3][2] + (((g_CurrentPlayer->pause_watch_position - (nodepos[1] * GE_WATCH_SCALE)) - matrices->m[3][2]) * t);
|
||||
|
||||
matrix_4x4_set_basis_and_position_target(&targetmtx, 0.0f, 0.0f, 0.0f, g_CurrentPlayer->field_1E0, g_CurrentPlayer->field_1E4, g_CurrentPlayer->field_1E8, g_CurrentPlayer->field_1EC, g_CurrentPlayer->field_1F0, g_CurrentPlayer->field_1F4);
|
||||
matrix_4x4_get_rotation_around_xyz(matrices, &currot);
|
||||
@@ -8544,7 +8553,7 @@ Gfx *bondviewRenderWatch(Gfx *gdl)
|
||||
quaternion_slerp(quat1, quat2, t, quat3);
|
||||
quaternion_to_matrix(quat3, matrices->m);
|
||||
matrix_4x4_set_position(&targetpos, matrices);
|
||||
matrix_scalar_multiply(g_CurrentPlayer->watch_scale_destination, (f32 *) matrices);
|
||||
matrix_scalar_multiply(GE_WATCH_SCALE, (f32 *) matrices);
|
||||
total_seconds = time / 60;
|
||||
seconds = total_seconds % 60;
|
||||
total_minutes = total_seconds / 60;
|
||||
@@ -8601,7 +8610,7 @@ Gfx *bondviewRenderWatch(Gfx *gdl)
|
||||
matrix_4x4_7F058C88();
|
||||
gdl = draw_watch_current_page(gdl, finalmtx, (g_CurrentPlayer->watch_animation_state == 5) || (g_CurrentPlayer->watch_animation_state == 12));
|
||||
matrix_4x4_7F058C64();
|
||||
bondviewTransformManyPosToViewMatrix(g_CurrentPlayer->field_23C, objheader->numMatrices);
|
||||
bondviewTransformManyPosToViewMatrix(GE_WATCH_RENDERPOS, objheader->numMatrices);
|
||||
matrix_4x4_7F058C88();
|
||||
}
|
||||
|
||||
|
||||
@@ -1721,6 +1721,35 @@ Gfx *set_enviro_fog_for_items_in_solo_watch_menu(Gfx *gdl, ITEM_IDS itemid, Mtxf
|
||||
{
|
||||
for (j = 0; j != 20; j += 4)
|
||||
{
|
||||
#ifdef PORT
|
||||
/* D140: the raw byte offsets 0x48/0x5c into `Switches` (a ModelNode*
|
||||
* array) assume 4-byte N64 pointers. 0x48/4 = 18, 0x5c/4 = 23; j
|
||||
* steps 0..16 by 4, so k = j>>2 selects Switches[18+k] / [23+k]. On
|
||||
* PC the 8-byte stride made the raw math read misaligned garbage ->
|
||||
* bogus non-NULL ModelNode* -> crash in modelGetNodeRwData. */
|
||||
ModelNode *sw48 = bodymodel->Switches[18 + (j >> 2)];
|
||||
ModelNode *sw5c = bodymodel->Switches[23 + (j >> 2)];
|
||||
|
||||
if (sw48 != NULL)
|
||||
{
|
||||
rwdata = modelGetNodeRwData((Model *) &model, sw48);
|
||||
|
||||
if (rwdata != NULL)
|
||||
{
|
||||
rwdata->Raw.unk00 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (sw5c != NULL)
|
||||
{
|
||||
rwdata = modelGetNodeRwData((Model *) &model, sw5c);
|
||||
|
||||
if (rwdata != NULL)
|
||||
{
|
||||
rwdata->Raw.unk00 = 1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if ((*((ModelNode **) ((((u8 *) bodymodel->Switches) + j) + 0x48))) != NULL)
|
||||
{
|
||||
rwdata = modelGetNodeRwData((Model *) &model, *((ModelNode **) ((((u8 *) bodymodel->Switches) + j) + 0x48)));
|
||||
@@ -1740,6 +1769,7 @@ Gfx *set_enviro_fog_for_items_in_solo_watch_menu(Gfx *gdl, ITEM_IDS itemid, Mtxf
|
||||
rwdata->Raw.unk00 = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-4
@@ -50,6 +50,23 @@
|
||||
#define OPTLABELS_HINT_Y 0xc3
|
||||
#endif
|
||||
|
||||
#ifdef PORT
|
||||
/* D118d: the watch inventory list has a "slam the stick hard" fast-scroll —
|
||||
* a raw *level* check `joyGetStickY < -0x46` / `>= 0x47` that steps the
|
||||
* cursor one item per frame while held. An N64 stick rarely sustains past
|
||||
* ~0x46 along an axis, so a human taps it; the port's digital sources
|
||||
* (keyboard W/S, and SDL pads which peg at 0x80) sit there every frame ->
|
||||
* runaway scroll (one W press skips several items). Drop the stick term for
|
||||
* the port: stick nav still works via the latched single-step path
|
||||
* (`watch_stick_y_pressed_*`, one step per press) and the -0x1e..-0x45
|
||||
* smooth-scroll band; D-pad / C-buttons keep their edge-detected repeat. */
|
||||
#define GE_WATCH_STICK_FASTUP(pad) 0
|
||||
#define GE_WATCH_STICK_FASTDOWN(pad) 0
|
||||
#else
|
||||
#define GE_WATCH_STICK_FASTUP(pad) (joyGetStickY(pad) >= 0x47)
|
||||
#define GE_WATCH_STICK_FASTDOWN(pad) (joyGetStickY(pad) < -0x46)
|
||||
#endif
|
||||
|
||||
// bss
|
||||
Mtx gfx_background_8007B0A0;
|
||||
Mtx gfx_background_8007B0E0;
|
||||
@@ -1024,7 +1041,7 @@ void game_options_inventory_navigation(void)
|
||||
|
||||
if (!get_debug_gunwatchpos_flag())
|
||||
{
|
||||
if (joyGetButtonsPressedThisFrame(PLAYER_1, U_JPAD | U_CBUTTONS) || joyGetStickY(PLAYER_1) >= 0x47)
|
||||
if (joyGetButtonsPressedThisFrame(PLAYER_1, U_JPAD | U_CBUTTONS) || GE_WATCH_STICK_FASTUP(PLAYER_1))
|
||||
{
|
||||
if (((s32) watch_inventory_cursor_pos > 0) && !watch_item_is_actively_selected)
|
||||
{
|
||||
@@ -1033,7 +1050,7 @@ void game_options_inventory_navigation(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (joyGetButtonsPressedThisFrame(PLAYER_1, D_JPAD | D_CBUTTONS) || joyGetStickY(PLAYER_1) < -0x46)
|
||||
if (joyGetButtonsPressedThisFrame(PLAYER_1, D_JPAD | D_CBUTTONS) || GE_WATCH_STICK_FASTDOWN(PLAYER_1))
|
||||
{
|
||||
goto down_body;
|
||||
}
|
||||
@@ -1156,14 +1173,14 @@ void sub_GAME_7F0A611C(f32 *arg0, s32 *arg1, s32 arg2, s32 *arg3, s32 *arg4, s32
|
||||
{
|
||||
if (!get_debug_gunwatchpos_flag())
|
||||
{
|
||||
if (joyGetButtonsPressedThisFrame(PLAYER_1, U_JPAD | U_CBUTTONS) || joyGetStickY(PLAYER_1) >= 0x47)
|
||||
if (joyGetButtonsPressedThisFrame(PLAYER_1, U_JPAD | U_CBUTTONS) || GE_WATCH_STICK_FASTUP(PLAYER_1))
|
||||
{
|
||||
if ((s32)*arg0 > 0 && arg7)
|
||||
{
|
||||
*arg0 -= 1.0f;
|
||||
}
|
||||
}
|
||||
else if (joyGetButtonsPressedThisFrame(PLAYER_1, D_JPAD | D_CBUTTONS) || joyGetStickY(PLAYER_1) < -0x46)
|
||||
else if (joyGetButtonsPressedThisFrame(PLAYER_1, D_JPAD | D_CBUTTONS) || GE_WATCH_STICK_FASTDOWN(PLAYER_1))
|
||||
{
|
||||
if ((s32)*arg0 < arg2 - 1 && arg7)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user