diff --git a/src/ObjCommon/StructuredDataDef/CommonStructuredDataDef.cpp b/src/ObjCommon/StructuredDataDef/CommonStructuredDataDef.cpp index e258f1ad..f7ba4c89 100644 --- a/src/ObjCommon/StructuredDataDef/CommonStructuredDataDef.cpp +++ b/src/ObjCommon/StructuredDataDef/CommonStructuredDataDef.cpp @@ -11,3 +11,9 @@ CommonStructuredDataDef::CommonStructuredDataDef(const int version) m_size_in_byte(0u) { } + +size_t CommonStructuredDataDef::CalculateChecksum() const +{ + // TODO: Implement + return 0u; +} diff --git a/src/ObjCommon/StructuredDataDef/CommonStructuredDataDef.h b/src/ObjCommon/StructuredDataDef/CommonStructuredDataDef.h index 7f14d888..2f762a79 100644 --- a/src/ObjCommon/StructuredDataDef/CommonStructuredDataDef.h +++ b/src/ObjCommon/StructuredDataDef/CommonStructuredDataDef.h @@ -21,4 +21,6 @@ public: CommonStructuredDataDef(); explicit CommonStructuredDataDef(int version); + + size_t CalculateChecksum() const; }; diff --git a/src/ObjCommon/StructuredDataDef/CommonStructuredDataEnum.cpp b/src/ObjCommon/StructuredDataDef/CommonStructuredDataEnum.cpp index 5da9fbef..f0f29161 100644 --- a/src/ObjCommon/StructuredDataDef/CommonStructuredDataEnum.cpp +++ b/src/ObjCommon/StructuredDataDef/CommonStructuredDataEnum.cpp @@ -1,5 +1,7 @@ #include "CommonStructuredDataEnum.h" +#include + CommonStructuredDataEnumEntry::CommonStructuredDataEnumEntry() : m_value(0u) { @@ -32,3 +34,11 @@ size_t CommonStructuredDataEnum::ElementCount() const { return m_reserved_entry_count > 0 ? static_cast(m_reserved_entry_count) : m_entries.size(); } + +void CommonStructuredDataEnum::SortEntries() +{ + std::sort(m_entries.begin(), m_entries.end(), [](const CommonStructuredDataEnumEntry& e1, const CommonStructuredDataEnumEntry& e2) + { + return e1.m_value < e2.m_value; + }); +} diff --git a/src/ObjCommon/StructuredDataDef/CommonStructuredDataEnum.h b/src/ObjCommon/StructuredDataDef/CommonStructuredDataEnum.h index 216c7ee6..93200ce5 100644 --- a/src/ObjCommon/StructuredDataDef/CommonStructuredDataEnum.h +++ b/src/ObjCommon/StructuredDataDef/CommonStructuredDataEnum.h @@ -25,4 +25,6 @@ struct CommonStructuredDataEnum CommonStructuredDataEnum(std::string name, int reservedEntryCount); _NODISCARD size_t ElementCount() const; + + void SortEntries(); }; diff --git a/src/ObjCommon/StructuredDataDef/CommonStructuredDataStruct.cpp b/src/ObjCommon/StructuredDataDef/CommonStructuredDataStruct.cpp index 03b14134..d61d9b1c 100644 --- a/src/ObjCommon/StructuredDataDef/CommonStructuredDataStruct.cpp +++ b/src/ObjCommon/StructuredDataDef/CommonStructuredDataStruct.cpp @@ -1,5 +1,7 @@ #include "CommonStructuredDataStruct.h" +#include + CommonStructuredDataStructEntry::CommonStructuredDataStructEntry() : m_offset_in_bits(0u) { @@ -30,3 +32,11 @@ CommonStructuredDataStruct::CommonStructuredDataStruct(std::string name) m_size_in_byte(0u) { } + +void CommonStructuredDataStruct::SortProperties() +{ + std::sort(m_properties.begin(), m_properties.end(), [](const CommonStructuredDataStructEntry& e1, const CommonStructuredDataStructEntry& e2) + { + return e1.m_offset_in_bits < e2.m_offset_in_bits; + }); +} diff --git a/src/ObjCommon/StructuredDataDef/CommonStructuredDataStruct.h b/src/ObjCommon/StructuredDataDef/CommonStructuredDataStruct.h index 1a41baaf..bbfc1c80 100644 --- a/src/ObjCommon/StructuredDataDef/CommonStructuredDataStruct.h +++ b/src/ObjCommon/StructuredDataDef/CommonStructuredDataStruct.h @@ -25,4 +25,6 @@ struct CommonStructuredDataStruct CommonStructuredDataStruct(); explicit CommonStructuredDataStruct(std::string name); + + void SortProperties(); }; diff --git a/src/ObjLoading/Parsing/StructuredDataDef/Sequence/StructuredDataDefScopeSequences.cpp b/src/ObjLoading/Parsing/StructuredDataDef/Sequence/StructuredDataDefScopeSequences.cpp index 2fafbcf9..8841d96e 100644 --- a/src/ObjLoading/Parsing/StructuredDataDef/Sequence/StructuredDataDefScopeSequences.cpp +++ b/src/ObjLoading/Parsing/StructuredDataDef/Sequence/StructuredDataDefScopeSequences.cpp @@ -130,6 +130,8 @@ namespace sdd::def_scope_sequences CreateDefaultStructWhenNoStructsSpecified(state); SetDefSizeFromRootStruct(state); + // TODO: Calculate checksum here + state->m_current_def = nullptr; state->m_def_types_by_name.clear(); state->m_def_indexed_arrays.clear(); diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp index 53711ece..1c068388 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp @@ -5,6 +5,7 @@ #include #include "Dumping/StructuredDataDef/StructuredDataDefDumper.h" +#include "StructuredDataDef/StructuredDataDefDumper.h" using namespace IW4; using namespace std::string_literals; @@ -343,6 +344,161 @@ void AssetDumperStructuredDataDefSet::DumpStruct(StructuredDataDefDumper& dumper dumper.EndStruct(); } +CommonStructuredDataType AssetDumperStructuredDataDefSet::ConvertType(const CommonStructuredDataDef* def, const StructuredDataType in) +{ + CommonStructuredDataType out; + + switch (in.type) + { + case DATA_INT: + out.m_category = CommonStructuredDataTypeCategory::INT; + break; + case DATA_BYTE: + out.m_category = CommonStructuredDataTypeCategory::BYTE; + break; + case DATA_BOOL: + out.m_category = CommonStructuredDataTypeCategory::BOOL; + break; + case DATA_FLOAT: + out.m_category = CommonStructuredDataTypeCategory::FLOAT; + break; + case DATA_SHORT: + out.m_category = CommonStructuredDataTypeCategory::SHORT; + break; + case DATA_STRING: + out.m_category = CommonStructuredDataTypeCategory::STRING; + out.m_info.string_length = in.u.stringDataLength; + break; + case DATA_ENUM: + assert(!def->m_enums.empty()); + out.m_category = CommonStructuredDataTypeCategory::ENUM; + out.m_info.type_index = std::max(std::min(static_cast(in.u.enumIndex), def->m_enums.size() - 1u), 0u); + break; + case DATA_STRUCT: + assert(!def->m_structs.empty()); + out.m_category = CommonStructuredDataTypeCategory::STRUCT; + out.m_info.type_index = std::max(std::min(static_cast(in.u.structIndex), def->m_structs.size() - 1u), 0u); + break; + case DATA_INDEXED_ARRAY: + assert(!def->m_indexed_arrays.empty()); + out.m_category = CommonStructuredDataTypeCategory::INDEXED_ARRAY; + out.m_info.type_index = std::max(std::min(static_cast(in.u.indexedArrayIndex), def->m_indexed_arrays.size() - 1u), 0u); + break; + case DATA_ENUM_ARRAY: + assert(!def->m_enumed_arrays.empty()); + out.m_category = CommonStructuredDataTypeCategory::ENUM_ARRAY; + out.m_info.type_index = std::max(std::min(static_cast(in.u.enumedArrayIndex), def->m_enumed_arrays.size() - 1u), 0u); + break; + case DATA_COUNT: + default: + assert(false); + break; + } + + return out; +} + +void AssetDumperStructuredDataDefSet::ConvertEnum(CommonStructuredDataEnum* out, const StructuredDataEnum* in, const size_t enumIndex) +{ + out->m_name = "ENUM_" + std::to_string(enumIndex); + out->m_reserved_entry_count = std::max(in->reservedEntryCount, 0); + + out->m_entries.resize(static_cast(std::max(in->entryCount, 0))); + for(auto i = 0u; i < out->m_entries.size(); i++) + { + auto& outEntry = out->m_entries[i]; + const auto& inEntry = in->entries[i]; + + outEntry.m_name = std::string(inEntry.string); + outEntry.m_value = inEntry.index; + } + + out->SortEntries(); +} + +void AssetDumperStructuredDataDefSet::ConvertStruct(const CommonStructuredDataDef* def, CommonStructuredDataStruct* out, const StructuredDataStruct* in, const size_t structIndex, const size_t rootIndex) +{ + if (structIndex == rootIndex) + out->m_name = "root"; + else + out->m_name = "STRUCT_" + std::to_string(structIndex); + + out->m_size_in_byte = static_cast(std::max(in->size, 0)); + out->m_bit_offset = in->bitOffset; + + out->m_properties.resize(static_cast(std::max(in->propertyCount, 0))); + for (auto i = 0u; i < out->m_properties.size(); i++) + { + auto& outProperty = out->m_properties[i]; + const auto& inProperty = in->properties[i]; + + outProperty.m_name = std::string(inProperty.name); + outProperty.m_type = ConvertType(def, inProperty.type); + + if (inProperty.type.type == DATA_BOOL) + outProperty.m_offset_in_bits = inProperty.offset; + else + outProperty.m_offset_in_bits = inProperty.offset * 8; + } + + out->SortProperties(); +} + +void AssetDumperStructuredDataDefSet::ConvertIndexedArray(const CommonStructuredDataDef* def, CommonStructuredDataIndexedArray* out, const StructuredDataIndexedArray* in) +{ + out->m_element_count = static_cast(std::max(in->arraySize, 0)); + out->m_element_size_in_bits = in->elementType.type == DATA_BOOL ? 1 : in->elementSize * 8; + out->m_array_type = ConvertType(def, in->elementType); +} + +void AssetDumperStructuredDataDefSet::ConvertEnumedArray(const CommonStructuredDataDef* def, CommonStructuredDataEnumedArray* out, const StructuredDataEnumedArray* in) +{ + assert(!def->m_enums.empty()); + out->m_element_size_in_bits = in->elementType.type == DATA_BOOL ? 1 : in->elementSize * 8; + out->m_array_type = ConvertType(def, in->elementType); + out->m_enum_index = std::max(std::min(static_cast(in->enumIndex), def->m_enums.size() - 1u), 0u); + + if (def->m_enums.empty()) + return; + + out->m_element_count = def->m_enums[out->m_enum_index]->ElementCount(); +} + +std::unique_ptr AssetDumperStructuredDataDefSet::ConvertDef(const StructuredDataDef* in) +{ + auto out = std::make_unique(); + + out->m_version = in->version; + out->m_checksum = in->formatChecksum; + out->m_size_in_byte = in->size; + + out->m_enums.resize(static_cast(std::max(in->enumCount, 0))); + out->m_structs.resize(static_cast(std::max(in->structCount, 0))); + out->m_indexed_arrays.resize(static_cast(std::max(in->indexedArrayCount, 0))); + out->m_enumed_arrays.resize(static_cast(std::max(in->enumedArrayCount, 0))); + + for (auto i = 0u; i < out->m_enums.size(); i++) + { + auto _enum = std::make_unique(); + ConvertEnum(_enum.get(), &in->enums[i], i); + out->m_enums[i] = std::move(_enum); + } + for (auto i = 0u; i < out->m_structs.size(); i++) + { + auto _struct = std::make_unique(); + ConvertStruct(out.get(), _struct.get(), &in->structs[i], i, static_cast(in->rootType.u.structIndex)); + out->m_structs[i] = std::move(_struct); + } + for (auto i = 0u; i < out->m_indexed_arrays.size(); i++) + ConvertIndexedArray(out.get(), &out->m_indexed_arrays[i], &in->indexedArrays[i]); + for (auto i = 0u; i < out->m_enumed_arrays.size(); i++) + ConvertEnumedArray(out.get(), &out->m_enumed_arrays[i], &in->enumedArrays[i]); + + out->m_root_type = ConvertType(out.get(), in->rootType); + + return out; +} + void AssetDumperStructuredDataDefSet::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { const auto* set = asset->Asset(); @@ -351,25 +507,10 @@ void AssetDumperStructuredDataDefSet::DumpAsset(AssetDumpingContext& context, XA if (!assetFile || set->defs == nullptr) return; - StructuredDataDefDumper dumper(*assetFile); - - for (auto defIndex = 0u; defIndex < set->defCount; defIndex++) + StructuredDataDefDumperNew newDumper(*assetFile); + for (auto i = 0u; i < set->defCount; i++) { - const auto& def = set->defs[defIndex]; - - dumper.WriteLineComment("Size: "s + std::to_string(def.size)); - dumper.WriteLineComment("Checksum: "s + std::to_string(def.formatChecksum)); - dumper.BeginVersion(def.version); - - for (auto enumIndex = 0; enumIndex < def.enumCount; enumIndex++) - DumpEnum(dumper, enumIndex, &def.enums[enumIndex]); - - const auto rootStructIndex = def.rootType.type == DATA_STRUCT ? def.rootType.u.structIndex : -1; - assert(rootStructIndex >= 0); - const auto structDumpingOrder = CalculateStructDumpingOrder(&def); - for (auto i = structDumpingOrder.rbegin(); i != structDumpingOrder.rend(); ++i) - DumpStruct(dumper, *i, &def.structs[*i], &def, rootStructIndex); - - dumper.EndVersion(); + const auto def = ConvertDef(&set->defs[i]); + newDumper.DumpDef(*def); } } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h index 6b586f96..3c6e96ab 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h @@ -6,6 +6,7 @@ #include "Dumping/AbstractAssetDumper.h" #include "Dumping/StructuredDataDef/StructuredDataDefDumper.h" #include "Game/IW4/IW4.h" +#include "StructuredDataDef/CommonStructuredDataDef.h" namespace IW4 { @@ -20,6 +21,12 @@ namespace IW4 static void DumpProperty(StructuredDataDefDumper& dumper, const StructuredDataStructProperty& property, const StructuredDataDef* def, int rootStructIndex); static void DumpStruct(StructuredDataDefDumper& dumper, size_t structIndex, const StructuredDataStruct* _struct, const StructuredDataDef* def, int rootStructIndex); + static CommonStructuredDataType ConvertType(const CommonStructuredDataDef* def, const StructuredDataType in); + static void ConvertEnum(CommonStructuredDataEnum* out, const StructuredDataEnum* in, size_t enumIndex); + static void ConvertStruct(const CommonStructuredDataDef* def, CommonStructuredDataStruct* out, const StructuredDataStruct* in, const size_t structIndex, const size_t rootIndex); + static void ConvertIndexedArray(const CommonStructuredDataDef* def, CommonStructuredDataIndexedArray* out, const StructuredDataIndexedArray* in); + static void ConvertEnumedArray(const CommonStructuredDataDef* def, CommonStructuredDataEnumedArray* out, const StructuredDataEnumedArray* in); + static std::unique_ptr ConvertDef(const StructuredDataDef* in); protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; diff --git a/src/ObjWriting/StructuredDataDef/StructuredDataDefDumper.cpp b/src/ObjWriting/StructuredDataDef/StructuredDataDefDumper.cpp new file mode 100644 index 00000000..5739c0c6 --- /dev/null +++ b/src/ObjWriting/StructuredDataDef/StructuredDataDefDumper.cpp @@ -0,0 +1,115 @@ +#include "StructuredDataDefDumper.h" + +StructuredDataDefDumperNew::StructuredDataDefDumperNew(std::ostream& stream) + : AbstractTextDumper(stream), + m_flags{} +{ +} + +void StructuredDataDefDumperNew::WriteLineComment(const std::string& comment) const +{ + Indent(); + m_stream << "// " << comment << "\n"; +} + +void StructuredDataDefDumperNew::DumpEnum(const CommonStructuredDataEnum& _enum) +{ + Indent(); + + if (_enum.m_reserved_entry_count > static_cast(_enum.m_entries.size())) + m_stream << "enum(" << _enum.m_reserved_entry_count << ") "; + else + m_stream << "enum "; + + m_stream << _enum.m_name << "\n"; + + Indent(); + m_stream << "{\n"; + + IncIndent(); + + const auto entryCount = _enum.m_entries.size(); + for(auto i = 0u; i < entryCount; i++) + { + Indent(); + m_stream << "\"" << _enum.m_entries[i].m_name << "\""; + + if (i + 1 < entryCount) + m_stream << ","; + m_stream << "\n"; + } + + DecIndent(); + Indent(); + m_stream << "};\n"; // end enum +} + +void StructuredDataDefDumperNew::DumpStruct(const CommonStructuredDataStruct& _struct) +{ + Indent(); + + m_stream << "struct " << _struct.m_name << "\n"; + + Indent(); + m_stream << "{\n"; + + IncIndent(); + + + DecIndent(); + Indent(); + m_stream << "};\n"; // end struct +} + +void StructuredDataDefDumperNew::DumpDef(const CommonStructuredDataDef& def) +{ + if (m_flags.m_empty_line_before_definition) + m_stream << "\n\n"; + else + m_flags.m_empty_line_before_definition = true; + + const auto calculatedChecksum = def.CalculateChecksum(); + + m_stream << "// ====================\n"; + m_stream << "// Version " << def.m_version << "\n"; + + if (calculatedChecksum != def.m_checksum) + m_stream << "// Calculated checksum did not match checksum in file\n// Overriding checksum to match original value\n"; + + m_stream << "// ====================\n"; + + m_stream << "version " << def.m_version << "\n{\n"; + IncIndent(); + + auto insertEmptyLine = false; + + if (calculatedChecksum != def.m_checksum) + { + Indent(); + m_stream << "checksumoverride " << def.m_checksum << ";\n"; + insertEmptyLine = true; + } + + for(const auto& _enum : def.m_enums) + { + if (insertEmptyLine) + m_stream << "\n"; + else + insertEmptyLine = true; + + DumpEnum(*_enum); + } + + for(const auto& _struct : def.m_structs) + { + if (insertEmptyLine) + m_stream << "\n"; + else + insertEmptyLine = true; + + DumpStruct(*_struct); + } + + DecIndent(); + m_stream << "}\n"; // end version +} diff --git a/src/ObjWriting/StructuredDataDef/StructuredDataDefDumper.h b/src/ObjWriting/StructuredDataDef/StructuredDataDefDumper.h new file mode 100644 index 00000000..b2aec37c --- /dev/null +++ b/src/ObjWriting/StructuredDataDef/StructuredDataDefDumper.h @@ -0,0 +1,23 @@ +#pragma once +#include + +#include "Dumping/AbstractTextDumper.h" +#include "StructuredDataDef/CommonStructuredDataDef.h" + +class StructuredDataDefDumperNew : AbstractTextDumper +{ + struct + { + bool m_empty_line_before_definition : 1; + } m_flags; + + void WriteLineComment(const std::string& comment) const; + + void DumpEnum(const CommonStructuredDataEnum& _enum); + void DumpStruct(const CommonStructuredDataStruct& _struct); + +public: + explicit StructuredDataDefDumperNew(std::ostream& stream); + + void DumpDef(const CommonStructuredDataDef& def); +};