diff --git a/source2gen/include/options.hpp b/source2gen/include/options.hpp index c2020a72..64e5e1db 100644 --- a/source2gen/include/options.hpp +++ b/source2gen/include/options.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace source2_gen { enum class Language { @@ -18,6 +20,7 @@ namespace source2_gen { Language emit_language{}; bool static_members{}; bool static_assertions{}; + std::vector known_types{}; /// @return @ref std::nullopt if "--help" was passed or parsing failed [[nodiscard]] diff --git a/source2gen/src/options.cpp b/source2gen/src/options.cpp index 3a97a3f1..10a6ee4e 100644 --- a/source2gen/src/options.cpp +++ b/source2gen/src/options.cpp @@ -29,6 +29,10 @@ std::optional source2_gen::Options::parse_args(int argc, c .default_value(false) .help("Don't generate static assertions for class size and field offsets (Generated SDK might not work. You can get banned for writing to wrong " "offsets!)"); + parser.add_argument("--known-types") + .nargs(argparse::nargs_pattern::any) + .default_value(std::vector{}) + .help("List of known template types (should be in your source2gen.hpp)"); try { parser.parse_args(argc, argv); @@ -46,5 +50,6 @@ std::optional source2_gen::Options::parse_args(int argc, c return source2_gen::Options{.emit_language = language.value(), .static_members = (language.value() != Language::c_ida) && !parser.is_used("no-static-members"), - .static_assertions = (language.value() != Language::c_ida) && !parser.is_used("no-static-assertions")}; + .static_assertions = (language.value() != Language::c_ida) && !parser.is_used("no-static-assertions"), + .known_types = parser.get>("--known-types")}; } diff --git a/source2gen/src/sdk/sdk.cpp b/source2gen/src/sdk/sdk.cpp index 29d40f38..a1f9bb81 100644 --- a/source2gen/src/sdk/sdk.cpp +++ b/source2gen/src/sdk/sdk.cpp @@ -10,6 +10,7 @@ #include "tools/field_parser.h" #include "tools/util.h" #include +#include #include #include @@ -899,6 +900,28 @@ namespace { std::list> cached_fields{}; std::list cached_datamap_fields{}; + const auto is_known_type = [&options](std::string type_name) { + // Checks if type name fully known including nested templates + + if (options.known_types.empty()) + return false; + + std::vector types; + auto parts = type_name | std::views::split('<'); + for (auto part : parts) { + std::string s(part.begin(), part.end()); + + s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return c == '>' || std::isspace(static_cast(c)); }), s.end()); + if (!s.empty()) + types.push_back(s); + } + if (!types.empty()) + types.pop_back(); + return std::all_of(types.begin(), types.end(), [&options](const std::string& t) { + return std::find(options.known_types.begin(), options.known_types.end(), t) != options.known_types.end(); + }); + }; + for (const auto& field : class_.GetFields()) { // Fall back to size=1 because there are no 0-sized types. // `RenderPrimitiveType_t` is the only type (in CS2 9035763) without size information. @@ -999,7 +1022,7 @@ namespace { .reset_tabs_count() .prop(codegen::Prop{.type_category = GetTypeCategory(field), .type_name = var_info.m_type, .name = var_info.formatted_name()}, false) .restore_tabs_count(); - } else if (std::string{field.m_pSchemaType->m_pszName}.contains('<')) { + } else if (std::string{field.m_pSchemaType->m_pszName}.contains('<') && !is_known_type(field.m_pSchemaType->m_pszName)) { // template type // This is a workaround to get the size of template types right.