diff options
| -rw-r--r-- | src/Companion.cpp | 5 | ||||
| -rw-r--r-- | src/Companion.h | 3 | ||||
| -rw-r--r-- | src/factories/DisplayListFactory.cpp | 21 | ||||
| -rw-r--r-- | src/factories/pm64/EffectDListFactory.cpp | 235 | ||||
| -rw-r--r-- | src/factories/pm64/EffectDListFactory.h | 24 |
5 files changed, 19 insertions, 269 deletions
diff --git a/src/Companion.cpp b/src/Companion.cpp index 08022d5..5fa80a6 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -83,7 +83,6 @@ #include "factories/pm64/ImgFXAnimFactory.h" #include "factories/pm64/TitleDataFactory.h" #include "factories/pm64/EntityGfxFactory.h" -#include "factories/pm64/EffectDListFactory.h" #endif #ifdef FZERO_SUPPORT @@ -211,7 +210,6 @@ void Companion::Init(const ExportType type, std::atomic<size_t>& assetCount) { 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>()); - this->RegisterFactory("PM64:EFFECT_DL", std::make_shared<PM64EffectDListFactory>()); #endif #ifdef SF64_SUPPORT @@ -1202,6 +1200,9 @@ void Companion::Process(std::atomic<size_t>& assetCount) { this->gConfig.gbi.version = GBIVersion::f3db; } else if(key == "F3DEX2") { this->gConfig.gbi.version = GBIVersion::f3dex2; + } else if(key == "F3DEX2_PM64") { + this->gConfig.gbi.version = GBIVersion::f3dex2; + this->gConfig.gbi.subversion = GBIMinorVersion::PM64; } else if(key == "F3DEXB") { this->gConfig.gbi.version = GBIVersion::f3dexb; } else if (key == "F3DEX_MK64") { diff --git a/src/Companion.h b/src/Companion.h index 5abe720..eb6de7d 100644 --- a/src/Companion.h +++ b/src/Companion.h @@ -35,7 +35,8 @@ enum class GBIVersion { enum class GBIMinorVersion { None, Mk64, - SM64 + SM64, + PM64 }; enum class TableMode { diff --git a/src/factories/DisplayListFactory.cpp b/src/factories/DisplayListFactory.cpp index 2947dfe..2ee3fbf 100644 --- a/src/factories/DisplayListFactory.cpp +++ b/src/factories/DisplayListFactory.cpp @@ -428,13 +428,20 @@ ExportResult DListBinaryExporter::Export(std::ostream &write, std::shared_ptr<IP auto ptr = w1; auto dec = Companion::Instance->GetSafeStringByAddr(ptr, "TEXTURE"); - // Export texture segment addresses as segmented addresses - N64Gfx value = gsDPSetTextureOTRImage(C0(21, 3), C0(19, 2), C0(0, 10), ptr); - w0 = value.words.w0; - w1 = value.words.w1; - - writer.Write(w0); - writer.Write(w1); + if (Companion::Instance->GetGBIMinorVersion() == GBIMinorVersion::PM64) { + // preserve original w0 bits (fmt/siz/width) exactly, the + // ROM already stores width-1. + uint32_t newW0 = (G_SETTIMG_OTR_HASH << 24) | (w0 & 0x00FFFFFF); + writer.Write(newW0); + writer.Write(ptr); + } else { + // Export texture segment addresses as segmented addresses + N64Gfx value = gsDPSetTextureOTRImage(C0(21, 3), C0(19, 2), C0(0, 10), ptr); + w0 = value.words.w0; + w1 = value.words.w1; + writer.Write(w0); + writer.Write(w1); + } if(dec.has_value()){ uint64_t hash = CRC64(dec.value().c_str()); diff --git a/src/factories/pm64/EffectDListFactory.cpp b/src/factories/pm64/EffectDListFactory.cpp deleted file mode 100644 index b179c4c..0000000 --- a/src/factories/pm64/EffectDListFactory.cpp +++ /dev/null @@ -1,235 +0,0 @@ -// PM64 Effect Display List Factory -// -// Overrides the standard DListFactory binary exporter to fix a width encoding bug -// in torch's G_SETTIMG → G_SETTIMG_OTR_HASH conversion. -// -// The standard DListFactory uses gsDPSetTextureOTRImage(fmt, siz, C0(0,10), ptr) -// which double-subtracts 1 from the width (the original DL already stores width-1, -// and the macro subtracts 1 again). This causes texture_to_load.width to be off by 1, -// which breaks effects that use G_LOADTILE (e.g. sparkles with its 176x22 texture strip). -// -// This factory preserves the original w0 bits exactly: -// newW0 = (G_SETTIMG_OTR_HASH << 24) | (w0 & 0x00FFFFFF) - -#include "EffectDListFactory.h" -#include "Companion.h" -#include "utils/Decompressor.h" -#include "spdlog/spdlog.h" -#include "n64/gbi-otr.h" -#include "strhash64/StrHash64.h" - -// F3DEX2 opcodes -#define F3DEX2_G_VTX 0x01 -#define F3DEX2_G_DL 0xDE -#define F3DEX2_G_MTX 0xDA -#define F3DEX2_G_ENDDL 0xDF -#define F3DEX2_G_SETTIMG 0xFD -#define F3DEX2_G_MOVEMEM 0xDC - -#define C0(pos, width) ((w0 >> (pos)) & ((1U << width) - 1)) - -// Override DListFactory::parse to skip auto-discovery of sub-DL/VTX/light assets. -// All effect assets are already defined in the YAML files, so auto-discovery via -// Companion::Instance->AddAsset() is unnecessary and harmful: it re-registers -// sub-DLs as "GFX" type (standard DListFactory), overwriting the YAML-defined -// "PM64:EFFECT_DL" entries and causing them to be exported by the standard -// DListBinaryExporter instead of PM64EffectDListBinaryExporter. -std::optional<std::shared_ptr<IParsedData>> PM64EffectDListFactory::parse(std::vector<uint8_t>& raw_buffer, YAML::Node& node) { - auto [_, segment] = Decompressor::AutoDecode(node, raw_buffer); - LUS::BinaryReader reader(segment.data, segment.size); - reader.SetEndianness(Torch::Endianness::Big); - - std::vector<uint32_t> gfxs; - bool processing = true; - - while (processing) { - auto w0 = reader.ReadUInt32(); - auto w1 = reader.ReadUInt32(); - uint8_t opcode = w0 >> 24; - - if (opcode == F3DEX2_G_ENDDL) { - processing = false; - } - - if (opcode == F3DEX2_G_DL) { - // If this is a branch (G_DL_NO_PUSH), stop processing like the base class does - if ((w0 >> 16) & 0x01) { - processing = false; - } - // Intentionally skip Companion::Instance->AddAsset() — assets defined in YAML - } - - gfxs.push_back(w0); - gfxs.push_back(w1); - } - - return std::make_shared<DListData>(gfxs); -} - -ExportResult PM64EffectDListBinaryExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node& node, std::string* replacement) { - auto cmds = std::static_pointer_cast<DListData>(raw)->mGfxs; - auto writer = LUS::BinaryWriter(); - - 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 - auto bhash = CRC64((*replacement).c_str()); - writer.Write(static_cast<uint32_t>(G_MARKER << 24)); - writer.Write(static_cast<uint32_t>(0xBEEFBEEF)); - writer.Write(static_cast<uint32_t>(bhash >> 32)); - writer.Write(static_cast<uint32_t>(bhash & 0xFFFFFFFF)); - - for (size_t i = 0; i < cmds.size(); i += 2) { - auto w0 = cmds[i]; - auto w1 = cmds[i + 1]; - uint8_t opcode = w0 >> 24; - - if (opcode == F3DEX2_G_VTX) { - auto ptr = w1; - auto dec = Companion::Instance->GetSafeStringByAddr(ptr, "VTX"); - - if (dec.has_value()) { - uint64_t hash = CRC64(dec.value().c_str()); - if (hash == 0) { - throw std::runtime_error("Vtx hash is 0 for " + dec.value()); - } - // Construct G_VTX_OTR_HASH: preserve n/v0 bits, zero the vtx offset - 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>(hash >> 32)); - writer.Write(static_cast<uint32_t>(hash & 0xFFFFFFFF)); - } else { - SPDLOG_WARN("Could not find vtx at 0x{:X}", ptr); - writer.Write(w0); - writer.Write(w1); - } - continue; - } - - if (opcode == F3DEX2_G_DL) { - auto ptr = w1; - // Use GetStringByAddr (no type check) since sub-DLs are registered as - // "PM64:EFFECT_DL" in YAML, not "GFX" as the standard factory expects. - auto dec = Companion::Instance->GetStringByAddr(ptr); - auto branch = (w0 >> 16) & 0x01; // G_DL_NO_PUSH - - // Construct G_DL_OTR_HASH - uint32_t newW0 = (G_DL_OTR_HASH << 24) | (branch << 16); - writer.Write(newW0); - writer.Write(static_cast<uint32_t>(0)); - - if (dec.has_value()) { - uint64_t hash = CRC64(dec.value().c_str()); - writer.Write(static_cast<uint32_t>(hash >> 32)); - writer.Write(static_cast<uint32_t>(hash & 0xFFFFFFFF)); - } else { - SPDLOG_WARN("Could not find display list at 0x{:X}", ptr); - writer.Write(w0); - writer.Write(w1); - } - - if (branch) { - // Append G_ENDDL after a branch - writer.Write(static_cast<uint32_t>(F3DEX2_G_ENDDL << 24)); - writer.Write(static_cast<uint32_t>(0)); - } - continue; - } - - if (opcode == F3DEX2_G_MOVEMEM) { - auto ptr = w1; - auto res = Companion::Instance->GetStringByAddr(ptr); - bool hasOffset = false; - - if (!res.has_value()) { - res = Companion::Instance->GetStringByAddr(ptr - 0x8); - hasOffset = res.has_value(); - if (!hasOffset) { - SPDLOG_WARN("Could not find light {:X}", ptr); - } - } - - uint8_t index = C0(0, 8); - uint8_t offset = C0(8, 8) * 8; - - uint32_t newW0 = (G_MOVEMEM_OTR_HASH << 24) | (w0 & 0x00FFFFFF); - uint32_t newW1 = _SHIFTL(index, 24, 8) | _SHIFTL(offset, 16, 8) | _SHIFTL((uint8_t)(hasOffset ? 1 : 0), 8, 8); - - writer.Write(newW0); - writer.Write(newW1); - - if (res.has_value()) { - uint64_t hash = CRC64(res.value().c_str()); - writer.Write(static_cast<uint32_t>(hash >> 32)); - writer.Write(static_cast<uint32_t>(hash & 0xFFFFFFFF)); - } else { - SPDLOG_WARN("Could not find light at 0x{:X}", ptr); - writer.Write(w0); - writer.Write(w1); - } - continue; - } - - if (opcode == F3DEX2_G_SETTIMG) { - auto ptr = w1; - auto dec = Companion::Instance->GetSafeStringByAddr(ptr, "TEXTURE"); - - // FIX: Preserve original w0 bits (fmt/siz/width) exactly. - uint32_t newW0 = (G_SETTIMG_OTR_HASH << 24) | (w0 & 0x00FFFFFF); - writer.Write(newW0); - writer.Write(ptr); - - if (dec.has_value()) { - uint64_t hash = CRC64(dec.value().c_str()); - if (hash == 0) { - throw std::runtime_error("Texture hash is 0 for " + dec.value()); - } - writer.Write(static_cast<uint32_t>(hash >> 32)); - writer.Write(static_cast<uint32_t>(hash & 0xFFFFFFFF)); - } else { - SPDLOG_WARN("Could not find texture at 0x{:X}", ptr); - writer.Write(w0); - writer.Write(w1); - } - continue; - } - - if (opcode == F3DEX2_G_MTX) { - auto ptr = w1; - auto dec = Companion::Instance->GetSafeStringByAddr(ptr, "MTX"); - - uint32_t newW0 = (G_MTX_OTR << 24) | (w0 & 0x00FFFFFF); - writer.Write(newW0); - writer.Write(static_cast<uint32_t>(0)); - - if (dec.has_value()) { - uint64_t hash = CRC64(dec.value().c_str()); - if (hash == 0) { - throw std::runtime_error("Matrix hash is 0 for " + dec.value()); - } - writer.Write(static_cast<uint32_t>(hash >> 32)); - writer.Write(static_cast<uint32_t>(hash & 0xFFFFFFFF)); - } else { - SPDLOG_WARN("Could not find matrix at 0x{:X}", ptr); - writer.Write(w0); - writer.Write(w1); - } - continue; - } - - // All other opcodes: pass through unchanged - writer.Write(w0); - writer.Write(w1); - } - - writer.Finish(write); - return std::nullopt; -} diff --git a/src/factories/pm64/EffectDListFactory.h b/src/factories/pm64/EffectDListFactory.h deleted file mode 100644 index 9f9380a..0000000 --- a/src/factories/pm64/EffectDListFactory.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "factories/DisplayListFactory.h" - -class PM64EffectDListBinaryExporter : public BaseExporter { - ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; -}; - -class PM64EffectDListFactory : public DListFactory { -public: - // Override parse to skip auto-discovery of sub-DL/VTX/light assets. - // DListFactory::parse auto-discovers G_DL references and registers them as "GFX" type, - // which overwrites YAML-defined "PM64:EFFECT_DL" entries and causes sub-DLs to be - // exported by the standard DListBinaryExporter (with the width-subtraction bug). - // All effect assets are already defined in the YAML, so no auto-discovery is needed. - std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; - - std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { - return { - REGISTER(Header, DListHeaderExporter) - REGISTER(Binary, PM64EffectDListBinaryExporter) - }; - } -}; |
