From 1cc5be2f643c28369f21bcbbf4480eac2fdca62f Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 1 Apr 2021 20:29:42 +0200 Subject: [PATCH] Load iw4x zones for iw4 --- .../AssetDumpers/AssetDumperLocalizeEntry.cpp | 5 +-- src/ZoneCommon/Game/IW4/ZoneConstantsIW4.h | 7 +++ .../Game/IW4/ZoneLoaderFactoryIW4.cpp | 32 +++++++++++-- .../Processor/ProcessorIW4xDecryption.cpp | 45 +++++++++++++++++++ .../Processor/ProcessorIW4xDecryption.h | 16 +++++++ .../Loading/Processor/ProcessorInflate.cpp | 2 +- 6 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 src/ZoneLoading/Loading/Processor/ProcessorIW4xDecryption.cpp create mode 100644 src/ZoneLoading/Loading/Processor/ProcessorIW4xDecryption.h diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLocalizeEntry.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLocalizeEntry.cpp index d06bc22f..af3e26c3 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLocalizeEntry.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLocalizeEntry.cpp @@ -17,13 +17,12 @@ void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool< const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone->m_language); fs::path stringsPath(context.m_base_path); stringsPath.append(language); - stringsPath.append("/localizedstrings"); + stringsPath.append("localizedstrings"); create_directories(stringsPath); auto stringFilePath(stringsPath); - stringFilePath.append(context.m_zone->m_name); - stringFilePath.append(".str"); + stringFilePath.append(context.m_zone->m_name + ".str"); std::ofstream stringFile(stringFilePath, std::fstream::out | std::ofstream::binary); diff --git a/src/ZoneCommon/Game/IW4/ZoneConstantsIW4.h b/src/ZoneCommon/Game/IW4/ZoneConstantsIW4.h index 6330f772..621e91ee 100644 --- a/src/ZoneCommon/Game/IW4/ZoneConstantsIW4.h +++ b/src/ZoneCommon/Game/IW4/ZoneConstantsIW4.h @@ -15,7 +15,14 @@ namespace IW4 static constexpr const char* MAGIC_SIGNED_INFINITY_WARD = "IWff0100"; static constexpr const char* MAGIC_SIGNED_OAT = "ABff0100"; static constexpr const char* MAGIC_UNSIGNED = "IWffu100"; + static constexpr const char* MAGIC_IW4X = "IW4x"; static constexpr int ZONE_VERSION = 276; + static constexpr int IW4X_ZONE_VERSION = 3; + + static_assert(std::char_traits::length(MAGIC_SIGNED_INFINITY_WARD) == sizeof(ZoneHeader::m_magic)); + static_assert(std::char_traits::length(MAGIC_SIGNED_OAT) == sizeof(ZoneHeader::m_magic)); + static_assert(std::char_traits::length(MAGIC_UNSIGNED) == sizeof(ZoneHeader::m_magic)); + static_assert(std::char_traits::length(MAGIC_IW4X) == sizeof(ZoneHeader::m_magic) - sizeof(uint32_t)); static constexpr const char* MAGIC_AUTH_HEADER = "IWffs100"; inline static const uint8_t RSA_PUBLIC_KEY_INFINITY_WARD[] diff --git a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp index a5ba798e..c1e46935 100644 --- a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp +++ b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp @@ -15,6 +15,7 @@ #include "Loading/Processor/ProcessorAuthedBlocks.h" #include "Loading/Processor/ProcessorCaptureData.h" #include "Loading/Processor/ProcessorInflate.h" +#include "Loading/Processor/ProcessorIW4xDecryption.h" #include "Loading/Steps/StepVerifyMagic.h" #include "Loading/Steps/StepSkipBytes.h" #include "Loading/Steps/StepVerifyFileName.h" @@ -36,27 +37,43 @@ class ZoneLoaderFactory::Impl return GameLanguage::LANGUAGE_NONE; } - static bool CanLoad(ZoneHeader& header, bool* isSecure, bool* isOfficial) + static bool CanLoad(ZoneHeader& header, bool* isSecure, bool* isOfficial, bool* isIw4x) { assert(isSecure != nullptr); assert(isOfficial != nullptr); + assert(isIw4x != nullptr); if (header.m_version != ZoneConstants::ZONE_VERSION) { return false; } - if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_INFINITY_WARD, 8)) + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_IW4X, std::char_traits::length(ZoneConstants::MAGIC_IW4X))) + { + if(*reinterpret_cast(&header.m_magic[std::char_traits::length(ZoneConstants::MAGIC_IW4X)]) == ZoneConstants::IW4X_ZONE_VERSION) + { + *isSecure = false; + *isOfficial = false; + *isIw4x = true; + return true; + } + + return false; + } + + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_INFINITY_WARD, std::char_traits::length(ZoneConstants::MAGIC_SIGNED_INFINITY_WARD))) { *isSecure = true; *isOfficial = true; + *isIw4x = false; return true; } - if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, 8)) + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits::length(ZoneConstants::MAGIC_UNSIGNED))) { *isSecure = false; *isOfficial = true; + *isIw4x = false; return true; } @@ -153,9 +170,10 @@ public: { bool isSecure; bool isOfficial; + bool isIw4x; // Check if this file is a supported IW4 zone. - if (!CanLoad(header, &isSecure, &isOfficial)) + if (!CanLoad(header, &isSecure, &isOfficial, &isIw4x)) return nullptr; // Create new zone @@ -180,6 +198,12 @@ public: zoneLoader->AddLoadingStep(std::make_unique(std::make_unique(ZoneConstants::AUTHED_CHUNK_SIZE))); + if (isIw4x) // IW4x has one extra byte of padding here for protection purposes + { + zoneLoader->AddLoadingStep(std::make_unique(std::make_unique())); + zoneLoader->AddLoadingStep(std::make_unique(1)); + } + // Start of the XFile struct zoneLoader->AddLoadingStep(std::make_unique(8)); // Skip size and externalSize fields since they are not interesting for us diff --git a/src/ZoneLoading/Loading/Processor/ProcessorIW4xDecryption.cpp b/src/ZoneLoading/Loading/Processor/ProcessorIW4xDecryption.cpp new file mode 100644 index 00000000..bb66145b --- /dev/null +++ b/src/ZoneLoading/Loading/Processor/ProcessorIW4xDecryption.cpp @@ -0,0 +1,45 @@ +#include "ProcessorIW4xDecryption.h" + +#include + +ProcessorIW4xDecryption::ProcessorIW4xDecryption() + : m_last_byte(0u) +{ +} + +uint8_t ProcessorIW4xDecryption::RotateLeft(const uint8_t value, const unsigned count) +{ + assert(count < sizeof(value) * 8); + return static_cast(value << count | (value >> ((sizeof(value) * 8) - count))); +} + +uint8_t ProcessorIW4xDecryption::RotateRight(uint8_t value, const unsigned count) +{ + assert(count < sizeof(value) * 8); + return static_cast(value >> count | (value << ((sizeof(value) * 8) - count))); +} + +size_t ProcessorIW4xDecryption::Load(void* buffer, const size_t length) +{ + const auto readLen = m_base_stream->Load(buffer, length); + + auto* charBuffer = static_cast(buffer); + for(auto i = 0u; i < readLen; i++) + { + auto value = charBuffer[i]; + value ^= m_last_byte; + value = RotateLeft(value, 4); + value ^= -1; + value = RotateRight(value, 6); + + charBuffer[i] = value; + m_last_byte = value; + } + + return readLen; +} + +int64_t ProcessorIW4xDecryption::Pos() +{ + return m_base_stream->Pos(); +} diff --git a/src/ZoneLoading/Loading/Processor/ProcessorIW4xDecryption.h b/src/ZoneLoading/Loading/Processor/ProcessorIW4xDecryption.h new file mode 100644 index 00000000..6f9acc37 --- /dev/null +++ b/src/ZoneLoading/Loading/Processor/ProcessorIW4xDecryption.h @@ -0,0 +1,16 @@ +#pragma once +#include "Loading/StreamProcessor.h" + +class ProcessorIW4xDecryption final : public StreamProcessor +{ + uint8_t m_last_byte; + + static uint8_t RotateLeft(uint8_t value, unsigned count); + static uint8_t RotateRight(uint8_t value, unsigned count); + +public: + ProcessorIW4xDecryption(); + + size_t Load(void* buffer, size_t length) override; + int64_t Pos() override; +}; diff --git a/src/ZoneLoading/Loading/Processor/ProcessorInflate.cpp b/src/ZoneLoading/Loading/Processor/ProcessorInflate.cpp index 1b2c23f4..2a4c7a52 100644 --- a/src/ZoneLoading/Loading/Processor/ProcessorInflate.cpp +++ b/src/ZoneLoading/Loading/Processor/ProcessorInflate.cpp @@ -69,7 +69,7 @@ public: throw InvalidCompressionException(); } - return m_stream.avail_out; + return length - m_stream.avail_out; } };