diff --git a/Source/Resource/XmlNode.h b/Source/Resource/XmlNode.h index a5fa000c..0a6acf66 100644 --- a/Source/Resource/XmlNode.h +++ b/Source/Resource/XmlNode.h @@ -11,10 +11,12 @@ struct XmlNode { void Clear(); XmlNode* FindChild(const char* name); XmlNode* FindSibling(const char* name); + const std::string& Text(); + std::string DecodeEntity(const char* begin, const char* end); /* 0000 */ const char* name; - /* 0004 */ std::uint32_t unknown_0004; - /* 0008 */ std::uint32_t unknown_0008; + /* 0004 */ char* unknown_0004; + /* 0008 */ char* unknown_0008; /* 000C */ std::string text; /* 0024 */ XmlNode* sibling; /* 0028 */ XmlNode* child; @@ -22,6 +24,9 @@ struct XmlNode { struct XmlDocument : XmlNode { void Reset(); + char* TerminateName(char* begin, char* end); + char* ParseElements(XmlNode* parent, char* cursor, char* end); + bool Parse(const char* begin, const char* end); /* 002C */ void* buffer; }; diff --git a/Source/Resource/XmlParsing.cpp b/Source/Resource/XmlParsing.cpp new file mode 100644 index 00000000..45c60138 --- /dev/null +++ b/Source/Resource/XmlParsing.cpp @@ -0,0 +1,163 @@ +#include "Resource/XmlNode.h" + +#include + +// OFFSET: 0x00AFA770 +// FUNCTION: fn_18298 +const std::string& XmlNode::Text() { + if (this->text.size() == 0) { + char* cursor = this->unknown_0004; + while (*cursor != '>' && *cursor != '\0') { + ++cursor; + } + + char* end = this->unknown_0008; + for (++cursor; cursor != end; ++cursor) { + for (XmlNode* node = this->child; node != nullptr; node = node->child) { + if (cursor == node->unknown_0004) { + cursor = node->unknown_0008; + } + + for (XmlNode* sibling = node->sibling; sibling != nullptr; sibling = sibling->sibling) { + if (cursor == sibling->unknown_0004) { + cursor = sibling->unknown_0008; + } + } + } + + const char value = *cursor; + if (value == '<') { + break; + } + + if (value == '\r' || value == '\n') { + continue; + } + + if (value == '&' && std::strcmp(this->name, "registrationweb") != 0) { + this->text += this->DecodeEntity(cursor + 1, end); + while (cursor != end && *cursor != ';') { + ++cursor; + } + } else { + this->text.append(1, value); + } + } + } + + return this->text; +} + +// OFFSET: 0x00AFA940 +// FUNCTION: fn_18299 +std::string XmlNode::DecodeEntity(const char* begin, const char* end) { + if (begin[0] == 'l' && begin[1] == 't') { + return std::string(1, '<'); + } + + if (begin[0] == 'g' && begin[1] == 't') { + return std::string(1, '>'); + } + + if (begin[0] == 'a' && begin[1] == 'm' && begin[2] == 'p') { + return std::string(1, '&'); + } + + return std::string(); +} + +// OFFSET: 0x00AFAA50 +// FUNCTION: fn_18300 +char* XmlDocument::TerminateName(char* begin, char* end) { + char* cursor = begin; + while (cursor != end && *cursor != ' ' && *cursor != '\t' && *cursor != '>' && *cursor != '/') { + ++cursor; + } + + *cursor = '\0'; + return begin; +} + +// OFFSET: 0x00AFAA90 +// FUNCTION: fn_18301 +char* XmlDocument::ParseElements(XmlNode* parent, char* cursor, char* end) { + while (cursor != end) { + const char value = *cursor; + if (value == '<') { + char* last = end - 1; + if (cursor[1] == '/') { + char* close = cursor; + while (close != last && *close != '>') { + ++close; + } + + char* next = close + 1; + if (std::strcmp(this->TerminateName(cursor + 2, end), parent->name) == 0) { + parent->unknown_0008 = next; + return next; + } + + cursor = next; + continue; + } + + if (cursor[1] == '?' || cursor[1] == '!') { + while (cursor != last && *cursor != '>') { + ++cursor; + } + + ++cursor; + continue; + } + + char* close = cursor; + while (close != last && *close != '>') { + ++close; + } + + char* next = close + 1; + XmlNode* node = new XmlNode(this->TerminateName(cursor + 1, end)); + if (parent->child == nullptr) { + parent->child = node; + } else { + parent->child->AppendSibling(node); + } + + if (next[-2] == '/') { + cursor = next; + continue; + } + + node->unknown_0004 = cursor; + cursor = this->ParseElements(node, next, end); + } else { + if (value == '&' && std::strcmp(parent->name, "registrationweb") != 0) { + while (cursor != end && *cursor != ';') { + ++cursor; + } + } + + ++cursor; + } + } + + return cursor; +} + +// OFFSET: 0x00AFAD00 +// FUNCTION: fn_18302 +bool XmlDocument::Parse(const char* begin, const char* end) { + if (this->child != nullptr) { + this->Reset(); + } + + const int length = static_cast(end - begin); + char* buffer = new char[length + 1]; + this->buffer = buffer; + std::memcpy(buffer, begin, length); + buffer[length] = '\0'; + this->unknown_0004 = buffer; + this->unknown_0008 = buffer + length; + this->ParseElements(this, buffer, buffer + length); + return this->child != nullptr; +} diff --git a/data/compile.json b/data/compile.json index ffc8f664..099a8636 100644 --- a/data/compile.json +++ b/data/compile.json @@ -534,6 +534,7 @@ "Source/Resource/World/TerrainResource.cpp": 3, "Source/Resource/World/WorldResource.cpp": 3, "Source/Resource/XmlNode.cpp": 3, + "Source/Resource/XmlParsing.cpp": 3, "Source/Script/LuaInstance.cpp": 5, "Source/Script/LuaScript.cpp": 5, "Source/UI/Acceptance/UIChooseSellBuyWnd.cpp": 2,