Add ZoneWriting basis

This commit is contained in:
Jan
2021-03-15 22:36:07 +01:00
parent 301f6e3e7a
commit 9d26c9c927
66 changed files with 1344 additions and 111 deletions

View File

@ -0,0 +1,12 @@
#pragma once
#include "Writing/IZoneWriterFactory.h"
namespace IW4
{
class ZoneWriterFactory final : public IZoneWriterFactory
{
public:
_NODISCARD bool SupportsZone(Zone* zone) const override;
_NODISCARD std::unique_ptr<ZoneWriter> CreateWriter(Zone* zone) const override;
};
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "Writing/ContentWriterBase.h"
#include "Writing/IContentWritingEntryPoint.h"
#include "Game/T6/T6.h"
namespace T6
{
class ContentWriter final : public ContentWriterBase, public IContentWritingEntryPoint
{
XAsset* varXAsset;
ScriptStringList* varScriptStringList;
void LoadScriptStringList(bool atStreamStart);
void LoadXAsset(bool atStreamStart);
void LoadXAssetArray(bool atStreamStart, size_t count);
public:
ContentWriter();
void WriteContent(Zone* zone, IZoneOutputStream* stream) override;
};
}

View File

@ -0,0 +1,133 @@
#include "ZoneWriterFactoryT6.h"
#include <cassert>
#include "ContentWriterT6.h"
#include "Utils/ICapturedDataProvider.h"
#include "Game/T6/T6.h"
#include "Game/T6/GameT6.h"
#include "Game/T6/ZoneConstantsT6.h"
#include "Writing/Processor/OutputProcessorXChunks.h"
#include "Writing/Processor/XChunks/XChunkOutputProcessorDeflate.h"
#include "Writing/Processor/XChunks/XChunkOutputProcessorSalsa20.h"
#include "Writing/Steps/StepAddOutputProcessor.h"
#include "Writing/Steps/StepWriteXBlockSizes.h"
#include "Writing/Steps/StepWriteZoneContentToFile.h"
#include "Writing/Steps/StepWriteZoneContentToMemory.h"
#include "Writing/Steps/StepWriteZoneHeader.h"
using namespace T6;
class ZoneWriterFactory::Impl
{
Zone* m_zone;
std::unique_ptr<ZoneWriter> m_writer;
public:
explicit Impl(Zone* zone)
: m_zone(zone),
m_writer(std::make_unique<ZoneWriter>())
{
}
void SetupBlocks() const
{
#define XBLOCK_DEF(name, type) std::make_unique<XBlock>(STR(name), name, type)
m_writer->AddXBlock(XBLOCK_DEF(T6::XFILE_BLOCK_TEMP, XBlock::Type::BLOCK_TYPE_TEMP));
m_writer->AddXBlock(XBLOCK_DEF(T6::XFILE_BLOCK_RUNTIME_VIRTUAL, XBlock::Type::BLOCK_TYPE_RUNTIME));
m_writer->AddXBlock(XBLOCK_DEF(T6::XFILE_BLOCK_RUNTIME_PHYSICAL, XBlock::Type::BLOCK_TYPE_RUNTIME));
m_writer->AddXBlock(XBLOCK_DEF(T6::XFILE_BLOCK_DELAY_VIRTUAL, XBlock::Type::BLOCK_TYPE_DELAY));
m_writer->AddXBlock(XBLOCK_DEF(T6::XFILE_BLOCK_DELAY_PHYSICAL, XBlock::Type::BLOCK_TYPE_DELAY));
m_writer->AddXBlock(XBLOCK_DEF(T6::XFILE_BLOCK_VIRTUAL, XBlock::Type::BLOCK_TYPE_NORMAL));
m_writer->AddXBlock(XBLOCK_DEF(T6::XFILE_BLOCK_PHYSICAL, XBlock::Type::BLOCK_TYPE_NORMAL));
m_writer->AddXBlock(XBLOCK_DEF(T6::XFILE_BLOCK_STREAMER_RESERVE, XBlock::Type::BLOCK_TYPE_NORMAL));
#undef XBLOCK_DEF
}
static ZoneHeader CreateHeaderForParams(const bool isSecure, const bool isOfficial, const bool isEncrypted)
{
ZoneHeader header{};
header.m_version = ZoneConstants::ZONE_VERSION;
if(isSecure)
{
if (isOfficial)
memcpy(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, sizeof(ZoneHeader::m_magic));
else
memcpy(header.m_magic, ZoneConstants::MAGIC_SIGNED_OAT, sizeof(ZoneHeader::m_magic));
}
else
{
if (isEncrypted)
memcpy(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, sizeof(ZoneHeader::m_magic));
else
memcpy(header.m_magic, ZoneConstants::MAGIC_UNSIGNED_SERVER, sizeof(ZoneHeader::m_magic));
}
return header;
}
ICapturedDataProvider* AddXChunkProcessor(const bool isEncrypted)
{
ICapturedDataProvider* result = nullptr;
auto xChunkProcessor = std::make_unique<OutputProcessorXChunks>(ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, ZoneConstants::VANILLA_BUFFER_SIZE);
if (isEncrypted)
{
// If zone is encrypted, the decryption is applied before the decompression. T6 Zones always use Salsa20.
auto chunkProcessorSalsa20 = std::make_unique<XChunkOutputProcessorSalsa20>(ZoneConstants::STREAM_COUNT, m_zone->m_name, ZoneConstants::SALSA20_KEY_TREYARCH,
sizeof(ZoneConstants::SALSA20_KEY_TREYARCH));
result = chunkProcessorSalsa20.get();
xChunkProcessor->AddChunkProcessor(std::move(chunkProcessorSalsa20));
}
// Decompress the chunks using zlib
xChunkProcessor->AddChunkProcessor(std::make_unique<XChunkOutputProcessorDeflate>());
m_writer->AddWritingStep(std::make_unique<StepAddOutputProcessor>(std::move(xChunkProcessor)));
// If there is encryption, the signed data of the zone is the final hash blocks provided by the Salsa20 IV adaption algorithm
return result;
}
std::unique_ptr<ZoneWriter> CreateWriter()
{
// TODO Support signed fastfiles
bool isSecure = false;
bool isEncrypted = true;
SetupBlocks();
auto contentInMemory = std::make_unique<StepWriteZoneContentToMemory>(std::make_unique<ContentWriter>(), m_zone, ZoneConstants::OFFSET_BLOCK_BIT_COUNT, ZoneConstants::INSERT_BLOCK);
auto* contentInMemoryPtr = contentInMemory.get();
m_writer->AddWritingStep(std::move(contentInMemory));
// Write zone header
m_writer->AddWritingStep(std::make_unique<StepWriteZoneHeader>(CreateHeaderForParams(isSecure, false, isEncrypted)));
// Setup loading XChunks from the zone from this point on.
auto* signatureDataProvider = AddXChunkProcessor(isEncrypted);
// Start of the XFile struct
//m_writer->AddWritingStep(std::make_unique<StepSkipBytes>(8)); // Skip size and externalSize fields since they are not interesting for us
m_writer->AddWritingStep(std::make_unique<StepWriteXBlockSizes>(m_zone));
// Start of the zone content
m_writer->AddWritingStep(std::make_unique<StepWriteZoneContentToFile>(contentInMemoryPtr));
// Return the fully setup zoneloader
return std::move(m_writer);
}
};
bool ZoneWriterFactory::SupportsZone(Zone* zone) const
{
return zone->m_game == &g_GameT6;
}
std::unique_ptr<ZoneWriter> ZoneWriterFactory::CreateWriter(Zone* zone) const
{
Impl impl(zone);
return impl.CreateWriter();
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <memory>
#include "Writing/IZoneWriterFactory.h"
namespace T6
{
class ZoneWriterFactory final : public IZoneWriterFactory
{
class Impl;
public:
_NODISCARD bool SupportsZone(Zone* zone) const override;
_NODISCARD std::unique_ptr<ZoneWriter> CreateWriter(Zone* zone) const override;
};
}

View File

View File

@ -0,0 +1,6 @@
#pragma once
class AssetWriter
{
};

View File

@ -0,0 +1,29 @@
#pragma once
#include "Zone/Zone.h"
#include "Zone/Stream/IZoneOutputStream.h"
class ContentWriterBase
{
protected:
static constexpr void* PTR_FOLLOWING = reinterpret_cast<void*>(-1);
static constexpr void* PTR_INSERT = reinterpret_cast<void*>(-2);
const char** varXString;
Zone* m_zone;
IZoneOutputStream* m_stream;
ContentWriterBase();
ContentWriterBase(Zone* zone, IZoneOutputStream* stream);
void WriteXString(bool atStreamStart) const;
void WriteXStringArray(bool atStreamStart, size_t count);
public:
virtual ~ContentWriterBase() = default;
ContentWriterBase(const ContentWriterBase& other) = default;
ContentWriterBase(ContentWriterBase&& other) noexcept = default;
ContentWriterBase& operator=(const ContentWriterBase& other) = default;
ContentWriterBase& operator=(ContentWriterBase&& other) noexcept = default;
};

View File

@ -0,0 +1,17 @@
#pragma once
#include "Zone/Zone.h"
#include "Zone/Stream/IZoneOutputStream.h"
class IContentWritingEntryPoint
{
public:
IContentWritingEntryPoint() = default;
virtual ~IContentWritingEntryPoint() = default;
IContentWritingEntryPoint(const IContentWritingEntryPoint& other) = default;
IContentWritingEntryPoint(IContentWritingEntryPoint&& other) noexcept = default;
IContentWritingEntryPoint& operator=(const IContentWritingEntryPoint& other) = default;
IContentWritingEntryPoint& operator=(IContentWritingEntryPoint&& other) noexcept = default;
virtual void WriteContent(Zone* zone, IZoneOutputStream* stream) = 0;
};

View File

@ -0,0 +1,19 @@
#pragma once
#include "Writing/ZoneWriter.h"
#include "IWritingStream.h"
class ZoneWriter;
class IWritingStep
{
public:
IWritingStep() = default;
virtual ~IWritingStep() = default;
IWritingStep(const IWritingStep& other) = default;
IWritingStep(IWritingStep&& other) noexcept = default;
IWritingStep& operator=(const IWritingStep& other) = default;
IWritingStep& operator=(IWritingStep&& other) noexcept = default;
virtual void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) = 0;
};

View File

@ -0,0 +1,18 @@
#pragma once
#include <cstdint>
#include <cstddef>
class IWritingStream
{
public:
IWritingStream() = default;
virtual ~IWritingStream() = default;
IWritingStream(const IWritingStream& other) = default;
IWritingStream(IWritingStream&& other) noexcept = default;
IWritingStream& operator=(const IWritingStream& other) = default;
IWritingStream& operator=(IWritingStream&& other) noexcept = default;
virtual void Write(const void* buffer, size_t length) = 0;
virtual int64_t Pos() = 0;
};

View File

@ -0,0 +1,19 @@
#pragma once
#include "Utils/ClassUtils.h"
#include "ZoneWriter.h"
#include "Zone/Zone.h"
class IZoneWriterFactory
{
public:
IZoneWriterFactory() = default;
virtual ~IZoneWriterFactory() = default;
IZoneWriterFactory(const IZoneWriterFactory& other) = default;
IZoneWriterFactory(IZoneWriterFactory&& other) noexcept = default;
IZoneWriterFactory& operator=(const IZoneWriterFactory& other) = default;
IZoneWriterFactory& operator=(IZoneWriterFactory&& other) noexcept = default;
_NODISCARD virtual bool SupportsZone(Zone* zone) const = 0;
_NODISCARD virtual std::unique_ptr<ZoneWriter> CreateWriter(Zone* zone) const = 0;
};

View File

@ -0,0 +1,31 @@
#include "InMemoryZoneData.h"
#include <cassert>
InMemoryZoneData::InMemoryZoneData()
: m_total_size(0)
{
m_buffers.emplace_back(BUFFER_SIZE);
}
InMemoryZoneData::MemoryBuffer::MemoryBuffer(const size_t size)
: m_data(std::make_unique<char[]>(size)),
m_size(0)
{
}
void* InMemoryZoneData::GetBufferOfSize(const size_t size)
{
assert(size <= BUFFER_SIZE);
if(m_buffers.back().m_size + size > BUFFER_SIZE)
{
m_buffers.emplace_back(BUFFER_SIZE);
}
auto& backBuffer = m_buffers.back();
void* result = &backBuffer.m_data[backBuffer.m_size];
backBuffer.m_size += size;
m_total_size += size;
return result;
}

View File

@ -0,0 +1,25 @@
#pragma once
#include <vector>
#include <memory>
class InMemoryZoneData
{
static constexpr size_t BUFFER_SIZE = 0x10000;
public:
class MemoryBuffer
{
public:
std::unique_ptr<char[]> m_data;
size_t m_size;
explicit MemoryBuffer(size_t size);
};
int64_t m_total_size;
std::vector<MemoryBuffer> m_buffers;
InMemoryZoneData();
void* GetBufferOfSize(size_t size);
};

View File

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

View File

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

View File

@ -0,0 +1,46 @@
#include "OutputProcessorXChunks.h"
class OutputProcessorXChunks::Impl
{
};
OutputProcessorXChunks::OutputProcessorXChunks(int numStreams, size_t xChunkSize)
{
}
OutputProcessorXChunks::OutputProcessorXChunks(int numStreams, size_t xChunkSize, size_t vanillaBufferSize)
{
}
OutputProcessorXChunks::~OutputProcessorXChunks()
{
delete m_impl;
m_impl = nullptr;
}
OutputProcessorXChunks::OutputProcessorXChunks(OutputProcessorXChunks&& other) noexcept
: m_impl(other.m_impl)
{
other.m_impl = nullptr;
}
OutputProcessorXChunks& OutputProcessorXChunks::operator=(OutputProcessorXChunks&& other) noexcept
{
m_impl = other.m_impl;
other.m_impl = nullptr;
return *this;
}
void OutputProcessorXChunks::AddChunkProcessor(std::unique_ptr<IXChunkOutputProcessor> chunkProcessor) const
{
}
void OutputProcessorXChunks::Write(const void* buffer, size_t length)
{
}
int64_t OutputProcessorXChunks::Pos()
{
return 0;
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <memory>
#include "Writing/OutputStreamProcessor.h"
#include "XChunks/IXChunkOutputProcessor.h"
class OutputProcessorXChunks final : public OutputStreamProcessor
{
class Impl;
Impl* m_impl;
public:
OutputProcessorXChunks(int numStreams, size_t xChunkSize);
OutputProcessorXChunks(int numStreams, size_t xChunkSize, size_t vanillaBufferSize);
~OutputProcessorXChunks() override;
OutputProcessorXChunks(const OutputProcessorXChunks& other) = delete;
OutputProcessorXChunks(OutputProcessorXChunks&& other) noexcept;
OutputProcessorXChunks& operator=(const OutputProcessorXChunks& other) = delete;
OutputProcessorXChunks& operator=(OutputProcessorXChunks&& other) noexcept;
void AddChunkProcessor(std::unique_ptr<IXChunkOutputProcessor> chunkProcessor) const;
void Write(const void* buffer, size_t length) override;
int64_t Pos() override;
};

View File

@ -0,0 +1,17 @@
#pragma once
#include <cstdint>
#include <cstddef>
class IXChunkOutputProcessor
{
public:
IXChunkOutputProcessor() = default;
virtual ~IXChunkOutputProcessor() = default;
IXChunkOutputProcessor(const IXChunkOutputProcessor& other) = default;
IXChunkOutputProcessor(IXChunkOutputProcessor&& other) noexcept = default;
IXChunkOutputProcessor& operator=(const IXChunkOutputProcessor& other) = default;
IXChunkOutputProcessor& operator=(IXChunkOutputProcessor&& other) noexcept = 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,8 @@
#pragma once
#include "IXChunkOutputProcessor.h"
class XChunkOutputProcessorDeflate final : public IXChunkOutputProcessor
{
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,20 @@
#pragma once
#include "IXChunkOutputProcessor.h"
#include "Utils/ICapturedDataProvider.h"
class XChunkOutputProcessorSalsa20 final : public IXChunkOutputProcessor, public ICapturedDataProvider
{
class Impl;
Impl* m_impl;
public:
XChunkOutputProcessorSalsa20(int streamCount, std::string& zoneName, const uint8_t* salsa20Key, size_t keySize);
~XChunkOutputProcessorSalsa20() override;
XChunkOutputProcessorSalsa20(const XChunkOutputProcessorSalsa20& other) = delete;
XChunkOutputProcessorSalsa20(XChunkOutputProcessorSalsa20&& other) noexcept = default;
XChunkOutputProcessorSalsa20& operator=(const XChunkOutputProcessorSalsa20& other) = delete;
XChunkOutputProcessorSalsa20& operator=(XChunkOutputProcessorSalsa20&& other) noexcept = default;
size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override;
void GetCapturedData(const uint8_t** pCapturedData, size_t* pSize) override;
};

View File

@ -0,0 +1,16 @@
#include "StepAddOutputProcessor.h"
#include <cassert>
StepAddOutputProcessor::StepAddOutputProcessor(std::unique_ptr<OutputStreamProcessor> streamProcessor)
: m_stream_processor(std::move(streamProcessor))
{
}
void StepAddOutputProcessor::PerformStep(ZoneWriter* zoneLoader, IWritingStream* stream)
{
assert(zoneLoader != nullptr);
assert(m_stream_processor != nullptr);
zoneLoader->AddStreamProcessor(std::move(m_stream_processor));
m_stream_processor = nullptr;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <memory>
#include "Writing/IWritingStep.h"
class StepAddOutputProcessor final : public IWritingStep
{
std::unique_ptr<OutputStreamProcessor> m_stream_processor;
public:
explicit StepAddOutputProcessor(std::unique_ptr<OutputStreamProcessor> streamProcessor);
void PerformStep(ZoneWriter* zoneLoader, IWritingStream* stream) override;
};

View File

@ -0,0 +1,10 @@
#include "StepWriteXBlockSizes.h"
StepWriteXBlockSizes::StepWriteXBlockSizes(Zone* zone)
: m_zone(zone)
{
}
void StepWriteXBlockSizes::PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream)
{
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "Writing/IWritingStep.h"
#include "Zone/Zone.h"
class StepWriteXBlockSizes final : public IWritingStep
{
Zone* m_zone;
public:
explicit StepWriteXBlockSizes(Zone* zone);
void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) override;
};

View File

@ -0,0 +1,20 @@
#include "StepWriteZero.h"
#include <algorithm>
StepWriteZero::StepWriteZero(const size_t count)
: m_count(count)
{
}
void StepWriteZero::PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream)
{
const uint64_t num = 0;
size_t toWrite;
for(auto i = 0u; i < m_count; i += toWrite)
{
toWrite = std::min(sizeof(uint64_t), m_count - i);
stream->Write(&num, toWrite);
}
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "Writing/IWritingStep.h"
class StepWriteZero final : public IWritingStep
{
size_t m_count;
public:
explicit StepWriteZero(size_t count);
void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) override;
};

View File

@ -0,0 +1,10 @@
#include "StepWriteZoneContentToFile.h"
StepWriteZoneContentToFile::StepWriteZoneContentToFile(StepWriteZoneContentToMemory* zoneMemory)
: m_memory(zoneMemory)
{
}
void StepWriteZoneContentToFile::PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream)
{
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "StepWriteZoneContentToMemory.h"
#include "Writing/IWritingStep.h"
class StepWriteZoneContentToFile final : public IWritingStep
{
StepWriteZoneContentToMemory* m_memory;
public:
explicit StepWriteZoneContentToFile(StepWriteZoneContentToMemory* zoneMemory);
void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) override;
};

View File

@ -0,0 +1,27 @@
#include "StepWriteZoneContentToMemory.h"
#include "Zone/Stream/Impl/InMemoryZoneOutputStream.h"
StepWriteZoneContentToMemory::StepWriteZoneContentToMemory(std::unique_ptr<IContentWritingEntryPoint> entryPoint, Zone* zone, int offsetBlockBitCount, block_t insertBlock)
: m_content_loader(std::move(entryPoint)),
m_zone_data(std::make_unique<InMemoryZoneData>()),
m_zone(zone),
m_offset_block_bit_count(offsetBlockBitCount),
m_insert_block(insertBlock)
{
}
void StepWriteZoneContentToMemory::PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream)
{
std::vector<XBlock*> blocks;
for (const auto& block : zoneWriter->m_blocks)
blocks.push_back(block.get());
const auto zoneOutputStream = std::make_unique<InMemoryZoneOutputStream>(m_zone_data.get(), std::move(blocks), m_offset_block_bit_count, m_insert_block);
m_content_loader->WriteContent(m_zone, zoneOutputStream.get());
}
InMemoryZoneData* StepWriteZoneContentToMemory::GetData() const
{
return m_zone_data.get();
}

View File

@ -0,0 +1,23 @@
#pragma once
#include <memory>
#include "Utils/ClassUtils.h"
#include "Writing/IContentWritingEntryPoint.h"
#include "Writing/InMemoryZoneData.h"
#include "Writing/IWritingStep.h"
class StepWriteZoneContentToMemory final : public IWritingStep
{
std::unique_ptr<IContentWritingEntryPoint> m_content_loader;
std::unique_ptr<InMemoryZoneData> m_zone_data;
Zone* m_zone;
int m_offset_block_bit_count;
block_t m_insert_block;
public:
StepWriteZoneContentToMemory(std::unique_ptr<IContentWritingEntryPoint> entryPoint, Zone* zone, int offsetBlockBitCount, block_t insertBlock);
void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) override;
_NODISCARD InMemoryZoneData* GetData() const;
};

