diff --git a/src/Linker/Game/T5/ZoneCreatorT5.cpp b/src/Linker/Game/T5/ZoneCreatorT5.cpp index e69de29b..988c439c 100644 --- a/src/Linker/Game/T5/ZoneCreatorT5.cpp +++ b/src/Linker/Game/T5/ZoneCreatorT5.cpp @@ -0,0 +1,96 @@ +#include "ZoneCreatorT5.h" + +#include + +#include "ObjLoading.h" +#include "AssetLoading/AssetLoadingContext.h" +#include "Game/T5/GameT5.h" +#include "Game/T5/GameAssetPoolT5.h" + +using namespace T5; + +ZoneCreator::ZoneCreator() +{ + for (auto assetType = 0; assetType < ASSET_TYPE_COUNT; assetType++) + { + AddAssetTypeName(assetType, GameAssetPoolT5::AssetTypeNameByType(assetType)); + } +} + +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); + + for (auto assetType = 0; assetType < ASSET_TYPE_COUNT; assetType++) + zone->m_pools->InitPoolDynamic(assetType); +} + +bool ZoneCreator::SupportsGame(const std::string& gameName) const +{ + return gameName == g_GameT5.GetShortName(); +} + +std::unique_ptr ZoneCreator::CreateZoneForDefinition(ZoneCreationContext& context) const +{ + auto zone = std::make_unique(context.m_zone_name, 0, &g_GameT5); + CreateZoneAssetPools(zone.get()); + + 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/T5/ZoneCreatorT5.h b/src/Linker/Game/T5/ZoneCreatorT5.h index e69de29b..0c676019 100644 --- a/src/Linker/Game/T5/ZoneCreatorT5.h +++ b/src/Linker/Game/T5/ZoneCreatorT5.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +#include "Zone/ZoneTypes.h" +#include "ZoneCreation/IZoneCreator.h" + +namespace T5 +{ + class ZoneCreator final : public IZoneCreator + { + 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: + ZoneCreator(); + + _NODISCARD bool SupportsGame(const std::string& gameName) const override; + _NODISCARD std::unique_ptr CreateZoneForDefinition(ZoneCreationContext& context) const override; + }; +} diff --git a/src/Linker/Linker.cpp b/src/Linker/Linker.cpp index 19fb8300..76ee0aeb 100644 --- a/src/Linker/Linker.cpp +++ b/src/Linker/Linker.cpp @@ -20,6 +20,7 @@ #include "ZoneCreation/ZoneCreationContext.h" #include "ZoneCreation/IZoneCreator.h" #include "Game/IW4/ZoneCreatorIW4.h" +#include "Game/T5/ZoneCreatorT5.h" #include "Game/T6/ZoneCreatorT6.h" #include "Utils/ObjFileStream.h" @@ -33,6 +34,7 @@ const IZoneCreator* const ZONE_CREATORS[] { new IW3::ZoneCreator(), new IW4::ZoneCreator(), + new T5::ZoneCreator(), new T6::ZoneCreator() }; diff --git a/src/ZoneWriting/Game/T5/ContentWriterT5.cpp b/src/ZoneWriting/Game/T5/ContentWriterT5.cpp index e69de29b..a0d6de23 100644 --- a/src/ZoneWriting/Game/T5/ContentWriterT5.cpp +++ b/src/ZoneWriting/Game/T5/ContentWriterT5.cpp @@ -0,0 +1,216 @@ +#include "ContentWriterT5.h" + +#include +#include + +#include "Game/T5/XAssets/clipmap_t/clipmap_t_write_db.h" +#include "Game/T5/XAssets/comworld/comworld_write_db.h" +#include "Game/T5/XAssets/ddlroot_t/ddlroot_t_write_db.h" +#include "Game/T5/XAssets/destructibledef/destructibledef_write_db.h" +#include "Game/T5/XAssets/emblemset/emblemset_write_db.h" +#include "Game/T5/XAssets/font_s/font_s_write_db.h" +#include "Game/T5/XAssets/fxeffectdef/fxeffectdef_write_db.h" +#include "Game/T5/XAssets/fximpacttable/fximpacttable_write_db.h" +#include "Game/T5/XAssets/gameworldmp/gameworldmp_write_db.h" +#include "Game/T5/XAssets/gameworldsp/gameworldsp_write_db.h" +#include "Game/T5/XAssets/gfximage/gfximage_write_db.h" +#include "Game/T5/XAssets/gfxlightdef/gfxlightdef_write_db.h" +#include "Game/T5/XAssets/gfxworld/gfxworld_write_db.h" +#include "Game/T5/XAssets/glasses/glasses_write_db.h" +#include "Game/T5/XAssets/localizeentry/localizeentry_write_db.h" +#include "Game/T5/XAssets/mapents/mapents_write_db.h" +#include "Game/T5/XAssets/material/material_write_db.h" +#include "Game/T5/XAssets/materialtechniqueset/materialtechniqueset_write_db.h" +#include "Game/T5/XAssets/menudef_t/menudef_t_write_db.h" +#include "Game/T5/XAssets/menulist/menulist_write_db.h" +#include "Game/T5/XAssets/packindex/packindex_write_db.h" +#include "Game/T5/XAssets/physconstraints/physconstraints_write_db.h" +#include "Game/T5/XAssets/physpreset/physpreset_write_db.h" +#include "Game/T5/XAssets/rawfile/rawfile_write_db.h" +#include "Game/T5/XAssets/sndbank/sndbank_write_db.h" +#include "Game/T5/XAssets/snddriverglobals/snddriverglobals_write_db.h" +#include "Game/T5/XAssets/sndpatch/sndpatch_write_db.h" +#include "Game/T5/XAssets/stringtable/stringtable_write_db.h" +#include "Game/T5/XAssets/weaponvariantdef/weaponvariantdef_write_db.h" +#include "Game/T5/XAssets/xanimparts/xanimparts_write_db.h" +#include "Game/T5/XAssets/xglobals/xglobals_write_db.h" +#include "Game/T5/XAssets/xmodel/xmodel_write_db.h" + +#include "Writing/WritingException.h" + +using namespace T5; + +ContentWriter::ContentWriter() + : varXAssetList(nullptr), + varXAsset(nullptr), + varScriptStringList(nullptr) +{ +} + +void ContentWriter::CreateXAssetList(XAssetList& xAssetList, MemoryManager& memory) const +{ + if (!m_zone->m_script_strings.Empty()) + { + assert(m_zone->m_script_strings.Count() <= SCR_STRING_MAX + 1); + xAssetList.stringList.count = m_zone->m_script_strings.Count(); + xAssetList.stringList.strings = static_cast(memory.Alloc(sizeof(const char*) * m_zone->m_script_strings.Count())); + + for (auto i = 0u; i < m_zone->m_script_strings.Count(); i++) + { + xAssetList.stringList.strings[i] = m_zone->m_script_strings[i].c_str(); + } + } + else + { + xAssetList.stringList.count = 0; + xAssetList.stringList.strings = nullptr; + } + + const auto assetCount = m_zone->m_pools->GetTotalAssetCount(); + if (assetCount > 0) + { + xAssetList.assetCount = assetCount; + xAssetList.assets = static_cast(memory.Alloc(sizeof(XAsset) * assetCount)); + + const auto end = m_zone->m_pools->end(); + auto index = 0u; + for (auto i = m_zone->m_pools->begin(); i != end; ++i) + { + auto& asset = xAssetList.assets[index++]; + asset.type = static_cast((*i)->m_type); + asset.header.data = (*i)->m_ptr; + } + } + else + { + xAssetList.assetCount = 0; + xAssetList.assets = nullptr; + } +} + +void ContentWriter::WriteScriptStringList(const bool atStreamStart) +{ + m_stream->PushBlock(XFILE_BLOCK_VIRTUAL); + + if (atStreamStart) + varScriptStringList = m_stream->Write(varScriptStringList); + + if (varScriptStringList->strings != nullptr) + { + m_stream->Align(alignof(const char*)); + varXString = varScriptStringList->strings; + WriteXStringArray(true, varScriptStringList->count); + + m_stream->MarkFollowing(varScriptStringList->strings); + } + + m_stream->PopBlock(); +} + +void ContentWriter::WriteXAsset(const bool atStreamStart) +{ +#define WRITE_ASSET(type_index, typeName, headerEntry) \ + case type_index: \ + { \ + Writer_##typeName writer(varXAsset->header.headerEntry, m_zone, m_stream); \ + writer.Write(&varXAsset->header.headerEntry); \ + break; \ + } +#define SKIP_ASSET(type_index, typeName, headerEntry) \ + case type_index: \ + break; + + assert(varXAsset != nullptr); + + if (atStreamStart) + varXAsset = m_stream->Write(varXAsset); + + switch (varXAsset->type) + { + WRITE_ASSET(ASSET_TYPE_PHYSPRESET, PhysPreset, physPreset) + WRITE_ASSET(ASSET_TYPE_PHYSCONSTRAINTS, PhysConstraints, physConstraints) + WRITE_ASSET(ASSET_TYPE_DESTRUCTIBLEDEF, DestructibleDef, destructibleDef) + WRITE_ASSET(ASSET_TYPE_XANIMPARTS, XAnimParts, parts) + WRITE_ASSET(ASSET_TYPE_XMODEL, XModel, model) + WRITE_ASSET(ASSET_TYPE_MATERIAL, Material, material) + WRITE_ASSET(ASSET_TYPE_TECHNIQUE_SET, MaterialTechniqueSet, techniqueSet) + WRITE_ASSET(ASSET_TYPE_IMAGE, GfxImage, image) + WRITE_ASSET(ASSET_TYPE_SOUND, SndBank, sound) + WRITE_ASSET(ASSET_TYPE_SOUND_PATCH, SndPatch, soundPatch) + WRITE_ASSET(ASSET_TYPE_CLIPMAP, clipMap_t, clipMap) + WRITE_ASSET(ASSET_TYPE_CLIPMAP_PVS, clipMap_t, clipMap) + WRITE_ASSET(ASSET_TYPE_COMWORLD, ComWorld, comWorld) + WRITE_ASSET(ASSET_TYPE_GAMEWORLD_SP, GameWorldSp, gameWorldSp) + WRITE_ASSET(ASSET_TYPE_GAMEWORLD_MP, GameWorldMp, gameWorldMp) + WRITE_ASSET(ASSET_TYPE_MAP_ENTS, MapEnts, mapEnts) + WRITE_ASSET(ASSET_TYPE_GFXWORLD, GfxWorld, gfxWorld) + WRITE_ASSET(ASSET_TYPE_LIGHT_DEF, GfxLightDef, lightDef) + WRITE_ASSET(ASSET_TYPE_FONT, Font_s, font) + WRITE_ASSET(ASSET_TYPE_MENULIST, MenuList, menuList) + WRITE_ASSET(ASSET_TYPE_MENU, menuDef_t, menu) + WRITE_ASSET(ASSET_TYPE_LOCALIZE_ENTRY, LocalizeEntry, localize) + WRITE_ASSET(ASSET_TYPE_WEAPON, WeaponVariantDef, weapon) + WRITE_ASSET(ASSET_TYPE_SNDDRIVER_GLOBALS, SndDriverGlobals, sndDriverGlobals) + WRITE_ASSET(ASSET_TYPE_FX, FxEffectDef, fx) + WRITE_ASSET(ASSET_TYPE_IMPACT_FX, FxImpactTable, impactFx) + WRITE_ASSET(ASSET_TYPE_RAWFILE, RawFile, rawfile) + WRITE_ASSET(ASSET_TYPE_STRINGTABLE, StringTable, stringTable) + WRITE_ASSET(ASSET_TYPE_PACK_INDEX, PackIndex, packIndex) + WRITE_ASSET(ASSET_TYPE_XGLOBALS, XGlobals, xGlobals) + WRITE_ASSET(ASSET_TYPE_DDL, ddlRoot_t, ddlRoot) + WRITE_ASSET(ASSET_TYPE_GLASSES, Glasses, glasses) + WRITE_ASSET(ASSET_TYPE_EMBLEMSET, EmblemSet, emblemSet) + + default: + { + std::ostringstream str; + str << "Unsupported asset type: " << varXAsset->type << "."; + throw WritingException(str.str()); + } + } + +#undef WRITE_ASSET +#undef SKIP_ASSET +} + +void ContentWriter::WriteXAssetArray(const bool atStreamStart, const size_t count) +{ + assert(varXAsset != nullptr); + + if (atStreamStart) + varXAsset = m_stream->Write(varXAsset, count); + + for (size_t index = 0; index < count; index++) + { + WriteXAsset(false); + varXAsset++; + } +} + +void ContentWriter::WriteContent(Zone* zone, IZoneOutputStream* stream) +{ + m_zone = zone; + m_stream = stream; + + m_stream->PushBlock(XFILE_BLOCK_VIRTUAL); + + MemoryManager memory; + XAssetList assetList{}; + + CreateXAssetList(assetList, memory); + + varXAssetList = static_cast(m_stream->WriteDataRaw(&assetList, sizeof(assetList))); + + varScriptStringList = &varXAssetList->stringList; + WriteScriptStringList(false); + + if (varXAssetList->assets != nullptr) + { + m_stream->Align(alignof(XAsset)); + varXAsset = varXAssetList->assets; + WriteXAssetArray(true, varXAssetList->assetCount); + m_stream->MarkFollowing(varXAssetList->assets); + } + + m_stream->PopBlock(); +} diff --git a/src/ZoneWriting/Game/T5/ContentWriterT5.h b/src/ZoneWriting/Game/T5/ContentWriterT5.h index e69de29b..51b118ad 100644 --- a/src/ZoneWriting/Game/T5/ContentWriterT5.h +++ b/src/ZoneWriting/Game/T5/ContentWriterT5.h @@ -0,0 +1,26 @@ +#pragma once +#include "Writing/ContentWriterBase.h" +#include "Writing/IContentWritingEntryPoint.h" +#include "Game/T5/T5.h" + +namespace T5 +{ + class ContentWriter final : public ContentWriterBase, public IContentWritingEntryPoint + { + XAssetList* varXAssetList; + XAsset* varXAsset; + ScriptStringList* varScriptStringList; + + void CreateXAssetList(XAssetList& xAssetList, MemoryManager& memory) const; + + void WriteScriptStringList(bool atStreamStart); + + void WriteXAsset(bool atStreamStart); + void WriteXAssetArray(bool atStreamStart, size_t count); + + public: + ContentWriter(); + + void WriteContent(Zone* zone, IZoneOutputStream* stream) override; + }; +} diff --git a/src/ZoneWriting/Game/T5/ZoneWriterFactoryT5.cpp b/src/ZoneWriting/Game/T5/ZoneWriterFactoryT5.cpp index e69de29b..4eae84b6 100644 --- a/src/ZoneWriting/Game/T5/ZoneWriterFactoryT5.cpp +++ b/src/ZoneWriting/Game/T5/ZoneWriterFactoryT5.cpp @@ -0,0 +1,89 @@ +#include "ZoneWriterFactoryT5.h" + +#include + +#include "ContentWriterT5.h" +#include "Game/T5/T5.h" +#include "Game/T5/GameT5.h" +#include "Game/T5/ZoneConstantsT5.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 T5; + +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(T5::XFILE_BLOCK_TEMP, XBlock::Type::BLOCK_TYPE_TEMP)); + m_writer->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_RUNTIME, XBlock::Type::BLOCK_TYPE_RUNTIME)); + m_writer->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_LARGE_RUNTIME, XBlock::Type::BLOCK_TYPE_RUNTIME)); + m_writer->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_PHYSICAL_RUNTIME, XBlock::Type::BLOCK_TYPE_RUNTIME)); + m_writer->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_VIRTUAL, XBlock::Type::BLOCK_TYPE_NORMAL)); + m_writer->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_LARGE, XBlock::Type::BLOCK_TYPE_NORMAL)); + m_writer->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_PHYSICAL, 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_GameT5; +} + +std::unique_ptr ZoneWriterFactory::CreateWriter(Zone* zone) const +{ + Impl impl(zone); + return impl.CreateWriter(); +} diff --git a/src/ZoneWriting/Game/T5/ZoneWriterFactoryT5.h b/src/ZoneWriting/Game/T5/ZoneWriterFactoryT5.h index e69de29b..04d67bb4 100644 --- a/src/ZoneWriting/Game/T5/ZoneWriterFactoryT5.h +++ b/src/ZoneWriting/Game/T5/ZoneWriterFactoryT5.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include "Writing/IZoneWriterFactory.h" + +namespace T5 +{ + 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/ZoneWriting.cpp b/src/ZoneWriting/ZoneWriting.cpp index 8f583c7a..cb8a47e2 100644 --- a/src/ZoneWriting/ZoneWriting.cpp +++ b/src/ZoneWriting/ZoneWriting.cpp @@ -2,6 +2,7 @@ #include "Game/IW3/ZoneWriterFactoryIW3.h" #include "Game/IW4/ZoneWriterFactoryIW4.h" +#include "Game/T5/ZoneWriterFactoryT5.h" #include "Game/T6/ZoneWriterFactoryT6.h" #include "Writing/IZoneWriterFactory.h" @@ -9,6 +10,7 @@ IZoneWriterFactory* ZoneWriterFactories[] { new IW3::ZoneWriterFactory(), new IW4::ZoneWriterFactory(), + new T5::ZoneWriterFactory(), new T6::ZoneWriterFactory() };