D157: objective MinDificulty read at wrong byte on LE -> campaign never unlocks

ROOT CAUSE of 'complete Dam, Facility stays locked'. The type-23
objective record's MinDificulty is a big-endian s32 at word 3; on N64 the
value is its low byte == offset 0xF, and struct objective_entry reads
'difficulty' as s8 @0xF. d88_propdefs.py bswap32's word 3, so on
little-endian the value moves to offset 0xC -> objective_entry.difficulty
@0xF reads 0 for EVERY objective. get_difficulty_for_objective() then
returns Agent(0) for all, so objectiveIsAllComplete() on Agent evaluates
objectives that should be difficulty-gated out (Dam obj 0 'Neutralize all
alarms' is Secret Agent; obj 1/2 are 00 Agent) -> they're incomplete ->
never returns TRUE -> end_of_mission_briefing() / the unlock write never
runs. User was right: you don't touch an alarm on Agent Dam.

Fix (D151-class, #ifdef PORT): reorder struct objective_entry's word-3
tail so 'difficulty' reads @0xC (post-swap position); N64 layout kept
under #else. Also fixes dump_objectives.py (was reading byte 0xC = the
BE high byte = always 0; hence LEVEL-OBJECTIVES.md said 'Agent' for
everything). Dam now decodes obj0=Secret Agent, obj1/2=00 Agent,
obj3=Agent. -level_09 coverage identical (phash delta is the M-30 RC2
texture change, not this).
This commit is contained in:
James Dansereau committed 2026-08-31 10:25:16 -04:00
1 parent 9c23e5bcb6
commit 4cd8ccaeac
3 files changed
+17 -6

No files matched your search

+3 -1
View File
@@ -156,7 +156,9 @@ def dump_level(levelid, lav_name):
nblocks += 1
idx = bs32(buf, off + 4)
textid = be32(buf, off + 8)
diff = buf[off + 12]
# MinDificulty is a big-endian s32 at word 3 (offset 0xC); the value
# lives in its low byte == offset 0xF.
diff = buf[off + 15]
print(f"Objective {idx} [briefing text id {textid}] min difficulty: "
f"{DIFF_NAMES.get(diff, diff)}")
j = i + 1