diff --git a/docs/PCPortResearch.md b/docs/PCPortResearch.md index 36515ee6..6c4fda91 100644 --- a/docs/PCPortResearch.md +++ b/docs/PCPortResearch.md @@ -4817,7 +4817,23 @@ Depot, sevb, sevx. `PROPDEF_PC_BYTES == real compiler sizeof`. D122's totality note flagged `[u16|u16]` half-swap but not the `union{ptr; s32}` index-slot case. -### Proposed fix (NOT applied — ABI/layout only, `#ifdef PORT`) +### APPLIED (M-20, commit pending) — ABI/layout only, `#ifdef PORT` + +Fix below applied verbatim. `tools_pc/d88_propdefs.py`: `PROPDEF_PC_BYTES` +`14/19 -> 32`, `44 -> 40`; the `(14,19,38,44)` handler now emits each +`Index{k}` into the low 4 B of the 8-aligned slot at PC `8 + 8*k` +(`nidx = {14:2,19:2,38:2,44:3}`). `loadobjectmodel.c sizepropdef()` PORT +switch: `LINK/SWITCH/LOCK_DOOR -> return 8`, `SAFE_ITEM -> return 10`, +`TAG` stays `6`. Regen chain run. Verified: `-level_09` framediff 3/3 +PASS; `-level_20` behaviour byte-identical to a freshly-built pre-D132 +baseline (the `frame_000320` phash delta is pre-existing stale-golden / +Silo slowdown, NOT a regression — confirmed by building the baseline); +Archives (`-level_25`, SWITCH+SAFE_ITEM), Streets (`-level_29`, 20x +LOCK_DOOR), Dam (`-level_33`, SWITCH+LOCK_DOOR) all load + render + +no-crash. Sweep runs this pass were heavily flaky (level_09 itself +0-framed on a loaded machine) — pure D117/watchdog noise, no crashes. + +### Original proposed fix (as written pre-apply) `tools_pc/d88_propdefs.py` — `PROPDEF_PC_BYTES`: `14: 24 -> 32`, `19: 24 -> 32`, `44: 24 -> 40` (38 stays 32). Replace the diff --git a/src/game/loadobjectmodel.c b/src/game/loadobjectmodel.c index 05b4b41e..701c2c44 100644 --- a/src/game/loadobjectmodel.c +++ b/src/game/loadobjectmodel.c @@ -78,13 +78,17 @@ s32 sizepropdef(PropDefHeaderRecord *pdef) case PROPDEF_OBJECTIVE_ENTER_ROOM: case PROPDEF_WATCH_MENU_OBJECTIVE_TEXT: case PROPDEF_OBJECTIVE_DEPOSIT_OBJECT_IN_ROOM: return 6; + case PROPDEF_TAG: return 6; + case PROPDEF_CAMERAPOS: return 7; + /* D132: LINK / SWITCH / LOCK_DOOR declare their Index{1,2} fields as a + * union with a pointer, so each is an 8B/8-aligned slot on PC, and the + * record ends in a *next the proplvreset2 walk writes -> PC sizeof 32 + * (8 words). SAFE_ITEM has 3 such unions + *next -> sizeof 40 (10). */ case PROPDEF_LINK: case PROPDEF_SWITCH: - case PROPDEF_TAG: - case PROPDEF_SAFE_ITEM: return 6; - case PROPDEF_CAMERAPOS: return 7; case PROPDEF_GUARD: case PROPDEF_LOCK_DOOR: return 8; + case PROPDEF_SAFE_ITEM: return 10; case PROPDEF_RENAME: return 12; case PROPDEF_PROP: case PROPDEF_ALARM: diff --git a/tools_pc/d88_propdefs.py b/tools_pc/d88_propdefs.py index 17b16779..55d2378b 100644 --- a/tools_pc/d88_propdefs.py +++ b/tools_pc/d88_propdefs.py @@ -129,9 +129,9 @@ PROPDEF_PC_BYTES = { 10: 288, # MonitorObjRecord 11: 664, # MultiMonitorObjRecord 13: 248, # AutogunRecord - 14: 24, # LinkRecord (3w + next* grown, 8-aligned) + 14: 32, # LinkRecord (D132: union{ptr;s32} Index1@8 Index2@16 next*@24) 18: 12, # GuardAttributeRecord - 19: 24, # SwitchRecord = LinkRecord + 19: 32, # SwitchRecord = LinkRecord (D132) 20: 200, # MultiAmmoCrateRecord 21: 152, # BodyArmourRecord 22: 24, # TagObjectRecord @@ -148,7 +148,7 @@ PROPDEF_PC_BYTES = { 38: 32, # LockDoorRecord 39: 208, # VehichleRecord (D122: 144 prefix + widened tail) 40: 208, # AircraftRecord (D122) - 44: 24, # SafeObjectRecord fragment (5w + next*), refine on use + 44: 40, # SafeObjectRecord (D132: 3x union{ptr;s32} Index1@8 Index2@16 Index3@24 next*@32) 45: 248, # TankRecord (D122: 144 prefix + 23-word scalar tail + collision*) 46: 28, # CutsceneRecord 47: 168, # TintedGlassRecord @@ -286,11 +286,16 @@ def convert_record(src, so, type_byte): return bytes(out) # renobj* @40 zero if type_byte in (14, 19, 38, 44): # LINK / SWITCH / LOCK_DOOR / SAFE_ITEM + # D132: each Index{1,2,3} field shares a union with a pointer, so on + # PC it lives in the LOW 4 bytes of an 8B/8-aligned slot at PC offset + # 8 + 8*(N-1); the record ends in a *next the setup walk writes. The + # N64 image packs the indices as tight 4-byte words; emitting them + # there put Index1 where the compiler reads Index2. + nidx = {14: 2, 19: 2, 38: 2, 44: 3}[type_byte] out[0:4] = _hdr_word(src[so:so + 4]) - # remaining serialized words: Index1/Index2 are s32 in file (zero or id); - # trailing next* is not serialized. - for i in range(1, n64w): - out[4 * i:4 * i + 4] = _bswap32(src[so + 4 * i: so + 4 * i + 4]) + for k in range(nidx): + w = src[so + 4 * (k + 1): so + 4 * (k + 1) + 4] + out[8 + 8 * k: 12 + 8 * k] = _bswap32(w) # low 4B, LE; high 4B = 0 return bytes(out) if type_byte == 18: # SetGuardAttribute: hdr + s32 chrnum + [s16 unk8][s8 unkA][s8 GrenadeProb]