From 152d463ff4507c66ceb3f55ca6bb149bf9b1bc4d Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Thu, 30 Apr 2026 17:43:07 -0700 Subject: [PATCH] decompressed: sort section.relocs by address after parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recompiler walks instructions linearly and advances reloc_index only forward (recompilation.cpp: while section.relocs[reloc_index].address < vram). This requires the section's relocs to be sorted by address — out-of-order entries are skipped silently and emitted as literal immediates instead of RELOC_HI16/RELOC_LO16/RELOC_R_MIPS_26 macros. Stadium's raw fragment reloc table orders entries by HI16+LO16 pair adjacency, NOT by instruction address. For stadium_models sub-fragment 4 (variant 384, hash 0x242995EF6B92F471), the raw table holds: [HI16 @ off 0x50][HI16 @ off 0x30][LO16 @ off 0x60] Both HI16 entries pair correctly via raw-list adjacency (target_section_offset is computed before this sort). But when the recompiler walks instruction at PC 0x8FF00030, the already-advanced reloc_index points at the entry for offset 0x50 (seen first in raw order) and the address comparison fails. The HI16 at 0x30 is silently skipped and emitted as the literal `S32(0x8FF1 << 16)`. The matching LO16 at 0x60 IS encountered in linear order, so it emits correctly as `RELOC_LO16(384, 0xB14C)`. The asymmetric pair produces: ctx->r3 = S32(0x8FF1 << 16); // HI literal ctx->r2 = ADD32(ctx->r3, (int16_t)RELOC_LO16(384, 0xB14C)); // LO reloc'd For section 384 with runtime base 0x8027FAB0, RELOC_LO16(384, 0xB14C) sign-extends to -0x5404, yielding `0x8FF10000 + (-0x5404) = 0x8FF0ABFC` instead of the intended `runtime_base + 0xB14C = 0x8028ABFC`. The result lands back in the pattern bucket and process_geo_layout walks bogus geo data → cmd_byte=0xFF → lookup-miss 0x00000E00 → crash. Fix: std::sort section_out.relocs by address ascending after parsing. Pairing has already been computed via raw-list adjacency (which is independent of address), so the sort only affects the recompiler's lookup order. Verified: variant 384's recompiled C now emits all three lui instructions as RELOC_HI16(384, ...), attract demo no longer crashes on 0x8FF0ABFC. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/decompressed.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/decompressed.cpp b/src/decompressed.cpp index 8f65ce5..21b8114 100644 --- a/src/decompressed.cpp +++ b/src/decompressed.cpp @@ -1,5 +1,6 @@ #include "decompressed.h" +#include #include #include #include @@ -239,6 +240,25 @@ bool parse_fragment_relocs(const std::vector& bytes, } } + // The recompiler walks instructions linearly and advances + // reloc_index only forward (recompilation.cpp: while + // section.relocs[reloc_index].address < vram). It REQUIRES the + // relocs to be sorted by address — out-of-order entries are + // skipped silently and emitted as literal immediates. + // + // Stadium's raw reloc table is ordered by HI16+LO16 pair + // adjacency, NOT by instruction address. For example, in + // stadium_models sub-fragment 4 (variant 384), the table holds: + // [HI16 @ off 0x50][HI16 @ off 0x30][LO16 @ off 0x60] + // Pairing has already baked target_section_offset above (the + // HI16 at 0x30 pairs with the LO16 at 0x60 via raw-list + // adjacency, which is independent of address). Now sort by + // address so the recompiler's linear walk hits every entry. + std::sort(section_out.relocs.begin(), section_out.relocs.end(), + [](const Reloc& a, const Reloc& b) { + return a.address < b.address; + }); + return true; }