diff --git a/src/Linker/Game/IW3/ZoneCreatorIW3.cpp b/src/Linker/Game/IW3/ZoneCreatorIW3.cpp index b5c4697d..2cef97a3 100644 --- a/src/Linker/Game/IW3/ZoneCreatorIW3.cpp +++ b/src/Linker/Game/IW3/ZoneCreatorIW3.cpp @@ -1,5 +1,9 @@ #include "ZoneCreatorIW3.h" +#include + +#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 ZoneCreator::CreateGdtList(ZoneCreationContext& context) +{ + std::vector 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& 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(zone, zone->m_priority); @@ -34,10 +65,32 @@ bool ZoneCreator::SupportsGame(const std::string& gameName) const std::unique_ptr ZoneCreator::CreateZoneForDefinition(ZoneCreationContext& context) const { auto zone = std::make_unique(context.m_zone_name, 0, &g_GameIW3); - zone->m_pools = std::make_unique(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(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; } diff --git a/src/Linker/Game/IW3/ZoneCreatorIW3.h b/src/Linker/Game/IW3/ZoneCreatorIW3.h index 86ad1a88..37c77262 100644 --- a/src/Linker/Game/IW3/ZoneCreatorIW3.h +++ b/src/Linker/Game/IW3/ZoneCreatorIW3.h @@ -12,6 +12,8 @@ namespace IW3 std::unordered_map m_asset_types_by_name; void AddAssetTypeName(asset_type_t assetType, std::string name); + static std::vector CreateGdtList(ZoneCreationContext& context); + bool CreateIgnoredAssetMap(ZoneCreationContext& context, std::unordered_map& ignoredAssetMap) const; void CreateZoneAssetPools(Zone* zone) const; public: diff --git a/src/Linker/Linker.cpp b/src/Linker/Linker.cpp index dfebe8a9..19fb8300 100644 --- a/src/Linker/Linker.cpp +++ b/src/Linker/Linker.cpp @@ -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() }; diff --git a/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp b/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp index 5e20f475..e172bfaf 100644 --- a/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp +++ b/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp @@ -84,13 +84,13 @@ void ZoneDefinitionOutputStream::WriteEntry(const std::string& entryKey, const s { m_stream << entryKey << ","; - if(entryValue.find('"') != std::string::npos + if (entryValue.find('"') != std::string::npos || entryValue.find("//") != std::string::npos) { m_stream << '"'; - for(const auto& c : entryValue) + for (const auto& c : entryValue) { - switch(c) + switch (c) { case '"': m_stream << "\\\""; @@ -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; diff --git a/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp b/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp index 89669378..77c59f53 100644 --- a/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp +++ b/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp @@ -85,12 +85,6 @@ public: SetupBlock(zoneLoader); - // Skip unknown 1 byte field that the game ignores as well - // zoneLoader->AddLoadingStep(std::make_unique(1)); - - // Skip timestamp - // zoneLoader->AddLoadingStep(std::make_unique(8)); - zoneLoader->AddLoadingStep(std::make_unique(std::make_unique(ZoneConstants::AUTHED_CHUNK_SIZE))); // Start of the XFile struct diff --git a/src/ZoneWriting/Game/IW3/ZoneWriterFactoryIW3.cpp b/src/ZoneWriting/Game/IW3/ZoneWriterFactoryIW3.cpp index e69de29b..50cc4bff 100644 --- a/src/ZoneWriting/Game/IW3/ZoneWriterFactoryIW3.cpp +++ b/src/ZoneWriting/Game/IW3/ZoneWriterFactoryIW3.cpp @@ -0,0 +1,91 @@ +#include "ZoneWriterFactoryIW3.h" + +#include + +#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 m_writer; + +public: + explicit Impl(Zone* zone) + : m_zone(zone), + m_writer(std::make_unique()) + { + } + + void SetupBlocks() const + { +#define XBLOCK_DEF(name, type) std::make_unique(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 CreateWriter() + { + SetupBlocks(); + + auto contentInMemory = std::make_unique(std::make_unique(), 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(CreateHeaderForParams())); + + m_writer->AddWritingStep(std::make_unique(std::make_unique())); + + // Start of the XFile struct + m_writer->AddWritingStep(std::make_unique(contentInMemoryPtr)); + m_writer->AddWritingStep(std::make_unique(m_zone)); + + // Start of the zone content + m_writer->AddWritingStep(std::make_unique(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 ZoneWriterFactory::CreateWriter(Zone* zone) const +{ + Impl impl(zone); + return impl.CreateWriter(); +} diff --git a/src/ZoneWriting/Game/IW3/ZoneWriterFactoryIW3.h b/src/ZoneWriting/Game/IW3/ZoneWriterFactoryIW3.h index e69de29b..f73d235a 100644 --- a/src/ZoneWriting/Game/IW3/ZoneWriterFactoryIW3.h +++ b/src/ZoneWriting/Game/IW3/ZoneWriterFactoryIW3.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#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 CreateWriter(Zone* zone) const override; + }; +} diff --git a/src/ZoneWriting/Writing/Processor/OutputProcessorDeflate.cpp b/src/ZoneWriting/Writing/Processor/OutputProcessorDeflate.cpp new file mode 100644 index 00000000..84c6eaac --- /dev/null +++ b/src/ZoneWriting/Writing/Processor/OutputProcessorDeflate.cpp @@ -0,0 +1,138 @@ +#include "OutputProcessorDeflate.h" + +#include +#include +#include +#include + +#include "Utils/ClassUtils.h" +#include "Writing/WritingException.h" + +class OutputProcessorDeflate::Impl +{ + z_stream m_stream{}; + OutputProcessorDeflate* m_base; + + std::unique_ptr m_buffer; + size_t m_buffer_size; + +public: + Impl(OutputProcessorDeflate* baseClass, const size_t bufferSize) + : m_buffer(std::make_unique(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(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(); +} diff --git a/src/ZoneWriting/Writing/Processor/OutputProcessorDeflate.h b/src/ZoneWriting/Writing/Processor/OutputProcessorDeflate.h new file mode 100644 index 00000000..a7e7036e --- /dev/null +++ b/src/ZoneWriting/Writing/Processor/OutputProcessorDeflate.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +#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; +}; \ No newline at end of file diff --git a/src/ZoneWriting/ZoneWriting.cpp b/src/ZoneWriting/ZoneWriting.cpp index 42f103df..8f583c7a 100644 --- a/src/ZoneWriting/ZoneWriting.cpp +++ b/src/ZoneWriting/ZoneWriting.cpp @@ -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() }; @@ -15,7 +17,7 @@ bool ZoneWriting::WriteZone(std::ostream& stream, Zone* zone) std::unique_ptr zoneWriter; for (auto* factory : ZoneWriterFactories) { - if(factory->SupportsZone(zone)) + if (factory->SupportsZone(zone)) { zoneWriter = factory->CreateWriter(zone); break;