mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
D126: objective sub-record ->next pointer growth desyncs propdef walk
criteria_picture (30) / criteria_roomentered (32) / criteria_deposit (33) / setup_objective_text (35) each end in a T*next list pointer that the setup walk writes unconditionally (set_parent_cur_obj_* / setup_briefing_text_entry_parent). On x86-64 the pointer widens 4->8B and 8-aligns at offset 16, making the struct 24B/6w (N64 16/20). d88_propdefs.py emitted N64 size -> the runtime 8-byte ->next store clobbered the next propdef record's header -> walk desync -> command indices drifted ~100 -> setupDoor linkedDoor resolution landed on the wrong record -> crash. Fixes C3r (Bunker2 -level_27), C4 (Depot -level_30), C6 (Surface2 -level_43) in one change. Sweep 13 -> 16/21. Bunker1/Silo/Aztec/Archives/Egypt/Train unregressed; d125_check.py 21/21 MATCH. - tools_pc/d88_propdefs.py: PROPDEF_PC_BYTES[30/32/33/35]=24 + typed handler - src/game/loadobjectmodel.c: sizepropdef PORT returns 6 for those types
This commit is contained in:
1 parent
060a7252e5
commit
73dfa8d81f
5 files changed
+100
-29
No files matched your search
+19
-22
@@ -27,19 +27,20 @@ Build = `f2beae4b` + `tools_pc/d88_propdefs.py` (D123) + `port/src/gimgfixup.c`
|
||||
| Runway | 35 | CRASH | `import_texture_i8` gfx_pc.cpp:821 (fault 0x721b8ee8) — same as Facility | **C2** |
|
||||
| Facility | 34 | CRASH | `import_texture_i8` gfx_pc.cpp:821 (fault 0x72181ee8) — model-GDL relocation align bug, D124, NOT fixed | **C2** |
|
||||
| Jungle | 37 | CRASH | `gfx_sp_matrix` gfx_pc.cpp:1046 (fault 0x401c68e0) — C2 texture crash fixed (D124), now explosion-DL `G_MTX` (D75/matrix family) | **C2m** |
|
||||
| Aztec | 28 | **PASS** 91.0% | — (C3 fixed, D125) | — |
|
||||
| Bunker2 | 27 | CRASH | `door7F054FB4` propobj.c:13536 (`var_s1->openPosition`, `var_s1`=0xffff.. from `linkedDoor`) | **C3r** |
|
||||
| Depot | 30 | CRASH | `sub_GAME_7F00324C` prop.c:902 (`sp4C->room` after `walkTilesBetweenPoints`) | **C4** |
|
||||
| Aztec | 28 | **PASS** 90.8% | — (C3 fixed, D125) | — |
|
||||
| Bunker2 | 27 | **PASS** 91.5% | — (C3r fixed, D126) | — |
|
||||
| Depot | 30 | **PASS** 79.8% | — (C4 fixed, D126) | — |
|
||||
| Control | 23 | CRASH | `sub_GAME_7F0BA2D4` bg.c:5723 (`portal_pts->numPoints`, via chrprop.c:62) | **C5** |
|
||||
| Surface2 | 43 | CRASH | `modelLoad` loadobjectmodel.c:393 (`PitemZ_entries[modelid].header->RootNode`) | **C6** |
|
||||
| Surface2 | 43 | **PASS** 70.4% | — (C6 fixed, D126) | — |
|
||||
| Surface1 | 36 | CRASH | `sndSetupSound` snd.c:653 (fault 0x56220001…, ascii-ish) | **C7** |
|
||||
|
||||
**13 / 21 PASS.** D123 cleared C1 on all 6 (Dam/Frigate/Statue/Cradle/Streets
|
||||
PASS; Runway falls through to C2). D124 cleared Jungle's texture crash but
|
||||
it now hits an explosion-DL matrix crash. D125 (`d88_emit.py:374` 8-byte
|
||||
slice / 4-byte literal → boundpad-name blob drift) cleared C3-Aztec;
|
||||
Bunker2 falls through to a separate DOOR-tail `linkedDoor` layout bug
|
||||
(C3r). 8 crashes remain in 6 classes.
|
||||
**16 / 21 PASS.** D123 cleared C1 on all 6. D124 cleared Jungle's texture
|
||||
crash (now hits an explosion-DL matrix crash). D125 cleared C3-Aztec.
|
||||
**D126** (objective sub-record `->next` pointer widens 4→8B → 8-byte store
|
||||
clobbers the next propdef record's header → walk desync → wrong command
|
||||
indices) cleared **C3r Bunker2 + C4 Depot + C6 Surface2** in one fix
|
||||
(`d88_propdefs.py` types 30/32/33/35 → 24B/6w). 5 crashes remain in 4
|
||||
classes: C2 Runway+Facility, C2m Jungle, C5 Control, C7 Surface1.
|
||||
|
||||
Notes: Streets passed this run but the C1 agent saw a `gfx_sp_matrix`
|
||||
crash — likely the same explosion-DL `G_MTX` (C2m) as Jungle, timing-
|
||||
@@ -162,20 +163,16 @@ id from a propDef record). Fault addr 0x0. Files: `tools_pc/d88_propdefs.py`,
|
||||
crash on load, not silence. May be dodge-able with a narrow guard until
|
||||
audio lands. Files: `src/snd.c`, `port/src/` audio stubs.
|
||||
|
||||
## Next (8 crashes, 6 classes — priority order)
|
||||
## Next (5 crashes, 4 classes — priority order)
|
||||
1. **C2** Runway + Facility — model-GDL relocation align bug in
|
||||
`objecthandler_2.c`/`texLoadFromGdl` (D80/D82/D83 area). Shared infra,
|
||||
2 levels + likely helps Jungle/Streets. Biggest win.
|
||||
2. **C3r** Bunker2 — DOOR-tail `linkedDoorOffset`/`linkedDoor` layout in
|
||||
`d88_propdefs.py` vs compiled PC `DoorRecord` (Aztec cleared by D125);
|
||||
fold into the converter write-audit. Brief `docs/BRIEF-C3-C6-prop-model.md`.
|
||||
3. **C2m** Jungle (+ Streets intermittent) — explosion-DL `G_MTX`
|
||||
(`gfx_sp_matrix`), D75/matrix family.
|
||||
4. **C6** Surface2 — `PitemZ_entries` modelid (D122/D123 continuation).
|
||||
5. **C5** Control — BG portal `offset_portal` resolve.
|
||||
6. **C4** Depot — BG tile/room table.
|
||||
7. **C7** Surface1 — `sndSetupSound`; parked-audio, may just need a narrow
|
||||
load guard.
|
||||
2 levels + likely helps Jungle. Biggest win.
|
||||
2. **C2m** Jungle — explosion-DL `G_MTX` (`gfx_sp_matrix`), D75/matrix
|
||||
family.
|
||||
3. **C5** Control — BG portal `g_BgPortals[].offset_portal` unresolved/BE,
|
||||
`bg.c:5723` via `chrprop.c:62`. BG sidecar (`d69_emit.py`).
|
||||
4. **C7** Surface1 — `sndSetupSound` `snd.c:653`; parked-audio, may just
|
||||
need a narrow load guard until Phase 3.
|
||||
|
||||
Re-run `tools_pc/level_sweep.sh` after each class fix; keep
|
||||
`-level_09`/`-level_20` green (`tools_pc/framediff.py`).
|
||||
@@ -819,6 +819,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D122 | per-level prop/item model-load crash (Dam/Facility/Runway `modelLoad`/`modelInitRwData`): `d88_propdefs.py` had no handler for 6 ObjectRecord-derived propDef types (47/39/40/45/13/20) → generic arm half-swapped the `[s16 obj][s16 pad]` word → OOB `PitemZ_entries[]` | converter fixed; residual chr/fast3d crashes on those levels are separate |
|
||||
| D123 | crash class C1: `chrIsNotDeadOrShot` NULL deref on 6 levels (Dam/Runway/Frigate/Statue/Streets/Cradle). D122's `OBJ_TAIL_DESC` zeroed the widened `VehichleRecord/AircraftRecord.ailist` slot (w32), but `prop.c:1764/1786` reads a pre-populated int AI-list id there → `ailistFindById(0)` → `GAILIST_AIM_AT_BOND` → `ai()` runs a CHR aim list with `ChrEntityp==NULL` | converter fixed (`OBJ_ID_WORDS`); C1 cleared on all 6, residual crashes are fast3d (C2) |
|
||||
| D125 | crash classes C3+C6 (Aztec/Bunker2/Surface2): **root cause found (M-16)** — `tools_pc/d88_emit.py:374` assigns a 4-byte literal to an 8-byte slice (`out[dst_o+0x30:dst_o+0x38] = b"\x00…"`, the stan zero-fill in `emit_pad`); every pad/boundpad record silently shrinks the output bytearray by 4 B, and once the tail falls below the boundpad plink string blob, later verbatim leaf writes hit Python out-of-range slice semantics (insert at current end, not relocated offset) → boundpad names drift/truncate (Aztec pad33 `p138d2`→`8d2`) → `stanPackId()` reject → `getposstan()` NULL stan → `setupDoor` leaves door `model=NULL` → crash `propobj.c:13601`. M-14's "propDefs zeros in RAM" was a misread; sidecar propdefs were always correct (M-15) and post-load RAM matches the sidecar byte-for-byte (M-16) | **resolved (M-16b)** — line 374 → 8 NULs; all 21 sidecars regen'd; **Aztec `-level_28` now PASSES** (was C3 CRASH). Bunker2 falls through to a separate DOOR-tail `linkedDoor` layout bug (C3 residual) |
|
||||
| D126 | crash classes C3r/C4/C6 (Bunker2 `-level_27` `door7F054FB4` propobj.c:13523, Depot `-level_30` prop.c:902, Surface2 `-level_43` loadobjectmodel.c:393): the objective sub-records `criteria_picture` (30), `criteria_roomentered` (32), `criteria_deposit` (33), `setup_objective_text` (35) each end in a `T *next` list pointer that the setup walk (`set_parent_cur_obj_*` / `setup_briefing_text_entry_parent`) writes unconditionally. On PC that pointer widens 4→8B and lands 8-aligned at offset 16 → struct is 24B/6w (N64 16/20). `d88_propdefs.py` emitted them at N64 size via the generic arm → the runtime 8-byte `->next` write clobbered the *next* record's header → propdef walk desynced, command indices drifted ~100, `linkedDoorOffset + arg2` resolved to the wrong record → door `linkedDoor` chain walked into garbage. | resolved — `d88_propdefs.py` PROPDEF_PC_BYTES[30/32/33/35]=24 + typed handler; `loadobjectmodel.c` sizepropdef PORT returns 6. Bunker2/Depot/Surface2 now PASS (13→16/21) |
|
||||
| D124 | crash class C2: fast3d bad texture pointer. **Jungle** (`0xabcd0824`): `gimgSyncCompiledGlobalDLs()` slot-detect keyed on the post-fixup marker, which `texLoad()` had already overwritten → compiled `globalDL_0xNNN` explosion DLs kept link-time `IMAGESEG` words → latent on every level, tripped by the first explosion-DL draw. **Facility** (`0x72181ee8`): separate — model/prop GDL from the `texLoadFromGdl()`/`sub_GAME_7F0762E0` relocation path writes non-16-aligned `dst` (N64 8B vs PC 16B `Gfx` stride mix in `objecthandler_2.c`), open D80/D82/D83 area | Jungle fixed (`port/src/gimgfixup.c`); Facility diagnosed, not fixed |
|
||||
|
||||
Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS
|
||||
@@ -4355,6 +4356,39 @@ has linked double-doors, Aztec's are singletons so it dodged it. Folded
|
||||
into the converter write-audit (C3 residual). Sweep: 12 → 13/21.
|
||||
All M-16 probes reverted, scratch files deleted, `d125_check.py` kept.
|
||||
|
||||
**D126 (session M-17) — objective-subrecord `->next` pointer growth desyncs
|
||||
the propdef walk (C3r Bunker2 + C4 Depot + C6 Surface2).** The Bunker2
|
||||
`door7F054FB4` crash was NOT a DOOR-tail layout bug (the M-16b hypothesis).
|
||||
Instrumenting the `proplvreset2` first-loop walk showed `pdefIndex` drifting
|
||||
~100 ahead of the offline `convert_stream` record index; a byte dump proved
|
||||
the RAM propDefs matched the sidecar exactly *at load* but had record N+1's
|
||||
header zeroed *by the time the walk reached it*. Cause: four objective
|
||||
sub-record types — `criteria_picture` (30), `criteria_roomentered` (32),
|
||||
`criteria_deposit` (33), `setup_objective_text` (35) — are
|
||||
`{ s32 x N; T *next; }`. `set_parent_cur_obj_photograph/enter_room/
|
||||
deposited_in_room` and `setup_briefing_text_entry_parent` (`objective.c`)
|
||||
write `arg0->next` unconditionally while the setup walk visits each record,
|
||||
building a runtime linked list. On N64 `next` is a 4-byte word at the end of
|
||||
a 16/20-byte record — no spill. On x86-64 the pointer widens to 8B and the
|
||||
compiler 8-aligns it at offset 16, so the struct is 24B; the 8-byte
|
||||
`->next` store lands at record+16..24, clobbering the *following* record's
|
||||
`PropDefHeaderRecord` (type byte at +0x13). Walk desyncs → command indices
|
||||
drift → `setupDoor`'s `setupGetPtrToCommandByIndex(linkedDoorOffset + arg2)`
|
||||
returns the wrong record → `door->linkedDoor` chain walks into non-door
|
||||
memory → crash. Depot (`prop.c:902` tile-walk room) and Surface2
|
||||
(`PitemZ_entries[modelid]`, the C6 "D122 continuation") were the same
|
||||
desync landing on different downstream derefs. Fix:
|
||||
`tools_pc/d88_propdefs.py` `PROPDEF_PC_BYTES[30/32/33/35] = 24` + a typed
|
||||
handler emitting the leading s32 words then an 8-byte zero `next`;
|
||||
`src/game/loadobjectmodel.c` `sizepropdef` PORT returns 6 for those types
|
||||
(N64 `#else` kept). Regen'd all 21 sidecars, `d125_check.py` 21/21 MATCH.
|
||||
**Bunker2 `-level_27`, Depot `-level_30`, Surface2 `-level_43` now PASS**
|
||||
(91.5% / 79.8% / 70.4%); Bunker1/Silo/Aztec/Archives/Egypt/Train
|
||||
unregressed. Sweep 13 → 16/21. Generalisable quirk appended to
|
||||
`PORT-LEARNINGS.md` §A. Remaining crashes: C2 Runway/Facility (model-GDL
|
||||
align), C2m Jungle (`G_MTX`), C5 Control (BG portal), C7 Surface1
|
||||
(`sndSetupSound`).
|
||||
|
||||
**D124-Facility addendum (session M-14 — partial, NOT fixed, out of time).**
|
||||
Re-instrumented with a `GE_C2GDL` probe in `sub_GAME_7F0762E0`
|
||||
(`objecthandler_2.c`) + a bad-`G_SETTIMG` catcher in fast3d
|
||||
|
||||
@@ -26,6 +26,19 @@ state.
|
||||
N64 via act-union@0x2C+84; act union moves to ~0x38 on PC).
|
||||
- **Open landmine:** raw hardcoded-offset accessors into `struct player`
|
||||
/ `struct hand` — see `docs/AUDIT-M6-player-offsets.md`.
|
||||
- **D126 corollary — a trailing runtime list pointer in a ROM-serialized
|
||||
record.** If a fixed-size ROM record ends with a `T *next` (or `*child`,
|
||||
`*parent`) that game code *writes* while walking the setup stream
|
||||
(linked-list build), that field is 4B/end-of-record on N64 but widens to
|
||||
8B and 8-aligns on PC — the store spills past the N64 record size into
|
||||
the *next* record and silently corrupts a polymorphic walk downstream
|
||||
(type byte clobbered → wrong strides → wrong command indices → wrong
|
||||
pointer resolution, crashing far away). Offline converter (`d*_emit.py`)
|
||||
MUST emit these at the real PC `sizeof` with the pointer slot widened +
|
||||
the matching `#ifdef PORT` `sizepropdef`/stride. Instances: `d88_propdefs.py`
|
||||
types 30/32/33/35 (`criteria_*` / `setup_objective_text` `->next`),
|
||||
22 (`TagObjectRecord.NextTag`, already handled). Check every record type
|
||||
the walk can see for a `set_parent_*` / `*_entry_parent` writer.
|
||||
|
||||
## B. 16-byte PC `Gfx` / `Vtx` vs 8-byte N64
|
||||
|
||||
|
||||
@@ -66,11 +66,18 @@ s32 sizepropdef(PropDefHeaderRecord *pdef)
|
||||
case PROPDEF_OBJECTIVE_DEPOSIT_OBJECT: return 2;
|
||||
case PROPDEF_GUARD_ATTRIBUTE:
|
||||
case PROPDEF_OBJECTIVE_COPY_ITEM: return 3;
|
||||
case PROPDEF_OBJECTIVE_START:
|
||||
case PROPDEF_OBJECTIVE_START: return 4;
|
||||
/* D126: criteria_picture / criteria_roomentered / criteria_deposit /
|
||||
* setup_objective_text each end in a T*next list pointer that the setup
|
||||
* walk (set_parent_cur_obj_* / setup_briefing_text_entry_parent) writes
|
||||
* unconditionally. The pointer widens 4->8B and lands 8-aligned at
|
||||
* offset 16, so the PC struct is 24B / 6 words (N64: 16/20). Converter
|
||||
* emits the matching stride (tools_pc/d88_propdefs.py PROPDEF_PC_BYTES).
|
||||
* N64 counts were: PHOTOGRAPH/ENTER_ROOM/WATCH_MENU 4, DEPOSIT_IN_ROOM 5. */
|
||||
case PROPDEF_OBJECTIVE_PHOTOGRAPH:
|
||||
case PROPDEF_OBJECTIVE_ENTER_ROOM:
|
||||
case PROPDEF_WATCH_MENU_OBJECTIVE_TEXT: return 4;
|
||||
case PROPDEF_OBJECTIVE_DEPOSIT_OBJECT_IN_ROOM: return 5;
|
||||
case PROPDEF_WATCH_MENU_OBJECTIVE_TEXT:
|
||||
case PROPDEF_OBJECTIVE_DEPOSIT_OBJECT_IN_ROOM: return 6;
|
||||
case PROPDEF_LINK:
|
||||
case PROPDEF_SWITCH:
|
||||
case PROPDEF_TAG:
|
||||
|
||||
@@ -138,12 +138,12 @@ PROPDEF_PC_BYTES = {
|
||||
23: 16, # OBJECTIVE_START (4w, no serialized pointer)
|
||||
24: 4, # OBJECTIVE_END
|
||||
25: 8, 26: 8, 27: 8, 28: 8, 29: 8, # OBJ_* (2w)
|
||||
30: 16, # OBJ_PHOTOGRAPH (4w serialized, lastprop* not serialized)
|
||||
30: 24, # OBJ_PHOTOGRAPH (D126: criteria_picture, trailing ->next widens)
|
||||
31: 4, # OBJ_NULL
|
||||
32: 16, # OBJ_ENTER_ROOM
|
||||
33: 20, # OBJ_DEPOSIT_IN_ROOM
|
||||
32: 24, # OBJ_ENTER_ROOM (D126: criteria_roomentered, trailing ->next widens)
|
||||
33: 24, # OBJ_DEPOSIT_IN_ROOM (D126: criteria_deposit, trailing ->next widens)
|
||||
34: 12, # OBJ_COPY_ITEM (3w)
|
||||
35: 16, # WATCH_MENU_OBJ_TEXT (4w)
|
||||
35: 24, # WATCH_MENU_OBJ_TEXT (D126: setup_objective_text, trailing ->next widens)
|
||||
37: 48, # RenameObjectRecord
|
||||
38: 32, # LockDoorRecord
|
||||
39: 208, # VehichleRecord (D122: 144 prefix + widened tail)
|
||||
@@ -335,6 +335,26 @@ def convert_record(src, so, type_byte):
|
||||
assert pc <= pcb, (type_byte, pc, pcb)
|
||||
return bytes(out)
|
||||
|
||||
if type_byte in (30, 32, 33, 35):
|
||||
# D126: objective sub-records with a trailing runtime linked-list pointer
|
||||
# 30 criteria_picture / 32 criteria_roomentered /
|
||||
# 33 criteria_deposit / 35 setup_objective_text
|
||||
# all share the shape `{ s32 x N; T *next; }`. The `next` word is a
|
||||
# runtime list pointer -- set_parent_cur_obj_* / setup_briefing_text_
|
||||
# entry_parent overwrite it unconditionally during the setup walk -- so
|
||||
# its ROM value is don't-care. On PC the pointer widens 4->8B and lands
|
||||
# 8-aligned at offset 16 (compiler pads the 3-s32 variants), making the
|
||||
# struct 24B / 6 words (was 16/20 on N64). Without the grow, that 8-byte
|
||||
# write clobbers the next record's header -> propdef walk desyncs, command
|
||||
# indices drift, linked-door resolution lands on the wrong record
|
||||
# (Bunker2 -level_27 crash in door7F054FB4). Emit the leading s32 words
|
||||
# then 8 zero bytes for `next`.
|
||||
out[0:4] = _hdr_word(src[so:so + 4])
|
||||
for i in range(1, n64w - 1):
|
||||
out[4 * i:4 * i + 4] = _bswap32(src[so + 4 * i: so + 4 * i + 4])
|
||||
# next* @ offset 16 already zero (bytes 12..15 = alignment pad, also zero)
|
||||
return bytes(out)
|
||||
|
||||
# ---- generic: header-only records (objectives, END, DOOR_SCALE, ...) ----
|
||||
# No serialized pointers. header word special, rest plain bswap32.
|
||||
out[0:4] = _hdr_word(src[so:so + 4])
|
||||
|
||||
Reference in new issue
Block a user