From 51003e5f6db6215d1fdf7d010c2aa817b55ca701 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 20 Jan 2022 22:08:40 +0100 Subject: [PATCH] Dump structured data def structs in correct order and sort entries by offset --- .../StructuredDataDefDumper.cpp | 65 +++++++--- .../StructuredDataDefDumper.h | 23 +++- .../AssetDumperStructuredDataDefSet.cpp | 112 ++++++++++++++++-- .../AssetDumperStructuredDataDefSet.h | 8 +- 4 files changed, 173 insertions(+), 35 deletions(-) diff --git a/src/ObjWriting/Dumping/StructuredDataDef/StructuredDataDefDumper.cpp b/src/ObjWriting/Dumping/StructuredDataDef/StructuredDataDefDumper.cpp index 27289b71..e2719e65 100644 --- a/src/ObjWriting/Dumping/StructuredDataDef/StructuredDataDefDumper.cpp +++ b/src/ObjWriting/Dumping/StructuredDataDef/StructuredDataDefDumper.cpp @@ -1,12 +1,22 @@ #include "StructuredDataDefDumper.h" +#include #include +#include + +StructuredDataDefDumper::StructEntry::StructEntry(std::string stringValue, const size_t offset) + : m_string_value(std::move(stringValue)), + m_offset(offset) +{ +} StructuredDataDefDumper::StructuredDataDefDumper(std::ostream& stream) : AbstractTextDumper(stream), m_block(Block::BLOCK_NONE), m_flags{}, - m_enum_size(0u) + m_enum_entry_count(0u), + m_struct_property_count(0u), + m_current_property_offset(0u) { } @@ -64,7 +74,7 @@ void StructuredDataDefDumper::BeginEnum(const std::string& enumName, const size_ m_block = Block::BLOCK_ENUM; m_enum_entries.resize(enumEntryCount); - m_enum_size = enumEntryCount; + m_enum_entry_count = enumEntryCount; } void StructuredDataDefDumper::EndEnum() @@ -75,7 +85,7 @@ void StructuredDataDefDumper::EndEnum() return; bool firstEntry = true; - for(const auto& entry : m_enum_entries) + for (const auto& entry : m_enum_entries) { if (firstEntry) firstEntry = false; @@ -100,15 +110,15 @@ void StructuredDataDefDumper::EndEnum() void StructuredDataDefDumper::WriteEnumEntry(const std::string& entryName, const size_t entryValue) { assert(m_block == Block::BLOCK_ENUM); - assert(entryValue < m_enum_size); + assert(entryValue < m_enum_entry_count); - if (m_block != Block::BLOCK_ENUM || entryValue >= m_enum_size) + if (m_block != Block::BLOCK_ENUM || entryValue >= m_enum_entry_count) return; m_enum_entries[entryValue] = entryName; } -void StructuredDataDefDumper::BeginStruct(const std::string& structName) +void StructuredDataDefDumper::BeginStruct(const std::string& structName, const size_t structPropertyCount) { assert(m_flags.m_in_version); assert(m_block == Block::BLOCK_NONE); @@ -116,6 +126,9 @@ void StructuredDataDefDumper::BeginStruct(const std::string& structName) if (m_block != Block::BLOCK_NONE) return; + m_struct_property_count = structPropertyCount; + m_struct_properties.reserve(structPropertyCount); + if (m_flags.m_empty_line_before_block) m_stream << "\n"; @@ -136,14 +149,27 @@ void StructuredDataDefDumper::EndStruct() if (m_block != Block::BLOCK_STRUCT) return; + std::sort(m_struct_properties.begin(), m_struct_properties.end(), [](const StructEntry& e1, const StructEntry& e2) + { + return e1.m_offset < e2.m_offset; + }); + + for (auto& structProperty : m_struct_properties) + { + Indent(); + m_stream << structProperty.m_string_value << ";\n"; + } + DecIndent(); Indent(); m_stream << "};\n"; m_block = Block::BLOCK_NONE; m_flags.m_empty_line_before_block = true; + m_struct_properties.clear(); + m_struct_property_count = 0u; } -void StructuredDataDefDumper::BeginProperty(const std::string& propertyName) +void StructuredDataDefDumper::BeginProperty(const std::string& propertyName, const size_t propertyOffset) { assert(m_flags.m_in_version); assert(m_block == Block::BLOCK_STRUCT); @@ -151,19 +177,20 @@ void StructuredDataDefDumper::BeginProperty(const std::string& propertyName) if (m_block != Block::BLOCK_STRUCT) return; - m_property_name = propertyName; + m_current_property_name = propertyName; + m_current_property_offset = propertyOffset; m_block = Block::BLOCK_PROPERTY; } void StructuredDataDefDumper::AddPropertyArraySpecifier(const std::string& specifierName) { - m_property_array_specifiers.emplace_back(specifierName); + m_current_property_array_specifiers.emplace_back(specifierName); } void StructuredDataDefDumper::SetPropertyTypeName(const std::string& typeName) { - m_property_type_name = typeName; + m_current_property_type_name = typeName; } void StructuredDataDefDumper::EndProperty() @@ -173,19 +200,19 @@ void StructuredDataDefDumper::EndProperty() if (m_block != Block::BLOCK_PROPERTY) return; - Indent(); + std::ostringstream ss; + ss << m_current_property_type_name << " " << m_current_property_name; - m_stream << m_property_type_name << " " << m_property_name; - - for(const auto& arraySpecifierName : m_property_array_specifiers) + for (const auto& arraySpecifierName : m_current_property_array_specifiers) { - m_stream << "[" << arraySpecifierName << "]"; + ss << "[" << arraySpecifierName << "]"; } - m_stream << ";\n"; + m_struct_properties.emplace_back(ss.str(), m_current_property_offset); m_block = Block::BLOCK_STRUCT; - m_property_array_specifiers.clear(); - m_property_name = std::string(); - m_property_type_name = std::string(); + m_current_property_array_specifiers.clear(); + m_current_property_name = std::string(); + m_current_property_offset = 0u; + m_current_property_type_name = std::string(); } diff --git a/src/ObjWriting/Dumping/StructuredDataDef/StructuredDataDefDumper.h b/src/ObjWriting/Dumping/StructuredDataDef/StructuredDataDefDumper.h index dc26cd31..b13fa1a2 100644 --- a/src/ObjWriting/Dumping/StructuredDataDef/StructuredDataDefDumper.h +++ b/src/ObjWriting/Dumping/StructuredDataDef/StructuredDataDefDumper.h @@ -6,6 +6,14 @@ class StructuredDataDefDumper : AbstractTextDumper { + struct StructEntry + { + std::string m_string_value; + size_t m_offset; + + StructEntry(std::string stringValue, size_t offset); + }; + enum class Block { BLOCK_NONE = 0, @@ -22,11 +30,14 @@ class StructuredDataDefDumper : AbstractTextDumper } m_flags; std::vector m_enum_entries; - size_t m_enum_size; + size_t m_enum_entry_count; - std::string m_property_name; - std::string m_property_type_name; - std::vector m_property_array_specifiers; + std::vector m_struct_properties; + size_t m_struct_property_count; + std::string m_current_property_name; + size_t m_current_property_offset; + std::string m_current_property_type_name; + std::vector m_current_property_array_specifiers; public: explicit StructuredDataDefDumper(std::ostream& stream); @@ -38,9 +49,9 @@ public: void EndEnum(); void WriteEnumEntry(const std::string& entryName, size_t entryValue); - void BeginStruct(const std::string& structName); + void BeginStruct(const std::string& structName, size_t structPropertyCount); void EndStruct(); - void BeginProperty(const std::string& propertyName); + void BeginProperty(const std::string& propertyName, size_t propertyOffset); void AddPropertyArraySpecifier(const std::string& specifierName); void SetPropertyTypeName(const std::string& typeName); void EndProperty(); diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp index 070452a7..58b48dd2 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp @@ -7,6 +7,101 @@ using namespace IW4; +bool AssetDumperStructuredDataDefSet::GetNextHeadValue(const StructuredDataDef* def, const bool isFirstStruct, const std::vector& structsIncludedInOrder, size_t& nextHeadValue) +{ + if(isFirstStruct + && def->rootType.type == DATA_STRUCT + && def->rootType.u.structIndex >= 0 + && def->rootType.u.structIndex < def->structCount) + { + nextHeadValue = def->rootType.u.structIndex; + return true; + } + + const auto firstNotIncludedStruct = std::find(structsIncludedInOrder.begin(), structsIncludedInOrder.end(), false); + assert(firstNotIncludedStruct != structsIncludedInOrder.end()); + + if (firstNotIncludedStruct == structsIncludedInOrder.end()) + return false; + + nextHeadValue = static_cast(firstNotIncludedStruct - structsIncludedInOrder.begin()); + return false; +} + +StructuredDataType AssetDumperStructuredDataDefSet::GetBaseType(const StructuredDataDef* def, StructuredDataType type) +{ + while(true) + { + if (type.type == DATA_INDEXED_ARRAY) + { + if (def->indexedArrays != nullptr && type.u.indexedArrayIndex >= 0 && type.u.indexedArrayIndex < def->indexedArrayCount) + type = def->indexedArrays[type.u.indexedArrayIndex].elementType; + else + break; + } + else if (type.type == DATA_ENUM_ARRAY) + { + if (def->enumedArrays != nullptr && type.u.enumedArrayIndex >= 0 && type.u.enumedArrayIndex < def->enumedArrayCount) + type = def->enumedArrays[type.u.enumedArrayIndex].elementType; + else + break; + } + else + break; + } + + return type; +} + +std::vector AssetDumperStructuredDataDefSet::CalculateStructDumpingOrder(const StructuredDataDef* def) +{ + if (def->structCount <= 0 || def->structs == nullptr) + return {}; + + const auto structCount = static_cast(def->structCount); + + std::vector result; + auto resultStructHead = 0u; + auto resultStructTail = 0u; + + result.reserve(def->structCount); + std::vector structIncludedInOrder(def->structCount); + + while(resultStructTail < structCount) + { + size_t nextHeadValue; + if (!GetNextHeadValue(def, resultStructHead == 0, structIncludedInOrder, nextHeadValue)) + return result; + result.push_back(nextHeadValue); + structIncludedInOrder[nextHeadValue] = true; + ++resultStructHead; + + while(resultStructHead > resultStructTail) + { + const auto& currentStruct = def->structs[result[resultStructTail++]]; + + if(currentStruct.properties == nullptr) + continue; + + for(auto i = 0; i < currentStruct.propertyCount; i++) + { + const auto baseType = GetBaseType(def, currentStruct.properties[i].type); + if(baseType.type == DATA_STRUCT + && baseType.u.structIndex >= 0 + && static_cast(baseType.u.structIndex) < structCount + && structIncludedInOrder[static_cast(baseType.u.structIndex)] == false) + { + result.push_back(static_cast(baseType.u.structIndex)); + structIncludedInOrder[static_cast(baseType.u.structIndex)] = true; + ++resultStructHead; + } + } + } + } + + return result; +} + bool AssetDumperStructuredDataDefSet::ShouldDump(XAssetInfo* asset) { return true; @@ -38,7 +133,7 @@ void AssetDumperStructuredDataDefSet::DumpEnum(StructuredDataDefDumper& dumper, void AssetDumperStructuredDataDefSet::DumpProperty(StructuredDataDefDumper& dumper, const StructuredDataStructProperty& property, const StructuredDataDef* def, const int rootStructIndex) { - dumper.BeginProperty(property.name); + dumper.BeginProperty(property.name, property.offset); auto currentType = property.type; auto stopTypeIteration = false; @@ -145,14 +240,13 @@ void AssetDumperStructuredDataDefSet::DumpProperty(StructuredDataDefDumper& dump dumper.EndProperty(); } -void AssetDumperStructuredDataDefSet::DumpStruct(StructuredDataDefDumper& dumper, const int structIndex, const StructuredDataStruct* _struct, const StructuredDataDef* def, const bool isRoot) +void AssetDumperStructuredDataDefSet::DumpStruct(StructuredDataDefDumper& dumper, const size_t structIndex, const StructuredDataStruct* _struct, const StructuredDataDef* def, const int rootStructIndex) { if (!_struct->properties || _struct->propertyCount <= 0) return; std::string structName; - - if (isRoot) + if (static_cast(structIndex) == rootStructIndex) { structName = "root"; } @@ -163,9 +257,8 @@ void AssetDumperStructuredDataDefSet::DumpStruct(StructuredDataDefDumper& dumper structName = ss.str(); } - dumper.BeginStruct(structName); - - const auto rootStructIndex = def->rootType.type == DATA_STRUCT ? def->rootType.u.structIndex : -1; + dumper.BeginStruct(structName, static_cast(_struct->propertyCount)); + for (auto i = 0; i < _struct->propertyCount; i++) { const auto& property = _struct->properties[i]; @@ -197,8 +290,9 @@ void AssetDumperStructuredDataDefSet::DumpAsset(AssetDumpingContext& context, XA const auto rootStructIndex = def.rootType.type == DATA_STRUCT ? def.rootType.u.structIndex : -1; assert(rootStructIndex >= 0); - for (auto structIndex = 0; structIndex < def.structCount; structIndex++) - DumpStruct(dumper, structIndex, &def.structs[structIndex], &def, structIndex == rootStructIndex); + const auto structDumpingOrder = CalculateStructDumpingOrder(&def); + for (auto i = structDumpingOrder.rbegin(); i != structDumpingOrder.rend(); ++i) + DumpStruct(dumper, *i, &def.structs[*i], &def, rootStructIndex); dumper.EndVersion(); } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h index 995735b4..362747e7 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include "Dumping/AbstractAssetDumper.h" #include "Dumping/StructuredDataDef/StructuredDataDefDumper.h" #include "Game/IW4/IW4.h" @@ -8,9 +11,12 @@ namespace IW4 { class AssetDumperStructuredDataDefSet final : public AbstractAssetDumper { + static bool GetNextHeadValue(const StructuredDataDef* def, bool isFirstStruct, const std::vector& structsIncludedInOrder, size_t& nextHeadValue); + static StructuredDataType GetBaseType(const StructuredDataDef* def, StructuredDataType type); + static std::vector CalculateStructDumpingOrder(const StructuredDataDef* def); static void DumpEnum(StructuredDataDefDumper& dumper, int enumIndex, const StructuredDataEnum* _enum); static void DumpProperty(StructuredDataDefDumper& dumper, const StructuredDataStructProperty& property, const StructuredDataDef* def, int rootStructIndex); - static void DumpStruct(StructuredDataDefDumper& dumper, int structIndex, const StructuredDataStruct* _struct, const StructuredDataDef* def, bool isRoot); + static void DumpStruct(StructuredDataDefDumper& dumper, size_t structIndex, const StructuredDataStruct* _struct, const StructuredDataDef* def, int rootStructIndex); protected: bool ShouldDump(XAssetInfo* asset) override;