diff options
| author | Bassel Shmali <bassel@mobilabsolutions.com> | 2026-03-28 21:05:05 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-28 14:05:05 -0600 |
| commit | fdceee77ae54d0d57cad47108c602408adf83854 (patch) | |
| tree | ff91ea1fbd30803a414f9de1f561c64d9e644648 | |
| parent | 4b4f8fab70ef9569f0ff7ce1a54b3f03762d11dc (diff) | |
PM64: remove EntityGfxFactory - enable floats support in ShapeFactory (#217)
| -rw-r--r-- | src/Companion.cpp | 2 | ||||
| -rw-r--r-- | src/factories/pm64/EntityGfxFactory.cpp | 593 | ||||
| -rw-r--r-- | src/factories/pm64/EntityGfxFactory.h | 40 | ||||
| -rw-r--r-- | src/factories/pm64/ShapeFactory.cpp | 41 |
4 files changed, 21 insertions, 655 deletions
diff --git a/src/Companion.cpp b/src/Companion.cpp index c61ab8a..743368f 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -82,7 +82,6 @@ #include "factories/pm64/StoryImageFactory.h" #include "factories/pm64/ImgFXAnimFactory.h" #include "factories/pm64/TitleDataFactory.h" -#include "factories/pm64/EntityGfxFactory.h" #endif #ifdef FZERO_SUPPORT @@ -209,7 +208,6 @@ void Companion::Init(const ExportType type, std::atomic<size_t>& assetCount) { this->RegisterFactory("PM64:STORY_IMAGE", std::make_shared<PM64StoryImageFactory>()); this->RegisterFactory("PM64:IMGFX_ANIM", std::make_shared<PM64ImgFXAnimFactory>()); this->RegisterFactory("PM64:TITLE_DATA", std::make_shared<PM64TitleDataFactory>()); - this->RegisterFactory("PM64:ENTITY_GFX", std::make_shared<PM64EntityGfxFactory>()); #endif #ifdef SF64_SUPPORT diff --git a/src/factories/pm64/EntityGfxFactory.cpp b/src/factories/pm64/EntityGfxFactory.cpp deleted file mode 100644 index ecaf00b..0000000 --- a/src/factories/pm64/EntityGfxFactory.cpp +++ /dev/null @@ -1,593 +0,0 @@ -#include "EntityGfxFactory.h" -#include "Companion.h" -#include "spdlog/spdlog.h" -#include <unordered_set> -#include <unordered_map> -#include <sstream> -#include "n64/gbi-otr.h" -#include "strhash64/StrHash64.h" - -// F3DEX2 GBI opcodes -#define F3DEX2_G_ENDDL 0xDF -#define F3DEX2_G_VTX 0x01 -#define F3DEX2_G_DL 0xDE -#define F3DEX2_G_SETTIMG 0xFD -#define F3DEX2_G_MOVEMEM 0xDC -#define F3DEX2_G_MTX 0xDA - -// Walk a display list at the given offset, byte-swap commands from BE to native, -// collect them, and recursively process nested display lists. -// Buffer is const — overlapping display lists share tail commands, -// so we must never modify the shared ROM data. -static void WalkDisplayList(const uint8_t* data, uint32_t offset, size_t bufferSize, - std::unordered_set<uint32_t>& visited, std::vector<PM64EntityDisplayListInfo>& collected) { - if (offset >= bufferSize - 8) - return; - if (visited.count(offset)) - return; - visited.insert(offset); - - const uint8_t* ptr = data + offset; - const uint8_t* endPtr = data + bufferSize; - - PM64EntityDisplayListInfo dlInfo; - dlInfo.offset = offset; - - while (ptr + 8 <= endPtr) { - const uint32_t* words = reinterpret_cast<const uint32_t*>(ptr); - uint32_t w0 = BSWAP32(words[0]); - uint32_t w1 = BSWAP32(words[1]); - uint8_t opcode = (w0 >> 24) & 0xFF; - - // G_VTX: w1 is a segment 0xA address, strip segment byte - if (opcode == F3DEX2_G_VTX) { - w1 = w1 & 0x00FFFFFF; - } - - // G_SETTIMG: w1 is a segment 0xA address, strip segment byte - if (opcode == F3DEX2_G_SETTIMG) { - w1 = w1 & 0x00FFFFFF; - } - - // G_MOVEMEM: w1 is a segment 0xA address - if (opcode == F3DEX2_G_MOVEMEM) { - w1 = w1 & 0x00FFFFFF; - } - - // G_MTX: w1 is a segment 0xA address - if (opcode == F3DEX2_G_MTX) { - w1 = w1 & 0x00FFFFFF; - } - - // G_DL: w1 is a segment 0xA address, strip and recurse - if (opcode == F3DEX2_G_DL) { - uint32_t nestedOffset = w1 & 0x00FFFFFF; - w1 = nestedOffset; - WalkDisplayList(data, nestedOffset, bufferSize, visited, collected); - } - - dlInfo.commands.push_back(w0); - dlInfo.commands.push_back(w1); - - if (opcode == F3DEX2_G_ENDDL) { - break; - } - ptr += 8; - } - - if (!dlInfo.commands.empty()) { - collected.push_back(std::move(dlInfo)); - } -} - -std::optional<std::shared_ptr<IParsedData>> PM64EntityGfxFactory::parse(std::vector<uint8_t>& buffer, - YAML::Node& node) { - auto offset = GetSafeNode<uint32_t>(node, "offset"); - auto size = GetSafeNode<uint32_t>(node, "size"); - auto dlistsNode = node["dlists"]; - - if (offset + size > buffer.size()) { - SPDLOG_ERROR("PM64:ENTITY_GFX: Data at offset 0x{:X} exceeds buffer (need {}, have {})", offset, size, - buffer.size() - offset); - return std::nullopt; - } - - // Copy entity graphics data from ROM (initial size from YAML) - std::vector<uint8_t> entityData(buffer.data() + offset, buffer.data() + offset + size); - - // Walk each display list specified in the YAML - std::unordered_set<uint32_t> visited; - std::vector<PM64EntityDisplayListInfo> collectedDLs; - - if (dlistsNode && dlistsNode.IsSequence()) { - for (size_t i = 0; i < dlistsNode.size(); i++) { - uint32_t dlOffset = dlistsNode[i].as<uint32_t>(); - if (dlOffset < size) { - WalkDisplayList(entityData.data(), dlOffset, entityData.size(), visited, collectedDLs); - } - } - } - - // Find maximum offset referenced by any DL command (textures, vertices, matrices - // may live beyond the declared size in shared ROM space) - uint32_t maxReferencedEnd = size; - for (const auto& dl : collectedDLs) { - for (size_t i = 0; i < dl.commands.size(); i += 2) { - uint32_t w0 = dl.commands[i]; - uint32_t w1 = dl.commands[i + 1]; - uint8_t opcode = (w0 >> 24) & 0xFF; - - if (opcode == F3DEX2_G_VTX) { - uint32_t n = (w0 >> 12) & 0xFF; - uint32_t end = w1 + n * 16; - if (end > maxReferencedEnd) - maxReferencedEnd = end; - } else if (opcode == F3DEX2_G_SETTIMG || opcode == F3DEX2_G_MTX || opcode == F3DEX2_G_MOVEMEM) { - // We don't know exact sizes yet, but the offset itself must be in-bounds - // Add a generous margin (textures can be large) - if (w1 >= maxReferencedEnd) - maxReferencedEnd = w1 + 0x800; - } - } - } - - // Expand buffer from ROM if DLs reference data beyond the declared size - if (maxReferencedEnd > size) { - uint32_t expandedSize = maxReferencedEnd; - if (offset + expandedSize > buffer.size()) { - expandedSize = buffer.size() - offset; - } - entityData.assign(buffer.data() + offset, buffer.data() + offset + expandedSize); - } - - // Collect standalone Mtx offsets from YAML (not reachable by DL walking) - std::vector<uint32_t> standaloneMtx; - auto standaloneMtxNode = node["standalone_mtx"]; - if (standaloneMtxNode && standaloneMtxNode.IsSequence()) { - for (size_t i = 0; i < standaloneMtxNode.size(); i++) { - standaloneMtx.push_back(standaloneMtxNode[i].as<uint32_t>()); - } - } - - auto data = std::make_shared<PM64EntityGfxData>(std::move(entityData), std::move(collectedDLs)); - data->mStandaloneMtx = std::move(standaloneMtx); - return data; -} - -// Export a display list as an OTR DisplayList resource -static void ExportEntityDisplayList(const std::string& entityName, const PM64EntityDisplayListInfo& dlInfo) { - char pathBuf[256]; - snprintf(pathBuf, sizeof(pathBuf), "%s/dlist_%X", entityName.c_str(), dlInfo.offset); - std::string path = pathBuf; - std::string fullPath = Companion::Instance->RelativePath(path); - - auto writer = LUS::BinaryWriter(); - BaseExporter::WriteHeader(writer, Torch::ResourceType::DisplayList, 0); - - // GBI version byte (F3DEX2) - writer.Write(static_cast<int8_t>(GBIVersion::f3dex2)); - - // Pad to 8-byte alignment - while (writer.GetBaseAddress() % 8 != 0) - writer.Write(static_cast<int8_t>(0xFF)); - - // G_MARKER with resource hash - uint64_t hash = CRC64(fullPath.c_str()); - writer.Write(static_cast<uint32_t>(G_MARKER << 24)); - writer.Write(static_cast<uint32_t>(0xBEEFBEEF)); - writer.Write(static_cast<uint32_t>(hash >> 32)); - writer.Write(static_cast<uint32_t>(hash & 0xFFFFFFFF)); - - // Write commands, converting to OTR format - for (size_t i = 0; i < dlInfo.commands.size(); i += 2) { - uint32_t w0 = dlInfo.commands[i]; - uint32_t w1 = dlInfo.commands[i + 1]; - uint8_t opcode = (w0 >> 24) & 0xFF; - - if (opcode == F3DEX2_G_SETTIMG) { - // Convert to G_SETTIMG_OTR_HASH - char texPath[256]; - snprintf(texPath, sizeof(texPath), "%s/tex_%X", entityName.c_str(), w1); - std::string fullTexPath = Companion::Instance->RelativePath(texPath); - uint64_t texHash = CRC64(fullTexPath.c_str()); - - uint32_t newW0 = (G_SETTIMG_OTR_HASH << 24) | (w0 & 0x00FFFFFF); - writer.Write(newW0); - writer.Write(static_cast<uint32_t>(0)); - writer.Write(static_cast<uint32_t>(texHash >> 32)); - writer.Write(static_cast<uint32_t>(texHash & 0xFFFFFFFF)); - } else if (opcode == F3DEX2_G_VTX) { - // Convert to G_VTX_OTR_HASH - char vtxPath[256]; - snprintf(vtxPath, sizeof(vtxPath), "%s/vtx_%X", entityName.c_str(), w1); - std::string fullVtxPath = Companion::Instance->RelativePath(vtxPath); - uint64_t vtxHash = CRC64(fullVtxPath.c_str()); - - uint32_t newW0 = (G_VTX_OTR_HASH << 24) | (w0 & 0x00FFFFFF); - writer.Write(newW0); - writer.Write(static_cast<uint32_t>(0)); - writer.Write(static_cast<uint32_t>(vtxHash >> 32)); - writer.Write(static_cast<uint32_t>(vtxHash & 0xFFFFFFFF)); - } else if (opcode == F3DEX2_G_MTX) { - // Convert to G_MTX_OTR (hash-based) - char mtxPath[256]; - snprintf(mtxPath, sizeof(mtxPath), "%s/mtx_%X", entityName.c_str(), w1); - std::string fullMtxPath = Companion::Instance->RelativePath(mtxPath); - uint64_t mtxHash = CRC64(fullMtxPath.c_str()); - - // Preserve flags from original w0 (push/nopush, load/mul, projection/modelview) - uint32_t newW0 = (G_MTX_OTR << 24) | (w0 & 0x00FFFFFF); - writer.Write(newW0); - writer.Write(static_cast<uint32_t>(0)); - writer.Write(static_cast<uint32_t>(mtxHash >> 32)); - writer.Write(static_cast<uint32_t>(mtxHash & 0xFFFFFFFF)); - } else if (opcode == F3DEX2_G_MOVEMEM) { - // Convert to G_MOVEMEM_OTR_HASH - char mmPath[256]; - snprintf(mmPath, sizeof(mmPath), "%s/mm_%X", entityName.c_str(), w1); - std::string fullMmPath = Companion::Instance->RelativePath(mmPath); - uint64_t mmHash = CRC64(fullMmPath.c_str()); - - uint8_t index = w0 & 0xFF; - uint8_t mmOffset = ((w0 >> 8) & 0xFF) * 8; - - writer.Write(static_cast<uint32_t>(G_MOVEMEM_OTR_HASH << 24)); - writer.Write(static_cast<uint32_t>((index << 24) | (mmOffset << 16))); - writer.Write(static_cast<uint32_t>(mmHash >> 32)); - writer.Write(static_cast<uint32_t>(mmHash & 0xFFFFFFFF)); - } else if (opcode == F3DEX2_G_DL) { - // Convert to G_DL_OTR_HASH - char nestedPath[256]; - snprintf(nestedPath, sizeof(nestedPath), "%s/dlist_%X", entityName.c_str(), w1); - std::string fullNestedPath = Companion::Instance->RelativePath(nestedPath); - uint64_t nestedHash = CRC64(fullNestedPath.c_str()); - - uint8_t pushFlag = (w0 >> 16) & 0x01; - uint32_t otrW0 = (0x31u << 24) | (pushFlag << 16); - writer.Write(otrW0); - writer.Write(static_cast<uint32_t>(0)); - writer.Write(static_cast<uint32_t>(nestedHash >> 32)); - writer.Write(static_cast<uint32_t>(nestedHash & 0xFFFFFFFF)); - } else { - // Standard 8-byte command - writer.Write(w0); - writer.Write(w1); - } - } - - std::stringstream ss; - writer.Finish(ss); - std::string str = ss.str(); - std::vector<char> data(str.begin(), str.end()); - Companion::Instance->RegisterCompanionFile(path, data); -} - -// Export vertex data as a Vertex resource (V1 format with float ob[]) -static void ExportVertexResource_Entity(const std::string& entityName, const uint8_t* data, uint32_t offset, - uint32_t size, uint32_t totalSize) { - char pathBuf[256]; - snprintf(pathBuf, sizeof(pathBuf), "%s/vtx_%X", entityName.c_str(), offset); - std::string path = pathBuf; - - auto writer = LUS::BinaryWriter(); - BaseExporter::WriteHeader(writer, Torch::ResourceType::Vertex, 0); - - // Write vertex count and per-vertex data (read from raw BE ROM data) - uint32_t count = size / 16; - writer.Write(count); - for (uint32_t i = 0; i < count; i++) { - const uint8_t* src = data + offset + i * 16; - writer.Write(static_cast<int16_t>((src[0] << 8) | src[1])); // ob[0] - writer.Write(static_cast<int16_t>((src[2] << 8) | src[3])); // ob[1] - writer.Write(static_cast<int16_t>((src[4] << 8) | src[5])); // ob[2] - writer.Write(static_cast<uint16_t>((src[6] << 8) | src[7])); // flag - writer.Write(static_cast<int16_t>((src[8] << 8) | src[9])); // tc[0] - writer.Write(static_cast<int16_t>((src[10] << 8) | src[11])); // tc[1] - writer.Write(src[12]); - writer.Write(src[13]); - writer.Write(src[14]); - writer.Write(src[15]); // cn[4] - } - - std::stringstream ss; - writer.Finish(ss); - std::string str = ss.str(); - std::vector<char> fileData(str.begin(), str.end()); - Companion::Instance->RegisterCompanionFile(path, fileData); -} - -// Map N64 fmt/siz to Torch TextureType enum value -static uint32_t N64FmtSizToTextureType(uint32_t fmt, uint32_t siz) { - switch (fmt) { - case 0: // G_IM_FMT_RGBA - return (siz == 3) ? 1 : 2; // RGBA32bpp or RGBA16bpp - case 2: // G_IM_FMT_CI - return (siz == 0) ? 3 : 4; // Palette4bpp or Palette8bpp - case 4: // G_IM_FMT_I - return (siz == 0) ? 5 : 6; // Grayscale4bpp or Grayscale8bpp - case 3: // G_IM_FMT_IA - if (siz == 0) - return 7; // GrayscaleAlpha4bpp - if (siz == 1) - return 8; // GrayscaleAlpha8bpp - return 9; // GrayscaleAlpha16bpp - default: - return 2; // Default to RGBA16bpp - } -} - -// Export texture/palette data as a Texture resource (V1 format) -// Fast3D interpreter reads pixel/palette data as BE byte pairs — keep raw ROM byte order. -static void ExportTextureResource(const std::string& entityName, const uint8_t* data, uint32_t offset, uint32_t size, - const char* prefix, uint32_t settimgW0) { - if (offset + size > 0x100000) - return; // Sanity check - - char pathBuf[256]; - snprintf(pathBuf, sizeof(pathBuf), "%s/%s_%X", entityName.c_str(), prefix, offset); - std::string path = pathBuf; - - // Extract format info from the G_SETTIMG w0 word - uint32_t fmt = (settimgW0 >> 21) & 0x7; - uint32_t siz = (settimgW0 >> 19) & 0x3; - uint32_t width = (settimgW0 & 0xFFF) + 1; - - // Compute height from data size and pixel format - uint32_t bitsPerPixel; - switch (siz) { - case 0: - bitsPerPixel = 4; - break; - case 1: - bitsPerPixel = 8; - break; - case 2: - bitsPerPixel = 16; - break; - case 3: - bitsPerPixel = 32; - break; - default: - bitsPerPixel = 16; - break; - } - uint32_t bytesPerRow = (width * bitsPerPixel + 7) / 8; - uint32_t height = (bytesPerRow > 0) ? (size / bytesPerRow) : 1; - if (height == 0) - height = 1; - - auto writer = LUS::BinaryWriter(); - BaseExporter::WriteHeader(writer, Torch::ResourceType::Texture, 1); - writer.Write(N64FmtSizToTextureType(fmt, siz)); // Type - writer.Write(width); // Width - writer.Write(height); // Height - writer.Write(static_cast<uint32_t>(0)); // Flags - writer.Write(1.0f); // HByteScale - writer.Write(1.0f); // VPixelScale - writer.Write(static_cast<uint32_t>(size)); // ImageDataSize - writer.Write(const_cast<char*>(reinterpret_cast<const char*>(data + offset)), size); - - std::stringstream ss; - writer.Finish(ss); - std::string str = ss.str(); - std::vector<char> fileData(str.begin(), str.end()); - Companion::Instance->RegisterCompanionFile(path, fileData); -} - -// Export matrix data as a Blob resource — convert N64 fixed-point to float[4][4] -static void ExportMatrixBlob(const std::string& entityName, const uint8_t* data, uint32_t offset) { - const uint32_t MTX_SIZE = 64; // N64 Mtx is 64 bytes (s15.16 interleaved) - std::vector<uint8_t> mtxData(data + offset, data + offset + MTX_SIZE); - - // Byte-swap 32-bit words from BE - for (uint32_t i = 0; i + 4 <= MTX_SIZE; i += 4) { - uint32_t* v = reinterpret_cast<uint32_t*>(mtxData.data() + i); - *v = BSWAP32(*v); - } - - // Decode interleaved integer/fraction parts to float[4][4] - int32_t* addr = reinterpret_cast<int32_t*>(mtxData.data()); - float matrix[4][4]; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 2; j++) { - int32_t int_part = addr[i * 2 + j]; - uint32_t frac_part = addr[8 + i * 2 + j]; - matrix[i][j * 2] = (int32_t)((int_part & 0xFFFF0000) | (frac_part >> 16)) / 65536.0f; - matrix[i][j * 2 + 1] = (int32_t)((int_part << 16) | (frac_part & 0xFFFF)) / 65536.0f; - } - } - - char pathBuf[256]; - snprintf(pathBuf, sizeof(pathBuf), "%s/mtx_%X", entityName.c_str(), offset); - std::string path = pathBuf; - - auto writer = LUS::BinaryWriter(); - BaseExporter::WriteHeader(writer, Torch::ResourceType::Blob, 0); - writer.Write(static_cast<uint32_t>(MTX_SIZE)); - writer.Write(reinterpret_cast<char*>(matrix), MTX_SIZE); - - std::stringstream ss; - writer.Finish(ss); - std::string str = ss.str(); - std::vector<char> fileData(str.begin(), str.end()); - Companion::Instance->RegisterCompanionFile(path, fileData); -} - -// Export G_MOVEMEM data (lights, viewports) as a Blob resource -static void ExportMovememBlob(const std::string& entityName, const uint8_t* data, uint32_t offset, uint32_t size, - uint8_t index) { - std::vector<uint8_t> mmData(data + offset, data + offset + size); - - // Viewport data has s16 fields that need byte-swap - if (index == 0x08) { // G_MV_VIEWPORT - for (uint32_t i = 0; i + 2 <= size; i += 2) { - uint16_t* v = reinterpret_cast<uint16_t*>(mmData.data() + i); - *v = BSWAP16(*v); - } - } - - char pathBuf[256]; - snprintf(pathBuf, sizeof(pathBuf), "%s/mm_%X", entityName.c_str(), offset); - std::string path = pathBuf; - - auto writer = LUS::BinaryWriter(); - BaseExporter::WriteHeader(writer, Torch::ResourceType::Blob, 0); - writer.Write(static_cast<uint32_t>(size)); - writer.Write(reinterpret_cast<char*>(mmData.data()), size); - - std::stringstream ss; - writer.Finish(ss); - std::string str = ss.str(); - std::vector<char> fileData(str.begin(), str.end()); - Companion::Instance->RegisterCompanionFile(path, fileData); -} - -ExportResult PM64EntityGfxBinaryExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, - std::string& entryName, YAML::Node& node, std::string* replacement) { - auto entityData = std::static_pointer_cast<PM64EntityGfxData>(raw); - - // Extract entity name from entry path - std::string entityName = entryName; - size_t lastSlash = entryName.rfind('/'); - if (lastSlash != std::string::npos) { - entityName = entryName.substr(lastSlash + 1); - } - - // Collect all vertex, texture, matrix, and movemem offsets from display lists - std::unordered_set<uint32_t> vtxOffsets; - std::unordered_map<uint32_t, uint32_t> texInfo; // offset → G_SETTIMG w0 - std::unordered_set<uint32_t> mtxOffsets; - std::unordered_map<uint32_t, uint32_t> mmInfo; // offset → w0 - - for (const auto& dl : entityData->mDisplayLists) { - for (size_t i = 0; i < dl.commands.size(); i += 2) { - uint32_t w0 = dl.commands[i]; - uint32_t w1 = dl.commands[i + 1]; - uint8_t opcode = (w0 >> 24) & 0xFF; - - if (opcode == F3DEX2_G_VTX) { - vtxOffsets.insert(w1); - } else if (opcode == F3DEX2_G_SETTIMG) { - if (texInfo.find(w1) == texInfo.end()) { - texInfo[w1] = w0; - } - } else if (opcode == F3DEX2_G_MTX) { - mtxOffsets.insert(w1); - } else if (opcode == F3DEX2_G_MOVEMEM) { - if (mmInfo.find(w1) == mmInfo.end()) { - mmInfo[w1] = w0; - } - } - } - } - - // Export vertex blobs - std::vector<uint32_t> sortedVtxOffsets(vtxOffsets.begin(), vtxOffsets.end()); - std::sort(sortedVtxOffsets.begin(), sortedVtxOffsets.end()); - - for (size_t i = 0; i < sortedVtxOffsets.size(); i++) { - uint32_t vtxOff = sortedVtxOffsets[i]; - // Find max vertex count from G_VTX commands referencing this offset - uint32_t vtxSize = 0; - for (const auto& dl : entityData->mDisplayLists) { - for (size_t j = 0; j < dl.commands.size(); j += 2) { - uint32_t w0 = dl.commands[j]; - uint32_t w1 = dl.commands[j + 1]; - uint8_t op = (w0 >> 24) & 0xFF; - if (op == F3DEX2_G_VTX && w1 == vtxOff) { - uint32_t n = (w0 >> 12) & 0xFF; - uint32_t candidateSize = n * 16; - if (candidateSize > vtxSize) - vtxSize = candidateSize; - } - } - } - if (vtxSize == 0) - vtxSize = 256; - if (vtxOff + vtxSize <= entityData->mBuffer.size()) { - ExportVertexResource_Entity(entityName, entityData->mBuffer.data(), vtxOff, vtxSize, - entityData->mBuffer.size()); - } - } - - // Export texture resources - std::vector<uint32_t> sortedTexOffsets; - for (const auto& [off, w0] : texInfo) { - sortedTexOffsets.push_back(off); - } - std::sort(sortedTexOffsets.begin(), sortedTexOffsets.end()); - - for (size_t i = 0; i < sortedTexOffsets.size(); i++) { - uint32_t texOff = sortedTexOffsets[i]; - uint32_t texSize; - if (i + 1 < sortedTexOffsets.size()) { - texSize = sortedTexOffsets[i + 1] - texOff; - } else { - // Last texture - find next known structure after it - uint32_t nextOff = entityData->mBuffer.size(); - for (const auto& dl : entityData->mDisplayLists) { - if (dl.offset > texOff && dl.offset < nextOff) { - nextOff = dl.offset; - } - } - for (uint32_t vo : sortedVtxOffsets) { - if (vo > texOff && vo < nextOff) { - nextOff = vo; - } - } - for (uint32_t mo : mtxOffsets) { - if (mo > texOff && mo < nextOff) { - nextOff = mo; - } - } - texSize = nextOff - texOff; - } - if (texOff + texSize <= entityData->mBuffer.size()) { - ExportTextureResource(entityName, entityData->mBuffer.data(), texOff, texSize, "tex", texInfo[texOff]); - } - } - - // Export matrix blobs (from DL references) - for (uint32_t mtxOff : mtxOffsets) { - if (mtxOff + 64 <= entityData->mBuffer.size()) { - ExportMatrixBlob(entityName, entityData->mBuffer.data(), mtxOff); - } - } - - // Export standalone matrix blobs (not reachable by DL walking, e.g. Chest lid, Padlock shackle) - for (uint32_t mtxOff : entityData->mStandaloneMtx) { - if (mtxOffsets.count(mtxOff) == 0 && mtxOff + 64 <= entityData->mBuffer.size()) { - ExportMatrixBlob(entityName, entityData->mBuffer.data(), mtxOff); - } - } - - // Export movemem data blobs - for (const auto& [mmOff, mmW0] : mmInfo) { - uint32_t sizeField = (mmW0 >> 19) & 0x1F; - uint32_t dataSize = (sizeField + 1) * 8; - uint8_t index = mmW0 & 0xFF; - if (mmOff + dataSize <= entityData->mBuffer.size()) { - ExportMovememBlob(entityName, entityData->mBuffer.data(), mmOff, dataSize, index); - } - } - - // Export each display list - for (const auto& dl : entityData->mDisplayLists) { - ExportEntityDisplayList(entityName, dl); - } - - // Write main blob (entire entity data) - auto writer = LUS::BinaryWriter(); - WriteHeader(writer, Torch::ResourceType::Blob, 0); - writer.Write(static_cast<uint32_t>(entityData->mBuffer.size())); - writer.Write(reinterpret_cast<char*>(entityData->mBuffer.data()), entityData->mBuffer.size()); - writer.Finish(write); - - return std::nullopt; -} - -ExportResult PM64EntityGfxHeaderExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, - std::string& entryName, YAML::Node& node, std::string* replacement) { - // Header generation handled by tools/extract-entity-offsets.py - return std::nullopt; -} diff --git a/src/factories/pm64/EntityGfxFactory.h b/src/factories/pm64/EntityGfxFactory.h deleted file mode 100644 index c64712b..0000000 --- a/src/factories/pm64/EntityGfxFactory.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "factories/BaseFactory.h" -#include "types/RawBuffer.h" -#include <vector> - -struct PM64EntityDisplayListInfo { - uint32_t offset; - std::vector<uint32_t> commands; -}; - -class PM64EntityGfxData : public IParsedData { -public: - std::vector<uint8_t> mBuffer; - std::vector<PM64EntityDisplayListInfo> mDisplayLists; - std::vector<uint32_t> mStandaloneMtx; // Mtx offsets not reachable by DL walking - - PM64EntityGfxData(std::vector<uint8_t>&& buffer, std::vector<PM64EntityDisplayListInfo>&& displayLists) - : mBuffer(std::move(buffer)), mDisplayLists(std::move(displayLists)) { - } -}; - -class PM64EntityGfxBinaryExporter : public BaseExporter { - ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; -}; - -class PM64EntityGfxHeaderExporter : public BaseExporter { - ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; -}; - -class PM64EntityGfxFactory : public BaseFactory { -public: - std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; - inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { - return { - REGISTER(Header, PM64EntityGfxHeaderExporter) - REGISTER(Binary, PM64EntityGfxBinaryExporter) - }; - } -}; diff --git a/src/factories/pm64/ShapeFactory.cpp b/src/factories/pm64/ShapeFactory.cpp index f4826a2..6d1b661 100644 --- a/src/factories/pm64/ShapeFactory.cpp +++ b/src/factories/pm64/ShapeFactory.cpp @@ -139,19 +139,20 @@ static void ByteSwapDisplayList(uint8_t* data, uint32_t offset, size_t size) { } // Handle G_VTX - convert vertex address to vertex-table-relative offset - // With GBI_FLOATS, Vtx is 24 bytes (not 16), so convert byte offset accordingly if (opcode == F3DEX2_G_VTX) { // w1 contains the N64 vertex address - convert to file offset first uint32_t vtxFileOffset = N64AddrToOffset(w1); - // Then convert to vertex-table-relative offset with 16->24 byte stride conversion - uint32_t vtxByteOffset; + // Then convert to vertex-table-relative byte offset (stride is 16, same as sizeof(Vtx)) if (gVertexTableOffset > 0 && vtxFileOffset >= gVertexTableOffset) { - vtxByteOffset = vtxFileOffset - gVertexTableOffset; + w1 = vtxFileOffset - gVertexTableOffset; } else { - vtxByteOffset = vtxFileOffset; + w1 = vtxFileOffset; + } + if (Companion::Instance->GetConfig().gbi.useFloats) { + // N64 Vtx is 16 bytes, float Vtx is 24 bytes — rescale the byte offset + uint32_t vtxIndex = w1 / 16; + w1 = vtxIndex * 24; } - uint32_t vtxIndex = vtxByteOffset / 16; - w1 = vtxIndex * 24; // sizeof(Vtx) with GBI_FLOATS = 24 } // Handle G_SETTIMG - convert texture address to file offset @@ -264,27 +265,27 @@ static void ByteSwapModelGroupData(uint8_t* data, uint32_t offset, size_t size) group[3] = static_cast<uint32_t>(numChildren); group[4] = childList; - // Convert N64 fixed-point matrix (s15.16 interleaved) to float[4][4] - // Multiple groups can share the same matrix — only convert once + // Byte-swap transform matrix — multiple groups can share the same matrix, only convert once if (IsValidOffset(transformMatrix, size - 0x40) && !gVisitedMatrices.count(transformMatrix)) { gVisitedMatrices.insert(transformMatrix); uint32_t* raw = reinterpret_cast<uint32_t*>(data + transformMatrix); - // First byte-swap all 16 words from BE for (int i = 0; i < 16; i++) { raw[i] = BSWAP32(raw[i]); } - // Decode interleaved integer/fraction parts to float - int32_t* addr = reinterpret_cast<int32_t*>(raw); - float matrix[4][4]; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 2; j++) { - int32_t int_part = addr[i * 2 + j]; - uint32_t frac_part = addr[8 + i * 2 + j]; - matrix[i][j * 2] = (int32_t)((int_part & 0xFFFF0000) | (frac_part >> 16)) / 65536.0f; - matrix[i][j * 2 + 1] = (int32_t)((int_part << 16) | (frac_part & 0xFFFF)) / 65536.0f; + if (Companion::Instance->GetConfig().gbi.useFloats) { + // Decode interleaved integer/fraction parts to float[4][4] + int32_t* addr = reinterpret_cast<int32_t*>(raw); + float matrix[4][4]; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 2; j++) { + int32_t int_part = addr[i * 2 + j]; + uint32_t frac_part = addr[8 + i * 2 + j]; + matrix[i][j * 2] = (int32_t)((int_part & 0xFFFF0000) | (frac_part >> 16)) / 65536.0f; + matrix[i][j * 2 + 1] = (int32_t)((int_part << 16) | (frac_part & 0xFFFF)) / 65536.0f; + } } + memcpy(raw, matrix, sizeof(matrix)); } - memcpy(raw, matrix, sizeof(matrix)); } // Byte-swap child list and recurse into child nodes |
