Load iw4x zones for iw4

This commit is contained in:
Jan 2021-04-01 20:29:42 +02:00
parent 590a108a1b
commit 1cc5be2f64
6 changed files with 99 additions and 8 deletions

View File

@ -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);

View File

@ -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<char>::length(MAGIC_SIGNED_INFINITY_WARD) == sizeof(ZoneHeader::m_magic));
static_assert(std::char_traits<char>::length(MAGIC_SIGNED_OAT) == sizeof(ZoneHeader::m_magic));
static_assert(std::char_traits<char>::length(MAGIC_UNSIGNED) == sizeof(ZoneHeader::m_magic));
static_assert(std::char_traits<char>::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[]

View File

@ -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<char>::length(ZoneConstants::MAGIC_IW4X)))
{
*isSecure = true;
*isOfficial = true;
if(*reinterpret_cast<uint32_t*>(&header.m_magic[std::char_traits<char>::length(ZoneConstants::MAGIC_IW4X)]) == ZoneConstants::IW4X_ZONE_VERSION)
{
*isSecure = false;
*isOfficial = false;
*isIw4x = true;
return true;
}
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, 8))
return false;
}
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_INFINITY_WARD, std::char_traits<char>::length(ZoneConstants::MAGIC_SIGNED_INFINITY_WARD)))
{
*isSecure = true;
*isOfficial = true;
*isIw4x = false;
return true;
}
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::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<StepAddProcessor>(std::make_unique<ProcessorInflate>(ZoneConstants::AUTHED_CHUNK_SIZE)));
if (isIw4x) // IW4x has one extra byte of padding here for protection purposes
{
zoneLoader->AddLoadingStep(std::make_unique<StepAddProcessor>(std::make_unique<ProcessorIW4xDecryption>()));
zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(1));
}
// Start of the XFile struct
zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(8));
// Skip size and externalSize fields since they are not interesting for us

View File

@ -0,0 +1,45 @@
#include "ProcessorIW4xDecryption.h"
#include <cassert>
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<uint8_t>(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<uint8_t>(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<uint8_t*>(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();
}

View File

@ -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;
};

View File

@ -69,7 +69,7 @@ public:
throw InvalidCompressionException();
}
return m_stream.avail_out;
return length - m_stream.avail_out;
}
};