diff --git a/ZAPD/Main.cpp b/ZAPD/Main.cpp index 98d11f0e0..4a58909f7 100644 --- a/ZAPD/Main.cpp +++ b/ZAPD/Main.cpp @@ -319,6 +319,13 @@ bool Parse(const std::string& xmlFilePath, const std::string& basePath, const st ZFile* file = new ZFile(fileMode, child, basePath, outPath, "", xmlFilePath, false); Globals::Instance->files.push_back(file); } + else + { + throw std::runtime_error( + StringHelper::Sprintf("Parse: Fatal error in '%s'.\n\t Found a resource outside of " + "a File element: '%s'\n", + xmlFilePath.c_str(), child->Name())); + } } for (ZFile* file : Globals::Instance->files) diff --git a/ZAPD/ZArray.cpp b/ZAPD/ZArray.cpp index f00d85d41..dabe5260f 100644 --- a/ZAPD/ZArray.cpp +++ b/ZAPD/ZArray.cpp @@ -7,6 +7,7 @@ REGISTER_ZFILENODE(Array, ZArray); ZArray::ZArray(ZFile* nParent) : ZResource(nParent) { + canHaveInner = true; } void ZArray::ParseXML(tinyxml2::XMLElement* reader) diff --git a/ZAPD/ZFile.cpp b/ZAPD/ZFile.cpp index ca7f6bd93..79a0a4dea 100644 --- a/ZAPD/ZFile.cpp +++ b/ZAPD/ZFile.cpp @@ -1,6 +1,7 @@ #include "ZFile.h" #include #include +#include #include "Directory.h" #include "File.h" #include "Globals.h" @@ -125,18 +126,55 @@ void ZFile::ParseXML(ZFileMode mode, XMLElement* reader, std::string filename, b rawData = File::ReadAllBytes(basePath + "/" + name); } + std::unordered_set nameSet; + std::unordered_set outNameSet; + std::unordered_set offsetSet; + auto nodeMap = *GetNodeMap(); int rawDataIndex = 0; for (XMLElement* child = reader->FirstChildElement(); child != NULL; child = child->NextSiblingElement()) { - if (child->Attribute("Offset") != NULL) - rawDataIndex = - strtol(StringHelper::Split(child->Attribute("Offset"), "0x")[1].c_str(), NULL, 16); + const char* nameXml = child->Attribute("Name"); + const char* outNameXml = child->Attribute("OutName"); + const char* offsetXml = child->Attribute("Offset"); if (Globals::Instance->verbosity >= VERBOSITY_INFO) - printf("%s: 0x%06X\n", child->Attribute("Name"), rawDataIndex); + printf("%s: 0x%06X\n", nameXml, rawDataIndex); + + if (offsetXml != NULL) + { + rawDataIndex = strtol(StringHelper::Split(offsetXml, "0x")[1].c_str(), NULL, 16); + + if (offsetSet.find(offsetXml) != offsetSet.end()) + { + throw std::runtime_error(StringHelper::Sprintf( + "ZFile::ParseXML: Error in '%s'.\n\t Repeated 'Offset' attribute: %s \n", + name.c_str(), offsetXml)); + } + offsetSet.insert(offsetXml); + } + if (outNameXml != NULL) + { + if (outNameSet.find(outNameXml) != outNameSet.end()) + { + throw std::runtime_error(StringHelper::Sprintf( + "ZFile::ParseXML: Error in '%s'.\n\t Repeated 'OutName' attribute: %s \n", + name.c_str(), outNameXml)); + } + outNameSet.insert(outNameXml); + } + if (nameXml != NULL) + { + if (nameSet.find(nameXml) != nameSet.end()) + { + throw std::runtime_error(StringHelper::Sprintf( + "ZFile::ParseXML: Error in '%s'.\n\t Repeated 'Name' attribute: %s \n", + name.c_str(), nameXml)); + } + nameSet.insert(nameXml); + } string nodeName = string(child->Name()); @@ -173,8 +211,18 @@ void ZFile::ParseXML(ZFileMode mode, XMLElement* reader, std::string filename, b resources.push_back(nRes); rawDataIndex += nRes->GetRawDataSize(); } + else if (string(child->Name()) == "File") + { + throw std::runtime_error(StringHelper::Sprintf( + "ZFile::ParseXML: Error in '%s'.\n\t Can't declare a File inside a File.\n", + name.c_str())); + } else { + throw std::runtime_error( + StringHelper::Sprintf("ZFile::ParseXML: Error in '%s'.\n\t Unknown element found " + "inside a File element: '%s'.\n", + name.c_str(), nodeName.c_str())); } } } diff --git a/ZAPD/ZLimb.cpp b/ZAPD/ZLimb.cpp index ee596e68c..49b01fa24 100644 --- a/ZAPD/ZLimb.cpp +++ b/ZAPD/ZLimb.cpp @@ -6,6 +6,8 @@ using namespace std; +REGISTER_ZFILENODE(Limb, ZLimb); + Struct_800A57C0::Struct_800A57C0(const std::vector& rawData, uint32_t fileOffset) { unk_0 = BitConverter::ToUInt16BE(rawData, fileOffset + 0x00); diff --git a/ZAPD/ZResource.cpp b/ZAPD/ZResource.cpp index 9ffcc74f0..767fc0f4a 100644 --- a/ZAPD/ZResource.cpp +++ b/ZAPD/ZResource.cpp @@ -1,5 +1,8 @@ #include "ZResource.h" +#include +#include "StringHelper.h" + using namespace std; ZResource::ZResource(ZFile* nParent) @@ -42,7 +45,19 @@ void ZResource::ParseXML(tinyxml2::XMLElement* reader) if (reader != nullptr) { if (reader->Attribute("Name") != nullptr) + { name = reader->Attribute("Name"); + static std::regex r("[a-zA-Z_]+[a-zA-Z0-9_]*", + std::regex::icase | std::regex::optimize); + + if (!std::regex_match(name, r)) + { + throw std::domain_error( + StringHelper::Sprintf("ZResource::ParseXML: Fatal error in '%s'.\n\t Resource " + "with invalid 'Name' attribute.\n", + name.c_str())); + } + } else name = ""; @@ -55,6 +70,14 @@ void ZResource::ParseXML(tinyxml2::XMLElement* reader) isCustomAsset = true; else isCustomAsset = false; + + if (!canHaveInner && !reader->NoChildren()) + { + throw std::runtime_error( + StringHelper::Sprintf("ZResource::ParseXML: Fatal error in '%s'.\n\t Resource '%s' " + "with inner element/child detected.\n", + name.c_str(), reader->Name())); + } } } diff --git a/ZAPD/ZResource.h b/ZAPD/ZResource.h index 03fadd97f..e319080c7 100644 --- a/ZAPD/ZResource.h +++ b/ZAPD/ZResource.h @@ -97,6 +97,7 @@ class ZResource std::vector rawData; int rawDataIndex; std::string sourceOutput; + bool canHaveInner = false; // Can this type have an inner node? bool isCustomAsset; // If set to true, create a reference for the asset in the file, but don't // actually try to extract it from the file }; diff --git a/ZAPD/ZRoom/ZRoom.cpp b/ZAPD/ZRoom/ZRoom.cpp index 7876d7407..b35244241 100644 --- a/ZAPD/ZRoom/ZRoom.cpp +++ b/ZAPD/ZRoom/ZRoom.cpp @@ -56,6 +56,7 @@ ZRoom::ZRoom(ZFile* nParent) : ZResource(nParent) extDefines = ""; scene = nullptr; roomCount = -1; + canHaveInner = true; } ZRoom::~ZRoom() @@ -67,9 +68,8 @@ ZRoom::~ZRoom() void ZRoom::ExtractFromXML(tinyxml2::XMLElement* reader, const std::vector& nRawData, const int nRawDataIndex, const std::string& nRelPath) { - rawData = nRawData; - rawDataIndex = nRawDataIndex; - name = string(reader->Attribute("Name")); + ZResource::ExtractFromXML(reader, nRawData, nRawDataIndex, nRelPath); + // room->scene = nScene; scene = Globals::Instance->lastScene;