View File

@ -0,0 +1,11 @@
#include "StepWriteZoneHeader.h"
StepWriteZoneHeader::StepWriteZoneHeader(const ZoneHeader header)
: m_header(header)
{
}
void StepWriteZoneHeader::PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream)
{
stream->Write(&m_header, sizeof(m_header));
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "Writing/IWritingStep.h"
class StepWriteZoneHeader final : public IWritingStep
{
ZoneHeader m_header;
public:
explicit StepWriteZoneHeader(ZoneHeader header);
void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) override;
};

View File

@ -0,0 +1,15 @@
#include "StepWriteZoneSizes.h"
StepWriteZoneSizes::StepWriteZoneSizes(StepWriteZoneContentToMemory* memory)
: m_memory(memory)
{
}
void StepWriteZoneSizes::PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream)
{
auto totalSize = static_cast<size_t>(m_memory->GetData()->m_total_size);
size_t externalSize = 0;
stream->Write(&totalSize, sizeof(totalSize));
stream->Write(&externalSize, sizeof(externalSize));
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "StepWriteZoneContentToMemory.h"
#include "Writing/IWritingStep.h"
class StepWriteZoneSizes final : public IWritingStep
{
StepWriteZoneContentToMemory* m_memory;
public:
explicit StepWriteZoneSizes(StepWriteZoneContentToMemory* memory);
void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) override;
};

