From bb3e7d9e8845ebf8aafc39472f165182be0d7e1a Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 12 May 2021 21:47:34 +0200 Subject: [PATCH] Load T5 fastfiles --- src/Common/Game/T5/T5_Assets.h | 6 +- src/Common/Image/IwiTypes.h | 42 ++++ src/ObjLoading/Game/T5/ObjLoaderT5.cpp | 28 ++- src/ObjLoading/Image/IwiLoader.cpp | 108 ++++++++++ src/ObjLoading/Image/IwiLoader.h | 3 + .../T5/AssetDumpers/AssetDumperRawFile.cpp | 97 ++++++++- .../Game/T5/AssetDumpers/AssetDumperRawFile.h | 4 + src/ObjWriting/ObjWriting.cpp | 2 + src/Unlinker/Game/T5/ZoneDefWriterT5.cpp | 45 +++++ src/Unlinker/Game/T5/ZoneDefWriterT5.h | 16 ++ src/Unlinker/Unlinker.cpp | 3 +- .../Game/T5/XAssets/WeaponVariantDef.txt | 5 + src/ZoneCommon/Game/T5/ZoneConstantsT5.h | 26 +++ src/ZoneLoading/Game/T5/ContentLoaderT5.cpp | 188 ++++++++++++++++++ src/ZoneLoading/Game/T5/ContentLoaderT5.h | 23 +++ .../Game/T5/ZoneLoaderFactoryT5.cpp | 104 ++++++++++ src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.h | 15 ++ src/ZoneLoading/ZoneLoading.cpp | 2 + 18 files changed, 711 insertions(+), 6 deletions(-) diff --git a/src/Common/Game/T5/T5_Assets.h b/src/Common/Game/T5/T5_Assets.h index baa5a094..f16a9db2 100644 --- a/src/Common/Game/T5/T5_Assets.h +++ b/src/Common/Game/T5/T5_Assets.h @@ -616,9 +616,9 @@ namespace T5 struct XModel { const char* name; - char numBones; - char numRootBones; - char numsurfs; + unsigned char numBones; + unsigned char numRootBones; + unsigned char numsurfs; char lodRampType; uint16_t* boneNames; char* parentList; diff --git a/src/Common/Image/IwiTypes.h b/src/Common/Image/IwiTypes.h index 10a38894..f0f0794e 100644 --- a/src/Common/Image/IwiTypes.h +++ b/src/Common/Image/IwiTypes.h @@ -135,6 +135,48 @@ namespace iwi13 float gamma; uint32_t fileSizeForPicmip[8]; }; + + enum class IwiFormat + { + IMG_FORMAT_INVALID = 0x0, + IMG_FORMAT_BITMAP_RGBA = 0x1, + IMG_FORMAT_BITMAP_RGB = 0x2, + IMG_FORMAT_BITMAP_LUMINANCE_ALPHA = 0x3, + IMG_FORMAT_BITMAP_LUMINANCE = 0x4, + IMG_FORMAT_BITMAP_ALPHA = 0x5, + IMG_FORMAT_WAVELET_RGBA = 0x6, + IMG_FORMAT_WAVELET_RGB = 0x7, + IMG_FORMAT_WAVELET_LUMINANCE_ALPHA = 0x8, + IMG_FORMAT_WAVELET_LUMINANCE = 0x9, + IMG_FORMAT_WAVELET_ALPHA = 0xA, + IMG_FORMAT_DXT1 = 0xB, + IMG_FORMAT_DXT3 = 0xC, + IMG_FORMAT_DXT5 = 0xD, + IMG_FORMAT_DXN = 0xE, + IMG_FORMAT_BITMAP_RGB565 = 0xF, + IMG_FORMAT_BITMAP_RGB5A3 = 0x10, + IMG_FORMAT_BITMAP_C8 = 0x11, + IMG_FORMAT_BITMAP_RGBA8 = 0x12, + IMG_FORMAT_A16B16G16R16F = 0x13, + IMG_FORMAT_COUNT = 0x14, + }; + + enum IwiFlags + { + IMG_FLAG_NOPICMIP = 1 << 0, + IMG_FLAG_NOMIPMAPS = 1 << 1, + IMG_FLAG_CUBEMAP = 1 << 2, + IMG_FLAG_VOLMAP = 1 << 3, + IMG_FLAG_STREAMING = 1 << 4, + IMG_FLAG_LEGACY_NORMALS = 1 << 5, + IMG_FLAG_CLAMP_U = 1 << 6, + IMG_FLAG_CLAMP_V = 1 << 7, + IMG_FLAG_FORCE_SYSTEM = 1 << 8, + IMG_FLAG_DYNAMIC = 1 << 16, + IMG_FLAG_RENDER_TARGET = 1 << 17, + IMG_FLAG_SYSTEMMEM = 1 << 18, + }; + } // T6 diff --git a/src/ObjLoading/Game/T5/ObjLoaderT5.cpp b/src/ObjLoading/Game/T5/ObjLoaderT5.cpp index 69e3b337..cf84edbc 100644 --- a/src/ObjLoading/Game/T5/ObjLoaderT5.cpp +++ b/src/ObjLoading/Game/T5/ObjLoaderT5.cpp @@ -8,8 +8,10 @@ #include "AssetLoaders/AssetLoaderRawFile.h" #include "AssetLoaders/AssetLoaderStringTable.h" #include "AssetLoading/AssetLoadingManager.h" +#include "Image/Dx9TextureLoader.h" #include "Image/Texture.h" #include "Image/IwiLoader.h" +#include "Image/IwiTypes.h" using namespace T5; @@ -83,7 +85,31 @@ void ObjLoader::UnloadContainersOfZone(Zone* zone) const void ObjLoader::LoadImageFromLoadDef(GfxImage* image, Zone* zone) { - // TODO: Load Texture from LoadDef here + const auto* loadDef = image->texture.loadDef; + Dx9TextureLoader textureLoader(zone->GetMemory()); + + textureLoader.Width(image->width).Height(image->height).Depth(image->depth); + + if (loadDef->flags & iwi13::IMG_FLAG_VOLMAP) + textureLoader.Type(TextureType::T_3D); + else if (loadDef->flags & iwi13::IMG_FLAG_CUBEMAP) + textureLoader.Type(TextureType::T_CUBE); + else + textureLoader.Type(TextureType::T_2D); + + textureLoader.Format(static_cast(loadDef->format)); + textureLoader.HasMipMaps(!(loadDef->flags & iwi13::IMG_FLAG_NOMIPMAPS)); + Texture* loadedTexture = textureLoader.LoadTexture(image->texture.loadDef->data); + + if (loadedTexture != nullptr) + { + image->texture.texture = loadedTexture; + image->cardMemory.platform[0] = 0; + + const auto textureMipCount = loadedTexture->GetMipMapCount(); + for (auto mipLevel = 0; mipLevel < textureMipCount; mipLevel++) + image->cardMemory.platform[0] += static_cast(loadedTexture->GetSizeOfMipLevel(mipLevel) * loadedTexture->GetFaceCount()); + } } void ObjLoader::LoadImageFromIwi(GfxImage* image, ISearchPath* searchPath, Zone* zone) diff --git a/src/ObjLoading/Image/IwiLoader.cpp b/src/ObjLoading/Image/IwiLoader.cpp index 051447fd..c7206de6 100644 --- a/src/ObjLoading/Image/IwiLoader.cpp +++ b/src/ObjLoading/Image/IwiLoader.cpp @@ -228,6 +228,111 @@ Texture* IwiLoader::LoadIwi8(std::istream& stream) const return texture; } +const ImageFormat* IwiLoader::GetFormat13(int8_t format) +{ + switch (static_cast(format)) + { + case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGBA: + return &ImageFormat::FORMAT_R8_G8_B8_A8; + case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGB: + return &ImageFormat::FORMAT_R8_G8_B8; + case iwi13::IwiFormat::IMG_FORMAT_BITMAP_ALPHA: + return &ImageFormat::FORMAT_A8; + case iwi13::IwiFormat::IMG_FORMAT_DXT1: + return &ImageFormat::FORMAT_BC1; + case iwi13::IwiFormat::IMG_FORMAT_DXT3: + return &ImageFormat::FORMAT_BC2; + case iwi13::IwiFormat::IMG_FORMAT_DXT5: + return &ImageFormat::FORMAT_BC3; + case iwi13::IwiFormat::IMG_FORMAT_DXN: + return &ImageFormat::FORMAT_BC5; + case iwi13::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE_ALPHA: + return &ImageFormat::FORMAT_R8_A8; + case iwi13::IwiFormat::IMG_FORMAT_BITMAP_LUMINANCE: + return &ImageFormat::FORMAT_R8; + case iwi13::IwiFormat::IMG_FORMAT_WAVELET_RGBA: // used + case iwi13::IwiFormat::IMG_FORMAT_WAVELET_RGB: // used + case iwi13::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA: + case iwi13::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE: + case iwi13::IwiFormat::IMG_FORMAT_WAVELET_ALPHA: + case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGB565: + case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGB5A3: + case iwi13::IwiFormat::IMG_FORMAT_BITMAP_C8: + case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGBA8: + case iwi13::IwiFormat::IMG_FORMAT_A16B16G16R16F: + printf("Unsupported IWI format: %i\n", format); + break; + default: + printf("Unknown IWI format: %i\n", format); + break; + } + + return nullptr; +} + +Texture* IwiLoader::LoadIwi13(std::istream& stream) const +{ + iwi13::IwiHeader header{}; + + stream.read(reinterpret_cast(&header), sizeof(header)); + if (stream.gcount() != sizeof(header)) + return nullptr; + + const auto* format = GetFormat6(header.format); + if (format == nullptr) + return nullptr; + + auto width = header.dimensions[0]; + auto height = header.dimensions[1]; + auto depth = header.dimensions[2]; + auto hasMipMaps = !(header.flags & iwi13::IwiFlags::IMG_FLAG_NOMIPMAPS); + + Texture* texture; + if (header.flags & iwi13::IwiFlags::IMG_FLAG_CUBEMAP) + { + texture = m_memory_manager->Create(format, width, height, hasMipMaps); + } + else if (header.flags & iwi13::IwiFlags::IMG_FLAG_VOLMAP) + { + texture = m_memory_manager->Create(format, width, height, depth, hasMipMaps); + } + else + { + texture = m_memory_manager->Create(format, width, height, hasMipMaps); + } + + texture->Allocate(); + + auto currentFileSize = sizeof(iwi13::IwiHeader) + sizeof(IwiVersion); + const auto mipMapCount = hasMipMaps ? texture->GetMipMapCount() : 1; + + for (auto currentMipLevel = mipMapCount - 1; currentMipLevel >= 0; currentMipLevel--) + { + const auto sizeOfMipLevel = texture->GetSizeOfMipLevel(currentMipLevel) * texture->GetFaceCount(); + currentFileSize += sizeOfMipLevel; + + if (currentMipLevel < static_cast(std::extent::value) + && currentFileSize != header.fileSizeForPicmip[currentMipLevel]) + { + printf("Iwi has invalid file size for picmip %i\n", currentMipLevel); + + m_memory_manager->Delete(texture); + return nullptr; + } + + stream.read(reinterpret_cast(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel); + if (stream.gcount() != sizeOfMipLevel) + { + printf("Unexpected eof of iwi in mip level %i\n", currentMipLevel); + + m_memory_manager->Delete(texture); + return nullptr; + } + } + + return texture; +} + const ImageFormat* IwiLoader::GetFormat27(int8_t format) { switch (static_cast(format)) @@ -358,6 +463,9 @@ Texture* IwiLoader::LoadIwi(std::istream& stream) case 8: return LoadIwi8(stream); + case 13: + return LoadIwi13(stream); + case 27: return LoadIwi27(stream); diff --git a/src/ObjLoading/Image/IwiLoader.h b/src/ObjLoading/Image/IwiLoader.h index 8063617d..c0633740 100644 --- a/src/ObjLoading/Image/IwiLoader.h +++ b/src/ObjLoading/Image/IwiLoader.h @@ -14,6 +14,9 @@ class IwiLoader static const ImageFormat* GetFormat8(int8_t format); Texture* LoadIwi8(std::istream& stream) const; + static const ImageFormat* GetFormat13(int8_t format); + Texture* LoadIwi13(std::istream& stream) const; + static const ImageFormat* GetFormat27(int8_t format); Texture* LoadIwi27(std::istream& stream) const; diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.cpp index a8c1ce27..d51f4a3a 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.cpp @@ -1,7 +1,91 @@ #include "AssetDumperRawFile.h" +#include +#include +#include + using namespace T5; +namespace fs = std::filesystem; + +void AssetDumperRawFile::DumpGsc(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) +{ + const auto* rawFile = asset->Asset(); + + if (rawFile->len <= 8) + { + std::cout << "Invalid len of gsc file \"" << rawFile->name << "\"" << std::endl; + return; + } + + const auto outLen = reinterpret_cast(rawFile->buffer)[0]; + const auto inLen = reinterpret_cast(rawFile->buffer)[1]; + + assert(inLen == static_cast(rawFile->len) - 8); + + if(inLen > static_cast(rawFile->len - 8) + 1) + { + std::cout << "Invalid compression of gsc file \"" << rawFile->name << "\": " << inLen << std::endl; + return; + } + + if(outLen > GSC_MAX_SIZE) + { + std::cout << "Invalid size of gsc file \"" << rawFile->name << "\": " << outLen << std::endl; + return; + } + + z_stream_s zs{}; + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + zs.opaque = Z_NULL; + zs.avail_in = 0; + zs.next_in = Z_NULL; + + int ret = inflateInit(&zs); + + if (ret != Z_OK) + { + throw std::runtime_error("Initializing inflate failed"); + } + + zs.next_in = reinterpret_cast(&rawFile->buffer[8]); + zs.avail_in = inLen; + + Bytef buffer[0x1000]; + + size_t writtenSize = 0; + while (zs.avail_in > 0) + { + zs.next_out = buffer; + zs.avail_out = sizeof buffer; + ret = inflate(&zs, Z_SYNC_FLUSH); + + if (ret < 0) + { + std::cout << "Inflate failed for dumping gsc file \"" << rawFile->name << "\"" << std::endl; + inflateEnd(&zs); + return; + } + + const auto inflateOutSize = sizeof buffer - zs.avail_out; + + if(writtenSize + inflateOutSize >= outLen) + { + // Last byte is a \0 byte. Skip it. + stream.write(reinterpret_cast(buffer), inflateOutSize - 1); + } + else + { + stream.write(reinterpret_cast(buffer), inflateOutSize); + } + writtenSize += inflateOutSize; + } + + inflateEnd(&zs); +} + bool AssetDumperRawFile::ShouldDump(XAssetInfo* asset) { return true; @@ -20,5 +104,16 @@ std::string AssetDumperRawFile::GetFileNameForAsset(Zone* zone, XAssetInfo* asset, std::ostream& stream) { const auto* rawFile = asset->Asset(); - stream.write(rawFile->buffer, rawFile->len); + + const fs::path rawFilePath(rawFile->name); + const auto extension = rawFilePath.extension().string(); + + if(extension == ".gsc" || extension == ".csc") + { + DumpGsc(context, asset, stream); + } + else + { + stream.write(rawFile->buffer, rawFile->len); + } } \ No newline at end of file diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.h index 91d9bacf..bffcf9e6 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.h +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.h @@ -7,6 +7,10 @@ namespace T5 { class AssetDumperRawFile final : public AbstractAssetDumper { + constexpr static size_t GSC_MAX_SIZE = 0xC000000; + + void DumpGsc(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream); + protected: bool ShouldDump(XAssetInfo* asset) override; bool CanDumpAsRaw() override; diff --git a/src/ObjWriting/ObjWriting.cpp b/src/ObjWriting/ObjWriting.cpp index e40d76e3..304eaa58 100644 --- a/src/ObjWriting/ObjWriting.cpp +++ b/src/ObjWriting/ObjWriting.cpp @@ -2,6 +2,7 @@ #include "Dumping/IZoneDumper.h" #include "Game/IW3/ZoneDumperIW3.h" #include "Game/IW4/ZoneDumperIW4.h" +#include "Game/T5/ZoneDumperT5.h" #include "Game/T6/ZoneDumperT6.h" ObjWriting::Configuration_t ObjWriting::Configuration; @@ -10,6 +11,7 @@ const IZoneDumper* const ZONE_DUMPER[] { new IW3::ZoneDumper(), new IW4::ZoneDumper(), + new T5::ZoneDumper(), new T6::ZoneDumper() }; diff --git a/src/Unlinker/Game/T5/ZoneDefWriterT5.cpp b/src/Unlinker/Game/T5/ZoneDefWriterT5.cpp index e69de29b..1cc9e1cf 100644 --- a/src/Unlinker/Game/T5/ZoneDefWriterT5.cpp +++ b/src/Unlinker/Game/T5/ZoneDefWriterT5.cpp @@ -0,0 +1,45 @@ +#include "ZoneDefWriterT5.h" + +#include + +#include "Game/T5/GameT5.h" +#include "Game/T5/GameAssetPoolT5.h" + +using namespace T5; + +bool ZoneDefWriter::CanHandleZone(Zone* zone) const +{ + return zone->m_game == &g_GameT5; +} + +void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs* args, Zone* zone) const +{ +} + +void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs* args, Zone* zone) const +{ + const auto* pools = dynamic_cast(zone->m_pools.get()); + + assert(pools); + if (!pools) + return; + + // Localized strings are all collected in one string file. So only add this to the zone file. + if (!pools->m_localize->m_asset_lookup.empty()) + { + stream.WriteEntry(pools->GetAssetTypeName(ASSET_TYPE_LOCALIZE_ENTRY), zone->m_name); + } + + for (const auto& asset : *pools) + { + switch (asset->m_type) + { + case ASSET_TYPE_LOCALIZE_ENTRY: + break; + + default: + stream.WriteEntry(pools->GetAssetTypeName(asset->m_type), asset->m_name); + break; + } + } +} diff --git a/src/Unlinker/Game/T5/ZoneDefWriterT5.h b/src/Unlinker/Game/T5/ZoneDefWriterT5.h index e69de29b..bca2c2e3 100644 --- a/src/Unlinker/Game/T5/ZoneDefWriterT5.h +++ b/src/Unlinker/Game/T5/ZoneDefWriterT5.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ContentLister/ZoneDefWriter.h" + +namespace T5 +{ + class ZoneDefWriter final : public AbstractZoneDefWriter + { + protected: + void WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs* args, Zone* zone) const override; + void WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs* args, Zone* zone) const override; + + public: + bool CanHandleZone(Zone* zone) const override; + }; +} \ No newline at end of file diff --git a/src/Unlinker/Unlinker.cpp b/src/Unlinker/Unlinker.cpp index ebe6f28a..f8c70111 100644 --- a/src/Unlinker/Unlinker.cpp +++ b/src/Unlinker/Unlinker.cpp @@ -17,8 +17,8 @@ #include "ObjContainer/IWD/IWD.h" #include "UnlinkerArgs.h" #include "Game/IW3/ZoneDefWriterIW3.h" - #include "Game/IW4/ZoneDefWriterIW4.h" +#include "Game/T5/ZoneDefWriterT5.h" #include "Game/T6/ZoneDefWriterT6.h" #include "Utils/ObjFileStream.h" @@ -28,6 +28,7 @@ const IZoneDefWriter* const ZONE_DEF_WRITERS[] { new IW3::ZoneDefWriter(), new IW4::ZoneDefWriter(), + new T5::ZoneDefWriter(), new T6::ZoneDefWriter() }; diff --git a/src/ZoneCode/Game/T5/XAssets/WeaponVariantDef.txt b/src/ZoneCode/Game/T5/XAssets/WeaponVariantDef.txt index 5263b0c9..295c08b1 100644 --- a/src/ZoneCode/Game/T5/XAssets/WeaponVariantDef.txt +++ b/src/ZoneCode/Game/T5/XAssets/WeaponVariantDef.txt @@ -16,6 +16,11 @@ set count hideTags 32; set reusable hideTags; set string szAmmoName; set string szClipName; +reorder: +... +szDisplayName +szAltWeaponName +szXAnims; // WeaponDef use WeaponDef; diff --git a/src/ZoneCommon/Game/T5/ZoneConstantsT5.h b/src/ZoneCommon/Game/T5/ZoneConstantsT5.h index e69de29b..6a1cfc8d 100644 --- a/src/ZoneCommon/Game/T5/ZoneConstantsT5.h +++ b/src/ZoneCommon/Game/T5/ZoneConstantsT5.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +#include "Zone/ZoneTypes.h" +#include "Game/T5/T5.h" + +namespace T5 +{ + class ZoneConstants final + { + ZoneConstants() = default; + + public: + static constexpr const char* MAGIC_UNSIGNED = "IWffu100"; + static constexpr int ZONE_VERSION = 473; + + static_assert(std::char_traits::length(MAGIC_UNSIGNED) == sizeof(ZoneHeader::m_magic)); + + static constexpr size_t AUTHED_CHUNK_SIZE = 0x2000; + static constexpr unsigned AUTHED_CHUNK_COUNT_PER_GROUP = 256; + + static constexpr int OFFSET_BLOCK_BIT_COUNT = 3; + static constexpr block_t INSERT_BLOCK = XFILE_BLOCK_VIRTUAL; + }; +} diff --git a/src/ZoneLoading/Game/T5/ContentLoaderT5.cpp b/src/ZoneLoading/Game/T5/ContentLoaderT5.cpp index e69de29b..d83b1dbd 100644 --- a/src/ZoneLoading/Game/T5/ContentLoaderT5.cpp +++ b/src/ZoneLoading/Game/T5/ContentLoaderT5.cpp @@ -0,0 +1,188 @@ +#include "ContentLoaderT5.h" +#include "Game/T5/T5.h" +#include "Loading/Exception/UnsupportedAssetTypeException.h" + +#include + +#include "Game/T5/XAssets/clipmap_t/clipmap_t_load_db.h" +#include "Game/T5/XAssets/comworld/comworld_load_db.h" +#include "Game/T5/XAssets/ddlroot_t/ddlroot_t_load_db.h" +#include "Game/T5/XAssets/destructibledef/destructibledef_load_db.h" +#include "Game/T5/XAssets/emblemset/emblemset_load_db.h" +#include "Game/T5/XAssets/font_s/font_s_load_db.h" +#include "Game/T5/XAssets/fxeffectdef/fxeffectdef_load_db.h" +#include "Game/T5/XAssets/fximpacttable/fximpacttable_load_db.h" +#include "Game/T5/XAssets/gameworldmp/gameworldmp_load_db.h" +#include "Game/T5/XAssets/gameworldsp/gameworldsp_load_db.h" +#include "Game/T5/XAssets/gfximage/gfximage_load_db.h" +#include "Game/T5/XAssets/gfxlightdef/gfxlightdef_load_db.h" +#include "Game/T5/XAssets/gfxworld/gfxworld_load_db.h" +#include "Game/T5/XAssets/glasses/glasses_load_db.h" +#include "Game/T5/XAssets/localizeentry/localizeentry_load_db.h" +#include "Game/T5/XAssets/mapents/mapents_load_db.h" +#include "Game/T5/XAssets/material/material_load_db.h" +#include "Game/T5/XAssets/materialtechniqueset/materialtechniqueset_load_db.h" +#include "Game/T5/XAssets/menudef_t/menudef_t_load_db.h" +#include "Game/T5/XAssets/menulist/menulist_load_db.h" +#include "Game/T5/XAssets/packindex/packindex_load_db.h" +#include "Game/T5/XAssets/physconstraints/physconstraints_load_db.h" +#include "Game/T5/XAssets/physpreset/physpreset_load_db.h" +#include "Game/T5/XAssets/rawfile/rawfile_load_db.h" +#include "Game/T5/XAssets/sndbank/sndbank_load_db.h" +#include "Game/T5/XAssets/snddriverglobals/snddriverglobals_load_db.h" +#include "Game/T5/XAssets/sndpatch/sndpatch_load_db.h" +#include "Game/T5/XAssets/stringtable/stringtable_load_db.h" +#include "Game/T5/XAssets/weaponvariantdef/weaponvariantdef_load_db.h" +#include "Game/T5/XAssets/xanimparts/xanimparts_load_db.h" +#include "Game/T5/XAssets/xglobals/xglobals_load_db.h" +#include "Game/T5/XAssets/xmodel/xmodel_load_db.h" + +using namespace T5; + +ContentLoader::ContentLoader() +{ + varXAsset = nullptr; + varScriptStringList = nullptr; +} + +void ContentLoader::LoadScriptStringList(const bool atStreamStart) +{ + assert(m_zone->m_script_strings.Empty()); + + m_stream->PushBlock(XFILE_BLOCK_VIRTUAL); + + if (atStreamStart) + m_stream->Load(varScriptStringList); + + if (varScriptStringList->strings != nullptr) + { + assert(varScriptStringList->strings == PTR_FOLLOWING); + + varScriptStringList->strings = m_stream->Alloc(alignof(const char*)); + varXString = varScriptStringList->strings; + LoadXStringArray(true, varScriptStringList->count); + + for (int i = 0; i < varScriptStringList->count; i++) + { + if (varScriptStringList->strings[i]) + { + m_zone->m_script_strings.AddScriptString(varScriptStringList->strings[i]); + } + else + { + m_zone->m_script_strings.AddScriptString(""); + } + } + } + + m_stream->PopBlock(); + + assert(m_zone->m_script_strings.Count() <= SCR_STRING_MAX + 1); +} + +void ContentLoader::LoadXAsset(const bool atStreamStart) +{ +#define LOAD_ASSET(type_index, typeName, headerEntry) \ + case type_index: \ + { \ + Loader_##typeName loader(m_zone, m_stream); \ + loader.Load(&varXAsset->header.headerEntry); \ + break; \ + } +#define SKIP_ASSET(type_index, typeName, headerEntry) \ + case type_index: \ + break; + + assert(varXAsset != nullptr); + + if (atStreamStart) + m_stream->Load(varXAsset); + + switch (varXAsset->type) + { + LOAD_ASSET(ASSET_TYPE_PHYSPRESET, PhysPreset, physPreset) + LOAD_ASSET(ASSET_TYPE_PHYSCONSTRAINTS, PhysConstraints, physConstraints) + LOAD_ASSET(ASSET_TYPE_DESTRUCTIBLEDEF, DestructibleDef, destructibleDef) + LOAD_ASSET(ASSET_TYPE_XANIMPARTS, XAnimParts, parts) + LOAD_ASSET(ASSET_TYPE_XMODEL, XModel, model) + LOAD_ASSET(ASSET_TYPE_MATERIAL, Material, material) + LOAD_ASSET(ASSET_TYPE_TECHNIQUE_SET, MaterialTechniqueSet, techniqueSet) + LOAD_ASSET(ASSET_TYPE_IMAGE, GfxImage, image) + LOAD_ASSET(ASSET_TYPE_SOUND, SndBank, sound) + LOAD_ASSET(ASSET_TYPE_SOUND_PATCH, SndPatch, soundPatch) + LOAD_ASSET(ASSET_TYPE_CLIPMAP, clipMap_t, clipMap) + LOAD_ASSET(ASSET_TYPE_CLIPMAP_PVS, clipMap_t, clipMap) + LOAD_ASSET(ASSET_TYPE_COMWORLD, ComWorld, comWorld) + LOAD_ASSET(ASSET_TYPE_GAMEWORLD_SP, GameWorldSp, gameWorldSp) + LOAD_ASSET(ASSET_TYPE_GAMEWORLD_MP, GameWorldMp, gameWorldMp) + LOAD_ASSET(ASSET_TYPE_MAP_ENTS, MapEnts, mapEnts) + LOAD_ASSET(ASSET_TYPE_GFXWORLD, GfxWorld, gfxWorld) + LOAD_ASSET(ASSET_TYPE_LIGHT_DEF, GfxLightDef, lightDef) + LOAD_ASSET(ASSET_TYPE_FONT, Font_s, font) + LOAD_ASSET(ASSET_TYPE_MENULIST, MenuList, menuList) + LOAD_ASSET(ASSET_TYPE_MENU, menuDef_t, menu) + LOAD_ASSET(ASSET_TYPE_LOCALIZE_ENTRY, LocalizeEntry, localize) + LOAD_ASSET(ASSET_TYPE_WEAPON, WeaponVariantDef, weapon) + LOAD_ASSET(ASSET_TYPE_SNDDRIVER_GLOBALS, SndDriverGlobals, sndDriverGlobals) + LOAD_ASSET(ASSET_TYPE_FX, FxEffectDef, fx) + LOAD_ASSET(ASSET_TYPE_IMPACT_FX, FxImpactTable, impactFx) + LOAD_ASSET(ASSET_TYPE_RAWFILE, RawFile, rawfile) + LOAD_ASSET(ASSET_TYPE_STRINGTABLE, StringTable, stringTable) + LOAD_ASSET(ASSET_TYPE_PACK_INDEX, PackIndex, packIndex) + LOAD_ASSET(ASSET_TYPE_XGLOBALS, XGlobals, xGlobals) + LOAD_ASSET(ASSET_TYPE_DDL, ddlRoot_t, ddlRoot) + LOAD_ASSET(ASSET_TYPE_GLASSES, Glasses, glasses) + LOAD_ASSET(ASSET_TYPE_EMBLEMSET, EmblemSet, emblemSet) + + default: + { + throw UnsupportedAssetTypeException(varXAsset->type); + } + } + +#undef LOAD_ASSET +} + +void ContentLoader::LoadXAssetArray(const bool atStreamStart, const size_t count) +{ + assert(varXAsset != nullptr); + + if (atStreamStart) + m_stream->Load(varXAsset, count); + + for (asset_type_t assetType = 0; assetType < ASSET_TYPE_COUNT; assetType++) + { + m_zone->m_pools->InitPoolDynamic(assetType); + } + + for (size_t index = 0; index < count; index++) + { + LoadXAsset(false); + varXAsset++; + } +} + +void ContentLoader::Load(Zone* zone, IZoneInputStream* stream) +{ + m_zone = zone; + m_stream = stream; + + m_stream->PushBlock(XFILE_BLOCK_VIRTUAL); + + XAssetList assetList{}; + m_stream->LoadDataRaw(&assetList, sizeof assetList); + + varScriptStringList = &assetList.stringList; + LoadScriptStringList(false); + + if (assetList.assets != nullptr) + { + assert(assetList.assets == PTR_FOLLOWING); + + assetList.assets = m_stream->Alloc(alignof(XAsset)); + varXAsset = assetList.assets; + LoadXAssetArray(true, assetList.assetCount); + } + + m_stream->PopBlock(); +} diff --git a/src/ZoneLoading/Game/T5/ContentLoaderT5.h b/src/ZoneLoading/Game/T5/ContentLoaderT5.h index e69de29b..16aa600c 100644 --- a/src/ZoneLoading/Game/T5/ContentLoaderT5.h +++ b/src/ZoneLoading/Game/T5/ContentLoaderT5.h @@ -0,0 +1,23 @@ +#pragma once +#include "Loading/ContentLoaderBase.h" +#include "Loading/IContentLoadingEntryPoint.h" +#include "Game/T5/T5.h" + +namespace T5 +{ + class ContentLoader final : public ContentLoaderBase, public IContentLoadingEntryPoint + { + XAsset* varXAsset; + ScriptStringList* varScriptStringList; + + void LoadScriptStringList(bool atStreamStart); + + void LoadXAsset(bool atStreamStart); + void LoadXAssetArray(bool atStreamStart, size_t count); + + public: + ContentLoader(); + + void Load(Zone* zone, IZoneInputStream* stream) override; + }; +} diff --git a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp index e69de29b..b33a6d8f 100644 --- a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp +++ b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp @@ -0,0 +1,104 @@ +#include "ZoneLoaderFactoryT5.h" + +#include +#include +#include + +#include "Game/T5/T5.h" + +#include "Utils/ClassUtils.h" +#include "ContentLoaderT5.h" +#include "Game/T5/GameAssetPoolT5.h" +#include "Game/T5/GameT5.h" +#include "Game/GameLanguage.h" +#include "Game/T5/ZoneConstantsT5.h" +#include "Loading/Processor/ProcessorInflate.h" +#include "Loading/Steps/StepSkipBytes.h" +#include "Loading/Steps/StepAddProcessor.h" +#include "Loading/Steps/StepAllocXBlocks.h" +#include "Loading/Steps/StepLoadZoneContent.h" + +using namespace T5; + +class ZoneLoaderFactory::Impl +{ + static GameLanguage GetZoneLanguage(std::string& zoneName) + { + return GameLanguage::LANGUAGE_NONE; + } + + static bool CanLoad(ZoneHeader& header, bool* isSecure, bool* isOfficial) + { + assert(isSecure != nullptr); + assert(isOfficial != nullptr); + + if (header.m_version != ZoneConstants::ZONE_VERSION) + { + return false; + } + + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits::length(ZoneConstants::MAGIC_UNSIGNED))) + { + *isSecure = false; + *isOfficial = true; + return true; + } + + return false; + } + + static void SetupBlock(ZoneLoader* zoneLoader) + { +#define XBLOCK_DEF(name, type) std::make_unique(STR(name), name, type) + + zoneLoader->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_TEMP, XBlock::Type::BLOCK_TYPE_TEMP)); + zoneLoader->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_RUNTIME, XBlock::Type::BLOCK_TYPE_RUNTIME)); + zoneLoader->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_LARGE_RUNTIME, XBlock::Type::BLOCK_TYPE_RUNTIME)); + zoneLoader->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_PHYSICAL_RUNTIME, XBlock::Type::BLOCK_TYPE_RUNTIME)); + zoneLoader->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_VIRTUAL, XBlock::Type::BLOCK_TYPE_NORMAL)); + zoneLoader->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_LARGE, XBlock::Type::BLOCK_TYPE_NORMAL)); + zoneLoader->AddXBlock(XBLOCK_DEF(T5::XFILE_BLOCK_PHYSICAL, XBlock::Type::BLOCK_TYPE_NORMAL)); + +#undef XBLOCK_DEF + } + +public: + static ZoneLoader* CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) + { + bool isSecure; + bool isOfficial; + + // Check if this file is a supported IW4 zone. + if (!CanLoad(header, &isSecure, &isOfficial)) + return nullptr; + + // Create new zone + auto zone = std::make_unique(fileName, 0, &g_GameT5); + auto* zonePtr = zone.get(); + zone->m_pools = std::make_unique(zonePtr, 0); + zone->m_language = GetZoneLanguage(fileName); + + // File is supported. Now setup all required steps for loading this file. + auto* zoneLoader = new ZoneLoader(std::move(zone)); + + SetupBlock(zoneLoader); + + zoneLoader->AddLoadingStep(std::make_unique(std::make_unique(ZoneConstants::AUTHED_CHUNK_SIZE))); + + // Start of the XFile struct + zoneLoader->AddLoadingStep(std::make_unique(8)); + // Skip size and externalSize fields since they are not interesting for us + zoneLoader->AddLoadingStep(std::make_unique()); + + // Start of the zone content + zoneLoader->AddLoadingStep(std::make_unique(std::make_unique(), zonePtr, ZoneConstants::OFFSET_BLOCK_BIT_COUNT, ZoneConstants::INSERT_BLOCK)); + + // Return the fully setup zoneloader + return zoneLoader; + } +}; + +ZoneLoader* ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) +{ + return Impl::CreateLoaderForHeader(header, fileName); +} diff --git a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.h b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.h index e69de29b..4a55573c 100644 --- a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.h +++ b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Loading/IZoneLoaderFactory.h" +#include + +namespace T5 +{ + class ZoneLoaderFactory final : public IZoneLoaderFactory + { + class Impl; + + public: + ZoneLoader* CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) override; + }; +} diff --git a/src/ZoneLoading/ZoneLoading.cpp b/src/ZoneLoading/ZoneLoading.cpp index b5cdb021..afa8dfcf 100644 --- a/src/ZoneLoading/ZoneLoading.cpp +++ b/src/ZoneLoading/ZoneLoading.cpp @@ -6,6 +6,7 @@ #include "Game/IW3/ZoneLoaderFactoryIW3.h" #include "Game/IW4/ZoneLoaderFactoryIW4.h" +#include "Game/T5/ZoneLoaderFactoryT5.h" #include "Game/T6/ZoneLoaderFactoryT6.h" #include "Utils/ObjFileStream.h" @@ -15,6 +16,7 @@ IZoneLoaderFactory* ZoneLoaderFactories[] { new IW3::ZoneLoaderFactory(), new IW4::ZoneLoaderFactory(), + new T5::ZoneLoaderFactory(), new T6::ZoneLoaderFactory() };