summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/factories/pm64/AudioFactory.cpp12
-rw-r--r--src/factories/pm64/ShapeFactory.cpp57
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;
}