Add IW3 zone writing

This commit is contained in:
Jan 2021-04-17 11:19:55 +02:00
parent e6a10fb992
commit 687d1185a3
10 changed files with 342 additions and 13 deletions

View File

@ -1,5 +1,9 @@
#include "ZoneCreatorIW3.h"
#include <iostream>
#include "ObjLoading.h"
#include "AssetLoading/AssetLoadingContext.h"
#include "Game/IW3/GameIW3.h"
#include "Game/IW3/GameAssetPoolIW3.h"
@ -18,6 +22,33 @@ void ZoneCreator::AddAssetTypeName(asset_type_t assetType, std::string name)
m_asset_types_by_name.emplace(std::make_pair(std::move(name), assetType));
}
std::vector<Gdt*> ZoneCreator::CreateGdtList(ZoneCreationContext& context)
{
std::vector<Gdt*> gdtList;
gdtList.reserve(context.m_gdt_files.size());
for (const auto& gdt : context.m_gdt_files)
gdtList.push_back(gdt.get());
return gdtList;
}
bool ZoneCreator::CreateIgnoredAssetMap(ZoneCreationContext& context, std::unordered_map<std::string, asset_type_t>& ignoredAssetMap) const
{
for (const auto& ignoreEntry : context.m_ignored_assets)
{
const auto foundAssetTypeEntry = m_asset_types_by_name.find(ignoreEntry.m_type);
if (foundAssetTypeEntry == m_asset_types_by_name.end())
{
std::cout << "Unknown asset type \"" << ignoreEntry.m_type << "\" for ignore \"" << ignoreEntry.m_name << "\"" << std::endl;
return false;
}
ignoredAssetMap[ignoreEntry.m_name] = foundAssetTypeEntry->second;
}
return true;
}
void ZoneCreator::CreateZoneAssetPools(Zone* zone) const
{
zone->m_pools = std::make_unique<GameAssetPoolIW3>(zone, zone->m_priority);
@ -34,10 +65,32 @@ bool ZoneCreator::SupportsGame(const std::string& gameName) const
std::unique_ptr<Zone> ZoneCreator::CreateZoneForDefinition(ZoneCreationContext& context) const
{
auto zone = std::make_unique<Zone>(context.m_zone_name, 0, &g_GameIW3);
zone->m_pools = std::make_unique<GameAssetPoolIW3>(zone.get(), zone->m_priority);
CreateZoneAssetPools(zone.get());
for (auto assetType = 0; assetType < ASSET_TYPE_COUNT; assetType++)
zone->m_pools->InitPoolDynamic(assetType);
for (const auto& assetEntry : context.m_definition->m_assets)
{
if (!assetEntry.m_is_reference)
continue;
context.m_ignored_assets.emplace_back(assetEntry.m_asset_type, assetEntry.m_asset_name);
}
const auto assetLoadingContext = std::make_unique<AssetLoadingContext>(zone.get(), context.m_asset_search_path, CreateGdtList(context));
if (!CreateIgnoredAssetMap(context, assetLoadingContext->m_ignored_asset_map))
return nullptr;
for (const auto& assetEntry : context.m_definition->m_assets)
{
const auto foundAssetTypeEntry = m_asset_types_by_name.find(assetEntry.m_asset_type);
if (foundAssetTypeEntry == m_asset_types_by_name.end())
{
std::cout << "Unknown asset type \"" << assetEntry.m_asset_type << "\"" << std::endl;
return nullptr;
}
if (!ObjLoading::LoadAssetForZone(assetLoadingContext.get(), foundAssetTypeEntry->second, assetEntry.m_asset_name))
return nullptr;
}
return zone;
}

View File

@ -12,6 +12,8 @@ namespace IW3
std::unordered_map<std::string, asset_type_t> m_asset_types_by_name;
void AddAssetTypeName(asset_type_t assetType, std::string name);
static std::vector<Gdt*> CreateGdtList(ZoneCreationContext& context);
bool CreateIgnoredAssetMap(ZoneCreationContext& context, std::unordered_map<std::string, asset_type_t>& ignoredAssetMap) const;
void CreateZoneAssetPools(Zone* zone) const;
public:

View File

@ -16,6 +16,7 @@
#include "ObjContainer/IWD/IWD.h"
#include "LinkerArgs.h"
#include "ZoneWriting.h"
#include "Game/IW3/ZoneCreatorIW3.h"
#include "ZoneCreation/ZoneCreationContext.h"
#include "ZoneCreation/IZoneCreator.h"
#include "Game/IW4/ZoneCreatorIW4.h"
@ -30,6 +31,7 @@ namespace fs = std::filesystem;
const IZoneCreator* const ZONE_CREATORS[]
{
new IW3::ZoneCreator(),
new IW4::ZoneCreator(),
new T6::ZoneCreator()
};

View File

@ -107,6 +107,10 @@ void ZoneDefinitionOutputStream::WriteEntry(const std::string& entryKey, const s
}
m_stream << '"';
}
else if (entryValue.empty())
{
m_stream << R"("")";
}
else
{
m_stream << entryValue;

View File

@ -85,12 +85,6 @@ public:
SetupBlock(zoneLoader);
// Skip unknown 1 byte field that the game ignores as well
// zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(1));
// Skip timestamp
// zoneLoader->AddLoadingStep(std::make_unique<StepSkipBytes>(8));
zoneLoader->AddLoadingStep(std::make_unique<StepAddProcessor>(std::make_unique<ProcessorInflate>(ZoneConstants::AUTHED_CHUNK_SIZE)));
// Start of the XFile struct

View File

@ -0,0 +1,91 @@
#include "ZoneWriterFactoryIW3.h"
#include <cstring>
#include "ContentWriterIW3.h"
#include "Game/IW3/IW3.h"
#include "Game/IW3/GameIW3.h"
#include "Game/IW3/ZoneConstantsIW3.h"
#include "Writing/Processor/OutputProcessorDeflate.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"
#include "Writing/Steps/StepWriteZoneSizes.h"
using namespace IW3;
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(IW3::XFILE_BLOCK_TEMP, XBlock::Type::BLOCK_TYPE_TEMP));
m_writer->AddXBlock(XBLOCK_DEF(IW3::XFILE_BLOCK_RUNTIME, XBlock::Type::BLOCK_TYPE_RUNTIME));
m_writer->AddXBlock(XBLOCK_DEF(IW3::XFILE_BLOCK_LARGE_RUNTIME, XBlock::Type::BLOCK_TYPE_RUNTIME));
m_writer->AddXBlock(XBLOCK_DEF(IW3::XFILE_BLOCK_PHYSICAL_RUNTIME, XBlock::Type::BLOCK_TYPE_RUNTIME));
m_writer->AddXBlock(XBLOCK_DEF(IW3::XFILE_BLOCK_VIRTUAL, XBlock::Type::BLOCK_TYPE_NORMAL));
m_writer->AddXBlock(XBLOCK_DEF(IW3::XFILE_BLOCK_LARGE, XBlock::Type::BLOCK_TYPE_NORMAL));
m_writer->AddXBlock(XBLOCK_DEF(IW3::XFILE_BLOCK_PHYSICAL, XBlock::Type::BLOCK_TYPE_NORMAL));
m_writer->AddXBlock(XBLOCK_DEF(IW3::XFILE_BLOCK_VERTEX, XBlock::Type::BLOCK_TYPE_NORMAL));
m_writer->AddXBlock(XBLOCK_DEF(IW3::XFILE_BLOCK_INDEX, XBlock::Type::BLOCK_TYPE_NORMAL));
#undef XBLOCK_DEF
}
static ZoneHeader CreateHeaderForParams()
{
ZoneHeader header{};
header.m_version = ZoneConstants::ZONE_VERSION;
memcpy(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, sizeof(ZoneHeader::m_magic));
return header;
}
std::unique_ptr<ZoneWriter> CreateWriter()
{
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()));
m_writer->AddWritingStep(std::make_unique<StepAddOutputProcessor>(std::make_unique<OutputProcessorDeflate>()));
// Start of the XFile struct
m_writer->AddWritingStep(std::make_unique<StepWriteZoneSizes>(contentInMemoryPtr));
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_GameIW3;
}
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 IW3
{
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

@ -0,0 +1,138 @@
#include "OutputProcessorDeflate.h"
#include <stdexcept>
#include <cstdint>
#include <memory>
#include <zlib.h>
#include "Utils/ClassUtils.h"
#include "Writing/WritingException.h"
class OutputProcessorDeflate::Impl
{
z_stream m_stream{};
OutputProcessorDeflate* m_base;
std::unique_ptr<uint8_t[]> m_buffer;
size_t m_buffer_size;
public:
Impl(OutputProcessorDeflate* baseClass, const size_t bufferSize)
: m_buffer(std::make_unique<uint8_t[]>(bufferSize)),
m_buffer_size(bufferSize)
{
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;
m_stream.next_out = m_buffer.get();
m_stream.avail_out = m_buffer_size;
const int ret = deflateInit(&m_stream, Z_DEFAULT_COMPRESSION);
if (ret != Z_OK)
{
throw std::runtime_error("Initializing deflate failed");
}
}
~Impl()
{
deflateEnd(&m_stream);
}
Impl(const Impl& other) = delete;
Impl(Impl&& other) noexcept = default;
Impl& operator=(const Impl& other) = delete;
Impl& operator=(Impl&& other) noexcept = default;
void Write(const void* buffer, const size_t length)
{
m_stream.next_in = static_cast<const Bytef*>(buffer);
m_stream.avail_in = length;
while (m_stream.avail_in > 0)
{
if (m_stream.avail_out == 0)
{
m_base->m_base_stream->Write(m_buffer.get(), m_buffer_size);
m_stream.next_out = m_buffer.get();
m_stream.avail_out = m_buffer_size;
}
const auto ret = deflate(&m_stream, Z_NO_FLUSH);
if (ret != Z_OK)
throw WritingException("Failed to deflate memory of zone.");
}
}
void Flush()
{
m_stream.avail_in = 0;
m_stream.next_in = Z_NULL;
while(true)
{
if (m_stream.avail_out < m_buffer_size)
{
m_base->m_base_stream->Write(m_buffer.get(), m_buffer_size - m_stream.avail_out);
m_stream.next_out = m_buffer.get();
m_stream.avail_out = m_buffer_size;
}
const auto ret = deflate(&m_stream, Z_FINISH);
if(ret == Z_OK)
continue;
if (ret != Z_STREAM_END)
throw WritingException("Failed to flush deflate memory of zone.");
break;
}
if (m_stream.avail_out < m_buffer_size)
{
m_base->m_base_stream->Write(m_buffer.get(), m_buffer_size - m_stream.avail_out);
m_stream.next_out = m_buffer.get();
m_stream.avail_out = m_buffer_size;
}
}
_NODISCARD int64_t Pos() const
{
return m_base->m_base_stream->Pos();
}
};
OutputProcessorDeflate::OutputProcessorDeflate()
: m_impl(new Impl(this, DEFAULT_BUFFER_SIZE))
{
}
OutputProcessorDeflate::OutputProcessorDeflate(const size_t bufferSize)
: m_impl(new Impl(this, bufferSize))
{
}
OutputProcessorDeflate::~OutputProcessorDeflate()
{
delete m_impl;
m_impl = nullptr;
}
void OutputProcessorDeflate::Write(const void* buffer, const size_t length)
{
m_impl->Write(buffer, length);
}
void OutputProcessorDeflate::Flush()
{
m_impl->Flush();
}
int64_t OutputProcessorDeflate::Pos()
{
return m_impl->Pos();
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include "Writing/OutputStreamProcessor.h"
class OutputProcessorDeflate final : public OutputStreamProcessor
{
class Impl;
Impl* m_impl;
static constexpr size_t DEFAULT_BUFFER_SIZE = 0x40000;
public:
OutputProcessorDeflate();
OutputProcessorDeflate(size_t bufferSize);
~OutputProcessorDeflate() override;
OutputProcessorDeflate(const OutputProcessorDeflate& other) = delete;
OutputProcessorDeflate(OutputProcessorDeflate&& other) noexcept = default;
OutputProcessorDeflate& operator=(const OutputProcessorDeflate& other) = delete;
OutputProcessorDeflate& operator=(OutputProcessorDeflate&& other) noexcept = default;
void Write(const void* buffer, size_t length) override;
void Flush() override;
int64_t Pos() override;
};

View File

@ -1,11 +1,13 @@
#include "ZoneWriting.h"
#include "Game/IW3/ZoneWriterFactoryIW3.h"
#include "Game/IW4/ZoneWriterFactoryIW4.h"
#include "Game/T6/ZoneWriterFactoryT6.h"
#include "Writing/IZoneWriterFactory.h"
IZoneWriterFactory* ZoneWriterFactories[]
{
new IW3::ZoneWriterFactory(),
new IW4::ZoneWriterFactory(),
new T6::ZoneWriterFactory()
};