diff --git a/src/main/dumpers/schemas/metadatalist.h b/src/main/dumpers/schemas/metadatalist.h new file mode 100644 index 0000000..7cd1ff5 --- /dev/null +++ b/src/main/dumpers/schemas/metadatalist.h @@ -0,0 +1,98 @@ +/** + * ============================================================================= + * DumpSource2 + * Copyright (C) 2024 ValveResourceFormat Contributors + * + * Source2Gen + * Copyright (C) 2024 neverlosecc + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#pragma once +#include +#include "utils/hash.h" + +// List of printable metadata entries that are not tied to any structures. +// These are used to determine if we can include the corresponding metadata entry value in the dump. +// Original list sourced from Source2Gen project. + +constinit std::array string_metadata_entries = { + FNV32("MCellForDomain"), + FNV32("MCustomFGDMetadata"), + FNV32("MFieldVerificationName"), + FNV32("MKV3TransferName"), + FNV32("MNetworkAlias"), + FNV32("MNetworkChangeCallback"), + FNV32("MNetworkEncoder"), + FNV32("MNetworkExcludeByName"), + FNV32("MNetworkExcludeByUserGroup"), + FNV32("MNetworkIncludeByName"), + FNV32("MNetworkIncludeByUserGroup"), + FNV32("MNetworkReplayCompatField"), + FNV32("MNetworkSerializer"), + FNV32("MNetworkTypeAlias"), + FNV32("MNetworkUserGroup"), + FNV32("MNetworkUserGroupProxy"), + FNV32("MParticleReplacementOp"), + FNV32("MPropertyArrayElementNameKey"), + FNV32("MPropertyAttributeChoiceName"), + FNV32("MPropertyAttributeEditor"), + FNV32("MPropertyAttributeRange"), + FNV32("MPropertyAttributeSuggestionName"), + FNV32("MPropertyCustomEditor"), + FNV32("MPropertyCustomFGDType"), + FNV32("MPropertyDescription"), + FNV32("MPropertyExtendedEditor"), + FNV32("MPropertyFriendlyName"), + FNV32("MPropertyFriendlyName"), + FNV32("MPropertyGroupName"), + FNV32("MPropertyIconName"), + FNV32("MPropertyStartGroup"), + FNV32("MPropertySuppressExpr"), + FNV32("MPulseEditorHeaderIcon"), + FNV32("MResourceBlockType"), + FNV32("MScriptDescription"), + FNV32("MSrc1ImportAttributeName"), + FNV32("MSrc1ImportDmElementType"), + FNV32("MVDataOutlinerIcon"), + FNV32("MVDataOutlinerIconExpr"), + FNV32("MVDataUniqueMonotonicInt"), + FNV32("MVectorIsSometimesCoordinate"), +}; + +constinit std::array inline_string_metadata_entries = { + FNV32("MResourceTypeForInfoType"), + FNV32("MDiskDataForResourceType"), +}; + +constinit std::array integer_metadata_entries = { + FNV32("MNetworkVarEmbeddedFieldOffsetDelta"), + FNV32("MNetworkBitCount"), + FNV32("MNetworkPriority"), + FNV32("MParticleOperatorType"), + FNV32("MPropertySortPriority"), + FNV32("MParticleMinVersion"), + FNV32("MParticleMaxVersion"), + FNV32("MNetworkEncodeFlags"), + FNV32("MResourceVersion"), + FNV32("MVDataNodeType"), + FNV32("MVDataOverlayType"), + FNV32("MAlignment"), + FNV32("MGenerateArrayKeynamesFirstIndex"), +}; + +constinit std::array float_metadata_entries = { + FNV32("MNetworkMinValue"), + FNV32("MNetworkMaxValue"), +}; diff --git a/src/main/dumpers/schemas/schemas.cpp b/src/main/dumpers/schemas/schemas.cpp index d26c1ee..a8ff722 100644 --- a/src/main/dumpers/schemas/schemas.cpp +++ b/src/main/dumpers/schemas/schemas.cpp @@ -26,9 +26,52 @@ #include #include #include +#include "metadatalist.h" +#include +#include +#include namespace Dumpers::Schemas { +// Determine how and if to output metadata entry value based on it's type. +std::optional GetMetadataValue(const SchemaMetadataEntryData_t& entry) +{ + const auto hashedName = hash_32_fnv1a_const(entry.m_pszName); + if (std::ranges::find(string_metadata_entries, hashedName) != string_metadata_entries.end()) { + return fmt::format("\"{}\"", *static_cast(entry.m_pData)); + } + else if (std::ranges::find(integer_metadata_entries, hashedName) != integer_metadata_entries.end()) { + int result; + std::memcpy(&result, entry.m_pData, sizeof(int)); + return std::to_string(result); + } + else if (std::ranges::find(float_metadata_entries, hashedName) != float_metadata_entries.end()) { + float result; + std::memcpy(&result, entry.m_pData, sizeof(float)); + return std::to_string(result); + } + else if (std::ranges::find(inline_string_metadata_entries, hashedName) != inline_string_metadata_entries.end()) { + // max 8 characters. Also check for null term. + char* result = static_cast(entry.m_pData); + for (uint8_t i = 0; i < 8; ++i) { + if (result[i] == '\0') { + return fmt::format("\"{}\"", std::string(result, i)); + } + } + return fmt::format("\"{}\"", std::string(result, 8)); + } + return {}; +} + +void OutputMetadataEntry(const SchemaMetadataEntryData_t& entry, std::ofstream& output, bool tabulate) +{ + output << (tabulate ? "\t" : "") << "// " << entry.m_pszName; + const auto metadataValue = GetMetadataValue(entry); + if (metadataValue) + output << " = " << *metadataValue; + + output << "\n"; +} void DumpClasses(CSchemaSystemTypeScope* typeScope, std::filesystem::path schemaPath, std::map>& foundFiles) { @@ -53,6 +96,13 @@ void DumpClasses(CSchemaSystemTypeScope* typeScope, std::filesystem::path schema std::ofstream output((schemaPath / classInfo->m_pszProjectName / sanitizedFileName).replace_extension(".h")); + // Output metadata entries as comments before the class definition + for (uint16_t k = 0; k < classInfo->m_nStaticMetadataCount; k++) + { + const auto& metadataEntry = classInfo->m_pStaticMetadata[k]; + OutputMetadataEntry(metadataEntry, output, false); + } + output << "class " << classInfo->m_pszName; Globals::stringsIgnoreStream << classInfo->m_pszName << "\n"; @@ -64,6 +114,12 @@ void DumpClasses(CSchemaSystemTypeScope* typeScope, std::filesystem::path schema for (uint16_t k = 0; k < classInfo->m_nFieldCount; k++) { const auto& field = classInfo->m_pFields[k]; + // Output metadata entires as comments before the field definition + for (uint16_t l = 0; l < field.m_nStaticMetadataCount; l++) + { + const auto& metadataEntry = field.m_pStaticMetadata[l]; + OutputMetadataEntry(metadataEntry, output, true); + } output << "\t" << field.m_pType->m_sTypeName.String() << " " << field.m_pszName << ";\n"; Globals::stringsIgnoreStream << field.m_pszName << "\n"; @@ -96,6 +152,12 @@ void DumpEnums(CSchemaSystemTypeScope* typeScope, std::filesystem::path schemaPa std::ofstream output((schemaPath / enumInfo->m_pszProjectName / sanitizedFileName).replace_extension(".h")); + for (uint16_t k = 0; k < enumInfo->m_nStaticMetadataCount; k++) + { + const auto& metadataEntry = enumInfo->m_pStaticMetadata[k]; + OutputMetadataEntry(metadataEntry, output, false); + } + output << "enum " << enumInfo->m_pszName << " : "; Globals::stringsIgnoreStream << enumInfo->m_pszName << "\n"; @@ -122,6 +184,12 @@ void DumpEnums(CSchemaSystemTypeScope* typeScope, std::filesystem::path schemaPa for (uint16_t k = 0; k < enumInfo->m_nEnumeratorCount; k++) { const auto& field = enumInfo->m_pEnumerators[k]; + // Output metadata entires as comments before the field definition + for (uint16_t l = 0; l < field.m_nStaticMetadataCount; l++) + { + const auto& metadataEntry = field.m_pStaticMetadata[l]; + OutputMetadataEntry(metadataEntry, output, true); + } output << "\t" << field.m_pszName << " = " << field.m_nValue << ",\n"; Globals::stringsIgnoreStream << field.m_pszName << "\n"; diff --git a/src/main/utils/hash.h b/src/main/utils/hash.h new file mode 100644 index 0000000..1d63b86 --- /dev/null +++ b/src/main/utils/hash.h @@ -0,0 +1,36 @@ +/** + * ============================================================================= + * DumpSource2 + * Copyright (C) 2024 ValveResourceFormat Contributors + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once +#include +// FNV1a c++11 constexpr compile time hash functions, 32 and 64 bit +// str should be a null terminated string literal, value should be left out +// e.g hash_32_fnv1a_const("example") +// code license: public domain or equivalent +// post: https://notes.underscorediscovery.com/constexpr-fnv1a/ + +constexpr uint32_t val_32_const = 0x811c9dc5; +constexpr uint32_t prime_32_const = 0x1000193; + +inline constexpr uint32_t hash_32_fnv1a_const(const char* const str, const uint32_t value = val_32_const) noexcept { + return (str[0] == '\0') ? value : hash_32_fnv1a_const(&str[1], (value ^ uint32_t((uint8_t)str[0])) * prime_32_const); +} + +// Short name to be less verbose +#define FNV32(str) hash_32_fnv1a_const(str)