View File

@ -0,0 +1,16 @@
#include "WritingException.h"
WritingException::WritingException(std::string message)
: m_error_message(std::move(message))
{
}
char const* WritingException::what() const
{
return m_error_message.c_str();
}
const std::string& WritingException::Message() const
{
return m_error_message;
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <string>
#include <exception>
#include "Utils/ClassUtils.h"
class WritingException final : public std::exception
{
std::string m_error_message;
public:
explicit WritingException(std::string message);
_NODISCARD char const* what() const override;
_NODISCARD const std::string& Message() const;
};

View File

@ -0,0 +1,16 @@
#include "WritingFileStream.h"
WritingFileStream::WritingFileStream(std::ostream& stream)
: m_stream(stream)
{
}
void WritingFileStream::Write(const void* buffer, const size_t length)
{
m_stream.write(static_cast<const char*>(buffer), length);
}
int64_t WritingFileStream::Pos()
{
return m_stream.tellp();
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <ostream>
#include "IWritingStream.h"
class WritingFileStream final : public IWritingStream
{
std::ostream& m_stream;
public:
explicit WritingFileStream(std::ostream& stream);
void Write(const void* buffer, size_t length) override;
int64_t Pos() override;
};

View File

@ -0,0 +1,79 @@
#include "ZoneWriter.h"
#include <iostream>
#include "WritingException.h"
#include "WritingFileStream.h"
ZoneWriter::ZoneWriter()
: m_processor_chain_dirty(false)
{
}
IWritingStream* ZoneWriter::BuildWritingChain(IWritingStream* rootStream)
{
auto* currentStream = rootStream;
for (const auto& processor : m_processors)
{
processor->SetBaseStream(currentStream);
currentStream = processor.get();
}
m_processor_chain_dirty = false;
return currentStream;
}
void ZoneWriter::AddXBlock(std::unique_ptr<XBlock> block)
{
m_blocks.emplace_back(std::move(block));
}
void ZoneWriter::AddWritingStep(std::unique_ptr<IWritingStep> step)
{
m_steps.emplace_back(std::move(step));
}
void ZoneWriter::AddStreamProcessor(std::unique_ptr<OutputStreamProcessor> streamProcessor)
{
m_processors.push_back(std::move(streamProcessor));
m_processor_chain_dirty = true;
}
void ZoneWriter::RemoveStreamProcessor(OutputStreamProcessor* streamProcessor)
{
for (auto i = m_processors.begin(); i < m_processors.end(); ++i)
{
if (i->get() == streamProcessor)
{
m_processors.erase(i);
m_processor_chain_dirty = true;
break;
}
}
}
bool ZoneWriter::WriteZone(std::ostream& stream)
{
WritingFileStream fileStream(stream);
auto* endStream = BuildWritingChain(&fileStream);
try
{
for (const auto& step : m_steps)
{
step->PerformStep(this, endStream);
if (m_processor_chain_dirty)
endStream = BuildWritingChain(&fileStream);
}
}
catch (WritingException& e)
{
std::cout << "Writing fastfile failed: " << e.Message() << std::endl;
return false;
}
return true;
}

View File

@ -0,0 +1,33 @@
#pragma once
#include <vector>
#include <memory>
#include "IWritingStep.h"
#include "OutputStreamProcessor.h"
#include "Zone/Zone.h"
class IWritingStep;
class ZoneWriter
{
std::vector<std::unique_ptr<IWritingStep>> m_steps;
std::vector<std::unique_ptr<OutputStreamProcessor>> m_processors;
bool m_processor_chain_dirty;
IWritingStream* BuildWritingChain(IWritingStream* rootStream);
public:
std::vector<std::unique_ptr<XBlock>> m_blocks;
ZoneWriter();
void AddXBlock(std::unique_ptr<XBlock> block);
void AddWritingStep(std::unique_ptr<IWritingStep> step);
void AddStreamProcessor(std::unique_ptr<OutputStreamProcessor> streamProcessor);
void RemoveStreamProcessor(OutputStreamProcessor* streamProcessor);
bool WriteZone(std::ostream& stream);
};

View File

@ -1,7 +1,50 @@
#pragma once
class IZoneOutputStream
#include <cstddef>
#include <typeindex>
#include <typeinfo>
#include "Zone/Stream/IZoneStream.h"
class IZoneOutputStream : public IZoneStream
{
public:
virtual void Align(int alignTo) = 0;
};
virtual void* WriteDataRaw(void* dst, size_t size) = 0;
virtual void* WriteDataInBlock(void* dst, size_t size) = 0;
virtual void IncBlockPos(size_t size) = 0;
virtual void WriteNullTerminated(void* dst) = 0;
virtual bool ReusableShouldWrite(void** pPtr, size_t size, size_t count, std::type_index type) = 0;
template<typename T>
bool ReusableShouldWrite(T** pPtr)
{
return ReusableShouldWrite(pPtr, sizeof(T), 1, std::type_index(typeid(T)));
}
template<typename T>
bool ReusableShouldWrite(T** pPtr, size_t count)
{
return ReusableShouldWrite(pPtr, sizeof(T), count, std::type_index(typeid(T)));
}
template<typename T>
T* Write(T* dst)
{
return static_cast<T*>(WriteDataInBlock(const_cast<void*>(reinterpret_cast<const void*>(dst)), sizeof(T)));
}
template<typename T>
T* Write(T* dst, const uint32_t count)
{
return static_cast<T*>(WriteDataInBlock(const_cast<void*>(reinterpret_cast<const void*>(dst)), count * sizeof(T)));
}
template<typename T>
T* WritePartial(T* dst, const size_t size)
{
return static_cast<T*>(WriteDataInBlock(const_cast<void*>(reinterpret_cast<const void*>(dst)), size));
}
};

View File

@ -0,0 +1,196 @@
#include "InMemoryZoneOutputStream.h"
#include <cassert>
InMemoryZoneOutputStream::InMemoryZoneOutputStream(InMemoryZoneData* zoneData, std::vector<XBlock*> blocks, const int blockBitCount, const block_t insertBlock)
: m_zone_data(zoneData),
m_blocks(std::move(blocks)),
m_block_bit_count(blockBitCount),
m_insert_block(m_blocks[insertBlock])
{
}
InMemoryZoneOutputStream::ReusableEntry::ReusableEntry(void* startPtr, const size_t entrySize, const size_t entryCount, const uintptr_t startZonePtr)
: m_start_ptr(startPtr),
m_end_ptr(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(startPtr) + entrySize * entryCount)),
m_start_zone_ptr(startZonePtr),
m_entry_size(entrySize), m_entry_count(entryCount)
{
}
void InMemoryZoneOutputStream::PushBlock(const block_t block)
{
assert(block >= 0 && block < static_cast<block_t>(m_blocks.size()));
auto* newBlock = m_blocks[block];
assert(newBlock->m_index == block);
m_block_stack.push(newBlock);
if (newBlock->m_type == XBlock::Type::BLOCK_TYPE_TEMP)
{
if (m_temp_sizes.empty())
m_temp_sizes.push(0);
else
m_temp_sizes.push(m_temp_sizes.top());
}
}
block_t InMemoryZoneOutputStream::PopBlock()
{
assert(!m_block_stack.empty());
if (m_block_stack.empty())
return -1;
auto* poppedBlock = m_block_stack.top();
m_block_stack.pop();
// If temp block is popped, see if its size is bigger than the current maximum temp size
if (poppedBlock->m_type == XBlock::Type::BLOCK_TYPE_TEMP)
{
const auto tempSize = m_temp_sizes.top();
m_temp_sizes.pop();
if (tempSize > poppedBlock->m_buffer_size)
poppedBlock->m_buffer_size = tempSize;
}
return poppedBlock->m_index;
}
void InMemoryZoneOutputStream::Align(const int align)
{
assert(!m_block_stack.empty());
if (align > 0)
{
auto* block = m_block_stack.top();
if (block->m_type == XBlock::Type::BLOCK_TYPE_TEMP)
m_temp_sizes.top() = (m_temp_sizes.top() + align - 1) / align * align;
else
block->m_buffer_size = (block->m_buffer_size + align - 1) / align * align;
}
}
void* InMemoryZoneOutputStream::WriteDataRaw(void* src, const size_t size)
{
auto* result = m_zone_data->GetBufferOfSize(size);
memcpy(result, src, size);
return result;
}
void* InMemoryZoneOutputStream::WriteDataInBlock(void* src, const size_t size)
{
assert(!m_block_stack.empty());
if (m_block_stack.empty())
return nullptr;
auto* block = m_block_stack.top();
void* result = nullptr;
switch (block->m_type)
{
case XBlock::Type::BLOCK_TYPE_TEMP:
case XBlock::Type::BLOCK_TYPE_NORMAL:
result = m_zone_data->GetBufferOfSize(size);
memcpy(result, src, size);
break;
case XBlock::Type::BLOCK_TYPE_RUNTIME:
break;
case XBlock::Type::BLOCK_TYPE_DELAY:
assert(false);
break;
}
IncBlockPos(size);
return result;
}
void InMemoryZoneOutputStream::IncBlockPos(const size_t size)
{
assert(!m_block_stack.empty());
if (m_block_stack.empty())
return;
auto* block = m_block_stack.top();
if (block->m_type == XBlock::Type::BLOCK_TYPE_TEMP)
{
m_temp_sizes.top() += size;
}
else
{
block->m_buffer_size += size;
}
}
void InMemoryZoneOutputStream::WriteNullTerminated(void* src)
{
const auto len = strlen(static_cast<char*>(src));
WriteDataInBlock(src, len + 1);
}
uintptr_t InMemoryZoneOutputStream::GetCurrentZonePointer()
{
assert(!m_block_stack.empty());
uintptr_t ptr = 0;
ptr |= static_cast<uintptr_t>(m_block_stack.top()->m_index) << (sizeof(uintptr_t) * 8 - m_block_bit_count);
ptr |= m_block_stack.top()->m_buffer_size & (UINTPTR_MAX >> m_block_bit_count);
ptr++;
return ptr;
}
uintptr_t InMemoryZoneOutputStream::InsertPointer()
{
PushBlock(m_insert_block->m_index);
Align(sizeof(uintptr_t));
const auto result = GetCurrentZonePointer();
IncBlockPos(sizeof(uintptr_t));
PopBlock();
return result;
}
bool InMemoryZoneOutputStream::ReusableShouldWrite(void** pPtr, const size_t entrySize, const size_t entryCount, std::type_index type)
{
assert(!m_block_stack.empty());
const auto inTemp = m_block_stack.top()->m_type == XBlock::Type::BLOCK_TYPE_TEMP;
const auto foundEntriesForType = m_reusable_entries.find(type);
if (foundEntriesForType == m_reusable_entries.end())
{
std::vector<ReusableEntry> entries;
auto zoneOffset = inTemp ? InsertPointer() : GetCurrentZonePointer();
entries.emplace_back(*pPtr, entrySize, entryCount, zoneOffset);
m_reusable_entries.emplace(std::make_pair(type, std::move(entries)));
*pPtr = inTemp ? PTR_INSERT : PTR_FOLLOWING;
return true;
}
for(const auto& entry : foundEntriesForType->second)
{
if(*pPtr >= entry.m_start_ptr && *pPtr < entry.m_end_ptr)
{
assert((reinterpret_cast<uintptr_t>(*pPtr) - reinterpret_cast<uintptr_t>(entry.m_start_ptr)) % entrySize == 0);
*pPtr = reinterpret_cast<void*>(entry.m_start_zone_ptr + (reinterpret_cast<uintptr_t>(*pPtr) - reinterpret_cast<uintptr_t>(entry.m_start_ptr)));
return false;
}
}
auto zoneOffset = inTemp ? InsertPointer() : GetCurrentZonePointer();
foundEntriesForType->second.emplace_back(*pPtr, entrySize, entryCount, zoneOffset);
*pPtr = inTemp ? PTR_INSERT : PTR_FOLLOWING;
return true;
}

