From 66aad9381e2c3616be344727e9a32c17d550a13b Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Tue, 22 Sep 2026 19:01:02 +0200 Subject: [PATCH] fix: properly calculate matching memory layout with multiple variants --- .../Information/StructureInformation.cpp | 2 +- .../Domain/Information/StructureInformation.h | 2 +- .../Templates/AssetStructTestsTemplate.cpp | 4 +- .../Templates/Internal/BaseTemplate.cpp | 7 +++- .../Templates/Internal/BaseTemplate.h | 5 +++ .../Generating/Templates/ZoneLoadTemplate.cpp | 37 +++++++++---------- .../Templates/ZoneWriteTemplate.cpp | 24 ++++++------ .../CrossPlatformStructurePostProcessor.cpp | 8 ++-- 8 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.cpp b/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.cpp index 5ea50078..fd0f0883 100644 --- a/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.cpp +++ b/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.cpp @@ -5,7 +5,7 @@ StructureInformation::StructureInformation(DefinitionWithMembers* definition, Ty m_type_info(typeInfo), m_is_leaf(false), m_requires_marking(false), - m_has_matching_cross_platform_structure(false), + m_has_matching_cross_platform_memory_layout(false), m_embedded_reference_exists(false), m_non_embedded_reference_exists(false), m_single_pointer_reference_exists(false), diff --git a/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.h b/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.h index 89bda578..d7bb0658 100644 --- a/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.h +++ b/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.h @@ -27,7 +27,7 @@ public: bool m_is_leaf; bool m_requires_marking; - bool m_has_matching_cross_platform_structure; + bool m_has_matching_cross_platform_memory_layout; bool m_embedded_reference_exists; bool m_non_embedded_reference_exists; diff --git a/src/ZoneCodeGeneratorLib/Generating/Templates/AssetStructTestsTemplate.cpp b/src/ZoneCodeGeneratorLib/Generating/Templates/AssetStructTestsTemplate.cpp index 48c55069..7461e085 100644 --- a/src/ZoneCodeGeneratorLib/Generating/Templates/AssetStructTestsTemplate.cpp +++ b/src/ZoneCodeGeneratorLib/Generating/Templates/AssetStructTestsTemplate.cpp @@ -36,13 +36,13 @@ namespace LINE("{") m_intendation++; - if (m_env.m_asset->m_has_matching_cross_platform_structure) + if (MemoryLayoutMatches(*m_env.m_asset)) TestMethod(m_env.m_asset); for (const auto* structure : m_env.m_used_structures) { StructureComputations computations(structure->m_info); - if (!structure->m_info->m_definition->IsAnonymous() && !computations.IsAsset() && structure->m_info->m_has_matching_cross_platform_structure) + if (!structure->m_info->m_definition->IsAnonymous() && !computations.IsAsset() && MemoryLayoutMatches(*structure->m_info)) TestMethod(structure->m_info); } diff --git a/src/ZoneCodeGeneratorLib/Generating/Templates/Internal/BaseTemplate.cpp b/src/ZoneCodeGeneratorLib/Generating/Templates/Internal/BaseTemplate.cpp index 9a66c1a3..1e5e6f14 100644 --- a/src/ZoneCodeGeneratorLib/Generating/Templates/Internal/BaseTemplate.cpp +++ b/src/ZoneCodeGeneratorLib/Generating/Templates/Internal/BaseTemplate.cpp @@ -274,7 +274,7 @@ std::string BaseTemplate::MakeEvaluation(const IEvaluation* evaluation) bool BaseTemplate::ShouldGenerateFillMethod(const RenderingUsedType& type) { const auto isNotForeignAsset = type.m_is_context_asset || !type.m_info || !StructureComputations(type.m_info).IsAsset(); - const auto hasMismatchingStructure = type.m_info && type.m_type == type.m_info->m_definition && !type.m_info->m_has_matching_cross_platform_structure; + const auto hasMismatchingStructure = type.m_info && type.m_type == type.m_info->m_definition && !type.m_info->m_has_matching_cross_platform_memory_layout; const auto isEmbeddedDynamic = type.m_info && type.m_info->m_embedded_reference_exists && StructureComputations(type.m_info).GetDynamicMember(); return isNotForeignAsset && (hasMismatchingStructure || isEmbeddedDynamic); @@ -319,3 +319,8 @@ size_t BaseTemplate::OffsetForMemberModifier(const MemberInformation& memberInfo return curOffset + nestedBaseOffset; } + +bool BaseTemplate::MemoryLayoutMatches(const StructureInformation& structureInfo) const +{ + return !m_env.m_word_size_mismatch || structureInfo.m_has_matching_cross_platform_memory_layout; +} diff --git a/src/ZoneCodeGeneratorLib/Generating/Templates/Internal/BaseTemplate.h b/src/ZoneCodeGeneratorLib/Generating/Templates/Internal/BaseTemplate.h index daad73ed..3b37a5d6 100644 --- a/src/ZoneCodeGeneratorLib/Generating/Templates/Internal/BaseTemplate.h +++ b/src/ZoneCodeGeneratorLib/Generating/Templates/Internal/BaseTemplate.h @@ -45,6 +45,11 @@ protected: [[nodiscard]] size_t OffsetForMemberModifier(const MemberInformation& memberInfo, const DeclarationModifierComputations& modifier, size_t nestedBaseOffset) const; + /** + * Evaluates whether the struct/union has matching memory layouts between the generated code and game. + */ + [[nodiscard]] bool MemoryLayoutMatches(const StructureInformation& structureInfo) const; + std::ostream& m_out; unsigned m_intendation; diff --git a/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneLoadTemplate.cpp b/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneLoadTemplate.cpp index 2cfd4dd1..18d0c343 100644 --- a/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneLoadTemplate.cpp +++ b/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneLoadTemplate.cpp @@ -142,8 +142,8 @@ namespace // Variable Declarations: type varType; for (const auto* type : m_env.m_used_types) { - if (type->m_info && !type->m_info->m_definition->IsAnonymous() - && (!type->m_info->m_is_leaf || !type->m_info->m_has_matching_cross_platform_structure) && !StructureComputations(type->m_info).IsAsset()) + if (type->m_info && !type->m_info->m_definition->IsAnonymous() && (!type->m_info->m_is_leaf || !MemoryLayoutMatches(*type->m_info)) + && !StructureComputations(type->m_info).IsAsset()) { LINE(VariableDecl(type->m_type)) } @@ -349,8 +349,8 @@ namespace for (const auto* type : m_env.m_used_types) { - if (type->m_info && !type->m_info->m_definition->IsAnonymous() - && (!type->m_info->m_is_leaf || !type->m_info->m_has_matching_cross_platform_structure) && !StructureComputations(type->m_info).IsAsset()) + if (type->m_info && !type->m_info->m_definition->IsAnonymous() && (!type->m_info->m_is_leaf || !MemoryLayoutMatches(*type->m_info)) + && !StructureComputations(type->m_info).IsAsset()) { PrintVariableInitialization(type->m_type); } @@ -378,7 +378,7 @@ namespace LINE("{") m_intendation++; - const auto callFillForMember = memberInfo.m_type && !memberInfo.m_type->m_has_matching_cross_platform_structure; + const auto callFillForMember = memberInfo.m_type && !MemoryLayoutMatches(*memberInfo.m_type); if (callFillForMember) { @@ -458,7 +458,7 @@ namespace const DeclarationModifierComputations& modifier, const size_t nestedBaseOffset) { - if (memberInfo.m_type && !memberInfo.m_type->m_has_matching_cross_platform_structure) + if (memberInfo.m_type && !MemoryLayoutMatches(*memberInfo.m_type)) { LINEF("for (auto i = 0u; i < std::extent_v; i++)", structInfo.m_definition->m_name, memberInfo.m_member->m_name) LINE("{") @@ -488,7 +488,7 @@ namespace if (!hasAnonymousType) { - const auto hasMismatchingStructure = memberInfo.m_type && !memberInfo.m_type->m_has_matching_cross_platform_structure; + const auto hasMismatchingStructure = memberInfo.m_type && !MemoryLayoutMatches(*memberInfo.m_type); const auto hasDynamicMember = memberInfo.m_type && StructureComputations(memberInfo.m_type).GetDynamicMember(); if (hasMismatchingStructure || hasDynamicMember) { @@ -893,7 +893,7 @@ namespace void PrintLoadPtrArrayMethod_Loading(const DataDefinition* def, const StructureInformation* info) const { const auto alignment = info && def == info->m_definition ? MakeAllocAlignment(*info) : std::to_string(def->GetAlignment(m_env.m_word_size)); - if (info && !info->m_has_matching_cross_platform_structure && StructureComputations(info).GetDynamicMember()) + if (info && !MemoryLayoutMatches(*info) && StructureComputations(info).GetDynamicMember()) { assert(def == info->m_definition); LINE("// Alloc first for alignment, then proceed to read as game does") @@ -949,7 +949,7 @@ namespace LINE("{") m_intendation++; - if (info && !info->m_has_matching_cross_platform_structure) + if (info && !MemoryLayoutMatches(*info)) { LINEF("*{0} = m_stream.ConvertOffsetToPointerLookup(*{0}).Expect();", MakeTypePtrVarName(def)) } @@ -1036,7 +1036,7 @@ namespace LINE("") LINE("if (atStreamStart)") - if (info->m_has_matching_cross_platform_structure) + if (MemoryLayoutMatches(*info)) { m_intendation++; LINEF("m_stream.Load<{0}>({1}, count);", info->m_definition->GetFullName(), MakeTypeVarName(def)) @@ -1163,7 +1163,7 @@ namespace LINE(MakeCustomActionCall(member->m_post_load_action.get())) } } - else if (member->m_type && !member->m_type->m_has_matching_cross_platform_structure) + else if (member->m_type && !MemoryLayoutMatches(*member->m_type)) { LINEF("const auto fillArraySize = static_cast({0});", MakeEvaluation(modifier.GetArrayPointerCountEvaluation())) LINEF("const auto fill = m_stream.LoadWithFill({0} * fillArraySize);", member->m_member->m_type_declaration->m_type->GetSize(m_env.m_word_size)) @@ -1257,7 +1257,7 @@ namespace m_env.m_word_size_mismatch ? "false" : "true", MakeEvaluation(modifier.GetDynamicArraySizeEvaluation())) } - else if (info->m_has_matching_cross_platform_structure) + else if (MemoryLayoutMatches(*info)) { if (m_env.m_word_size_mismatch) { @@ -1336,7 +1336,7 @@ namespace LINE(MakeCustomActionCall(member->m_post_load_action.get())) } } - else if (member->m_type && !member->m_type->m_has_matching_cross_platform_structure) + else if (member->m_type && !MemoryLayoutMatches(*member->m_type)) { LINEF("{0} = {1};", MakeTypeVarName(member->m_member->m_type_declaration->m_type), MakeMemberAccess(info, member, modifier)) LINEF("FillStruct_{0}(m_stream.LoadWithFill({1}));", @@ -1427,8 +1427,7 @@ namespace [[nodiscard]] bool ShouldAllocOutOfBlock(const MemberInformation& member, const MemberLoadType loadType) const { - return m_env.m_word_size_mismatch - && ((member.m_type && !member.m_type->m_has_matching_cross_platform_structure) || loadType == MemberLoadType::POINTER_ARRAY); + return m_env.m_word_size_mismatch && ((member.m_type && !MemoryLayoutMatches(*member.m_type)) || loadType == MemberLoadType::POINTER_ARRAY); } [[nodiscard]] bool @@ -1589,7 +1588,7 @@ namespace LINE("{") m_intendation++; - if (info->m_has_matching_cross_platform_structure) + if (MemoryLayoutMatches(*info)) { LINEF("{0} = m_stream.ConvertOffsetToAliasNative({0});", MakeMemberAccess(info, member, modifier)) } @@ -1871,7 +1870,7 @@ namespace LINEF("assert({0} != nullptr);", MakeTypeVarName(info->m_definition)) const auto* dynamicMember = computations.GetDynamicMember(); - if (!info->m_has_matching_cross_platform_structure && dynamicMember && !info->m_non_embedded_reference_exists) + if (!MemoryLayoutMatches(*info) && dynamicMember && !info->m_non_embedded_reference_exists) { LINE("assert(!atStreamStart);") } @@ -1881,7 +1880,7 @@ namespace LINE("if (atStreamStart)") m_intendation++; - if (info->m_has_matching_cross_platform_structure) + if (MemoryLayoutMatches(*info)) { if (dynamicMember == nullptr) { @@ -2073,7 +2072,7 @@ namespace if (inTemp) { - if (info->m_has_matching_cross_platform_structure) + if (MemoryLayoutMatches(*info)) { LINEF("*{0} = m_stream.ConvertOffsetToAliasNative(*{0});", MakeTypePtrVarName(info->m_definition)) } diff --git a/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneWriteTemplate.cpp b/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneWriteTemplate.cpp index 321ecd7e..51c72f1d 100644 --- a/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneWriteTemplate.cpp +++ b/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneWriteTemplate.cpp @@ -128,8 +128,8 @@ namespace // Variable Declarations: type varType; for (const auto* type : m_env.m_used_types) { - if (type->m_info && !type->m_info->m_definition->IsAnonymous() - && (!type->m_info->m_is_leaf || !type->m_info->m_has_matching_cross_platform_structure) && !StructureComputations(type->m_info).IsAsset()) + if (type->m_info && !type->m_info->m_definition->IsAnonymous() && (!type->m_info->m_is_leaf || !MemoryLayoutMatches(*type->m_info)) + && !StructureComputations(type->m_info).IsAsset()) LINE(VariableDecl(type->m_type)) } for (const auto* type : m_env.m_used_types) @@ -374,8 +374,8 @@ namespace for (const auto* type : m_env.m_used_types) { - if (type->m_info && !type->m_info->m_definition->IsAnonymous() - && (!type->m_info->m_is_leaf || !type->m_info->m_has_matching_cross_platform_structure) && !StructureComputations(type->m_info).IsAsset()) + if (type->m_info && !type->m_info->m_definition->IsAnonymous() && (!type->m_info->m_is_leaf || !MemoryLayoutMatches(*type->m_info)) + && !StructureComputations(type->m_info).IsAsset()) PrintVariableInitialization(type->m_type); } for (const auto* type : m_env.m_used_types) @@ -488,7 +488,7 @@ namespace MakeSafeTypeName(member->m_member->m_type_declaration->m_type), MakeEvaluation(modifier.GetArrayPointerCountEvaluation())) } - else if (member->m_type && !member->m_type->m_has_matching_cross_platform_structure) + else if (member->m_type && !MemoryLayoutMatches(*member->m_type)) { LINEF("const auto fillArraySize = static_cast({0});", MakeEvaluation(modifier.GetArrayPointerCountEvaluation())) if (!computations.IsInRuntimeBlock()) @@ -626,7 +626,7 @@ namespace LINEF("{0} = {1};", MakeTypeVarName(member->m_member->m_type_declaration->m_type), MakeMemberAccess(info, member, modifier)) LINEF("Write_{0}(true);", MakeSafeTypeName(member->m_type->m_definition)) } - else if (member->m_type && !member->m_type->m_has_matching_cross_platform_structure) + else if (member->m_type && !MemoryLayoutMatches(*member->m_type)) { LINEF("{0} = {1};", MakeTypeVarName(member->m_member->m_type_declaration->m_type), MakeMemberAccess(info, member, modifier)) LINEF("FillStruct_{0}(m_stream->WriteWithFill({1}));", @@ -741,7 +741,7 @@ namespace if (writeType == MemberWriteType::ARRAY_POINTER) { - if (member->m_type && !member->m_type->m_has_matching_cross_platform_structure) + if (member->m_type && !MemoryLayoutMatches(*member->m_type)) { LINE_MIDDLEF(", {0}", member->m_type->m_definition->GetSize(m_env.m_word_size)) } @@ -750,7 +750,7 @@ namespace } else if (writeType == MemberWriteType::POINTER_ARRAY) { - if (member->m_type && !member->m_type->m_has_matching_cross_platform_structure) + if (member->m_type && !MemoryLayoutMatches(*member->m_type)) { LINE_MIDDLEF(", {0}", m_env.m_pointer_size) } @@ -1111,7 +1111,7 @@ namespace { LINE("if (atStreamStart)") - if (info->m_has_matching_cross_platform_structure) + if (MemoryLayoutMatches(*info)) { m_intendation++; if (dynamicMember == nullptr) @@ -1261,7 +1261,7 @@ namespace const DeclarationModifierComputations& modifier, const size_t nestedBaseOffset) { - if (memberInfo.m_type && !memberInfo.m_type->m_has_matching_cross_platform_structure) + if (memberInfo.m_type && !MemoryLayoutMatches(*memberInfo.m_type)) { LINEF("for (auto i = 0u; i < std::extent_v; i++)", structInfo.m_definition->m_name, memberInfo.m_member->m_name) LINE("{") @@ -1291,7 +1291,7 @@ namespace if (!hasAnonymousType) { - const auto hasMismatchingStructure = memberInfo.m_type && !memberInfo.m_type->m_has_matching_cross_platform_structure; + const auto hasMismatchingStructure = memberInfo.m_type && !MemoryLayoutMatches(*memberInfo.m_type); const auto hasDynamicMember = memberInfo.m_type && StructureComputations(memberInfo.m_type).GetDynamicMember(); if (hasMismatchingStructure || hasDynamicMember) { @@ -1584,7 +1584,7 @@ namespace LINE("") LINE("if (atStreamStart)") - if (info->m_has_matching_cross_platform_structure) + if (MemoryLayoutMatches(*info)) { m_intendation++; LINEF("{0} = m_stream->Write<{1}>({2}, count);", MakeTypeWrittenVarName(def), def->GetFullName(), MakeTypeVarName(def)) diff --git a/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/CrossPlatformStructurePostProcessor.cpp b/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/CrossPlatformStructurePostProcessor.cpp index e44b45b0..73eee090 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/CrossPlatformStructurePostProcessor.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/CrossPlatformStructurePostProcessor.cpp @@ -9,7 +9,7 @@ namespace bool CalculateHasMatchingCrossPlatformStructure(std::unordered_set& visitedStructures, StructureInformation* info) { if (visitedStructures.find(info) != visitedStructures.end()) - return info->m_has_matching_cross_platform_structure; + return info->m_has_matching_cross_platform_memory_layout; visitedStructures.emplace(info); @@ -19,19 +19,19 @@ namespace { if (modifier->GetType() == DeclarationModifierType::POINTER) { - info->m_has_matching_cross_platform_structure = false; + info->m_has_matching_cross_platform_memory_layout = false; return false; } } if (member->m_type != nullptr && member->m_type != info && !CalculateHasMatchingCrossPlatformStructure(visitedStructures, member->m_type)) { - info->m_has_matching_cross_platform_structure = false; + info->m_has_matching_cross_platform_memory_layout = false; return false; } } - info->m_has_matching_cross_platform_structure = true; + info->m_has_matching_cross_platform_memory_layout = true; return true; } } // namespace