diff --git a/tools/ZAPD/.gitrepo b/tools/ZAPD/.gitrepo index 1034fcf320..04b472ce2d 100644 --- a/tools/ZAPD/.gitrepo +++ b/tools/ZAPD/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/zeldaret/ZAPD.git branch = master - commit = d0cd6b3974706fc4d89c9b6545b8c17dd424b664 - parent = c0e02913038e871221b2fcfc8be65b8dc281aa4a + commit = 4f7b8393ec8a3abd59649c2ba669e951fb61f3d2 + parent = efb9badbf211c5d5065c1b25b58f0e0fe2d17ec4 method = merge cmdver = 0.4.3 diff --git a/tools/ZAPD/ExporterTest/Main.cpp b/tools/ZAPD/ExporterTest/Main.cpp index 732dcedc2f..4f683a1ba3 100644 --- a/tools/ZAPD/ExporterTest/Main.cpp +++ b/tools/ZAPD/ExporterTest/Main.cpp @@ -76,4 +76,4 @@ static void ImportExporters() // When ZAPD starts up, it will automatically call the below function, which in turn sets up our // exporters. -REGISTER_EXPORTER(ImportExporters) \ No newline at end of file +REGISTER_EXPORTER(ImportExporters); diff --git a/tools/ZAPD/ZAPD/Globals.cpp b/tools/ZAPD/ZAPD/Globals.cpp index 51880b5d51..036e4a5cbf 100644 --- a/tools/ZAPD/ZAPD/Globals.cpp +++ b/tools/ZAPD/ZAPD/Globals.cpp @@ -23,6 +23,16 @@ Globals::Globals() outputPath = Directory::GetCurrentDirectory(); } +Globals::~Globals() +{ + auto& exporters = GetExporterMap(); + + for (auto& it : exporters) + { + delete it.second; + } +} + void Globals::AddSegment(int32_t segment, ZFile* file) { if (std::find(segments.begin(), segments.end(), segment) == segments.end()) @@ -38,21 +48,21 @@ bool Globals::HasSegment(int32_t segment) return std::find(segments.begin(), segments.end(), segment) != segments.end(); } -std::map* Globals::GetExporterMap() +std::map& Globals::GetExporterMap() { static std::map exporters; - return &exporters; + return exporters; } void Globals::AddExporter(std::string exporterName, ExporterSet* exporterSet) { - auto exporters = GetExporterMap(); - (*exporters)[exporterName] = exporterSet; + auto& exporters = GetExporterMap(); + exporters[exporterName] = exporterSet; } ZResourceExporter* Globals::GetExporter(ZResourceType resType) { - auto exporters = *GetExporterMap(); + auto& exporters = GetExporterMap(); if (currentExporter != "" && exporters[currentExporter]->exporters.find(resType) != exporters[currentExporter]->exporters.end()) @@ -63,7 +73,7 @@ ZResourceExporter* Globals::GetExporter(ZResourceType resType) ExporterSet* Globals::GetExporterSet() { - auto exporters = *GetExporterMap(); + auto& exporters = GetExporterMap(); if (currentExporter != "") return exporters[currentExporter]; @@ -193,3 +203,11 @@ ExternalFile::ExternalFile(fs::path nXmlPath, fs::path nOutPath) : xmlPath{nXmlPath}, outPath{nOutPath} { } + +ExporterSet::~ExporterSet() +{ + for (auto& it : exporters) + { + delete it.second; + } +} diff --git a/tools/ZAPD/ZAPD/Globals.h b/tools/ZAPD/ZAPD/Globals.h index e90efc25fc..265f1af241 100644 --- a/tools/ZAPD/ZAPD/Globals.h +++ b/tools/ZAPD/ZAPD/Globals.h @@ -24,6 +24,8 @@ typedef void (*ExporterSetFuncVoid3)(); class ExporterSet { public: + ~ExporterSet(); + std::map exporters; ExporterSetFuncVoid parseArgsFunc = nullptr; ExporterSetFuncVoid2 parseFileModeFunc = nullptr; @@ -64,10 +66,11 @@ public: std::map symbolMap; std::string currentExporter; - static std::map* GetExporterMap(); + static std::map& GetExporterMap(); static void AddExporter(std::string exporterName, ExporterSet* exporterSet); Globals(); + ~Globals(); void AddSegment(int32_t segment, ZFile* file); bool HasSegment(int32_t segment); diff --git a/tools/ZAPD/ZAPD/ZCollision.h b/tools/ZAPD/ZAPD/ZCollision.h index 0c6710e472..d0325bfb95 100644 --- a/tools/ZAPD/ZAPD/ZCollision.h +++ b/tools/ZAPD/ZAPD/ZCollision.h @@ -82,7 +82,7 @@ public: std::vector polygons; std::vector polygonTypes; std::vector waterBoxes; - CameraDataList* camData; + CameraDataList* camData = nullptr; ZCollisionHeader(ZFile* nParent); ~ZCollisionHeader(); diff --git a/tools/ZAPD/ZAPD/ZDisplayList.cpp b/tools/ZAPD/ZAPD/ZDisplayList.cpp index f7be34a22d..63c5684228 100644 --- a/tools/ZAPD/ZAPD/ZDisplayList.cpp +++ b/tools/ZAPD/ZAPD/ZDisplayList.cpp @@ -1836,15 +1836,9 @@ std::string ZDisplayList::GetSourceOutputCode(const std::string& prefix) std::string declaration; - int32_t curAddr = vtxKeys[i]; - for (auto& vtx : item) - { declaration += StringHelper::Sprintf("\t%s,\n", vtx.GetBodySourceCode().c_str()); - curAddr += 16; - } - // Ensure there's always a trailing line feed to prevent dumb warnings. // Please don't remove this line, unless you somehow made a way to prevent // that warning when building the OoT repo. diff --git a/tools/ZAPD/ZAPD/ZFile.cpp b/tools/ZAPD/ZAPD/ZFile.cpp index 82cb84f83f..7cbfeba886 100644 --- a/tools/ZAPD/ZAPD/ZFile.cpp +++ b/tools/ZAPD/ZAPD/ZFile.cpp @@ -5,13 +5,14 @@ #include #include -#include -#include #include "Globals.h" #include "OutputFormatter.h" +#include "Utils/BinaryWriter.h" #include "Utils/Directory.h" #include "Utils/File.h" +#include "Utils/MemoryStream.h" #include "Utils/Path.h" +#include "Utils/StringHelper.h" #include "ZAnimation.h" #include "ZArray.h" #include "ZBackground.h" @@ -129,17 +130,47 @@ void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename) if (rangeStart > rangeEnd) throw std::runtime_error("Error: RangeStart must be before than RangeEnd."); - // Not every XML may have a segment number, so this doesn't make much sense anymore. - // if (reader->Attribute("Segment") == nullptr) - // throw std::runtime_error( - // StringHelper::Sprintf("ZFile::ParseXML: Error in '%s'.\n" - // "\t Missing 'Segment' attribute in File node. \n", - // name.c_str())); - - if (reader->Attribute("Segment") != nullptr) + const char* segmentXml = reader->Attribute("Segment"); + if (segmentXml != nullptr) { - segment = StringHelper::StrToL(reader->Attribute("Segment"), 10); - Globals::Instance->AddSegment(segment, this); + if (!StringHelper::HasOnlyDigits(segmentXml)) + { + throw std::runtime_error(StringHelper::Sprintf( + "error: Invalid segment value '%s': must be a decimal between 0 and 15 inclusive", + segmentXml)); + } + + segment = StringHelper::StrToL(segmentXml, 10); + if (segment > 15) + { + if (segment == 128) + { +#ifdef DEPRECATION_ON + fprintf(stderr, "warning: segment 128 is deprecated.\n\tRemove " + "'Segment=\"128\"' from the xml to use virtual addresses\n"); +#endif + } + else + { + throw std::runtime_error( + StringHelper::Sprintf("error: invalid segment value '%s': must be a decimal " + "number between 0 and 15 inclusive", + segmentXml)); + } + } + } + Globals::Instance->AddSegment(segment, this); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) + { + if (segment == 0x80) + { + printf("File '%s' using virtual addresses.\n", GetName().c_str()); + } + else + { + printf("File '%s' using segment %X.\n", GetName().c_str(), segment); + } } if (mode == ZFileMode::Extract || mode == ZFileMode::ExternalFile) @@ -938,6 +969,7 @@ std::string ZFile::ProcessDeclarations() lastItem.second->text += "\n" + curItem.second->text; declarations.erase(curItem.first); declarationKeys.erase(declarationKeys.begin() + i); + delete curItem.second; i--; continue; } diff --git a/tools/ZAPD/ZAPD/ZFile.h b/tools/ZAPD/ZAPD/ZFile.h index 93234761d0..9de6950e4a 100644 --- a/tools/ZAPD/ZAPD/ZFile.h +++ b/tools/ZAPD/ZAPD/ZFile.h @@ -34,7 +34,9 @@ public: std::map declarations; std::string defines; std::vector resources; - uint32_t segment; + + // Default to using virtual addresses + uint32_t segment = 0x80; uint32_t baseAddress, rangeStart, rangeEnd; bool isExternalFile = false; diff --git a/tools/ZAPD/ZAPD/ZResource.h b/tools/ZAPD/ZAPD/ZResource.h index 727eca2ccb..ff35786fac 100644 --- a/tools/ZAPD/ZAPD/ZResource.h +++ b/tools/ZAPD/ZAPD/ZResource.h @@ -209,6 +209,7 @@ class ZResourceExporter { public: ZResourceExporter() = default; + virtual ~ZResourceExporter() = default; virtual void Save(ZResource* res, fs::path outPath, BinaryWriter* writer) = 0; }; @@ -239,4 +240,4 @@ typedef ZResource*(ZResourceFactoryFunc)(ZFile* nParent); public: \ ZResExp_##expFunc() { expFunc(); } \ }; \ - static ZResExp_##expFunc inst_ZResExp_##expFunc; + static ZResExp_##expFunc inst_ZResExp_##expFunc diff --git a/tools/ZAPD/ZAPD/ZRoom/Commands/SetMesh.cpp b/tools/ZAPD/ZAPD/ZRoom/Commands/SetMesh.cpp index 4fa1f05cb2..d1c8abd5c5 100644 --- a/tools/ZAPD/ZAPD/ZRoom/Commands/SetMesh.cpp +++ b/tools/ZAPD/ZAPD/ZRoom/Commands/SetMesh.cpp @@ -208,6 +208,7 @@ ZDisplayList* PolygonDlist::MakeDlist(segptr_t ptr, [[maybe_unused]] const std:: parent->GetRawData(), dlistAddress, Globals::Instance->game == ZGame::OOT_SW97 ? DListType::F3DEX : DListType::F3DZEX); ZDisplayList* dlist = new ZDisplayList(parent); + parent->AddResource(dlist); dlist->ExtractFromBinary(dlistAddress, dlistLength); dlist->SetName(dlist->GetDefaultName(prefix)); GenDListDeclarations(zRoom, parent, dlist); diff --git a/tools/ZAPD/ZAPD/ZSkeleton.cpp b/tools/ZAPD/ZAPD/ZSkeleton.cpp index ebdf6455e6..84f00c8187 100644 --- a/tools/ZAPD/ZAPD/ZSkeleton.cpp +++ b/tools/ZAPD/ZAPD/ZSkeleton.cpp @@ -49,7 +49,11 @@ void ZSkeleton::ParseRawData() const auto& rawData = parent->GetRawData(); limbsArrayAddress = BitConverter::ToUInt32BE(rawData, rawDataIndex); limbCount = BitConverter::ToUInt8BE(rawData, rawDataIndex + 4); - dListCount = BitConverter::ToUInt8BE(rawData, rawDataIndex + 8); + + if (type == ZSkeletonType::Flex) + { + dListCount = BitConverter::ToUInt8BE(rawData, rawDataIndex + 8); + } if (limbsArrayAddress != 0 && GETSEGNUM(limbsArrayAddress) == parent->segment) { diff --git a/tools/ZAPD/docs/zapd_extraction_xml_reference.md b/tools/ZAPD/docs/zapd_extraction_xml_reference.md index 818d9f1c70..06b95bb575 100644 --- a/tools/ZAPD/docs/zapd_extraction_xml_reference.md +++ b/tools/ZAPD/docs/zapd_extraction_xml_reference.md @@ -98,7 +98,7 @@ This table summarizes if the asset will be marked `static` (✅) or not (❌) - `Name`: Required. The name of the file in `baserom/` which will be extracted. - `OutName`: Optional. The output name of the generated C source file. Defaults to the value passed to `Name`. - - `Segment`: Required. This is the segment number of the current file. Expects a decimal number, usually 6 if it is an object, or 128 for overlays (It's kinda a whacky hack to get around of the `0x80` addresses). + - `Segment`: Optional. This is the segment number of the current file. Expects a decimal number between 0 and 15 inclusive, usually 6 if it is an object. If not specified, the file will use VRAM instead of segmented addresses. - `BaseAddress`: Optional. RAM address of the file. Expects a hex number (with `0x` prefix). Default value: `0`. - `RangeStart`: Optional. File offset where the extraction will begin. Hex. Default value: `0x000000000`. - `RangeEnd`: Optional. File offset where the extraction will end. Hex. Default value: `0xFFFFFFFF`. @@ -106,6 +106,27 @@ This table summarizes if the asset will be marked `static` (✅) or not (❌) ------------------------- +### ExternalFile + +Allows ZAPD to map segmented addresses to variables declared in other files by using its XML. + +It is useful for objects that use variables from `gameplay_keep`, `gameplay_dangeon_keep`, `gameplay_field_keep`, etc. + +This tag can be used in the global `config.xml` file. + +- Example of this tag: + +```xml + +``` + +- Attributes: + + - `XmlPath`: Required. The path of the XML, relative to the value set by `ExternalXMLFolder` in the configuration file. + - `OutPath`: Required. The path were the header for the corresponding external file is. It is used to `#include` it in the generated `.c` file. + +------------------------- + ### Texture Textures are extracted as `.png` files.