diff options
| -rw-r--r-- | src/factories/pm64/AudioFactory.cpp | 12 | ||||
| -rw-r--r-- | src/factories/pm64/ShapeFactory.cpp | 57 |
2 files changed, 55 insertions, 14 deletions
diff --git a/src/factories/pm64/AudioFactory.cpp b/src/factories/pm64/AudioFactory.cpp index 8c0b0ee..655d579 100644 --- a/src/factories/pm64/AudioFactory.cpp +++ b/src/factories/pm64/AudioFactory.cpp @@ -399,9 +399,17 @@ static void ByteSwapAudioData(uint8_t* data, size_t size) { uint32_t subTableAbsOff = fileOffset + cmdOffset; if (CHECK_BOUNDS(subTableAbsOff, trackCount * 4, size)) { uint16_t* subEntries = reinterpret_cast<uint16_t*>(data + subTableAbsOff); + // Stop at the first offset==0 sentinel, the trailing + // unused slots can overlap the next sub-table, and + // double-swapping those bytes reverts them to BE + // (e.g. SOUND_FLO_BEANSTALK_START_GROWING). for (uint32_t t = 0; t < trackCount; t++) { - subEntries[t * 2] = BSWAP16(subEntries[t * 2]); // offset - subEntries[t * 2 + 1] = BSWAP16(subEntries[t * 2 + 1]); // info + uint16_t off = BSWAP16(subEntries[t * 2]); + if (off == 0) { + break; + } + subEntries[t * 2] = off; + subEntries[t * 2 + 1] = BSWAP16(subEntries[t * 2 + 1]); } } swappedSubTables.insert(cmdOffset); diff --git a/src/factories/pm64/ShapeFactory.cpp b/src/factories/pm64/ShapeFactory.cpp index 6d1b661..398f0c4 100644 --- a/src/factories/pm64/ShapeFactory.cpp +++ b/src/factories/pm64/ShapeFactory.cpp @@ -348,27 +348,60 @@ static void ByteSwapModelNode(uint8_t* data, uint32_t offset, size_t size) { } // Find the ROOT node (type=7) by scanning the shape data -// Returns the file offset of the ROOT node, or 0 if not found +// Returns the file offset of the ROOT node, or 0 if not found. +// +// Some shapes (e.g. hos_03) contain a dummy/unused node with type=7 that appears +// earlier in the file than the real root and passes the same surface-level +// validation. To disambiguate, we collect ALL type=7 candidates and prefer +// the one whose implied base addres equals the standard PM64 shape base 0x80210000. static uint32_t FindRootNodeOffset(uint8_t* data, size_t size) { + uint32_t headerRootAddr = BSWAP32(*reinterpret_cast<uint32_t*>(data)); + + uint32_t firstValid = 0; // fallback: behaviour before the disambiguation + uint32_t bestExact = 0; // implied base == 0x80210000 (the known PM64 base) + uint32_t bestAligned = 0; // implied base aligned to 0x10000 + for (uint32_t offset = 0x20; offset < size - 0x14; offset += 4) { int32_t type = static_cast<int32_t>(BSWAP32(*reinterpret_cast<uint32_t*>(data + offset))); - if (type == 7) { // SHAPE_TYPE_ROOT - // Validate surrounding fields look like a ModelNode - uint32_t displayAddr = BSWAP32(*reinterpret_cast<uint32_t*>(data + offset + 0x04)); - int32_t numProps = static_cast<int32_t>(BSWAP32(*reinterpret_cast<uint32_t*>(data + offset + 0x08))); - uint32_t groupAddr = BSWAP32(*reinterpret_cast<uint32_t*>(data + offset + 0x10)); + if (type != 7) // SHAPE_TYPE_ROOT + continue; - bool valid = (displayAddr == 0 || displayAddr > 0x80000000); - valid = valid && (groupAddr == 0 || groupAddr > 0x80000000); - valid = valid && (numProps >= 0 && numProps <= 100); + uint32_t displayAddr = BSWAP32(*reinterpret_cast<uint32_t*>(data + offset + 0x04)); + int32_t numProps = static_cast<int32_t>(BSWAP32(*reinterpret_cast<uint32_t*>(data + offset + 0x08))); + uint32_t groupAddr = BSWAP32(*reinterpret_cast<uint32_t*>(data + offset + 0x10)); - if (valid) { - return offset; - } + bool valid = (displayAddr == 0 || displayAddr > 0x80000000); + valid = valid && (groupAddr == 0 || groupAddr > 0x80000000); + valid = valid && (numProps >= 0 && numProps <= 100); + if (!valid) + continue; + + if (firstValid == 0) + firstValid = offset; + + // Skip the implied-base check when header[0] doesn't look like a valid + // N64 vaddr + if (headerRootAddr <= 0x80000000 || headerRootAddr <= offset) + continue; + + uint32_t impliedBase = headerRootAddr - offset; + if (impliedBase == 0x80210000) { + bestExact = offset; + break; // perfect match — stop scanning + } + if (bestAligned == 0 && (impliedBase & 0xFFFF) == 0) { + bestAligned = offset; } } + if (bestExact != 0) + return bestExact; + if (bestAligned != 0) + return bestAligned; + if (firstValid != 0) + return firstValid; + SPDLOG_WARN("Could not find ROOT node in shape data"); return 0; } |
