Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions src/main/dumpers/schemas/metadatalist.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <array>
#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 = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is way too overengineered, we do not care about few ms of performance, but do care about the code readibility. I am for using std map with std strings that will get hashed in std lib itself and map it to an enum of the different type values. Then resolve those enums in a switch case to the proper string value.

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"),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dupe

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"),
};
68 changes: 68 additions & 0 deletions src/main/dumpers/schemas/schemas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,52 @@
#include <map>
#include <unordered_set>
#include <algorithm>
#include "metadatalist.h"
#include <optional>
#include <cstring>
#include <fmt/format.h>

namespace Dumpers::Schemas
{
// Determine how and if to output metadata entry value based on it's type.
std::optional<std::string> 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<const char**>(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<char*>(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<std::string, std::unordered_set<std::string>>& foundFiles)
{
Expand All @@ -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";

Expand All @@ -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";
Expand Down Expand Up @@ -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";

Expand All @@ -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";
Expand Down
36 changes: 36 additions & 0 deletions src/main/utils/hash.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#pragma once
#include <cstdint>
// 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)