diff --git a/Source/Utility/LexiconSerializer.cpp b/Source/Utility/LexiconSerializer.cpp new file mode 100644 index 0000000..457a33d --- /dev/null +++ b/Source/Utility/LexiconSerializer.cpp @@ -0,0 +1,70 @@ +#include "Utility/LexiconSerializer.h" + +// FUNCTION: fn_17630 +void LexiconSerializer::Destroy(LexiconNode* node) { + if (node != nullptr) { + this->Destroy(node->child); + this->Destroy(node->sibling); + delete node; + } +} + +// FUNCTION: fn_17633 +std::uint32_t LexiconSerializer::Serialize(LexiconNode* node, std::uint32_t offset, unsigned char terminal) { + int siblings = 0; + for (LexiconNode* current = node; current != nullptr; current = current->sibling) { + ++siblings; + } + + std::uint32_t next = offset + static_cast(siblings) * 3u + 1u; + while (siblings > 0) { + this->buffer[offset] = static_cast((terminal << 7) | (next >> 16)); + this->buffer[offset + 1] = static_cast(next >> 8); + this->buffer[offset + 2] = static_cast(next); + offset += 3; + + unsigned char chain = 0; + for (LexiconNode* current = node; current != nullptr && current->sibling == nullptr; current = current->child) { + ++chain; + } + + if (chain > 1) { + this->buffer[next++] = chain; + LexiconNode* current = node; + unsigned char lastTerminal; + for (int remaining = chain; remaining != 0; --remaining) { + this->buffer[next++] = current->value; + lastTerminal = current->terminal; + if (lastTerminal == 1) { + this->buffer[next++] = 2; + } + + current = current->child; + } + + if (current != nullptr) { + const unsigned char last = this->buffer[next - 1]; + if (last >= 0x20) { + this->buffer[next++] = 4; + } else { + this->buffer[next - 1] = last | 4; + } + + next = this->Serialize(current, next, lastTerminal); + } + } else { + this->buffer[next++] = node->value; + if (node->child != nullptr) { + next = this->Serialize(node->child, next, node->terminal); + } else { + this->buffer[next++] = 0xC0; + } + } + + node = node->sibling; + --siblings; + } + + this->buffer[offset] = static_cast((terminal << 7) | 0x40); + return next; +} diff --git a/Source/Utility/LexiconSerializer.h b/Source/Utility/LexiconSerializer.h new file mode 100644 index 0000000..41bcb00 --- /dev/null +++ b/Source/Utility/LexiconSerializer.h @@ -0,0 +1,26 @@ +#ifndef RAGNAROK_SOURCE_UTILITY_LEXICONSERIALIZER_H +#define RAGNAROK_SOURCE_UTILITY_LEXICONSERIALIZER_H + +#include + +struct LexiconNode { + /* 0000 */ unsigned char value; + /* 0001 */ unsigned char terminal; + /* 0002 */ unsigned char unknown_0002[2]; + /* 0004 */ LexiconNode* sibling; + /* 0008 */ LexiconNode* child; +}; + +struct LexiconSerializer { + /* 0000 */ std::uint32_t unknown_0000[4]; + /* 0010 */ unsigned char* buffer; + + void Destroy(LexiconNode* node); + std::uint32_t Serialize(LexiconNode* node, std::uint32_t offset, unsigned char terminal); +}; + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(sizeof(LexiconNode) == 12); +#endif + +#endif diff --git a/compile.json b/compile.json index e60a794..83df415 100644 --- a/compile.json +++ b/compile.json @@ -584,6 +584,7 @@ "Source/Utility/Base64.cpp": 0, "Source/Utility/GuidParse.cpp": 0, "Source/Utility/LexiconBoundary.cpp": 0, + "Source/Utility/LexiconSerializer.cpp": 0, "Source/Utility/MD5.cpp": 0, "Source/Utility/Math/AffineLookAt.cpp": 0, "Source/Utility/Math/AffineMatrix.cpp": 0,