Rename ZoneLoader and ZoneWriter components to ZoneLoading and ZoneWriting to make a difference between the executive class and the component class

This commit is contained in:
Jan
2019-09-24 22:35:11 +02:00
parent 0d8432d4f7
commit 42af6df5d8
86 changed files with 22 additions and 13 deletions

View File

@ -0,0 +1,53 @@
#include "AssetLoader.h"
AssetLoader::AssetLoader(const asset_type_t assetType, IZoneScriptStringProvider* scriptStringProvider, Zone* zone, IZoneInputStream* stream)
{
m_asset_type = assetType;
m_script_string_provider = scriptStringProvider;
m_zone = zone;
m_stream = stream;
}
void AssetLoader::AddDependency(const asset_type_t type, std::string& name)
{
for(auto& existingDependency : m_dependencies)
{
if(existingDependency.m_type == type
&& existingDependency.m_name == name)
{
return;
}
}
XAssetDependency dependency;
dependency.m_type = type;
dependency.m_name = name;
m_dependencies.push_back(dependency);
}
scr_string_t AssetLoader::UseScriptString(const scr_string_t scrString)
{
std::string& scrStringValue = m_script_string_provider->GetZoneScriptString(scrString);
scr_string_t scriptStringIndex = 0;
for(auto& existingScriptString : m_used_script_strings)
{
if(existingScriptString == scrStringValue)
{
return scriptStringIndex;
}
scriptStringIndex++;
}
scriptStringIndex = static_cast<scr_string_t>(m_used_script_strings.size());
m_used_script_strings.push_back(scrStringValue);
return scriptStringIndex;
}
void* AssetLoader::LinkAsset(std::string name, void* asset)
{
return m_zone->GetPools()->AddAsset(m_asset_type, std::move(name), asset, m_used_script_strings, m_dependencies);
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "ContentLoader.h"
#include "IZoneScriptStringProvider.h"
#include "Zone/XAssetDependency.h"
class AssetLoader : public ContentLoader
{
asset_type_t m_asset_type;
IZoneScriptStringProvider* m_script_string_provider;
std::vector<std::string> m_used_script_strings;
std::vector<XAssetDependency> m_dependencies;
protected:
AssetLoader(asset_type_t assetType, IZoneScriptStringProvider* scriptStringProvider, Zone* zone, IZoneInputStream* stream);
void AddDependency(asset_type_t type, std::string& name);
scr_string_t UseScriptString(scr_string_t scrString);
void* LinkAsset(std::string name, void* asset);
};

View File

@ -0,0 +1,42 @@
#include "ContentLoader.h"
#include <cassert>
const void* ContentLoader::PTR_FOLLOWING = reinterpret_cast<void*>(-1);
const void* ContentLoader::PTR_INSERT = reinterpret_cast<void*>(-2);
ContentLoader::ContentLoader()
{
m_zone = nullptr;
m_stream = nullptr;
}
void ContentLoader::LoadXString(const char** pXString) const
{
assert(pXString != nullptr);
if(*pXString != nullptr)
{
if(*pXString == PTR_FOLLOWING)
{
*pXString = m_stream->Alloc<const char>();
m_stream->LoadNullTerminated();
}
else
{
*pXString = m_stream->ConvertOffsetToPointer<const char>(*pXString);
}
}
}
void ContentLoader::LoadXStringArray(const char** pArray, const size_t count, const bool atStreamStart) const
{
assert(pArray != nullptr);
if(atStreamStart)
m_stream->Load<const char*>(count);
for(size_t index = 0; index < count; index++)
{
LoadXString(&pArray[index]);
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "Zone/Zone.h"
#include "Zone/Stream/IZoneInputStream.h"
class ContentLoader
{
protected:
static const void* PTR_FOLLOWING;
static const void* PTR_INSERT;
Zone* m_zone;
IZoneInputStream* m_stream;
ContentLoader();
void LoadXString(const char** pXString) const;
void LoadXStringArray(const char** pArray, size_t count, bool atStreamStart) const;
public:
virtual ~ContentLoader() = default;
};

View File

@ -0,0 +1,16 @@
#include "BlockOverflowException.h"
BlockOverflowException::BlockOverflowException(XBlock* block)
{
m_block = block;
}
std::string BlockOverflowException::DetailedMessage()
{
return "XBlock " + m_block->m_name + " overflowed while trying to load zone.";
}
char const* BlockOverflowException::what() const
{
return "Invalid Zone. XBlock overflowed.";
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "LoadingException.h"
#include "Zone/XBlock.h"
class BlockOverflowException final : public LoadingException
{
XBlock* m_block;
public:
explicit BlockOverflowException(XBlock* block);
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,30 @@
#include "InvalidChunkSizeException.h"
InvalidChunkSizeException::InvalidChunkSizeException(const size_t size)
{
m_size = size;
m_max = 0;
}
InvalidChunkSizeException::InvalidChunkSizeException(const size_t size, const size_t max)
{
m_size = size;
m_max = max;
}
std::string InvalidChunkSizeException::DetailedMessage()
{
if(m_max > 0)
{
return "Zone chunk size has a chunk size of " + std::to_string(m_size) + " which is larger than the maximum of " + std::to_string(m_max);
}
else
{
return "Zone chunk size has an invalid chunk size of " + std::to_string(m_size);
}
}
char const* InvalidChunkSizeException::what() const
{
return "Zone has invalid chunk size";
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "LoadingException.h"
class InvalidChunkSizeException final : public LoadingException
{
size_t m_size;
size_t m_max;
public:
explicit InvalidChunkSizeException(size_t size);
InvalidChunkSizeException(size_t size, size_t max);
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,11 @@
#include "InvalidCompressionException.h"
std::string InvalidCompressionException::DetailedMessage()
{
return "Zone has invalid or unsupported compression. Inflate failed";
}
char const* InvalidCompressionException::what() const
{
return "Zone has invalid or unsupported compression. Inflate failed";
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "LoadingException.h"
class InvalidCompressionException final : public LoadingException
{
public:
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,17 @@
#include "InvalidFileNameException.h"
InvalidFileNameException::InvalidFileNameException(std::string& actualFileName, std::string& expectedFileName)
{
m_actual_file_name = actualFileName;
m_expected_file_name = expectedFileName;
}
std::string InvalidFileNameException::DetailedMessage()
{
return "Name verification failed: The fastfile was created as '" + m_expected_file_name + "' but loaded as '" + m_actual_file_name + "'";
}
char const* InvalidFileNameException::what() const
{
return "The filename when created and when loaded does not match";
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "LoadingException.h"
class InvalidFileNameException final : public LoadingException
{
std::string m_actual_file_name;
std::string m_expected_file_name;
public:
InvalidFileNameException(std::string& actualFileName, std::string& expectedFileName);
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,16 @@
#include "InvalidMagicException.h"
InvalidMagicException::InvalidMagicException(const char* expectedMagic)
{
m_expected_magic = expectedMagic;
}
std::string InvalidMagicException::DetailedMessage()
{
return "Expected magic '" + std::string(m_expected_magic) + "'";
}
char const* InvalidMagicException::what() const
{
return "Encountered invalid magic when loading.";
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "LoadingException.h"
class InvalidMagicException final : public LoadingException
{
const char* m_expected_magic;
public:
explicit InvalidMagicException(const char* expectedMagic);
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,16 @@
#include "InvalidOffsetBlockException.h"
InvalidOffsetBlockException::InvalidOffsetBlockException(const block_t referencedBlock)
{
m_referenced_block = referencedBlock;
}
std::string InvalidOffsetBlockException::DetailedMessage()
{
return "Zone tried to reference invalid block " + std::to_string(m_referenced_block);
}
char const* InvalidOffsetBlockException::what() const
{
return "Zone referenced invalid block";
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "LoadingException.h"
#include "Zone/ZoneTypes.h"
class InvalidOffsetBlockException final : public LoadingException
{
block_t m_referenced_block;
public:
explicit InvalidOffsetBlockException(block_t referencedBlock);
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,18 @@
#include "InvalidOffsetBlockOffsetException.h"
InvalidOffsetBlockOffsetException::InvalidOffsetBlockOffsetException(XBlock* block, const size_t referencedOffset)
{
m_referenced_block = block;
m_referenced_offset = referencedOffset;
}
std::string InvalidOffsetBlockOffsetException::DetailedMessage()
{
return "Zone referenced offset" + std::to_string(m_referenced_offset) + " of block " + m_referenced_block->m_name
+ " which is larger than its size " + std::to_string(m_referenced_block->m_buffer_size);
}
char const* InvalidOffsetBlockOffsetException::what() const
{
return "Zone referenced offset of block that is out of bounds";
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "LoadingException.h"
#include "Zone/XBlock.h"
class InvalidOffsetBlockOffsetException final : public LoadingException
{
XBlock* m_referenced_block;
size_t m_referenced_offset;
public:
InvalidOffsetBlockOffsetException(XBlock* block, size_t referencedOffset);
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,11 @@
#include "InvalidSignatureException.h"
std::string InvalidSignatureException::DetailedMessage()
{
return "Loaded fastfile has an invalid signature.";
}
char const* InvalidSignatureException::what() const
{
return "Loaded fastfile has an invalid signature.";
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "LoadingException.h"
class InvalidSignatureException final : public LoadingException
{
public:
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,17 @@
#include "InvalidVersionException.h"
InvalidVersionException::InvalidVersionException(const unsigned int expectedVersion, const unsigned int actualVersion)
{
m_expected_version = expectedVersion;
m_actual_version = actualVersion;
}
std::string InvalidVersionException::DetailedMessage()
{
return "Expected version " + std::to_string(m_expected_version) + " but encountered version " + std::to_string(m_actual_version);
}
char const* InvalidVersionException::what() const
{
return "Encountered invalid version when loading.";
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "LoadingException.h"
class InvalidVersionException final : public LoadingException
{
unsigned int m_expected_version;
unsigned int m_actual_version;
public:
InvalidVersionException(unsigned int expectedVersion, unsigned int actualVersion);
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,17 @@
#include "InvalidXBlockSizeException.h"
InvalidXBlockSizeException::InvalidXBlockSizeException(const uint64_t size, const uint64_t max)
{
m_size = size;
m_max = max;
}
std::string InvalidXBlockSizeException::DetailedMessage()
{
return "Zone uses more XBlock memory than allowed: " + std::to_string(m_size) + " (max is " + std::to_string(m_max) + ")";
}
char const* InvalidXBlockSizeException::what() const
{
return "Zone has invalid block size";
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "LoadingException.h"
class InvalidXBlockSizeException final : public LoadingException
{
uint64_t m_size;
uint64_t m_max;
public:
InvalidXBlockSizeException(uint64_t size, uint64_t max);
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,9 @@
#pragma once
#include <exception>
#include <string>
class LoadingException : public std::exception
{
public:
virtual std::string DetailedMessage() = 0;
};

View File

@ -0,0 +1,13 @@
#include "UnexpectedEndOfFileException.h"
UnexpectedEndOfFileException::UnexpectedEndOfFileException() = default;
std::string UnexpectedEndOfFileException::DetailedMessage()
{
return "Unexpected end of file";
}
char const* UnexpectedEndOfFileException::what() const
{
return "Unexpected end of file";
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "LoadingException.h"
class UnexpectedEndOfFileException final : public LoadingException
{
public:
UnexpectedEndOfFileException();
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,16 @@
#include "UnsupportedAssetTypeException.h"
UnsupportedAssetTypeException::UnsupportedAssetTypeException(const int assetType)
{
m_asset_type = assetType;
}
std::string UnsupportedAssetTypeException::DetailedMessage()
{
return "Zone has an unsupported asset type " + std::to_string(m_asset_type) + " and therefore cannot be loaded.";
}
char const* UnsupportedAssetTypeException::what() const
{
return "Zone has unsupported asset type.";
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "LoadingException.h"
class UnsupportedAssetTypeException final : public LoadingException
{
int m_asset_type;
public:
explicit UnsupportedAssetTypeException(int assetType);
std::string DetailedMessage() override;
char const* what() const override;
};

View File

@ -0,0 +1,12 @@
#pragma once
#include "Zone/Zone.h"
#include "Zone/Stream/IZoneInputStream.h"
class IContentLoadingEntryPoint
{
public:
virtual ~IContentLoadingEntryPoint() = default;
virtual void Load(Zone* zone, IZoneInputStream* stream) = 0;
};

View File

@ -0,0 +1,14 @@
#pragma once
#include "Loading/ZoneLoader.h"
#include "ILoadingStream.h"
class ZoneLoader;
class ILoadingStep
{
public:
virtual ~ILoadingStep() = default;
virtual void PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream) = 0;
};

View File

@ -0,0 +1,9 @@
#pragma once
class ILoadingStream
{
public:
virtual ~ILoadingStream() = default;
virtual size_t Load(void* buffer, size_t length) = 0;
};

View File

@ -0,0 +1,8 @@
#pragma once
#include <cstdint>
class ISignatureDataProvider
{
public:
virtual void GetSignatureData(const uint8_t** pSignatureData, size_t* pSize) = 0;
};

View File

@ -0,0 +1,8 @@
#pragma once
#include <cstdint>
class ISignatureProvider
{
public:
virtual void GetSignature(const uint8_t** pSignature, size_t* pSize) = 0;
};

View File

@ -0,0 +1,12 @@
#pragma once
#include "Zone/ZoneTypes.h"
#include "ZoneLoader.h"
class IZoneLoaderFactory
{
public:
virtual ~IZoneLoaderFactory() = default;
virtual ZoneLoader* CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) = 0;
};

View File

@ -0,0 +1,10 @@
#pragma once
#include "Zone/ZoneTypes.h"
#include <string>
class IZoneScriptStringProvider
{
public:
virtual std::string& GetZoneScriptString(scr_string_t scrString) = 0;
};

View File

@ -0,0 +1,11 @@
#include "LoadingFileStream.h"
LoadingFileStream::LoadingFileStream(FileAPI::File* file)
{
m_file = file;
}
size_t LoadingFileStream::Load(void* buffer, const size_t length)
{
return m_file->Read(buffer, 1, length);
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "ILoadingStream.h"
#include "Utils/FileAPI.h"
class LoadingFileStream : public ILoadingStream
{
FileAPI::File* m_file;
public:
explicit LoadingFileStream(FileAPI::File* file);
size_t Load(void* buffer, size_t length) override;
};

View File

@ -0,0 +1,73 @@
#include "ProcessorInflate.h"
#include "zlib.h"
#include <exception>
#include "zutil.h"
#include <cstdint>
class ProcessorInflate::ProcessorInflateImpl
{
z_stream m_stream{};
uint8_t m_in_buffer[0x800];
ProcessorInflate* m_base;
public:
ProcessorInflateImpl(ProcessorInflate* baseClass)
{
m_base = baseClass;
m_stream.zalloc = Z_NULL;
m_stream.zfree = Z_NULL;
m_stream.opaque = Z_NULL;
m_stream.avail_in = 0;
m_stream.next_in = Z_NULL;
const int ret = inflateInit2(&m_stream, -DEF_WBITS);
if(ret != Z_OK)
{
throw std::exception("Initializing inflate failed");
}
}
~ProcessorInflateImpl()
{
inflateEnd(&m_stream);
}
size_t Load(void* buffer, size_t length)
{
m_stream.next_out = static_cast<Bytef*>(buffer);
m_stream.avail_out = length;
while(m_stream.avail_out > 0)
{
if(m_stream.avail_in == 0)
{
m_stream.avail_in = m_base->m_base_stream->Load(m_in_buffer, sizeof(m_in_buffer));
if(m_stream.avail_in == 0) // EOF
return length - m_stream.avail_out;
}
inflate(&m_stream, Z_FULL_FLUSH);
}
return m_stream.avail_out;
}
};
ProcessorInflate::ProcessorInflate()
{
m_impl = new ProcessorInflateImpl(this);
}
ProcessorInflate::~ProcessorInflate()
{
delete m_impl;
m_impl = nullptr;
}
size_t ProcessorInflate::Load(void* buffer, const size_t length)
{
return m_impl->Load(buffer, length);
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "Loading/StreamProcessor.h"
class ProcessorInflate final : public StreamProcessor
{
class ProcessorInflateImpl;
ProcessorInflateImpl* m_impl;
public:
ProcessorInflate();
~ProcessorInflate() override;
size_t Load(void* buffer, size_t length) override;
};

View File

@ -0,0 +1,28 @@
#include "ProcessorStreamCipher.h"
ProcessorStreamCipher::ProcessorStreamCipher(IStreamCipher* cipher)
{
m_cipher = cipher;
}
ProcessorStreamCipher::~ProcessorStreamCipher()
{
delete m_cipher;
m_cipher = nullptr;
}
size_t ProcessorStreamCipher::Load(void* buffer, const size_t length)
{
if(m_base_stream != nullptr)
{
const size_t readSize = m_base_stream->Load(buffer, length);
if(readSize > 0)
m_cipher->Process(buffer, buffer, readSize);
return readSize;
}
return 0;
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "Loading/StreamProcessor.h"
#include "Crypto.h"
class ProcessorStreamCipher final : public StreamProcessor
{
IStreamCipher* m_cipher;
public:
explicit ProcessorStreamCipher(IStreamCipher* cipher);
~ProcessorStreamCipher() override;
size_t Load(void* buffer, size_t length) override;
};

View File

@ -0,0 +1,310 @@
#include "ProcessorXChunks.h"
#include "Zone/ZoneTypes.h"
#include "Loading/Exception/InvalidChunkSizeException.h"
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <cassert>
class DBLoadStream
{
int m_index;
uint8_t* m_input_buffer;
size_t m_input_size;
uint8_t* m_output_buffer;
size_t m_output_size;
size_t m_chunk_size;
bool m_is_loading;
std::mutex m_load_mutex;
std::condition_variable m_loading_finished;
std::thread m_load_thread;
std::vector<IXChunkProcessor*>& m_processors;
void Load()
{
std::lock_guard<std::mutex> lock(m_load_mutex);
bool firstProcessor = true;
for(auto processor : m_processors)
{
if(!firstProcessor)
{
uint8_t* previousInputBuffer = m_input_buffer;
m_input_buffer = m_output_buffer;
m_output_buffer = previousInputBuffer;
m_input_size = m_output_size;
m_output_size = 0;
}
m_output_size = processor->Process(m_index, m_input_buffer, m_input_size, m_output_buffer, m_chunk_size);
firstProcessor = false;
}
m_is_loading = false;
m_loading_finished.notify_all();
}
public:
DBLoadStream(const int streamIndex, const size_t chunkSize, std::vector<IXChunkProcessor*>& chunkProcessors) : m_processors(chunkProcessors)
{
m_index = streamIndex;
m_chunk_size = chunkSize;
m_input_buffer = new uint8_t[chunkSize];
m_output_buffer = new uint8_t[chunkSize];
m_input_size = 0;
m_output_size = 0;
m_is_loading = false;
}
~DBLoadStream()
{
delete[] m_input_buffer;
m_input_buffer = nullptr;
delete[] m_output_buffer;
m_output_buffer = nullptr;
}
uint8_t* GetInputBuffer() const
{
return m_input_buffer;
}
void StartLoading(const size_t inputSize)
{
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
{
ProcessorXChunks* m_base;
std::vector<DBLoadStream*> m_streams;
size_t m_chunk_size;
std::vector<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;
bool m_eof_reached;
unsigned int m_eof_stream;
void AdvanceStream(const unsigned int streamNum)
{
assert(streamNum >= 0 && streamNum < m_streams.size());
if(m_eof_reached)
return;
xchunk_size_t chunkSize;
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);
}
auto* stream = m_streams[streamNum];
const size_t loadedChunkSize = m_base->m_base_stream->Load(stream->GetInputBuffer(), chunkSize);
if(loadedChunkSize != chunkSize)
{
throw InvalidChunkSizeException(chunkSize);
}
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;
const unsigned int streamCount = m_streams.size();
for(unsigned int streamNum = 0; 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);
}
bool EndOfStream() const
{
return m_eof_reached && m_eof_stream == m_current_stream;
}
public:
ProcessorXChunksImpl(ProcessorXChunks* base, const int numStreams, const size_t xChunkSize)
{
assert(base != nullptr);
assert(numStreams > 0);
assert(xChunkSize > 0);
m_base = base;
for(int streamIndex = 0; streamIndex < numStreams; streamIndex++)
{
m_streams.push_back(new DBLoadStream(streamIndex, xChunkSize, m_chunk_processors));
}
m_chunk_size = xChunkSize;
m_initialized_streams = false;
m_current_stream = 0;
m_current_chunk = nullptr;
m_current_chunk_size = 0;
m_current_chunk_offset = 0;
m_eof_reached = false;
m_eof_stream = 0;
}
~ProcessorXChunksImpl()
{
for(auto* stream : m_streams)
{
delete stream;
}
m_streams.clear();
for(auto* processor : m_chunk_processors)
{
delete processor;
}
m_chunk_processors.clear();
}
void AddChunkProcessor(IXChunkProcessor* streamProcessor)
{
assert(streamProcessor != nullptr);
m_chunk_processors.push_back(streamProcessor);
}
size_t Load(void* buffer, const size_t length)
{
assert(buffer != nullptr);
if(!m_initialized_streams)
{
InitStreams();
}
size_t loadedSize = 0;
while(!EndOfStream() && loadedSize < length)
{
auto* bufferPos = static_cast<uint8_t*>(buffer) + loadedSize;
const size_t sizeToRead = length - loadedSize;
const size_t bytesLeftInCurrentChunk = m_current_chunk_size - m_current_chunk_offset;
if(sizeToRead > bytesLeftInCurrentChunk)
{
memcpy_s(bufferPos, sizeToRead, &m_current_chunk[m_current_chunk_offset], bytesLeftInCurrentChunk);
loadedSize += bytesLeftInCurrentChunk;
NextStream();
}
else
{
memcpy_s(bufferPos, sizeToRead, &m_current_chunk[m_current_chunk_offset], sizeToRead);
loadedSize += sizeToRead;
m_current_chunk_offset += sizeToRead;
if(m_current_chunk_offset == m_current_chunk_size)
{
NextStream();
}
}
}
return loadedSize;
}
};
ProcessorXChunks::ProcessorXChunks(const int numStreams, const size_t xChunkSize)
{
m_impl = new ProcessorXChunksImpl(this, numStreams, xChunkSize);
}
ProcessorXChunks::~ProcessorXChunks()
{
delete m_impl;
m_impl = nullptr;
}
void ProcessorXChunks::AddChunkProcessor(IXChunkProcessor* chunkProcessor) const
{
m_impl->AddChunkProcessor(chunkProcessor);
}
size_t ProcessorXChunks::Load(void* buffer, const size_t length)
{
return m_impl->Load(buffer, length);
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "Loading/StreamProcessor.h"
#include "XChunks/IXChunkProcessor.h"
class ProcessorXChunks : public StreamProcessor
{
class ProcessorXChunksImpl;
ProcessorXChunksImpl* m_impl;
public:
ProcessorXChunks(int numStreams, size_t xChunkSize);
~ProcessorXChunks() override;
size_t Load(void* buffer, size_t length) override;
void AddChunkProcessor(IXChunkProcessor* chunkProcessor) const;
};

View File

@ -0,0 +1,36 @@
#include "ChunkProcessorInflate.h"
#include "zlib.h"
#include "zutil.h"
#include <exception>
#include "Loading/Exception/InvalidCompressionException.h"
size_t ChunkProcessorInflate::Process(int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize)
{
z_stream stream{};
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
int ret = inflateInit2(&stream, -DEF_WBITS);
if(ret != Z_OK)
{
throw std::exception("Initializing inflate failed.");
}
stream.avail_in = inputLength;
stream.next_in = input;
stream.avail_out = outputBufferSize;
stream.next_out = output;
ret = inflate(&stream, Z_FULL_FLUSH);
if(ret != Z_STREAM_END)
{
throw InvalidCompressionException();
}
const size_t outputSize = stream.total_out;
inflateEnd(&stream);
return outputSize;
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "IXChunkProcessor.h"
class ChunkProcessorInflate : public IXChunkProcessor
{
public:
size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override;
};

View File

@ -0,0 +1,156 @@
#include "ChunkProcessorSalsa20.h"
#include "Crypto.h"
#include <cassert>
class StreamContextSalsa20
{
public:
IStreamCipher* m_salsa20;
IHashFunction* m_sha1;
StreamContextSalsa20()
{
m_salsa20 = nullptr;
m_sha1 = nullptr;
}
~StreamContextSalsa20()
{
delete m_salsa20;
m_salsa20 = nullptr;
delete m_sha1;
m_sha1 = nullptr;
}
};
class ChunkProcessorSalsa20::ChunkProcessorSalsa20Impl
{
static const int BLOCK_HASHES_COUNT = 200;
static const int SHA1_HASH_SIZE = 20;
static const int SALSA20_IV_SIZE = 8;
int m_stream_count;
StreamContextSalsa20* m_stream_contexts;
// m_block_hashes[BLOCK_HASHES_COUNT][numStreams][HASH_SIZE]
uint8_t* m_block_hashes;
unsigned int* m_stream_block_indices;
uint8_t* GetHashBlock(const int streamNumber) const
{
const size_t blockIndexOffset = m_stream_block_indices[streamNumber] * m_stream_count * SHA1_HASH_SIZE;
const size_t streamOffset = streamNumber * SHA1_HASH_SIZE;
return &m_block_hashes[blockIndexOffset + streamOffset];
}
public:
ChunkProcessorSalsa20Impl(const int streamCount, std::string& zoneName, const uint8_t* salsa20Key, size_t keySize)
{
m_stream_count = streamCount;
m_stream_contexts = new StreamContextSalsa20[streamCount];
m_block_hashes = new uint8_t[BLOCK_HASHES_COUNT * streamCount * SHA1_HASH_SIZE];
m_stream_block_indices = new unsigned int[streamCount];
InitStreams(zoneName, salsa20Key, keySize);
}
~ChunkProcessorSalsa20Impl()
{
delete[] m_stream_contexts;
delete[] m_block_hashes;
m_block_hashes = nullptr;
delete[] m_stream_block_indices;
m_stream_block_indices = nullptr;
}
void InitStreams(std::string& zoneName, const uint8_t* salsa20Key, size_t keySize) const
{
const int zoneNameLength = zoneName.length();
const size_t blockHashBufferSize = BLOCK_HASHES_COUNT * m_stream_count * SHA1_HASH_SIZE;
assert(blockHashBufferSize % 4 == 0);
size_t zoneNameOffset = 0;
for(size_t i = 0; i < blockHashBufferSize; i += 4)
{
*reinterpret_cast<uint32_t*>(&m_block_hashes[i]) = 0x1010101 * zoneName[zoneNameOffset++];
zoneNameOffset %= zoneNameLength;
}
for(int stream = 0; stream < m_stream_count; stream++)
{
m_stream_block_indices[stream] = 0;
m_stream_contexts[stream].m_salsa20 = Crypto::CreateSalsa20(salsa20Key, keySize);
m_stream_contexts[stream].m_sha1 = Crypto::CreateSHA1();
}
}
size_t Process(const int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize) const
{
assert(streamNumber >= 0 && streamNumber < m_stream_count);
assert(input != nullptr);
assert(output != nullptr);
assert(inputLength <= outputBufferSize);
StreamContextSalsa20& streamContext = m_stream_contexts[streamNumber];
// Initialize Salsa20 with an IV of the first 8 bytes of the current hash block
streamContext.m_salsa20->SetIV(GetHashBlock(streamNumber), SALSA20_IV_SIZE);
streamContext.m_salsa20->Process(input, output, inputLength);
// Hash decrypted XChunk
uint8_t blockSha1Hash[SHA1_HASH_SIZE];
streamContext.m_sha1->Init();
streamContext.m_sha1->Process(output, inputLength);
streamContext.m_sha1->Finish(&blockSha1Hash);
// Advance index to next hash block
m_stream_block_indices[streamNumber] = (m_stream_block_indices[streamNumber] + 1) % BLOCK_HASHES_COUNT;
uint8_t* nextHashBlock = GetHashBlock(streamNumber);
// XOR the upcoming hash block with the hash of the XChunk utilizing the previous hash block
for(unsigned int hashOffset = 0; hashOffset < sizeof(blockSha1Hash); hashOffset++)
{
nextHashBlock[hashOffset] ^= blockSha1Hash[hashOffset];
}
return inputLength;
}
void GetSignatureData(const uint8_t** pSignatureData, size_t* pSize) const
{
assert(pSignatureData != nullptr);
assert(pSize != nullptr);
*pSignatureData = m_block_hashes;
*pSize = BLOCK_HASHES_COUNT * m_stream_count * SHA1_HASH_SIZE;
}
};
ChunkProcessorSalsa20::ChunkProcessorSalsa20(const int streamCount, std::string& zoneName, const uint8_t* salsa20Key, size_t keySize)
{
m_impl = new ChunkProcessorSalsa20Impl(streamCount, zoneName, salsa20Key, keySize);
}
ChunkProcessorSalsa20::~ChunkProcessorSalsa20()
{
delete m_impl;
m_impl = nullptr;
}
size_t ChunkProcessorSalsa20::Process(const int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize)
{
return m_impl->Process(streamNumber, input, inputLength, output, outputBufferSize);
}
void ChunkProcessorSalsa20::GetSignatureData(const uint8_t** pSignatureData, size_t* pSize)
{
m_impl->GetSignatureData(pSignatureData, pSize);
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "IXChunkProcessor.h"
#include "Loading/ISignatureDataProvider.h"
#include <string>
class ChunkProcessorSalsa20 : public IXChunkProcessor, public ISignatureDataProvider
{
class ChunkProcessorSalsa20Impl;
ChunkProcessorSalsa20Impl* m_impl;
public:
ChunkProcessorSalsa20(int streamCount, std::string& zoneName, const uint8_t* salsa20Key, size_t keySize);
~ChunkProcessorSalsa20() override;
size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override;
void GetSignatureData(const uint8_t** pSignatureData, size_t* pSize) override;
};

View File

@ -0,0 +1,9 @@
#pragma once
#include <cstdint>
class IXChunkProcessor
{
public:
virtual ~IXChunkProcessor() = default;
virtual size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) = 0;
};

View File

@ -0,0 +1,22 @@
#include "StepAddProcessor.h"
#include <cassert>
StepAddProcessor::StepAddProcessor(StreamProcessor* streamProcessor)
{
m_stream_processor = streamProcessor;
}
StepAddProcessor::~StepAddProcessor()
{
delete m_stream_processor;
m_stream_processor = nullptr;
}
void StepAddProcessor::PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream)
{
assert(zoneLoader != nullptr);
assert(m_stream_processor != nullptr);
zoneLoader->AddStreamProcessor(m_stream_processor);
m_stream_processor = nullptr;
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "Loading/ILoadingStep.h"
class StepAddProcessor final : public ILoadingStep
{
StreamProcessor* m_stream_processor;
public:
explicit StepAddProcessor(StreamProcessor* streamProcessor);
~StepAddProcessor() override;
void PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream) override;
};

View File

@ -0,0 +1,30 @@
#include "StepAllocXBlocks.h"
#include "Loading/Exception/InvalidXBlockSizeException.h"
const uint64_t StepAllocXBlocks::MAX_XBLOCK_SIZE = 0x3C000000;
void StepAllocXBlocks::PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream)
{
const unsigned int blockCount = zoneLoader->m_blocks.size();
auto* blockSizes = new xblock_size_t[blockCount];
stream->Load(blockSizes, sizeof(xblock_size_t) * blockCount);
uint64_t totalMemory = 0;
for(unsigned int block = 0; block < blockCount; block++)
{
totalMemory += blockSizes[block];
}
if(totalMemory > MAX_XBLOCK_SIZE)
{
throw InvalidXBlockSizeException(totalMemory, MAX_XBLOCK_SIZE);
}
for(unsigned int block = 0; block < blockCount; block++)
{
zoneLoader->m_blocks[block]->Alloc(blockSizes[block]);
}
delete[] blockSizes;
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "Loading/ILoadingStep.h"
class StepAllocXBlocks final : public ILoadingStep
{
static const uint64_t MAX_XBLOCK_SIZE;
public:
void PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream) override;
};

View File

@ -0,0 +1,34 @@
#include "StepLoadSignature.h"
#include "Loading/Exception/UnexpectedEndOfFileException.h"
#include <cassert>
StepLoadSignature::StepLoadSignature(const size_t signatureSize)
{
m_signature_size = signatureSize;
m_signature = new uint8_t[signatureSize];
}
StepLoadSignature::~StepLoadSignature()
{
delete[] m_signature;
m_signature = nullptr;
}
void StepLoadSignature::PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream)
{
assert(stream != nullptr);
if(stream->Load(m_signature, m_signature_size) != m_signature_size)
throw UnexpectedEndOfFileException();
}
void StepLoadSignature::GetSignature(const uint8_t** pSignature, size_t* pSize)
{
assert(pSignature != nullptr);
assert(pSize != nullptr);
assert(m_signature != nullptr);
*pSignature = m_signature;
*pSize = m_signature_size;
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "Loading/ILoadingStep.h"
#include "Loading/ISignatureProvider.h"
class StepLoadSignature final : public ILoadingStep, public ISignatureProvider
{
uint8_t* m_signature;
size_t m_signature_size;
public:
explicit StepLoadSignature(size_t signatureSize);
~StepLoadSignature() override;
void PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream) override;
void GetSignature(const uint8_t** pSignature, size_t* pSize) override;
};

View File

@ -0,0 +1,26 @@
#include "StepLoadZoneContent.h"
#include "Zone/Stream/Impl/XBlockInputStream.h"
#include <cassert>
StepLoadZoneContent::StepLoadZoneContent(IContentLoadingEntryPoint* entryPoint, Zone* zone, const int offsetBlockBitCount, const block_t insertBlock)
{
m_content_loader = entryPoint;
m_zone = zone;
m_offset_block_bit_count = offsetBlockBitCount;
m_insert_block = insertBlock;
}
StepLoadZoneContent::~StepLoadZoneContent()
{
delete m_content_loader;
m_content_loader = nullptr;
}
void StepLoadZoneContent::PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream)
{
auto* inputStream = new XBlockInputStream(zoneLoader->m_blocks, stream, m_offset_block_bit_count, m_insert_block);
m_content_loader->Load(m_zone, inputStream);
delete inputStream;
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "Loading/ILoadingStep.h"
#include "Loading/IContentLoadingEntryPoint.h"
class StepLoadZoneContent final : public ILoadingStep
{
IContentLoadingEntryPoint* m_content_loader;
Zone* m_zone;
int m_offset_block_bit_count;
block_t m_insert_block;
public:
StepLoadZoneContent(IContentLoadingEntryPoint* entryPoint, Zone* zone, int offsetBlockBitCount, block_t insertBlock);
~StepLoadZoneContent();
void PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream) override;
};

View File

@ -0,0 +1,30 @@
#include "StepSkipBytes.h"
StepSkipBytes::StepSkipBytes(const unsigned int skipCount)
{
m_skip_count = skipCount;
}
void StepSkipBytes::PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream)
{
uint8_t tempBuffer[128];
unsigned int skippedBytes = 0;
while(skippedBytes < m_skip_count)
{
unsigned int toSkip;
if(m_skip_count - skippedBytes < sizeof(tempBuffer))
{
toSkip = m_skip_count - skippedBytes;
}
else
{
toSkip = sizeof(tempBuffer);
}
stream->Load(tempBuffer, toSkip);
skippedBytes += toSkip;
}
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "Loading/ILoadingStep.h"
class StepSkipBytes final : public ILoadingStep
{
unsigned int m_skip_count;
public:
explicit StepSkipBytes(unsigned int skipCount);
void PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream) override;
};

View File

@ -0,0 +1,42 @@
#include "StepVerifyFileName.h"
#include "Loading/Exception/InvalidFileNameException.h"
StepVerifyFileName::StepVerifyFileName(std::string fileName, const size_t fileNameBufferSize)
{
m_file_name = std::move(fileName);
m_file_name_buffer_size = fileNameBufferSize;
if(m_file_name.length() > m_file_name_buffer_size)
m_file_name.erase(m_file_name_buffer_size);
}
void StepVerifyFileName::PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream)
{
std::string originalFileName;
unsigned bufferOffset = 0;
char c;
for(; bufferOffset < m_file_name_buffer_size; bufferOffset++)
{
stream->Load(&c, sizeof(char));
if(c == '\00')
{
bufferOffset++;
break;
}
originalFileName += c;
}
// Skip the rest of the buffer which should be null bytes
while(bufferOffset < m_file_name_buffer_size)
{
stream->Load(&c, sizeof(char));
bufferOffset++;
}
if(originalFileName != m_file_name)
throw InvalidFileNameException(m_file_name, originalFileName);
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "Loading/ILoadingStep.h"
class StepVerifyFileName final : public ILoadingStep
{
std::string m_file_name;
size_t m_file_name_buffer_size;
public:
explicit StepVerifyFileName(std::string fileName, size_t fileNameBufferSize);
void PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream) override;
};

View File

@ -0,0 +1,21 @@
#include "StepVerifyMagic.h"
#include "Loading/Exception/InvalidMagicException.h"
StepVerifyMagic::StepVerifyMagic(const char* magic)
{
m_magic = magic;
}
void StepVerifyMagic::PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream)
{
const size_t magicLength = strlen(m_magic);
char currentCharacter;
for(unsigned i = 0; i < magicLength; i++)
{
stream->Load(&currentCharacter, sizeof(char));
if(currentCharacter != m_magic[i])
throw InvalidMagicException(m_magic);
}
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "Loading/ILoadingStep.h"
class StepVerifyMagic final : public ILoadingStep
{
const char* m_magic;
public:
explicit StepVerifyMagic(const char* magic);
void PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream) override;
};

View File

@ -0,0 +1,36 @@
#include "StepVerifySignature.h"
#include "Loading/Exception/InvalidSignatureException.h"
#include <cassert>
StepVerifySignature::StepVerifySignature(IPublicKeyAlgorithm* signatureAlgorithm, ISignatureProvider* signatureProvider, ISignatureDataProvider* signatureDataProvider)
{
m_algorithm = signatureAlgorithm;
m_signature_provider = signatureProvider;
m_signature_data_provider = signatureDataProvider;
}
StepVerifySignature::~StepVerifySignature()
{
delete m_algorithm;
m_algorithm = nullptr;
}
void StepVerifySignature::PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream)
{
assert(m_algorithm != nullptr);
assert(m_signature_provider != nullptr);
assert(m_signature_data_provider != nullptr);
const uint8_t* signature;
size_t signatureSize;
m_signature_provider->GetSignature(&signature, &signatureSize);
const uint8_t* signatureData;
size_t signatureDataSize;
m_signature_data_provider->GetSignatureData(&signatureData, &signatureDataSize);
if(!m_algorithm->Verify(signatureData, signatureDataSize, signature, signatureSize))
{
throw InvalidSignatureException();
}
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "Loading/ILoadingStep.h"
#include "Crypto.h"
#include "Loading/ISignatureProvider.h"
#include "Loading/ISignatureDataProvider.h"
class StepVerifySignature final : public ILoadingStep
{
IPublicKeyAlgorithm* m_algorithm;
ISignatureProvider* m_signature_provider;
ISignatureDataProvider* m_signature_data_provider;
public:
StepVerifySignature(IPublicKeyAlgorithm* signatureAlgorithm, ISignatureProvider* signatureProvider, ISignatureDataProvider* signatureDataProvider);
~StepVerifySignature();
void PerformStep(ZoneLoader* zoneLoader, ILoadingStream* stream) override;
};

View File

@ -0,0 +1,11 @@
#include "StreamProcessor.h"
StreamProcessor::StreamProcessor()
{
m_base_stream = nullptr;
}
void StreamProcessor::SetBaseStream(ILoadingStream* baseStream)
{
m_base_stream = baseStream;
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "ILoadingStream.h"
class StreamProcessor : public ILoadingStream
{
protected:
ILoadingStream* m_base_stream;
public:
StreamProcessor();
void SetBaseStream(ILoadingStream* baseStream);
};

View File

@ -0,0 +1,94 @@
#include "ZoneLoader.h"
#include "Exception/LoadingException.h"
#include "LoadingFileStream.h"
#include <algorithm>
ZoneLoader::ZoneLoader(Zone* zone)
{
m_zone = zone;
m_processor_chain_dirty = false;
}
ZoneLoader::~ZoneLoader()
{
for(auto step : m_steps)
{
delete step;
}
m_steps.clear();
for(auto processor : m_processors)
{
delete processor;
}
m_processors.clear();
}
ILoadingStream* ZoneLoader::BuildLoadingChain(ILoadingStream* rootStream)
{
ILoadingStream* currentStream = rootStream;
for(auto processor : m_processors)
{
processor->SetBaseStream(currentStream);
currentStream = processor;
}
m_processor_chain_dirty = false;
return currentStream;
}
void ZoneLoader::AddXBlock(XBlock* block)
{
m_blocks.push_back(block);
std::sort(m_blocks.begin(), m_blocks.end(), [](XBlock* b1, XBlock* b2) -> bool
{
return b1->m_index < b2->m_index;
});
}
void ZoneLoader::AddLoadingStep(ILoadingStep* step)
{
m_steps.push_back(step);
}
void ZoneLoader::AddStreamProcessor(StreamProcessor* streamProcessor)
{
m_processors.push_back(streamProcessor);
m_processor_chain_dirty = true;
}
Zone* ZoneLoader::LoadZone(FileAPI::File* file)
{
LoadingFileStream fileStream(file);
ILoadingStream* endStream = BuildLoadingChain(&fileStream);
try
{
for(auto step : m_steps)
{
step->PerformStep(this, endStream);
if(m_processor_chain_dirty)
{
endStream = BuildLoadingChain(&fileStream);
}
}
}
catch (LoadingException& e)
{
const std::string detailedMessage = e.DetailedMessage();
printf("Loading fastfile failed: %s\n", detailedMessage.c_str());
delete m_zone;
return nullptr;
}
m_zone->m_game->AddZone(m_zone);
return m_zone;
}

View File

@ -0,0 +1,35 @@
#pragma once
#include "ILoadingStep.h"
#include "Zone/Zone.h"
#include "Zone/XBlock.h"
#include "Utils/FileAPI.h"
#include "StreamProcessor.h"
#include <vector>
class ILoadingStep;
class ZoneLoader
{
std::vector<ILoadingStep*> m_steps;
std::vector<StreamProcessor*> m_processors;
bool m_processor_chain_dirty;
Zone* m_zone;
ILoadingStream* BuildLoadingChain(ILoadingStream* rootStream);
public:
std::vector<XBlock*> m_blocks;
explicit ZoneLoader(Zone* zone);
~ZoneLoader();
void AddXBlock(XBlock* block);
void AddLoadingStep(ILoadingStep* step);
void AddStreamProcessor(StreamProcessor* streamProcessor);
Zone* LoadZone(FileAPI::File* file);
};