From 8b16e81355a3605fb1d9d3eb4c4c696531f214a8 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sat, 17 Oct 2026 11:47:51 +0200 Subject: [PATCH] refactor: adapt iw4 and iw5 content writer and zone writer factory for both pc variants --- src/ZoneWriting/Game/IW4/ContentWriterIW4.cpp | 60 ++++++++++++++---- .../Game/IW4/ZoneWriterFactoryIW4.cpp | 3 +- src/ZoneWriting/Game/IW5/ContentWriterIW5.cpp | 62 ++++++++++++++----- .../Game/IW5/ZoneWriterFactoryIW5.cpp | 3 +- src/ZoneWriting/Writing/ContentWriterBase.cpp | 2 + src/ZoneWriting/Writing/ContentWriterBase.h | 1 + ...mpleZoneIW4.zone => SimpleZoneIW4_32.zone} | 2 + .../Game/IW4/Simple/SimpleZoneIW4_64.zone | 6 ++ test/SystemTests/Game/IW4/SimpleZoneIW4.cpp | 16 ++++- ...mpleZoneIW5.zone => SimpleZoneIW5_32.zone} | 2 + .../Game/IW5/Simple/SimpleZoneIW5_64.zone | 6 ++ test/SystemTests/Game/IW5/SimpleZoneIW5.cpp | 16 ++++- 12 files changed, 142 insertions(+), 37 deletions(-) rename test/SystemTests/Game/IW4/Simple/{SimpleZoneIW4.zone => SimpleZoneIW4_32.zone} (50%) create mode 100644 test/SystemTests/Game/IW4/Simple/SimpleZoneIW4_64.zone rename test/SystemTests/Game/IW5/Simple/{SimpleZoneIW5.zone => SimpleZoneIW5_32.zone} (50%) create mode 100644 test/SystemTests/Game/IW5/Simple/SimpleZoneIW5_64.zone diff --git a/src/ZoneWriting/Game/IW4/ContentWriterIW4.cpp b/src/ZoneWriting/Game/IW4/ContentWriterIW4.cpp index fa744520..b04565c5 100644 --- a/src/ZoneWriting/Game/IW4/ContentWriterIW4.cpp +++ b/src/ZoneWriting/Game/IW4/ContentWriterIW4.cpp @@ -1,6 +1,7 @@ #include "ContentWriterIW4.h" #include "Game/IW4/AssetWriter_iw4_pc32.h" +#include "Game/IW4/AssetWriter_iw4_pc64.h" #include "Writing/WritingException.h" #include @@ -67,8 +68,13 @@ void ContentWriter::WriteScriptStringList(const bool atStreamStart) #ifdef ARCH_x86 static_assert(offsetof(ScriptStringList, strings) == 4u); -#endif m_stream->MarkFollowing(varScriptStringListWritten.AtOffset(4)); +#else + if (m_word_size == GameWordSize::ARCH_64) + m_stream->MarkFollowing(varScriptStringListWritten.AtOffset(8)); + else + m_stream->MarkFollowing(varScriptStringListWritten.AtOffset(4)); +#endif } } @@ -80,10 +86,19 @@ void ContentWriter::WriteXAsset(const bool atStreamStart) #define WRITE_ASSET(type_index, typeName, headerEntry) \ case type_index: \ { \ - Writer_##typeName##_pc32 writer(varXAsset->header.headerEntry, m_zone, *m_stream); \ - writer.Write(varXAsset->header.headerEntry, varXAssetWritten.AtOffset(4)); \ + if (m_word_size == GameWordSize::ARCH_64) \ + { \ + Writer_##typeName##_pc64 writer(varXAsset->header.headerEntry, m_zone, *m_stream); \ + writer.Write(varXAsset->header.headerEntry, varXAssetWritten.AtOffset(8)); \ + } \ + else \ + { \ + Writer_##typeName##_pc32 writer(varXAsset->header.headerEntry, m_zone, *m_stream); \ + writer.Write(varXAsset->header.headerEntry, varXAssetWritten.AtOffset(4)); \ + } \ break; \ } + #define SKIP_ASSET(type_index, typeName, headerEntry) \ case type_index: \ break; @@ -156,11 +171,18 @@ void ContentWriter::WriteXAssetArray(const bool atStreamStart, const size_t coun #ifdef ARCH_x86 varXAssetWritten = m_stream->Write(varXAsset, count); #else - const auto fill = m_stream->WriteWithFill(8u * count); - varXAssetWritten = fill.Offset(); + if (m_word_size == GameWordSize::ARCH_64) + { + varXAssetWritten = m_stream->Write(varXAsset, count); + } + else + { + const auto fill = m_stream->WriteWithFill(8u * count); + varXAssetWritten = fill.Offset(); - for (size_t index = 0; index < count; index++) - fill.Fill(varXAsset[index].type, 8u * index); + for (size_t index = 0; index < count; index++) + fill.Fill(varXAsset[index].type, 8u * index); + } #endif } @@ -188,13 +210,20 @@ void ContentWriter::WriteContent(ZoneOutputStream& stream) static_assert(offsetof(XAssetList, assetCount) == 8u); varXAssetListWritten = m_stream->WriteDataRaw(&assetList, sizeof(assetList)); #else - const auto fillAccessor = m_stream->WriteWithFill(16u); - varXAssetListWritten = fillAccessor.Offset(); + if (m_word_size == GameWordSize::ARCH_64) + { + varXAssetListWritten = m_stream->WriteDataRaw(&assetList, sizeof(assetList)); + } + else + { + const auto fillAccessor = m_stream->WriteWithFill(16u); + varXAssetListWritten = fillAccessor.Offset(); - varScriptStringList = &varXAssetList->stringList; - fillAccessor.Fill(varScriptStringList->count, 0u); + varScriptStringList = &varXAssetList->stringList; + fillAccessor.Fill(varScriptStringList->count, 0u); - fillAccessor.Fill(varXAssetList->assetCount, 8u); + fillAccessor.Fill(varXAssetList->assetCount, 8u); + } #endif m_stream->PushBlock(XFILE_BLOCK_VIRTUAL); @@ -214,8 +243,13 @@ void ContentWriter::WriteContent(ZoneOutputStream& stream) #ifdef ARCH_x86 static_assert(offsetof(XAssetList, assets) == 12u); -#endif m_stream->MarkFollowing(varXAssetListWritten.AtOffset(12)); +#else + if (m_word_size == GameWordSize::ARCH_64) + m_stream->MarkFollowing(varXAssetListWritten.AtOffset(24)); + else + m_stream->MarkFollowing(varXAssetListWritten.AtOffset(12)); +#endif } m_stream->PopBlock(); diff --git a/src/ZoneWriting/Game/IW4/ZoneWriterFactoryIW4.cpp b/src/ZoneWriting/Game/IW4/ZoneWriterFactoryIW4.cpp index 6b31ac50..729a7d1f 100644 --- a/src/ZoneWriting/Game/IW4/ZoneWriterFactoryIW4.cpp +++ b/src/ZoneWriting/Game/IW4/ZoneWriterFactoryIW4.cpp @@ -61,6 +61,7 @@ namespace std::unique_ptr ZoneWriterFactory::CreateWriter(const Zone& zone) const { auto writer = std::make_unique(); + const auto* variant = IGameVariant::GetVariantById(zone.m_variant_id); // TODO Support signed fastfiles bool isSecure = false; @@ -69,7 +70,7 @@ std::unique_ptr ZoneWriterFactory::CreateWriter(const Zone& zone) co auto contentInMemory = std::make_unique(std::make_unique(zone), zone, - 32u, + variant->GetWordSize() == GameWordSize::ARCH_64 ? 64u : 32u, ZoneConstants::ZONE_POINTER_OFFSET_BIT_COUNT, ZoneConstants::ZONE_POINTER_BLOCK_BIT_COUNT, ZoneConstants::INSERT_BLOCK); diff --git a/src/ZoneWriting/Game/IW5/ContentWriterIW5.cpp b/src/ZoneWriting/Game/IW5/ContentWriterIW5.cpp index 61b24278..be0b5ce9 100644 --- a/src/ZoneWriting/Game/IW5/ContentWriterIW5.cpp +++ b/src/ZoneWriting/Game/IW5/ContentWriterIW5.cpp @@ -1,6 +1,7 @@ #include "ContentWriterIW5.h" #include "Game/IW5/AssetWriter_iw5_pc32.h" +#include "Game/IW5/AssetWriter_iw5_pc64.h" #include "Writing/WritingException.h" #include @@ -67,8 +68,13 @@ void ContentWriter::WriteScriptStringList(const bool atStreamStart) #ifdef ARCH_x86 static_assert(offsetof(ScriptStringList, strings) == 4u); -#endif m_stream->MarkFollowing(varScriptStringListWritten.AtOffset(4)); +#else + if (m_word_size == GameWordSize::ARCH_64) + m_stream->MarkFollowing(varScriptStringListWritten.AtOffset(8)); + else + m_stream->MarkFollowing(varScriptStringListWritten.AtOffset(4)); +#endif } } @@ -80,13 +86,18 @@ void ContentWriter::WriteXAsset(const bool atStreamStart) #define WRITE_ASSET(type_index, typeName, headerEntry) \ case type_index: \ { \ - Writer_##typeName##_pc32 writer(varXAsset->header.headerEntry, m_zone, *m_stream); \ - writer.Write(varXAsset->header.headerEntry, varXAssetWritten.AtOffset(4)); \ + if (m_word_size == GameWordSize::ARCH_64) \ + { \ + Writer_##typeName##_pc64 writer(varXAsset->header.headerEntry, m_zone, *m_stream); \ + writer.Write(varXAsset->header.headerEntry, varXAssetWritten.AtOffset(8)); \ + } \ + else \ + { \ + Writer_##typeName##_pc32 writer(varXAsset->header.headerEntry, m_zone, *m_stream); \ + writer.Write(varXAsset->header.headerEntry, varXAssetWritten.AtOffset(4)); \ + } \ break; \ } -#define SKIP_ASSET(type_index, typeName, headerEntry) \ - case type_index: \ - break; assert(varXAsset != nullptr); @@ -159,11 +170,18 @@ void ContentWriter::WriteXAssetArray(const bool atStreamStart, const size_t coun #ifdef ARCH_x86 varXAssetWritten = m_stream->Write(varXAsset, count); #else - const auto fill = m_stream->WriteWithFill(8u * count); - varXAssetWritten = fill.Offset(); + if (m_word_size == GameWordSize::ARCH_64) + { + varXAssetWritten = m_stream->Write(varXAsset, count); + } + else + { + const auto fill = m_stream->WriteWithFill(8u * count); + varXAssetWritten = fill.Offset(); - for (size_t index = 0; index < count; index++) - fill.Fill(varXAsset[index].type, 8u * index); + for (size_t index = 0; index < count; index++) + fill.Fill(varXAsset[index].type, 8u * index); + } #endif } @@ -191,13 +209,20 @@ void ContentWriter::WriteContent(ZoneOutputStream& stream) static_assert(offsetof(XAssetList, assetCount) == 8u); varXAssetListWritten = m_stream->WriteDataRaw(&assetList, sizeof(assetList)); #else - const auto fillAccessor = m_stream->WriteWithFill(16u); - varXAssetListWritten = fillAccessor.Offset(); + if (m_word_size == GameWordSize::ARCH_64) + { + varXAssetListWritten = m_stream->WriteDataRaw(&assetList, sizeof(assetList)); + } + else + { + const auto fillAccessor = m_stream->WriteWithFill(16u); + varXAssetListWritten = fillAccessor.Offset(); - varScriptStringList = &varXAssetList->stringList; - fillAccessor.Fill(varScriptStringList->count, 0u); + varScriptStringList = &varXAssetList->stringList; + fillAccessor.Fill(varScriptStringList->count, 0u); - fillAccessor.Fill(varXAssetList->assetCount, 8u); + fillAccessor.Fill(varXAssetList->assetCount, 8u); + } #endif m_stream->PushBlock(XFILE_BLOCK_VIRTUAL); @@ -217,8 +242,13 @@ void ContentWriter::WriteContent(ZoneOutputStream& stream) #ifdef ARCH_x86 static_assert(offsetof(XAssetList, assets) == 12u); -#endif m_stream->MarkFollowing(varXAssetListWritten.AtOffset(12)); +#else + if (m_word_size == GameWordSize::ARCH_64) + m_stream->MarkFollowing(varXAssetListWritten.AtOffset(24)); + else + m_stream->MarkFollowing(varXAssetListWritten.AtOffset(12)); +#endif } m_stream->PopBlock(); diff --git a/src/ZoneWriting/Game/IW5/ZoneWriterFactoryIW5.cpp b/src/ZoneWriting/Game/IW5/ZoneWriterFactoryIW5.cpp index c002697c..6a02d77b 100644 --- a/src/ZoneWriting/Game/IW5/ZoneWriterFactoryIW5.cpp +++ b/src/ZoneWriting/Game/IW5/ZoneWriterFactoryIW5.cpp @@ -62,6 +62,7 @@ namespace std::unique_ptr ZoneWriterFactory::CreateWriter(const Zone& zone) const { auto writer = std::make_unique(); + const auto* variant = IGameVariant::GetVariantById(zone.m_variant_id); // TODO Support signed fastfiles bool isSecure = false; @@ -70,7 +71,7 @@ std::unique_ptr ZoneWriterFactory::CreateWriter(const Zone& zone) co auto contentInMemory = std::make_unique(std::make_unique(zone), zone, - 32u, + variant->GetWordSize() == GameWordSize::ARCH_64 ? 64u : 32u, ZoneConstants::ZONE_POINTER_OFFSET_BIT_COUNT, ZoneConstants::ZONE_POINTER_BLOCK_BIT_COUNT, ZoneConstants::INSERT_BLOCK); diff --git a/src/ZoneWriting/Writing/ContentWriterBase.cpp b/src/ZoneWriting/Writing/ContentWriterBase.cpp index e9a596fc..b182d840 100644 --- a/src/ZoneWriting/Writing/ContentWriterBase.cpp +++ b/src/ZoneWriting/Writing/ContentWriterBase.cpp @@ -4,6 +4,7 @@ ContentWriterBase::ContentWriterBase(const Zone& zone) : m_zone(zone), + m_word_size(GameVariant::GetVariantById(zone.m_variant_id)->GetWordSize()), m_stream(nullptr), varXString(nullptr), varXStringWritten(nullptr) @@ -12,6 +13,7 @@ ContentWriterBase::ContentWriterBase(const Zone& zone) ContentWriterBase::ContentWriterBase(const Zone& zone, ZoneOutputStream& stream) : m_zone(zone), + m_word_size(GameVariant::GetVariantById(zone.m_variant_id)->GetWordSize()), m_stream(&stream), varXString(nullptr), varXStringWritten(nullptr) diff --git a/src/ZoneWriting/Writing/ContentWriterBase.h b/src/ZoneWriting/Writing/ContentWriterBase.h index 9d6dcb3a..c606a965 100644 --- a/src/ZoneWriting/Writing/ContentWriterBase.h +++ b/src/ZoneWriting/Writing/ContentWriterBase.h @@ -21,6 +21,7 @@ protected: void WriteXStringArray(bool atStreamStart, size_t count); const Zone& m_zone; + GameWordSize m_word_size; ZoneOutputStream* m_stream; const char** varXString; diff --git a/test/SystemTests/Game/IW4/Simple/SimpleZoneIW4.zone b/test/SystemTests/Game/IW4/Simple/SimpleZoneIW4_32.zone similarity index 50% rename from test/SystemTests/Game/IW4/Simple/SimpleZoneIW4.zone rename to test/SystemTests/Game/IW4/Simple/SimpleZoneIW4_32.zone index bdb55ff4..af03188f 100644 --- a/test/SystemTests/Game/IW4/Simple/SimpleZoneIW4.zone +++ b/test/SystemTests/Game/IW4/Simple/SimpleZoneIW4_32.zone @@ -1,4 +1,6 @@ >game,IW4 +>variant,pc32 +>name,SimpleZoneIW4 rawfile,SimpleZone.txt diff --git a/test/SystemTests/Game/IW4/Simple/SimpleZoneIW4_64.zone b/test/SystemTests/Game/IW4/Simple/SimpleZoneIW4_64.zone new file mode 100644 index 00000000..dcc2ccbd --- /dev/null +++ b/test/SystemTests/Game/IW4/Simple/SimpleZoneIW4_64.zone @@ -0,0 +1,6 @@ +>game,IW4 +>variant,pc64 +>name,SimpleZoneIW4 + +rawfile,SimpleZone.txt + diff --git a/test/SystemTests/Game/IW4/SimpleZoneIW4.cpp b/test/SystemTests/Game/IW4/SimpleZoneIW4.cpp index a6b924cb..aaea3943 100644 --- a/test/SystemTests/Game/IW4/SimpleZoneIW4.cpp +++ b/test/SystemTests/Game/IW4/SimpleZoneIW4.cpp @@ -15,7 +15,7 @@ using namespace std::literals; namespace { - TEST_CASE("Simple Zone(IW4)", "[iw4][system][simple]") + void PerformTest(const std::string& zoneFileName, GameVariantId expectedGameVariantId) { const auto assetSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/IW4/Simple").string(); const auto sourceSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/IW4/Simple").string(); @@ -30,7 +30,7 @@ namespace sourceSearchPath.c_str(), "--output-folder", outputPath.c_str(), - "SimpleZoneIW4", + zoneFileName.c_str(), }; LinkerArgs args; @@ -53,9 +53,19 @@ namespace auto zone = std::move(*maybeZone); REQUIRE(zone->m_game_id == GameId::IW4); - REQUIRE(zone->m_variant_id == GameVariantId::IW4_PC32); + REQUIRE(zone->m_variant_id == expectedGameVariantId); REQUIRE(zone->m_name == "SimpleZoneIW4"); REQUIRE(zone->m_pools.GetTotalAssetCount() == 1); REQUIRE(zone->m_pools.GetAsset("SimpleZone.txt")); } + + TEST_CASE("Simple Zone(IW4|pc32)", "[iw4][pc32][system][simple]") + { + PerformTest("SimpleZoneIW4_32", GameVariantId::IW4_PC32); + } + + TEST_CASE("Simple Zone(IW4|pc64)", "[iw4][pc64][system][simple]") + { + PerformTest("SimpleZoneIW4_64", GameVariantId::IW4_PC64); + } } // namespace diff --git a/test/SystemTests/Game/IW5/Simple/SimpleZoneIW5.zone b/test/SystemTests/Game/IW5/Simple/SimpleZoneIW5_32.zone similarity index 50% rename from test/SystemTests/Game/IW5/Simple/SimpleZoneIW5.zone rename to test/SystemTests/Game/IW5/Simple/SimpleZoneIW5_32.zone index 0c35d8b6..d0ea4399 100644 --- a/test/SystemTests/Game/IW5/Simple/SimpleZoneIW5.zone +++ b/test/SystemTests/Game/IW5/Simple/SimpleZoneIW5_32.zone @@ -1,4 +1,6 @@ >game,IW5 +>variant,pc32 +>name,SimpleZoneIW5 rawfile,SimpleZone.txt diff --git a/test/SystemTests/Game/IW5/Simple/SimpleZoneIW5_64.zone b/test/SystemTests/Game/IW5/Simple/SimpleZoneIW5_64.zone new file mode 100644 index 00000000..502f1bd9 --- /dev/null +++ b/test/SystemTests/Game/IW5/Simple/SimpleZoneIW5_64.zone @@ -0,0 +1,6 @@ +>game,IW5 +>variant,pc64 +>name,SimpleZoneIW5 + +rawfile,SimpleZone.txt + diff --git a/test/SystemTests/Game/IW5/SimpleZoneIW5.cpp b/test/SystemTests/Game/IW5/SimpleZoneIW5.cpp index 1bfd6630..6070107d 100644 --- a/test/SystemTests/Game/IW5/SimpleZoneIW5.cpp +++ b/test/SystemTests/Game/IW5/SimpleZoneIW5.cpp @@ -16,7 +16,7 @@ using namespace std::literals; namespace { - TEST_CASE("Simple Zone(IW5)", "[iw5][system][simple]") + void PerformTest(const std::string& zoneFileName, GameVariantId expectedGameVariantId) { const auto assetSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/IW5/Simple").string(); const auto sourceSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/IW5/Simple").string(); @@ -31,7 +31,7 @@ namespace sourceSearchPath.c_str(), "--output-folder", outputPath.c_str(), - "SimpleZoneIW5", + zoneFileName.c_str(), }; LinkerArgs args; @@ -54,9 +54,19 @@ namespace auto zone = std::move(*maybeZone); REQUIRE(zone->m_game_id == GameId::IW5); - REQUIRE(zone->m_variant_id == GameVariantId::IW5_PC32); + REQUIRE(zone->m_variant_id == expectedGameVariantId); REQUIRE(zone->m_name == "SimpleZoneIW5"); REQUIRE(zone->m_pools.GetTotalAssetCount() == 1); REQUIRE(zone->m_pools.GetAsset("SimpleZone.txt")); } + + TEST_CASE("Simple Zone(IW5|pc32)", "[iw5][pc32][system][simple]") + { + PerformTest("SimpleZoneIW5_32", GameVariantId::IW5_PC32); + } + + TEST_CASE("Simple Zone(IW5|pc64)", "[iw5][pc64][system][simple]") + { + PerformTest("SimpleZoneIW5_64", GameVariantId::IW5_PC64); + } } // namespace