#include #include #include #include #include #include #include #include #include #include #include #include #include namespace { enum class MetaCoreReflectedKind { Struct, Class, Enum }; struct MetaCoreReflectedField { std::string Name{}; std::string PropertySpec{}; }; struct MetaCoreReflectedEnumValue { std::string Name{}; }; struct MetaCoreReflectedType { MetaCoreReflectedKind Kind = MetaCoreReflectedKind::Struct; std::string Name{}; std::vector Fields{}; std::vector EnumValues{}; }; [[nodiscard]] std::string MetaCoreTrim(std::string value) { auto isWhitespace = [](unsigned char character) { return std::isspace(character) != 0; }; while (!value.empty() && isWhitespace(static_cast(value.front()))) { value.erase(value.begin()); } while (!value.empty() && isWhitespace(static_cast(value.back()))) { value.pop_back(); } return value; } [[nodiscard]] std::string MetaCoreBuildIncludePath(const std::filesystem::path& path) { const std::string full = path.generic_string(); constexpr std::string_view marker = "/Public/"; const std::size_t markerIndex = full.find(marker); if (markerIndex == std::string::npos) { return path.filename().generic_string(); } return full.substr(markerIndex + marker.size()); } [[nodiscard]] std::string MetaCoreExtractMacroArgument(std::string_view trimmed, std::string_view macroName) { const std::size_t prefixLength = macroName.size(); if (trimmed.size() <= prefixLength || trimmed[prefixLength] != '(') { return {}; } const std::size_t closeIndex = trimmed.rfind(')'); if (closeIndex == std::string_view::npos || closeIndex <= prefixLength) { return {}; } return MetaCoreTrim(std::string(trimmed.substr(prefixLength + 1, closeIndex - prefixLength - 1))); } [[nodiscard]] std::string MetaCoreEscapeCppString(std::string_view value) { std::string escaped; escaped.reserve(value.size()); for (const char character : value) { switch (character) { case '\\': escaped += "\\\\"; break; case '"': escaped += "\\\""; break; case '\n': escaped += "\\n"; break; case '\r': escaped += "\\r"; break; case '\t': escaped += "\\t"; break; default: escaped.push_back(character); break; } } return escaped; } [[nodiscard]] std::string MetaCoreToLower(std::string value) { std::transform(value.begin(), value.end(), value.begin(), [](unsigned char character) { return static_cast(std::tolower(character)); }); return value; } [[nodiscard]] std::string MetaCoreStripQuotes(std::string value) { value = MetaCoreTrim(std::move(value)); if (value.size() >= 2 && value.front() == '"' && value.back() == '"') { return value.substr(1, value.size() - 2); } return value; } [[nodiscard]] bool MetaCoreIsTruthyMetadataValue(std::string value) { value = MetaCoreToLower(MetaCoreTrim(std::move(value))); return value.empty() || value == "true" || value == "1" || value == "yes"; } [[nodiscard]] std::size_t MetaCoreFindUnquotedEquals(std::string_view value) { bool inString = false; bool escaping = false; for (std::size_t index = 0; index < value.size(); ++index) { const char character = value[index]; if (escaping) { escaping = false; continue; } if (character == '\\') { escaping = inString; continue; } if (character == '"') { inString = !inString; continue; } if (!inString && character == '=') { return index; } } return std::string_view::npos; } [[nodiscard]] std::vector MetaCoreSplitPropertySpec(std::string_view spec) { std::vector tokens; std::string token; bool inString = false; bool escaping = false; int nestedDepth = 0; for (const char character : spec) { if (escaping) { token.push_back(character); escaping = false; continue; } if (character == '\\') { token.push_back(character); escaping = inString; continue; } if (character == '"') { token.push_back(character); inString = !inString; continue; } if (!inString && (character == '(' || character == '[' || character == '{')) { ++nestedDepth; } else if (!inString && (character == ')' || character == ']' || character == '}')) { --nestedDepth; } if (!inString && nestedDepth == 0 && character == ',') { tokens.push_back(MetaCoreTrim(token)); token.clear(); continue; } token.push_back(character); } if (!MetaCoreTrim(token).empty()) { tokens.push_back(MetaCoreTrim(token)); } return tokens; } [[nodiscard]] std::unordered_map MetaCoreParsePropertySpec(std::string_view spec) { std::unordered_map metadata; for (const std::string& token : MetaCoreSplitPropertySpec(spec)) { const std::size_t equalsIndex = MetaCoreFindUnquotedEquals(token); if (equalsIndex == std::string_view::npos) { metadata.emplace(MetaCoreTrim(token), "true"); continue; } metadata.insert_or_assign( MetaCoreTrim(token.substr(0, equalsIndex)), MetaCoreTrim(token.substr(equalsIndex + 1)) ); } return metadata; } [[nodiscard]] std::optional MetaCoreParseEnumValue(std::string value) { const std::size_t commentIndex = value.find("//"); if (commentIndex != std::string::npos) { value = value.substr(0, commentIndex); } value = MetaCoreTrim(std::move(value)); if (value.empty() || value == "{" || value == "};") { return std::nullopt; } if (!value.empty() && value.back() == ',') { value.pop_back(); } const std::size_t equalsIndex = MetaCoreFindUnquotedEquals(value); if (equalsIndex != std::string::npos) { value = value.substr(0, equalsIndex); } value = MetaCoreTrim(std::move(value)); if (value.empty()) { return std::nullopt; } if (!std::regex_match(value, std::regex(R"([A-Za-z_]\w*)"))) { return std::nullopt; } return MetaCoreReflectedEnumValue{value}; } void MetaCoreEmitStringMetadataAssignment( std::ostringstream& output, const std::unordered_map& metadata, std::string_view metadataVariable, std::string_view propertyName, std::string_view fieldName ) { const auto iterator = metadata.find(std::string(propertyName)); if (iterator == metadata.end()) { return; } output << " " << metadataVariable << "." << fieldName << " = \"" << MetaCoreEscapeCppString(MetaCoreStripQuotes(iterator->second)) << "\";\n"; } void MetaCoreEmitOptionalDoubleMetadataAssignment( std::ostringstream& output, const std::unordered_map& metadata, std::string_view metadataVariable, std::string_view propertyName, std::string_view fieldName ) { const auto iterator = metadata.find(std::string(propertyName)); if (iterator == metadata.end()) { return; } output << " " << metadataVariable << "." << fieldName << " = " << MetaCoreStripQuotes(iterator->second) << ";\n"; } void MetaCoreEmitBoolMetadataAssignment( std::ostringstream& output, const std::unordered_map& metadata, std::string_view metadataVariable, std::string_view propertyName, std::string_view fieldName ) { const auto iterator = metadata.find(std::string(propertyName)); if (iterator == metadata.end()) { return; } output << " " << metadataVariable << "." << fieldName << " = " << (MetaCoreIsTruthyMetadataValue(iterator->second) ? "true" : "false") << ";\n"; } [[nodiscard]] std::vector MetaCoreParseHeader(const std::filesystem::path& path) { std::ifstream input(path); if (!input.is_open()) { throw std::runtime_error("Unable to open header: " + path.string()); } const std::regex typeRegex(R"(^(struct|class|enum\s+class)\s+([A-Za-z_]\w*))"); const std::regex fieldRegex(R"(^(.+?)\s+([A-Za-z_]\w*)\s*(?:\{.*\}|=.*)?;$)"); std::vector reflectedTypes; std::optional pendingKind; MetaCoreReflectedType* currentType = nullptr; bool expectingField = false; std::string pendingPropertySpec; std::string line; while (std::getline(input, line)) { const std::string trimmed = MetaCoreTrim(line); if (trimmed.empty() || trimmed.starts_with("//")) { continue; } if (trimmed.starts_with("MC_STRUCT")) { pendingKind = MetaCoreReflectedKind::Struct; continue; } if (trimmed.starts_with("MC_CLASS")) { pendingKind = MetaCoreReflectedKind::Class; continue; } if (trimmed.starts_with("MC_ENUM")) { pendingKind = MetaCoreReflectedKind::Enum; continue; } if (trimmed.starts_with("MC_PROPERTY")) { expectingField = true; pendingPropertySpec = MetaCoreExtractMacroArgument(trimmed, "MC_PROPERTY"); continue; } if (trimmed.starts_with("MC_GENERATED_BODY")) { continue; } if (pendingKind.has_value()) { std::smatch match; if (std::regex_search(trimmed, match, typeRegex)) { reflectedTypes.push_back(MetaCoreReflectedType{ pendingKind.value(), match[2].str(), {}, {} }); currentType = &reflectedTypes.back(); pendingKind.reset(); expectingField = false; continue; } } if (currentType != nullptr && trimmed == "};") { currentType = nullptr; expectingField = false; continue; } if (currentType != nullptr && currentType->Kind == MetaCoreReflectedKind::Enum) { if (auto enumValue = MetaCoreParseEnumValue(trimmed); enumValue.has_value()) { currentType->EnumValues.push_back(std::move(*enumValue)); } continue; } if (currentType == nullptr || currentType->Kind == MetaCoreReflectedKind::Enum || !expectingField) { continue; } std::smatch match; if (!std::regex_search(trimmed, match, fieldRegex)) { throw std::runtime_error("Unable to parse reflected field in " + path.string() + ": " + trimmed); } currentType->Fields.push_back(MetaCoreReflectedField{ match[2].str(), pendingPropertySpec }); expectingField = false; pendingPropertySpec.clear(); } return reflectedTypes; } [[nodiscard]] std::string MetaCoreBuildOutput( const std::string& functionName, const std::vector& headers ) { std::vector reflectedTypes; for (const auto& header : headers) { const auto parsed = MetaCoreParseHeader(header); reflectedTypes.insert(reflectedTypes.end(), parsed.begin(), parsed.end()); } std::ostringstream output; output << "#include \"MetaCoreFoundation/MetaCoreGeneratedReflection.h\"\n"; output << "#include \"MetaCoreFoundation/MetaCoreReflection.h\"\n"; for (const auto& header : headers) { output << "#include \"" << MetaCoreBuildIncludePath(header) << "\"\n"; } output << "\nnamespace MetaCore {\n\n"; output << "void " << functionName << "(MetaCoreTypeRegistry& registry) {\n"; for (const auto& reflectedType : reflectedTypes) { if (reflectedType.Kind == MetaCoreReflectedKind::Enum) { output << " auto " << reflectedType.Name << "Builder = MetaCoreRegisterGeneratedEnum<" << reflectedType.Name << ">(registry, \"" << reflectedType.Name << "\");\n"; for (const auto& enumValue : reflectedType.EnumValues) { output << " " << reflectedType.Name << "Builder.Value(\"" << MetaCoreEscapeCppString(enumValue.Name) << "\", " << reflectedType.Name << "::" << enumValue.Name << ");\n"; } continue; } output << " auto " << reflectedType.Name << "Builder = MetaCoreRegisterGeneratedStruct<" << reflectedType.Name << ">(registry, \"" << reflectedType.Name << "\");\n"; for (const auto& field : reflectedType.Fields) { if (!field.PropertySpec.empty()) { const std::string metadataVariable = reflectedType.Name + field.Name + "EditorMetadata"; const auto metadata = MetaCoreParsePropertySpec(field.PropertySpec); output << " MetaCoreFieldEditorMetadata " << metadataVariable << "{};\n"; output << " " << metadataVariable << ".RawSpec = \"" << MetaCoreEscapeCppString(field.PropertySpec) << "\";\n"; MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "DisplayName", "DisplayName"); MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "Group", "Group"); MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "Tooltip", "Tooltip"); MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "ResourceType", "ResourceType"); MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "Resource", "ResourceType"); MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "DefaultValue", "DefaultValue"); MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "Default", "DefaultValue"); MetaCoreEmitOptionalDoubleMetadataAssignment(output, metadata, metadataVariable, "Min", "Min"); MetaCoreEmitOptionalDoubleMetadataAssignment(output, metadata, metadataVariable, "Max", "Max"); MetaCoreEmitOptionalDoubleMetadataAssignment(output, metadata, metadataVariable, "Step", "Step"); MetaCoreEmitBoolMetadataAssignment(output, metadata, metadataVariable, "ReadOnly", "ReadOnly"); MetaCoreEmitBoolMetadataAssignment(output, metadata, metadataVariable, "Hidden", "Hidden"); MetaCoreEmitBoolMetadataAssignment(output, metadata, metadataVariable, "ResourceReference", "ResourceReference"); output << " " << reflectedType.Name << "Builder.Field<&" << reflectedType.Name << "::" << field.Name << ">(\"" << field.Name << "\", " << metadataVariable << ");\n"; continue; } output << " " << reflectedType.Name << "Builder.Field<&" << reflectedType.Name << "::" << field.Name << ">(\"" << field.Name << "\");\n"; } } output << "}\n\n} // namespace MetaCore\n"; return output.str(); } } // namespace int main(int argc, char** argv) { std::filesystem::path outputPath; std::string functionName; std::vector headers; for (int index = 1; index < argc; ++index) { const std::string_view argument(argv[index]); if (argument == "--output" && index + 1 < argc) { outputPath = argv[++index]; continue; } if (argument == "--function" && index + 1 < argc) { functionName = argv[++index]; continue; } if (argument == "--module" && index + 1 < argc) { ++index; continue; } headers.emplace_back(argv[index]); } if (outputPath.empty() || functionName.empty() || headers.empty()) { std::cerr << "Usage: MetaCoreHeaderTool --output --function [--module ] \n"; return 1; } try { const std::string output = MetaCoreBuildOutput(functionName, headers); std::ofstream file(outputPath, std::ios::trunc); if (!file.is_open()) { std::cerr << "Unable to open output file: " << outputPath << '\n'; return 1; } file << output; } catch (const std::exception& exception) { std::cerr << exception.what() << '\n'; return 1; } return 0; }