refactor: hide implementation details of zone loading processors

This commit is contained in:
Jan 2025-05-02 19:34:51 +01:00
parent eb16dfcd00
commit 955df98279
No known key found for this signature in database
GPG Key ID: 44B581F78FF5C57C
17 changed files with 608 additions and 646 deletions

View File

@ -77,7 +77,7 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
SetupBlock(*zoneLoader); SetupBlock(*zoneLoader);
zoneLoader->AddLoadingStep(std::make_unique<StepAddProcessor>(std::make_unique<ProcessorInflate>(ZoneConstants::AUTHED_CHUNK_SIZE))); zoneLoader->AddLoadingStep(std::make_unique<StepAddProcessor>(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE)));
// Start of the XFile struct // Start of the XFile struct
zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(8)); zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(8));

View File

@ -136,7 +136,7 @@ namespace
zoneLoader.AddLoadingStep(std::make_unique<StepVerifySignature>(std::move(rsa), subHeaderHashSignaturePtr, subHeaderHashPtr)); zoneLoader.AddLoadingStep(std::make_unique<StepVerifySignature>(std::move(rsa), subHeaderHashSignaturePtr, subHeaderHashPtr));
auto subHeaderCapture = std::make_unique<ProcessorCaptureData>(sizeof(DB_AuthSubHeader)); auto subHeaderCapture = processor::CreateProcessorCaptureData(sizeof(DB_AuthSubHeader));
auto* subHeaderCapturePtr = subHeaderCapture.get(); auto* subHeaderCapturePtr = subHeaderCapture.get();
zoneLoader.AddLoadingStep(std::make_unique<StepAddProcessor>(std::move(subHeaderCapture))); zoneLoader.AddLoadingStep(std::make_unique<StepAddProcessor>(std::move(subHeaderCapture)));
@ -155,7 +155,7 @@ namespace
zoneLoader.AddLoadingStep(std::make_unique<StepSkipBytes>(ZoneConstants::AUTHED_CHUNK_SIZE - sizeof(DB_AuthHeader))); zoneLoader.AddLoadingStep(std::make_unique<StepSkipBytes>(ZoneConstants::AUTHED_CHUNK_SIZE - sizeof(DB_AuthHeader)));
zoneLoader.AddLoadingStep(std::make_unique<StepAddProcessor>( zoneLoader.AddLoadingStep(std::make_unique<StepAddProcessor>(
std::make_unique<ProcessorAuthedBlocks>(ZoneConstants::AUTHED_CHUNK_COUNT_PER_GROUP, processor::CreateProcessorAuthedBlocks(ZoneConstants::AUTHED_CHUNK_COUNT_PER_GROUP,
ZoneConstants::AUTHED_CHUNK_SIZE, ZoneConstants::AUTHED_CHUNK_SIZE,
static_cast<unsigned>(std::extent_v<decltype(DB_AuthSubHeader::masterBlockHashes)>), static_cast<unsigned>(std::extent_v<decltype(DB_AuthSubHeader::masterBlockHashes)>),
cryptography::CreateSha256(), cryptography::CreateSha256(),
@ -193,11 +193,11 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
// Add steps for loading the auth header which also contain the signature of the zone if it is signed. // Add steps for loading the auth header which also contain the signature of the zone if it is signed.
AddAuthHeaderSteps(isSecure, isOfficial, *zoneLoader, fileName); AddAuthHeaderSteps(isSecure, isOfficial, *zoneLoader, fileName);
zoneLoader->AddLoadingStep(std::make_unique<StepAddProcessor>(std::make_unique<ProcessorInflate>(ZoneConstants::AUTHED_CHUNK_SIZE))); zoneLoader->AddLoadingStep(std::make_unique<StepAddProcessor>(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE)));
if (isIw4x) // IW4x has one extra byte of padding here for protection purposes 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<StepAddProcessor>(processor::CreateProcessorIW4xDecryption()));
zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(1)); zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(1));
} }

View File

@ -120,7 +120,7 @@ namespace
zoneLoader.AddLoadingStep(std::make_unique<StepVerifySignature>(std::move(rsa), subHeaderHashSignaturePtr, subHeaderHashPtr)); zoneLoader.AddLoadingStep(std::make_unique<StepVerifySignature>(std::move(rsa), subHeaderHashSignaturePtr, subHeaderHashPtr));
auto subHeaderCapture = std::make_unique<ProcessorCaptureData>(sizeof(DB_AuthSubHeader)); auto subHeaderCapture = processor::CreateProcessorCaptureData(sizeof(DB_AuthSubHeader));
auto* subHeaderCapturePtr = subHeaderCapture.get(); auto* subHeaderCapturePtr = subHeaderCapture.get();
zoneLoader.AddLoadingStep(std::make_unique<StepAddProcessor>(std::move(subHeaderCapture))); zoneLoader.AddLoadingStep(std::make_unique<StepAddProcessor>(std::move(subHeaderCapture)));
@ -139,7 +139,7 @@ namespace
zoneLoader.AddLoadingStep(std::make_unique<StepSkipBytes>(ZoneConstants::AUTHED_CHUNK_SIZE - sizeof(DB_AuthHeader))); zoneLoader.AddLoadingStep(std::make_unique<StepSkipBytes>(ZoneConstants::AUTHED_CHUNK_SIZE - sizeof(DB_AuthHeader)));
zoneLoader.AddLoadingStep(std::make_unique<StepAddProcessor>( zoneLoader.AddLoadingStep(std::make_unique<StepAddProcessor>(
std::make_unique<ProcessorAuthedBlocks>(ZoneConstants::AUTHED_CHUNK_COUNT_PER_GROUP, processor::CreateProcessorAuthedBlocks(ZoneConstants::AUTHED_CHUNK_COUNT_PER_GROUP,
ZoneConstants::AUTHED_CHUNK_SIZE, ZoneConstants::AUTHED_CHUNK_SIZE,
static_cast<unsigned>(std::extent_v<decltype(DB_AuthSubHeader::masterBlockHashes)>), static_cast<unsigned>(std::extent_v<decltype(DB_AuthSubHeader::masterBlockHashes)>),
cryptography::CreateSha256(), cryptography::CreateSha256(),
@ -176,7 +176,7 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
// Add steps for loading the auth header which also contain the signature of the zone if it is signed. // Add steps for loading the auth header which also contain the signature of the zone if it is signed.
AddAuthHeaderSteps(isSecure, isOfficial, *zoneLoader, fileName); AddAuthHeaderSteps(isSecure, isOfficial, *zoneLoader, fileName);
zoneLoader->AddLoadingStep(std::make_unique<StepAddProcessor>(std::make_unique<ProcessorInflate>(ZoneConstants::AUTHED_CHUNK_SIZE))); zoneLoader->AddLoadingStep(std::make_unique<StepAddProcessor>(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE)));
// Start of the XFile struct // Start of the XFile struct
zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(8)); zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(8));

View File

@ -77,7 +77,7 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
SetupBlock(*zoneLoader); SetupBlock(*zoneLoader);
zoneLoader->AddLoadingStep(std::make_unique<StepAddProcessor>(std::make_unique<ProcessorInflate>(ZoneConstants::AUTHED_CHUNK_SIZE))); zoneLoader->AddLoadingStep(std::make_unique<StepAddProcessor>(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE)));
// Start of the XFile struct // Start of the XFile struct
zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(8)); zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(8));

View File

@ -148,7 +148,7 @@ namespace
ICapturedDataProvider* AddXChunkProcessor(const bool isEncrypted, ZoneLoader& zoneLoader, std::string& fileName) ICapturedDataProvider* AddXChunkProcessor(const bool isEncrypted, ZoneLoader& zoneLoader, std::string& fileName)
{ {
ICapturedDataProvider* result = nullptr; ICapturedDataProvider* result = nullptr;
auto xChunkProcessor = std::make_unique<ProcessorXChunks>(ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, ZoneConstants::VANILLA_BUFFER_SIZE); auto xChunkProcessor = processor::CreateProcessorXChunks(ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, ZoneConstants::VANILLA_BUFFER_SIZE);
if (isEncrypted) if (isEncrypted)
{ {

View File

@ -5,39 +5,19 @@
#include "Loading/Exception/TooManyAuthedGroupsException.h" #include "Loading/Exception/TooManyAuthedGroupsException.h"
#include "Loading/Exception/UnexpectedEndOfFileException.h" #include "Loading/Exception/UnexpectedEndOfFileException.h"
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstring>
#include <memory> #include <memory>
class ProcessorAuthedBlocks::Impl class ProcessorAuthedBlocks final : public StreamProcessor
{ {
ProcessorAuthedBlocks* const m_base;
const unsigned m_authed_chunk_count;
const size_t m_chunk_size;
const unsigned m_max_master_block_count;
const std::unique_ptr<cryptography::IHashFunction> m_hash_function;
IHashProvider* const m_master_block_hash_provider;
const std::unique_ptr<uint8_t[]> m_chunk_hashes_buffer;
const std::unique_ptr<uint8_t[]> m_current_chunk_hash_buffer;
const std::unique_ptr<uint8_t[]> m_chunk_buffer;
unsigned m_current_group;
unsigned m_current_chunk_in_group;
size_t m_current_chunk_offset;
size_t m_current_chunk_size;
public: public:
Impl(ProcessorAuthedBlocks* base, ProcessorAuthedBlocks(const unsigned authedChunkCount,
const unsigned authedChunkCount,
const size_t chunkSize, const size_t chunkSize,
const unsigned maxMasterBlockCount, const unsigned maxMasterBlockCount,
std::unique_ptr<cryptography::IHashFunction> hashFunction, std::unique_ptr<cryptography::IHashFunction> hashFunction,
IHashProvider* masterBlockHashProvider) IHashProvider* masterBlockHashProvider)
: m_base(base), : m_authed_chunk_count(authedChunkCount),
m_authed_chunk_count(authedChunkCount),
m_chunk_size(chunkSize), m_chunk_size(chunkSize),
m_max_master_block_count(maxMasterBlockCount), m_max_master_block_count(maxMasterBlockCount),
m_hash_function(std::move(hashFunction)), m_hash_function(std::move(hashFunction)),
@ -53,13 +33,43 @@ public:
assert(m_authed_chunk_count * m_hash_function->GetHashSize() <= m_chunk_size); assert(m_authed_chunk_count * m_hash_function->GetHashSize() <= m_chunk_size);
} }
size_t Load(void* buffer, const size_t length) override
{
size_t loadedSize = 0;
while (loadedSize < length)
{
if (m_current_chunk_offset >= m_current_chunk_size)
{
if (!NextChunk())
return loadedSize;
}
auto sizeToWrite = length - loadedSize;
sizeToWrite = std::min(sizeToWrite, m_current_chunk_size - m_current_chunk_offset);
assert(length - loadedSize >= sizeToWrite);
memcpy(&static_cast<uint8_t*>(buffer)[loadedSize], &m_chunk_buffer[m_current_chunk_offset], sizeToWrite);
loadedSize += sizeToWrite;
m_current_chunk_offset += sizeToWrite;
}
return loadedSize;
}
int64_t Pos() override
{
return m_base_stream->Pos() - static_cast<int64_t>(m_current_chunk_size - m_current_chunk_offset);
}
private:
bool NextChunk() bool NextChunk()
{ {
m_current_chunk_offset = 0; m_current_chunk_offset = 0;
while (true) while (true)
{ {
m_current_chunk_size = m_base->m_base_stream->Load(m_chunk_buffer.get(), m_chunk_size); m_current_chunk_size = m_base_stream->Load(m_chunk_buffer.get(), m_chunk_size);
if (m_current_chunk_size == 0) if (m_current_chunk_size == 0)
return false; return false;
@ -79,7 +89,9 @@ public:
if (masterBlockHashSize != m_hash_function->GetHashSize() if (masterBlockHashSize != m_hash_function->GetHashSize()
|| std::memcmp(m_current_chunk_hash_buffer.get(), masterBlockHash, m_hash_function->GetHashSize()) != 0) || std::memcmp(m_current_chunk_hash_buffer.get(), masterBlockHash, m_hash_function->GetHashSize()) != 0)
{
throw InvalidHashException(); throw InvalidHashException();
}
memcpy(m_chunk_hashes_buffer.get(), m_chunk_buffer.get(), m_authed_chunk_count * m_hash_function->GetHashSize()); memcpy(m_chunk_hashes_buffer.get(), m_chunk_buffer.get(), m_authed_chunk_count * m_hash_function->GetHashSize());
@ -91,7 +103,9 @@ public:
&m_chunk_hashes_buffer[(m_current_chunk_in_group - 1) * m_hash_function->GetHashSize()], &m_chunk_hashes_buffer[(m_current_chunk_in_group - 1) * m_hash_function->GetHashSize()],
m_hash_function->GetHashSize()) m_hash_function->GetHashSize())
!= 0) != 0)
{
throw InvalidHashException(); throw InvalidHashException();
}
if (++m_current_chunk_in_group > m_authed_chunk_count) if (++m_current_chunk_in_group > m_authed_chunk_count)
{ {
@ -107,58 +121,31 @@ public:
} }
} }
size_t Load(void* buffer, const size_t length) const unsigned m_authed_chunk_count;
{ const size_t m_chunk_size;
size_t loadedSize = 0; const unsigned m_max_master_block_count;
while (loadedSize < length) const std::unique_ptr<cryptography::IHashFunction> m_hash_function;
{ IHashProvider* const m_master_block_hash_provider;
if (m_current_chunk_offset >= m_current_chunk_size) const std::unique_ptr<uint8_t[]> m_chunk_hashes_buffer;
{ const std::unique_ptr<uint8_t[]> m_current_chunk_hash_buffer;
if (!NextChunk())
return loadedSize;
}
size_t sizeToWrite = length - loadedSize; const std::unique_ptr<uint8_t[]> m_chunk_buffer;
if (sizeToWrite > m_current_chunk_size - m_current_chunk_offset) unsigned m_current_group;
sizeToWrite = m_current_chunk_size - m_current_chunk_offset; unsigned m_current_chunk_in_group;
assert(length - loadedSize >= sizeToWrite); size_t m_current_chunk_offset;
memcpy(&static_cast<uint8_t*>(buffer)[loadedSize], &m_chunk_buffer[m_current_chunk_offset], sizeToWrite); size_t m_current_chunk_size;
loadedSize += sizeToWrite;
m_current_chunk_offset += sizeToWrite;
}
return loadedSize;
}
int64_t Pos()
{
return m_base->m_base_stream->Pos() - (m_current_chunk_size - m_current_chunk_offset);
}
}; };
ProcessorAuthedBlocks::ProcessorAuthedBlocks(const unsigned authedChunkCount, namespace processor
const size_t chunkSize, {
const unsigned maxMasterBlockCount, std::unique_ptr<StreamProcessor> CreateProcessorAuthedBlocks(unsigned authedChunkCount,
size_t chunkSize,
unsigned maxMasterBlockCount,
std::unique_ptr<cryptography::IHashFunction> hashFunction, std::unique_ptr<cryptography::IHashFunction> hashFunction,
IHashProvider* masterBlockHashProvider) IHashProvider* masterBlockHashProvider)
: m_impl(new Impl(this, authedChunkCount, chunkSize, maxMasterBlockCount, std::move(hashFunction), masterBlockHashProvider))
{ {
return std::make_unique<ProcessorAuthedBlocks>(authedChunkCount, chunkSize, maxMasterBlockCount, std::move(hashFunction), masterBlockHashProvider);
} }
} // namespace processor
ProcessorAuthedBlocks::~ProcessorAuthedBlocks()
{
delete m_impl;
m_impl = nullptr;
}
size_t ProcessorAuthedBlocks::Load(void* buffer, const size_t length)
{
return m_impl->Load(buffer, length);
}
int64_t ProcessorAuthedBlocks::Pos()
{
return m_impl->Pos();
}

View File

@ -6,23 +6,11 @@
#include <memory> #include <memory>
class ProcessorAuthedBlocks final : public StreamProcessor namespace processor
{ {
class Impl; std::unique_ptr<StreamProcessor> CreateProcessorAuthedBlocks(unsigned authedChunkCount,
Impl* m_impl;
public:
ProcessorAuthedBlocks(unsigned authedChunkCount,
size_t chunkSize, size_t chunkSize,
unsigned maxMasterBlockCount, unsigned maxMasterBlockCount,
std::unique_ptr<cryptography::IHashFunction> hashFunction, std::unique_ptr<cryptography::IHashFunction> hashFunction,
IHashProvider* masterBlockHashProvider); IHashProvider* masterBlockHashProvider);
~ProcessorAuthedBlocks() override; }
ProcessorAuthedBlocks(const ProcessorAuthedBlocks& other) = delete;
ProcessorAuthedBlocks(ProcessorAuthedBlocks&& other) noexcept = default;
ProcessorAuthedBlocks& operator=(const ProcessorAuthedBlocks& other) = delete;
ProcessorAuthedBlocks& operator=(ProcessorAuthedBlocks&& other) noexcept = default;
size_t Load(void* buffer, size_t length) override;
int64_t Pos() override;
};

View File

@ -1,28 +1,29 @@
#include "ProcessorCaptureData.h" #include "ProcessorCaptureData.h"
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstring>
ProcessorCaptureData::ProcessorCaptureData(const size_t captureSize) namespace
{
class ProcessorCaptureData final : public processor::IProcessorCaptureData
{
public:
explicit ProcessorCaptureData(const size_t captureSize)
: m_data(std::make_unique<uint8_t[]>(captureSize)), : m_data(std::make_unique<uint8_t[]>(captureSize)),
m_capture_size(captureSize), m_capture_size(captureSize),
m_captured_data_size(0) m_captured_data_size(0)
{ {
} }
ProcessorCaptureData::~ProcessorCaptureData() = default; size_t Load(void* buffer, const size_t length) override
size_t ProcessorCaptureData::Load(void* buffer, const size_t length)
{ {
if (m_captured_data_size >= m_capture_size) if (m_captured_data_size >= m_capture_size)
return m_base_stream->Load(buffer, length); return m_base_stream->Load(buffer, length);
size_t dataToCapture = m_capture_size - m_captured_data_size; auto dataToCapture = m_capture_size - m_captured_data_size;
dataToCapture = std::min(length, dataToCapture);
if (length < dataToCapture) auto loadedSize = m_base_stream->Load(&m_data[m_captured_data_size], dataToCapture);
dataToCapture = length;
size_t loadedSize = m_base_stream->Load(&m_data[m_captured_data_size], dataToCapture);
assert(length >= loadedSize); assert(length >= loadedSize);
memcpy(buffer, &m_data[m_captured_data_size], loadedSize); memcpy(buffer, &m_data[m_captured_data_size], loadedSize);
@ -34,12 +35,12 @@ size_t ProcessorCaptureData::Load(void* buffer, const size_t length)
return loadedSize; return loadedSize;
} }
int64_t ProcessorCaptureData::Pos() int64_t Pos() override
{ {
return m_base_stream->Pos(); return m_base_stream->Pos();
} }
void ProcessorCaptureData::GetCapturedData(const uint8_t** pCapturedData, size_t* pSize) void GetCapturedData(const uint8_t** pCapturedData, size_t* pSize) override
{ {
assert(pCapturedData != nullptr); assert(pCapturedData != nullptr);
assert(pSize != nullptr); assert(pSize != nullptr);
@ -49,3 +50,18 @@ void ProcessorCaptureData::GetCapturedData(const uint8_t** pCapturedData, size_t
*pCapturedData = m_data.get(); *pCapturedData = m_data.get();
*pSize = m_captured_data_size; *pSize = m_captured_data_size;
} }
private:
std::unique_ptr<uint8_t[]> m_data;
size_t m_capture_size;
size_t m_captured_data_size;
};
} // namespace
namespace processor
{
std::unique_ptr<IProcessorCaptureData> CreateProcessorCaptureData(size_t captureSize)
{
return std::make_unique<ProcessorCaptureData>(captureSize);
}
} // namespace processor

View File

@ -1,20 +1,15 @@
#pragma once #pragma once
#include "Loading/StreamProcessor.h" #include "Loading/StreamProcessor.h"
#include "Utils/ICapturedDataProvider.h" #include "Utils/ICapturedDataProvider.h"
#include <memory> #include <memory>
class ProcessorCaptureData final : public StreamProcessor, public ICapturedDataProvider namespace processor
{
class IProcessorCaptureData : public StreamProcessor, public ICapturedDataProvider
{ {
std::unique_ptr<uint8_t[]> m_data;
const size_t m_capture_size;
size_t m_captured_data_size;
public:
explicit ProcessorCaptureData(size_t captureSize);
~ProcessorCaptureData() override;
size_t Load(void* buffer, size_t length) override;
int64_t Pos() override;
void GetCapturedData(const uint8_t** pCapturedData, size_t* pSize) override;
}; };
std::unique_ptr<IProcessorCaptureData> CreateProcessorCaptureData(size_t captureSize);
} // namespace processor

View File

@ -2,24 +2,29 @@
#include <cassert> #include <cassert>
ProcessorIW4xDecryption::ProcessorIW4xDecryption() namespace
: m_last_byte(0u)
{ {
} uint8_t RotateLeft(const uint8_t value, const unsigned count)
uint8_t ProcessorIW4xDecryption::RotateLeft(const uint8_t value, const unsigned count)
{ {
assert(count < sizeof(value) * 8); assert(count < sizeof(value) * 8);
return static_cast<uint8_t>(value << count | (value >> ((sizeof(value) * 8) - count))); return static_cast<uint8_t>(value << count | (value >> ((sizeof(value) * 8) - count)));
} }
uint8_t ProcessorIW4xDecryption::RotateRight(uint8_t value, const unsigned count) uint8_t RotateRight(const uint8_t value, const unsigned count)
{ {
assert(count < sizeof(value) * 8); assert(count < sizeof(value) * 8);
return static_cast<uint8_t>(value >> count | (value << ((sizeof(value) * 8) - count))); return static_cast<uint8_t>(value >> count | (value << ((sizeof(value) * 8) - count)));
} }
size_t ProcessorIW4xDecryption::Load(void* buffer, const size_t length) class ProcessorIW4xDecryption final : public StreamProcessor
{
public:
ProcessorIW4xDecryption()
: m_last_byte(0u)
{
}
size_t Load(void* buffer, const size_t length) override
{ {
const auto readLen = m_base_stream->Load(buffer, length); const auto readLen = m_base_stream->Load(buffer, length);
@ -39,7 +44,20 @@ size_t ProcessorIW4xDecryption::Load(void* buffer, const size_t length)
return readLen; return readLen;
} }
int64_t ProcessorIW4xDecryption::Pos() int64_t Pos() override
{ {
return m_base_stream->Pos(); return m_base_stream->Pos();
} }
private:
uint8_t m_last_byte;
};
} // namespace
namespace processor
{
std::unique_ptr<StreamProcessor> CreateProcessorIW4xDecryption()
{
return std::make_unique<ProcessorIW4xDecryption>();
}
} // namespace processor

View File

@ -1,16 +1,10 @@
#pragma once #pragma once
#include "Loading/StreamProcessor.h" #include "Loading/StreamProcessor.h"
class ProcessorIW4xDecryption final : public StreamProcessor #include <memory>
namespace processor
{ {
uint8_t m_last_byte; std::unique_ptr<StreamProcessor> CreateProcessorIW4xDecryption();
}
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

@ -8,21 +8,17 @@
#include <zlib.h> #include <zlib.h>
#include <zutil.h> #include <zutil.h>
class ProcessorInflate::Impl namespace
{ {
z_stream m_stream{}; constexpr size_t DEFAULT_BUFFER_SIZE = 0x2000;
ProcessorInflate* m_base;
std::unique_ptr<uint8_t[]> m_buffer;
size_t m_buffer_size;
class ProcessorInflate final : public StreamProcessor
{
public: public:
Impl(ProcessorInflate* baseClass, const size_t bufferSize) explicit ProcessorInflate(const size_t bufferSize)
: m_buffer(std::make_unique<uint8_t[]>(bufferSize)), : m_buffer(std::make_unique<uint8_t[]>(bufferSize)),
m_buffer_size(bufferSize) m_buffer_size(bufferSize)
{ {
m_base = baseClass;
m_stream.zalloc = Z_NULL; m_stream.zalloc = Z_NULL;
m_stream.zfree = Z_NULL; m_stream.zfree = Z_NULL;
m_stream.opaque = Z_NULL; m_stream.opaque = Z_NULL;
@ -32,22 +28,20 @@ public:
const int ret = inflateInit(&m_stream); const int ret = inflateInit(&m_stream);
if (ret != Z_OK) if (ret != Z_OK)
{
throw std::runtime_error("Initializing inflate failed"); throw std::runtime_error("Initializing inflate failed");
} }
}
~Impl() ~ProcessorInflate() override
{ {
inflateEnd(&m_stream); inflateEnd(&m_stream);
} }
Impl(const Impl& other) = delete; ProcessorInflate(const ProcessorInflate& other) = delete;
Impl(Impl&& other) noexcept = default; ProcessorInflate(ProcessorInflate&& other) noexcept = default;
Impl& operator=(const Impl& other) = delete; ProcessorInflate& operator=(const ProcessorInflate& other) = delete;
Impl& operator=(Impl&& other) noexcept = default; ProcessorInflate& operator=(ProcessorInflate&& other) noexcept = default;
size_t Load(void* buffer, const size_t length) size_t Load(void* buffer, const size_t length) override
{ {
m_stream.next_out = static_cast<Bytef*>(buffer); m_stream.next_out = static_cast<Bytef*>(buffer);
m_stream.avail_out = static_cast<unsigned>(length); m_stream.avail_out = static_cast<unsigned>(length);
@ -56,7 +50,7 @@ public:
{ {
if (m_stream.avail_in == 0) if (m_stream.avail_in == 0)
{ {
m_stream.avail_in = static_cast<unsigned>(m_base->m_base_stream->Load(m_buffer.get(), m_buffer_size)); m_stream.avail_in = static_cast<unsigned>(m_base_stream->Load(m_buffer.get(), m_buffer_size));
m_stream.next_in = m_buffer.get(); m_stream.next_in = m_buffer.get();
if (m_stream.avail_in == 0) // EOF if (m_stream.avail_in == 0) // EOF
@ -71,30 +65,28 @@ public:
return length - m_stream.avail_out; return length - m_stream.avail_out;
} }
};
ProcessorInflate::ProcessorInflate() int64_t Pos() override
: ProcessorInflate(DEFAULT_BUFFER_SIZE)
{
}
ProcessorInflate::ProcessorInflate(const size_t bufferSize)
: m_impl(new Impl(this, bufferSize))
{
}
ProcessorInflate::~ProcessorInflate()
{
delete m_impl;
m_impl = nullptr;
}
size_t ProcessorInflate::Load(void* buffer, const size_t length)
{
return m_impl->Load(buffer, length);
}
int64_t ProcessorInflate::Pos()
{ {
return m_base_stream->Pos(); return m_base_stream->Pos();
} }
private:
z_stream m_stream{};
std::unique_ptr<uint8_t[]> m_buffer;
size_t m_buffer_size;
};
} // namespace
namespace processor
{
std::unique_ptr<StreamProcessor> CreateProcessorInflate()
{
return std::make_unique<ProcessorInflate>(DEFAULT_BUFFER_SIZE);
}
std::unique_ptr<StreamProcessor> CreateProcessorInflate(size_t bufferSize)
{
return std::make_unique<ProcessorInflate>(bufferSize);
}
} // namespace processor

View File

@ -1,22 +1,11 @@
#pragma once #pragma once
#include "Loading/StreamProcessor.h" #include "Loading/StreamProcessor.h"
class ProcessorInflate final : public StreamProcessor #include <memory>
namespace processor
{ {
class Impl; std::unique_ptr<StreamProcessor> CreateProcessorInflate();
Impl* m_impl; std::unique_ptr<StreamProcessor> CreateProcessorInflate(size_t bufferSize);
} // namespace processor
static constexpr size_t DEFAULT_BUFFER_SIZE = 0x2000;
public:
ProcessorInflate();
ProcessorInflate(size_t bufferSize);
~ProcessorInflate() override;
ProcessorInflate(const ProcessorInflate& other) = delete;
ProcessorInflate(ProcessorInflate&& other) noexcept = default;
ProcessorInflate& operator=(const ProcessorInflate& other) = delete;
ProcessorInflate& operator=(ProcessorInflate&& other) noexcept = default;
size_t Load(void* buffer, size_t length) override;
int64_t Pos() override;
};

View File

@ -1,11 +1,16 @@
#include "ProcessorStreamCipher.h" #include "ProcessorStreamCipher.h"
ProcessorStreamCipher::ProcessorStreamCipher(std::unique_ptr<cryptography::IStreamCipher> cipher) namespace
{
class ProcessorStreamCipher final : public StreamProcessor
{
public:
explicit ProcessorStreamCipher(std::unique_ptr<cryptography::IStreamCipher> cipher)
: m_cipher(std::move(cipher)) : m_cipher(std::move(cipher))
{ {
} }
size_t ProcessorStreamCipher::Load(void* buffer, const size_t length) size_t Load(void* buffer, const size_t length) override
{ {
if (m_base_stream != nullptr) if (m_base_stream != nullptr)
{ {
@ -19,3 +24,21 @@ size_t ProcessorStreamCipher::Load(void* buffer, const size_t length)
return 0; return 0;
} }
int64_t Pos() override
{
return m_base_stream->Pos();
}
private:
std::unique_ptr<cryptography::IStreamCipher> m_cipher;
};
} // namespace
namespace processor
{
std::unique_ptr<StreamProcessor> CreateProcessorStreamCipher(std::unique_ptr<cryptography::IStreamCipher> cipher)
{
return std::make_unique<ProcessorStreamCipher>(std::move(cipher));
}
} // namespace processor

View File

@ -5,13 +5,7 @@
#include <memory> #include <memory>
class ProcessorStreamCipher final : public StreamProcessor namespace processor
{ {
public: std::unique_ptr<StreamProcessor> CreateProcessorStreamCipher(std::unique_ptr<cryptography::IStreamCipher> cipher);
explicit ProcessorStreamCipher(std::unique_ptr<cryptography::IStreamCipher> cipher); }
size_t Load(void* buffer, size_t length) override;
private:
std::unique_ptr<cryptography::IStreamCipher> m_cipher;
};

View File

@ -5,36 +5,78 @@
#include <cassert> #include <cassert>
#include <condition_variable> #include <condition_variable>
#include <cstring>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <optional>
#include <thread> #include <thread>
#include <vector> #include <vector>
class DBLoadStream namespace
{ {
int m_index; class DbLoadStream
{
public:
DbLoadStream(const int streamIndex, const size_t chunkSize, std::vector<std::unique_ptr<IXChunkProcessor>>& chunkProcessors)
: m_index(streamIndex),
m_input_size(0),
m_output_size(0),
m_chunk_size(chunkSize),
m_is_loading(false),
m_processors(chunkProcessors)
{
for (auto& buffer : m_buffers)
buffer = std::make_unique<uint8_t[]>(chunkSize);
std::unique_ptr<uint8_t[]> m_buffers[2]; m_input_buffer = m_buffers[0].get();
m_output_buffer = m_buffers[1].get();
}
uint8_t* m_input_buffer; [[nodiscard]] uint8_t* GetInputBuffer() const
size_t m_input_size; {
return m_input_buffer;
}
uint8_t* m_output_buffer; void StartLoading(const size_t inputSize)
size_t m_output_size; {
if (inputSize > 0)
{
std::unique_lock lock(m_load_mutex);
size_t m_chunk_size; if (m_is_loading)
{
m_loading_finished.wait(lock);
}
bool m_is_loading; m_input_size = inputSize;
std::mutex m_load_mutex; m_is_loading = true;
std::condition_variable m_loading_finished; m_load_thread = std::thread(&DbLoadStream::Load, this);
std::thread m_load_thread; m_load_thread.detach();
}
else
{
m_output_size = 0;
}
}
std::vector<std::unique_ptr<IXChunkProcessor>>& m_processors; void GetOutput(const uint8_t** pBuffer, size_t* pSize)
{
assert(pBuffer != nullptr);
assert(pSize != nullptr);
std::unique_lock lock(m_load_mutex);
if (m_is_loading)
{
m_loading_finished.wait(lock);
}
*pBuffer = m_output_buffer;
*pSize = m_output_size;
}
private:
void Load() void Load()
{ {
std::lock_guard<std::mutex> lock(m_load_mutex); std::lock_guard lock(m_load_mutex);
bool firstProcessor = true; bool firstProcessor = true;
@ -59,208 +101,51 @@ class DBLoadStream
m_loading_finished.notify_all(); m_loading_finished.notify_all();
} }
public: int m_index;
DBLoadStream(const int streamIndex, const size_t chunkSize, std::vector<std::unique_ptr<IXChunkProcessor>>& chunkProcessors)
: m_processors(chunkProcessors)
{
m_index = streamIndex;
m_chunk_size = chunkSize;
for (auto& buffer : m_buffers) std::unique_ptr<uint8_t[]> m_buffers[2];
buffer = std::make_unique<uint8_t[]>(chunkSize);
m_input_buffer = m_buffers[0].get(); uint8_t* m_input_buffer;
m_output_buffer = m_buffers[1].get(); size_t m_input_size;
m_input_size = 0; uint8_t* m_output_buffer;
m_output_size = 0; size_t m_output_size;
m_is_loading = false; size_t m_chunk_size;
}
[[nodiscard]] uint8_t* GetInputBuffer() const bool m_is_loading;
{ std::mutex m_load_mutex;
return m_input_buffer; std::condition_variable m_loading_finished;
} std::thread m_load_thread;
void StartLoading(const size_t inputSize) std::vector<std::unique_ptr<IXChunkProcessor>>& m_processors;
{
if (inputSize > 0)
{
std::unique_lock<std::mutex> lock(m_load_mutex);
if (m_is_loading)
{
m_loading_finished.wait(lock);
}
m_input_size = inputSize;
m_is_loading = true;
m_load_thread = std::thread(&DBLoadStream::Load, this);
m_load_thread.detach();
}
else
{
m_output_size = 0;
}
}
void GetOutput(const uint8_t** pBuffer, size_t* pSize)
{
assert(pBuffer != nullptr);
assert(pSize != nullptr);
std::unique_lock<std::mutex> lock(m_load_mutex);
if (m_is_loading)
{
m_loading_finished.wait(lock);
}
*pBuffer = m_output_buffer;
*pSize = m_output_size;
}
}; };
class ProcessorXChunks::ProcessorXChunksImpl class ProcessorXChunks final : public processor::IProcessorXChunks
{ {
ProcessorXChunks* m_base;
std::vector<std::unique_ptr<DBLoadStream>> m_streams;
size_t m_chunk_size;
size_t m_vanilla_buffer_size;
std::vector<std::unique_ptr<IXChunkProcessor>> m_chunk_processors;
bool m_initialized_streams;
unsigned int m_current_stream;
const uint8_t* m_current_chunk;
size_t m_current_chunk_size;
size_t m_current_chunk_offset;
size_t m_vanilla_buffer_offset;
bool m_eof_reached;
unsigned int m_eof_stream;
void AdvanceStream(const unsigned streamNum)
{
assert(streamNum < m_streams.size());
if (m_eof_reached)
return;
xchunk_size_t chunkSize;
if (m_vanilla_buffer_size > 0)
{
if (m_vanilla_buffer_offset + sizeof(chunkSize) > m_vanilla_buffer_size)
{
m_base->m_base_stream->Load(&chunkSize, m_vanilla_buffer_size - m_vanilla_buffer_offset);
m_vanilla_buffer_offset = 0;
}
m_vanilla_buffer_offset = (m_vanilla_buffer_offset + sizeof(chunkSize)) % m_vanilla_buffer_size;
}
const size_t readSize = m_base->m_base_stream->Load(&chunkSize, sizeof(chunkSize));
if (readSize == 0)
{
m_eof_reached = true;
m_eof_stream = streamNum;
return;
}
if (chunkSize > m_chunk_size)
{
throw InvalidChunkSizeException(chunkSize, m_chunk_size);
}
const auto& stream = m_streams[streamNum];
const size_t loadedChunkSize = m_base->m_base_stream->Load(stream->GetInputBuffer(), chunkSize);
if (loadedChunkSize != chunkSize)
{
throw InvalidChunkSizeException(chunkSize);
}
if (m_vanilla_buffer_size > 0)
{
m_vanilla_buffer_offset = (m_vanilla_buffer_offset + loadedChunkSize) % m_vanilla_buffer_size;
}
stream->StartLoading(loadedChunkSize);
}
void NextStream()
{
AdvanceStream(m_current_stream);
m_current_stream = (m_current_stream + 1) % m_streams.size();
m_current_chunk_offset = 0;
m_streams[m_current_stream]->GetOutput(&m_current_chunk, &m_current_chunk_size);
}
void InitStreams()
{
m_initialized_streams = true;
m_vanilla_buffer_offset = static_cast<size_t>(m_base->m_base_stream->Pos());
const auto streamCount = static_cast<unsigned>(m_streams.size());
for (auto streamNum = 0u; streamNum < streamCount; streamNum++)
{
AdvanceStream(streamNum);
}
m_current_stream = 0;
m_current_chunk_offset = 0;
m_streams[0]->GetOutput(&m_current_chunk, &m_current_chunk_size);
}
[[nodiscard]] bool EndOfStream() const
{
return m_eof_reached && m_eof_stream == m_current_stream;
}
public: public:
ProcessorXChunksImpl(ProcessorXChunks* base, const int numStreams, const size_t xChunkSize) ProcessorXChunks(const int numStreams, const size_t xChunkSize, const std::optional<size_t> vanillaBufferSize)
: m_chunk_size(xChunkSize),
m_vanilla_buffer_size(vanillaBufferSize),
m_initialized_streams(false),
m_current_stream(0),
m_current_chunk(nullptr),
m_current_chunk_size(0),
m_current_chunk_offset(0),
m_vanilla_buffer_offset(0),
m_eof_reached(false),
m_eof_stream(0)
{ {
assert(base != nullptr);
assert(numStreams > 0); assert(numStreams > 0);
assert(xChunkSize > 0); assert(xChunkSize > 0);
m_base = base;
for (int streamIndex = 0; streamIndex < numStreams; streamIndex++) for (int streamIndex = 0; streamIndex < numStreams; streamIndex++)
{ {
m_streams.emplace_back(std::make_unique<DBLoadStream>(streamIndex, xChunkSize, m_chunk_processors)); m_streams.emplace_back(std::make_unique<DbLoadStream>(streamIndex, xChunkSize, m_chunk_processors));
}
} }
m_chunk_size = xChunkSize; size_t Load(void* buffer, const size_t length) override
m_vanilla_buffer_size = 0;
m_initialized_streams = false;
m_current_stream = 0;
m_current_chunk = nullptr;
m_current_chunk_size = 0;
m_current_chunk_offset = 0;
m_vanilla_buffer_offset = 0;
m_eof_reached = false;
m_eof_stream = 0;
}
ProcessorXChunksImpl(ProcessorXChunks* base, const int numStreams, const size_t xChunkSize, const size_t vanillaBufferSize)
: ProcessorXChunksImpl(base, numStreams, xChunkSize)
{
m_vanilla_buffer_size = vanillaBufferSize;
}
void AddChunkProcessor(std::unique_ptr<IXChunkProcessor> streamProcessor)
{
assert(streamProcessor != nullptr);
m_chunk_processors.emplace_back(std::move(streamProcessor));
}
size_t Load(void* buffer, const size_t length)
{ {
assert(buffer != nullptr); assert(buffer != nullptr);
@ -300,39 +185,124 @@ public:
return loadedSize; return loadedSize;
} }
int64_t Pos() const int64_t Pos() override
{ {
return m_base->m_base_stream->Pos(); return m_base_stream->Pos();
} }
void AddChunkProcessor(std::unique_ptr<IXChunkProcessor> chunkProcessor) override
{
assert(chunkProcessor);
m_chunk_processors.emplace_back(std::move(chunkProcessor));
}
private:
void AdvanceStream(const unsigned streamNum)
{
assert(streamNum < m_streams.size());
if (m_eof_reached)
return;
xchunk_size_t chunkSize;
if (m_vanilla_buffer_size.has_value())
{
if (m_vanilla_buffer_offset + sizeof(chunkSize) > *m_vanilla_buffer_size)
{
m_base_stream->Load(&chunkSize, *m_vanilla_buffer_size - m_vanilla_buffer_offset);
m_vanilla_buffer_offset = 0;
}
m_vanilla_buffer_offset = (m_vanilla_buffer_offset + sizeof(chunkSize)) % *m_vanilla_buffer_size;
}
const size_t readSize = m_base_stream->Load(&chunkSize, sizeof(chunkSize));
if (readSize == 0)
{
m_eof_reached = true;
m_eof_stream = streamNum;
return;
}
if (chunkSize > m_chunk_size)
{
throw InvalidChunkSizeException(chunkSize, m_chunk_size);
}
const auto& stream = m_streams[streamNum];
const size_t loadedChunkSize = m_base_stream->Load(stream->GetInputBuffer(), chunkSize);
if (loadedChunkSize != chunkSize)
{
throw InvalidChunkSizeException(chunkSize);
}
if (m_vanilla_buffer_size.has_value())
{
m_vanilla_buffer_offset = (m_vanilla_buffer_offset + loadedChunkSize) % *m_vanilla_buffer_size;
}
stream->StartLoading(loadedChunkSize);
}
void NextStream()
{
AdvanceStream(m_current_stream);
m_current_stream = (m_current_stream + 1) % m_streams.size();
m_current_chunk_offset = 0;
m_streams[m_current_stream]->GetOutput(&m_current_chunk, &m_current_chunk_size);
}
void InitStreams()
{
m_initialized_streams = true;
m_vanilla_buffer_offset = static_cast<size_t>(m_base_stream->Pos());
const auto streamCount = static_cast<unsigned>(m_streams.size());
for (auto streamNum = 0u; streamNum < streamCount; streamNum++)
{
AdvanceStream(streamNum);
}
m_current_stream = 0;
m_current_chunk_offset = 0;
m_streams[0]->GetOutput(&m_current_chunk, &m_current_chunk_size);
}
[[nodiscard]] bool EndOfStream() const
{
return m_eof_reached && m_eof_stream == m_current_stream;
}
std::vector<std::unique_ptr<DbLoadStream>> m_streams;
size_t m_chunk_size;
std::optional<size_t> m_vanilla_buffer_size;
std::vector<std::unique_ptr<IXChunkProcessor>> m_chunk_processors;
bool m_initialized_streams;
unsigned int m_current_stream;
const uint8_t* m_current_chunk;
size_t m_current_chunk_size;
size_t m_current_chunk_offset;
size_t m_vanilla_buffer_offset;
bool m_eof_reached;
unsigned int m_eof_stream;
}; };
} // namespace
ProcessorXChunks::ProcessorXChunks(const int numStreams, const size_t xChunkSize) namespace processor
{ {
m_impl = new ProcessorXChunksImpl(this, numStreams, xChunkSize); std::unique_ptr<IProcessorXChunks> CreateProcessorXChunks(int numStreams, const size_t xChunkSize)
{
return std::make_unique<ProcessorXChunks>(numStreams, xChunkSize, std::nullopt);
} }
ProcessorXChunks::ProcessorXChunks(const int numStreams, const size_t xChunkSize, const size_t vanillaBufferSize) std::unique_ptr<IProcessorXChunks> CreateProcessorXChunks(int numStreams, const size_t xChunkSize, const size_t vanillaBufferSize)
{ {
m_impl = new ProcessorXChunksImpl(this, numStreams, xChunkSize, vanillaBufferSize); return std::make_unique<ProcessorXChunks>(numStreams, xChunkSize, vanillaBufferSize);
}
ProcessorXChunks::~ProcessorXChunks()
{
delete m_impl;
m_impl = nullptr;
}
void ProcessorXChunks::AddChunkProcessor(std::unique_ptr<IXChunkProcessor> chunkProcessor) const
{
m_impl->AddChunkProcessor(std::move(chunkProcessor));
}
size_t ProcessorXChunks::Load(void* buffer, const size_t length)
{
return m_impl->Load(buffer, length);
}
int64_t ProcessorXChunks::Pos()
{
return m_impl->Pos();
} }
} // namespace processor

View File

@ -4,18 +4,14 @@
#include <memory> #include <memory>
class ProcessorXChunks : public StreamProcessor namespace processor
{
class IProcessorXChunks : public StreamProcessor
{ {
class ProcessorXChunksImpl;
ProcessorXChunksImpl* m_impl;
public: public:
ProcessorXChunks(int numStreams, size_t xChunkSize); virtual void AddChunkProcessor(std::unique_ptr<IXChunkProcessor> chunkProcessor) = 0;
ProcessorXChunks(int numStreams, size_t xChunkSize, size_t vanillaBufferSize);
~ProcessorXChunks() override;
size_t Load(void* buffer, size_t length) override;
int64_t Pos() override;
void AddChunkProcessor(std::unique_ptr<IXChunkProcessor> chunkProcessor) const;
}; };
std::unique_ptr<IProcessorXChunks> CreateProcessorXChunks(int numStreams, size_t xChunkSize);
std::unique_ptr<IProcessorXChunks> CreateProcessorXChunks(int numStreams, size_t xChunkSize, size_t vanillaBufferSize);
} // namespace processor