View File

@ -0,0 +1,52 @@
#pragma once
#include <stack>
#include <unordered_map>
#include <vector>
#include "Writing/InMemoryZoneData.h"
#include "Zone/XBlock.h"
#include "Zone/Stream/IZoneOutputStream.h"
class InMemoryZoneOutputStream final : public IZoneOutputStream
{
static constexpr void* PTR_FOLLOWING = reinterpret_cast<void*>(-1);
static constexpr void* PTR_INSERT = reinterpret_cast<void*>(-2);
class ReusableEntry
{
public:
void* m_start_ptr;
void* m_end_ptr;
uintptr_t m_start_zone_ptr;
size_t m_entry_size;
size_t m_entry_count;
ReusableEntry(void* startPtr, size_t entrySize, size_t entryCount, uintptr_t startZonePtr);
};
InMemoryZoneData* m_zone_data;
std::vector<XBlock*> m_blocks;
std::stack<XBlock*> m_block_stack;
std::stack<size_t> m_temp_sizes;
int m_block_bit_count;
XBlock* m_insert_block;
std::unordered_map<std::type_index, std::vector<ReusableEntry>> m_reusable_entries;
uintptr_t GetCurrentZonePointer();
uintptr_t InsertPointer();
public:
InMemoryZoneOutputStream(InMemoryZoneData* zoneData, std::vector<XBlock*> blocks, int blockBitCount, block_t insertBlock);
void PushBlock(block_t block) override;
block_t PopBlock() override;
void Align(int align) override;
void* WriteDataRaw(void* src, size_t size) override;
void* WriteDataInBlock(void* src, size_t size) override;
void IncBlockPos(size_t size) override;
void WriteNullTerminated(void* src) override;
bool ReusableShouldWrite(void** pPtr, size_t entrySize, size_t entryCount, std::type_index type) override;
};

View File

@ -1,6 +1,16 @@
#include "ZoneWriting.h"
bool ZoneWriting::WriteZone(Zone* zone)
#include "Game/IW4/ZoneWriterFactoryIW4.h"
#include "Game/T6/ZoneWriterFactoryT6.h"
#include "Writing/IZoneWriterFactory.h"
IZoneWriterFactory* ZoneWriterFactories[]
{
new IW4::ZoneWriterFactory(),
new T6::ZoneWriterFactory()
};
bool ZoneWriting::WriteZone(std::ostream& stream, Zone* zone)
{
return true;
}

View File

@ -1,9 +1,11 @@
#pragma once
#include <string>
#include <ostream>
#include "Zone/Zone.h"
class ZoneWriting
{
public:
static bool WriteZone(Zone* zone);
static bool WriteZone(std::ostream& stream, Zone* zone);
};