diff --git a/.github/workflows/check-formatting.yaml b/.github/workflows/check-formatting.yaml index 30431ea4..48c32e5f 100644 --- a/.github/workflows/check-formatting.yaml +++ b/.github/workflows/check-formatting.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1a4412a0..ac5843e2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,6 +15,9 @@ jobs: build_arch: [x86, x64] runs-on: ubuntu-latest container: ubuntu:24.04 + defaults: + run: + shell: bash steps: - name: Install g++ and multilib run: | @@ -26,7 +29,7 @@ jobs: update-alternatives --set g++ /usr/bin/g++-13 - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive @@ -38,7 +41,9 @@ jobs: - name: Build working-directory: ${{ github.workspace }} - run: make -C build -j$(nproc) config=release_${{ matrix.build_arch }} all + run: | + make -C build -j$(nproc) config=release_${{ matrix.build_arch }} all + chmod +x build/bin/Release_${{ matrix.build_arch }}/{ImageConverter,Unlinker,Linker} - name: Test working-directory: ${{ github.workspace }}/build/lib/Release_${{ matrix.build_arch }}/tests @@ -63,7 +68,7 @@ jobs: runs-on: windows-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5cb96fe0..6f3a5094 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,6 +9,9 @@ jobs: build-release-linux: runs-on: ubuntu-latest container: ubuntu:24.04 + defaults: + run: + shell: bash steps: - name: Install g++ and multilib run: | @@ -20,7 +23,7 @@ jobs: update-alternatives --set g++ /usr/bin/g++-13 - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive @@ -28,11 +31,13 @@ jobs: working-directory: ${{ github.workspace }} env: PREMAKE_NO_PROMPT: 1 - run: ./generate.sh + run: ./generate.sh --oat-version=${{ github.ref_name }} - name: Build working-directory: ${{ github.workspace }} - run: make -C build -j$(nproc) config=release_x86 all + run: | + make -C build -j$(nproc) config=release_x86 all + chmod +x build/bin/Release_x86/{ImageConverter,Unlinker,Linker} - name: Upload artifacts uses: actions/upload-artifact@v4 @@ -45,7 +50,7 @@ jobs: runs-on: windows-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive @@ -56,7 +61,7 @@ jobs: working-directory: ${{ github.workspace }} env: PREMAKE_NO_PROMPT: 1 - run: ./generate.bat + run: ./generate.bat --oat-version=${{ github.ref_name }} - name: Build working-directory: ${{ github.workspace }} @@ -78,7 +83,7 @@ jobs: actions: read contents: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 - name: Zip artifacts run: | 7z a oat-linux.tar ./oat-linux/* diff --git a/.gitignore b/.gitignore index 143daee8..12fa1242 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ local/ -build/ +/build/ .vscode .idea user*.* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index b283a8c3..ffaaa975 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "thirdparty/lz4"] path = thirdparty/lz4 url = https://github.com/lz4/lz4.git +[submodule "thirdparty/webview"] + path = thirdparty/webview + url = https://github.com/Laupetin/webview.git diff --git a/premake5.lua b/premake5.lua index 4a0f4d8a..e1c1575a 100644 --- a/premake5.lua +++ b/premake5.lua @@ -95,25 +95,32 @@ include "thirdparty/catch2.lua" include "thirdparty/eigen.lua" include "thirdparty/libtomcrypt.lua" include "thirdparty/libtommath.lua" +include "thirdparty/lz4.lua" +include "thirdparty/lzx.lua" include "thirdparty/json.lua" include "thirdparty/minilzo.lua" include "thirdparty/minizip.lua" include "thirdparty/salsa20.lua" +include "thirdparty/webview.lua" include "thirdparty/zlib.lua" -include "thirdparty/lz4.lua" -- ThirdParty group: All projects that are external dependencies group "ThirdParty" catch2:project() eigen:project() - libtommath:project() libtomcrypt:project() + libtommath:project() + lz4:project() + lzx:project() json:project() minilzo:project() minizip:project() salsa20:project() zlib:project() - lz4:project() + + if _OPTIONS["modman"] then + webview:project() + end group "" -- ======================== @@ -123,6 +130,7 @@ include "src/Common.lua" include "src/Cryptography.lua" include "src/ImageConverter.lua" include "src/Linker.lua" +include "src/ModMan.lua" include "src/Parser.lua" include "src/RawTemplater.lua" include "src/Unlinker.lua" @@ -170,6 +178,10 @@ group "Tools" Linker:project() Unlinker:project() ImageConverter:project() + + if _OPTIONS["modman"] then + ModMan:project() + end group "" group "Raw" diff --git a/src/Common/Game/IGame.h b/src/Common/Game/IGame.h index 02419205..d8021ee2 100644 --- a/src/Common/Game/IGame.h +++ b/src/Common/Game/IGame.h @@ -1,11 +1,10 @@ #pragma once + #include "GameLanguage.h" #include #include -class Zone; - enum class GameId { IW3, @@ -17,6 +16,22 @@ enum class GameId COUNT }; +// The full uppercase names are macros in the standard lib +// So unfortunately not usable as values in the enum +enum class GameEndianness +{ + /* Little endian */ + LE, + /* Big endian */ + BE +}; + +enum class GameWordSize +{ + ARCH_32, + ARCH_64 +}; + static constexpr const char* GameId_Names[]{ "IW3", "IW4", @@ -39,9 +54,6 @@ public: [[nodiscard]] virtual GameId GetId() const = 0; [[nodiscard]] virtual const std::string& GetFullName() const = 0; [[nodiscard]] virtual const std::string& GetShortName() const = 0; - virtual void AddZone(Zone* zone) = 0; - virtual void RemoveZone(Zone* zone) = 0; - [[nodiscard]] virtual const std::vector& GetZones() const = 0; [[nodiscard]] virtual const std::vector& GetLanguagePrefixes() const = 0; static IGame* GetGameById(GameId gameId); diff --git a/src/Common/Game/IW3/CommonIW3.h b/src/Common/Game/IW3/CommonIW3.h index a01a6feb..43664b62 100644 --- a/src/Common/Game/IW3/CommonIW3.h +++ b/src/Common/Game/IW3/CommonIW3.h @@ -21,6 +21,11 @@ namespace IW3 return result; } + static constexpr uint32_t R_HashString(const char* string) + { + return R_HashString(string, 0u); + } + static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); static GfxColor Vec4PackGfxColor(const float (&in)[4]); diff --git a/src/Common/Game/IW3/GameIW3.cpp b/src/Common/Game/IW3/GameIW3.cpp index 2c080343..e95cdf5a 100644 --- a/src/Common/Game/IW3/GameIW3.cpp +++ b/src/Common/Game/IW3/GameIW3.cpp @@ -21,24 +21,6 @@ const std::string& Game::GetShortName() const return shortName; } -void Game::AddZone(Zone* zone) -{ - m_zones.push_back(zone); -} - -void Game::RemoveZone(Zone* zone) -{ - const auto foundEntry = std::ranges::find(m_zones, zone); - - if (foundEntry != m_zones.end()) - m_zones.erase(foundEntry); -} - -const std::vector& Game::GetZones() const -{ - return m_zones; -} - const std::vector& Game::GetLanguagePrefixes() const { static std::vector prefixes; diff --git a/src/Common/Game/IW3/GameIW3.h b/src/Common/Game/IW3/GameIW3.h index bfd896e6..07dfdb43 100644 --- a/src/Common/Game/IW3/GameIW3.h +++ b/src/Common/Game/IW3/GameIW3.h @@ -9,12 +9,6 @@ namespace IW3 [[nodiscard]] GameId GetId() const override; [[nodiscard]] const std::string& GetFullName() const override; [[nodiscard]] const std::string& GetShortName() const override; - void AddZone(Zone* zone) override; - void RemoveZone(Zone* zone) override; - [[nodiscard]] const std::vector& GetZones() const override; [[nodiscard]] const std::vector& GetLanguagePrefixes() const override; - - private: - std::vector m_zones; }; } // namespace IW3 diff --git a/src/Common/Game/IW3/IW3_Assets.h b/src/Common/Game/IW3/IW3_Assets.h index 4e27aa9a..eb25545d 100644 --- a/src/Common/Game/IW3/IW3_Assets.h +++ b/src/Common/Game/IW3/IW3_Assets.h @@ -97,6 +97,8 @@ namespace IW3 struct RawFile; struct StringTable; + typedef unsigned short ScriptString; + union XAssetHeader { // XModelPieces *xmodelPieces; // NOT AN ASSET @@ -215,7 +217,7 @@ namespace IW3 struct XAnimNotifyInfo { - uint16_t name; + ScriptString name; float time; }; @@ -309,7 +311,7 @@ namespace IW3 unsigned int indexCount; float framerate; float frequency; - uint16_t* names; + ScriptString* names; char* dataByte; int16_t* dataShort; int* dataInt; @@ -329,8 +331,8 @@ namespace IW3 struct DObjAnimMat { - float quat[4]; - float trans[3]; + vec4_t quat; + vec3_t trans; float transWeight; }; @@ -390,7 +392,7 @@ namespace IW3 struct type_align(16) GfxPackedVertex { - float xyz[3]; + vec3_t xyz; float binormalSign; GfxColor color; PackedTexCoords texCoord; @@ -404,7 +406,12 @@ namespace IW3 uint16_t* vertsBlend; }; - typedef tdef_align32(16) uint16_t r_index16_t; + struct XSurfaceTri + { + uint16_t i[3]; + }; + + typedef tdef_align32(16) XSurfaceTri XSurfaceTri16; struct XSurface { @@ -415,7 +422,7 @@ namespace IW3 char zoneHandle; uint16_t baseTriIndex; uint16_t baseVertIndex; - r_index16_t (*triIndices)[3]; + XSurfaceTri16* triIndices; XSurfaceVertexInfo vertInfo; GfxPackedVertex* verts0; unsigned int vertListCount; @@ -455,8 +462,8 @@ namespace IW3 struct XBoneInfo { - float bounds[2][3]; - float offset[3]; + vec3_t bounds[2]; + vec3_t offset; float radiusSquared; }; @@ -520,6 +527,11 @@ namespace IW3 char pad; }; + struct XModelQuat + { + int16_t v[4]; + }; + struct XModel { const char* name; @@ -527,11 +539,11 @@ namespace IW3 unsigned char numRootBones; unsigned char numsurfs; char lodRampType; - uint16_t* boneNames; - char* parentList; - int16_t (*quats)[4]; - float (*trans)[4]; - char* partClassification; + ScriptString* boneNames; + unsigned char* parentList; + XModelQuat* quats; + float* trans; + unsigned char* partClassification; DObjAnimMat* baseMat; XSurface* surfs; Material** materialHandles; @@ -541,10 +553,10 @@ namespace IW3 int contents; XBoneInfo* boneInfo; float radius; - float mins[3]; - float maxs[3]; + vec3_t mins; + vec3_t maxs; uint16_t numLods; - uint16_t collLod; + int16_t collLod; XModelStreamInfo streamInfo; int memUsage; char flags; @@ -566,7 +578,8 @@ namespace IW3 GFXS_BLEND_INVDESTALPHA = 0x8, GFXS_BLEND_DESTCOLOR = 0x9, GFXS_BLEND_INVDESTCOLOR = 0xA, - GFXS_BLEND_MASK = 0xF, + + GFXS_BLEND_COUNT }; enum GfxBlendOp @@ -577,7 +590,40 @@ namespace IW3 GFXS_BLENDOP_REVSUBTRACT = 0x3, GFXS_BLENDOP_MIN = 0x4, GFXS_BLENDOP_MAX = 0x5, - GFXS_BLENDOP_MASK = 0x7, + + GFXS_BLENDOP_COUNT + }; + + enum GfxAlphaTest_e + { + GFXS_ALPHA_TEST_GT_0 = 1, + GFXS_ALPHA_TEST_LT_128 = 2, + GFXS_ALPHA_TEST_GE_128 = 3, + + GFXS_ALPHA_TEST_COUNT + }; + + enum GfxCullFace_e + { + GFXS_CULL_NONE = 1, + GFXS_CULL_BACK = 2, + GFXS_CULL_FRONT = 3, + }; + + enum GfxDepthTest_e + { + GFXS_DEPTHTEST_ALWAYS = 0, + GFXS_DEPTHTEST_LESS = 1, + GFXS_DEPTHTEST_EQUAL = 2, + GFXS_DEPTHTEST_LESSEQUAL = 3 + }; + + enum GfxPolygonOffset_e + { + GFXS_POLYGON_OFFSET_0 = 0, + GFXS_POLYGON_OFFSET_1 = 1, + GFXS_POLYGON_OFFSET_2 = 2, + GFXS_POLYGON_OFFSET_SHADOWMAP = 3 }; enum GfxStencilOp @@ -589,10 +635,19 @@ namespace IW3 GFXS_STENCILOP_DECRSAT = 0x4, GFXS_STENCILOP_INVERT = 0x5, GFXS_STENCILOP_INCR = 0x6, - GFXS_STENCILOP_DECR = 0x7, + GFXS_STENCILOP_DECR = 0x7 + }; - GFXS_STENCILOP_COUNT, - GFXS_STENCILOP_MASK = 0x7 + enum GfxStencilFunc + { + GFXS_STENCILFUNC_NEVER = 0x0, + GFXS_STENCILFUNC_LESS = 0x1, + GFXS_STENCILFUNC_EQUAL = 0x2, + GFXS_STENCILFUNC_LESSEQUAL = 0x3, + GFXS_STENCILFUNC_GREATER = 0x4, + GFXS_STENCILFUNC_NOTEQUAL = 0x5, + GFXS_STENCILFUNC_GREATEREQUAL = 0x6, + GFXS_STENCILFUNC_ALWAYS = 0x7 }; enum GfxStateBitsEnum : unsigned int @@ -613,10 +668,10 @@ namespace IW3 GFXS0_ATEST_GE_128 = 0x3000, GFXS0_ATEST_MASK = 0x3000, - GFXS0_CULL_SHIFT = 0xE, GFXS0_CULL_NONE = 0x4000, GFXS0_CULL_BACK = 0x8000, GFXS0_CULL_FRONT = 0xC000, + GFXS0_CULL_SHIFT = 0xE, GFXS0_CULL_MASK = 0xC000, GFXS0_SRCBLEND_ALPHA_SHIFT = 0x10, @@ -638,18 +693,18 @@ namespace IW3 GFXS1_DEPTHWRITE = 0x1, GFXS1_DEPTHTEST_DISABLE = 0x2, - GFXS1_DEPTHTEST_SHIFT = 0x2, GFXS1_DEPTHTEST_ALWAYS = 0x0, GFXS1_DEPTHTEST_LESS = 0x4, GFXS1_DEPTHTEST_EQUAL = 0x8, GFXS1_DEPTHTEST_LESSEQUAL = 0xC, + GFXS1_DEPTHTEST_SHIFT = 0x2, GFXS1_DEPTHTEST_MASK = 0xC, - GFXS1_POLYGON_OFFSET_SHIFT = 0x4, GFXS1_POLYGON_OFFSET_0 = 0x0, GFXS1_POLYGON_OFFSET_1 = 0x10, GFXS1_POLYGON_OFFSET_2 = 0x20, GFXS1_POLYGON_OFFSET_SHADOWMAP = 0x30, + GFXS1_POLYGON_OFFSET_SHIFT = 0x4, GFXS1_POLYGON_OFFSET_MASK = 0x30, GFXS1_STENCIL_FRONT_ENABLE = 0x40, @@ -672,16 +727,61 @@ namespace IW3 GFXS1_STENCILOP_FRONTBACK_MASK = 0x1FF1FF00, }; + struct GfxStateBitsLoadBitsStructured + { + // Byte 0 + unsigned int srcBlendRgb : 4; // 0-3 + unsigned int dstBlendRgb : 4; // 4-7 + unsigned int blendOpRgb : 3; // 8-10 + unsigned int alphaTestDisabled : 1; // 11 + unsigned int alphaTest : 2; // 12-13 + unsigned int cullFace : 2; // 14-15 + unsigned int srcBlendAlpha : 4; // 16-19 + unsigned int dstBlendAlpha : 4; // 20-23 + unsigned int blendOpAlpha : 3; // 24-26 + unsigned int colorWriteRgb : 1; // 27 + unsigned int colorWriteAlpha : 1; // 28 + unsigned int unused1 : 2; // 29-30 + unsigned int polymodeLine : 1; // 31 + + // Byte 1 + unsigned int depthWrite : 1; // 0 + unsigned int depthTestDisabled : 1; // 1 + unsigned int depthTest : 2; // 2-3 + unsigned int polygonOffset : 2; // 4-5 + unsigned int stencilFrontEnabled : 1; // 6 + unsigned int stencilBackEnabled : 1; // 7 + unsigned int stencilFrontPass : 3; // 8-10 + unsigned int stencilFrontFail : 3; // 11-13 + unsigned int stencilFrontZFail : 3; // 14-16 + unsigned int stencilFrontFunc : 3; // 17-19 + unsigned int stencilBackPass : 3; // 20-22 + unsigned int stencilBackFail : 3; // 23-25 + unsigned int stencilBackZFail : 3; // 26-28 + unsigned int stencilBackFunc : 3; // 29-31 + }; + + union GfxStateBitsLoadBits + { + unsigned int raw[2]; + GfxStateBitsLoadBitsStructured structured; + }; + +#ifndef __zonecodegenerator + static_assert(sizeof(GfxStateBitsLoadBits) == 8); + static_assert(sizeof(GfxStateBitsLoadBitsStructured) == 8); +#endif + struct GfxStateBits { - unsigned int loadBits[2]; + GfxStateBitsLoadBits loadBits; }; struct type_align(16) MaterialConstantDef { unsigned int nameHash; char name[12]; - float literal[4]; + vec4_t literal; }; struct complex_s @@ -718,6 +818,26 @@ namespace IW3 water_t* water; }; + enum TextureFilter + { + TEXTURE_FILTER_DISABLED = 0x0, + TEXTURE_FILTER_NEAREST = 0x1, + TEXTURE_FILTER_LINEAR = 0x2, + TEXTURE_FILTER_ANISO2X = 0x3, + TEXTURE_FILTER_ANISO4X = 0x4, + + TEXTURE_FILTER_COUNT + }; + + enum SamplerStateBitsMipMap_e + { + SAMPLER_MIPMAP_ENUM_DISABLED, + SAMPLER_MIPMAP_ENUM_NEAREST, + SAMPLER_MIPMAP_ENUM_LINEAR, + + SAMPLER_MIPMAP_ENUM_COUNT + }; + enum SamplerStateBits_e { SAMPLER_FILTER_SHIFT = 0x0, @@ -743,13 +863,26 @@ namespace IW3 SAMPLER_CLAMP_MASK = 0xE0, }; + struct MaterialTextureDefSamplerState + { + unsigned char filter : 3; + unsigned char mipMap : 2; + unsigned char clampU : 1; + unsigned char clampV : 1; + unsigned char clampW : 1; + }; + +#ifndef __zonecodegenerator + static_assert(sizeof(MaterialTextureDefSamplerState) == 1u); +#endif + struct MaterialTextureDef { unsigned int nameHash; char nameStart; char nameEnd; - unsigned char samplerState; // SamplerStateBits_e - unsigned char semantic; // TextureSemantic + MaterialTextureDefSamplerState samplerState; // SamplerStateBits_e + unsigned char semantic; // TextureSemantic MaterialTextureDefInfo u; }; @@ -842,8 +975,9 @@ namespace IW3 CAMERA_REGION_LIT = 0x0, CAMERA_REGION_DECAL = 0x1, CAMERA_REGION_EMISSIVE = 0x2, - CAMERA_REGION_COUNT = 0x3, - CAMERA_REGION_NONE = 0x3, + + CAMERA_REGION_COUNT, + CAMERA_REGION_NONE = CAMERA_REGION_COUNT, }; enum MaterialStateFlags @@ -2799,6 +2933,30 @@ namespace IW3 MISSILE_GUIDANCE_COUNT = 0x4, }; + enum hitLocation_t + { + HITLOC_NONE = 0x0, + HITLOC_HELMET = 0x1, + HITLOC_HEAD = 0x2, + HITLOC_NECK = 0x3, + HITLOC_TORSO_UPR = 0x4, + HITLOC_TORSO_LWR = 0x5, + HITLOC_R_ARM_UPR = 0x6, + HITLOC_L_ARM_UPR = 0x7, + HITLOC_R_ARM_LWR = 0x8, + HITLOC_L_ARM_LWR = 0x9, + HITLOC_R_HAND = 0xA, + HITLOC_L_HAND = 0xB, + HITLOC_R_LEG_UPR = 0xC, + HITLOC_L_LEG_UPR = 0xD, + HITLOC_R_LEG_LWR = 0xE, + HITLOC_L_LEG_LWR = 0xF, + HITLOC_R_FOOT = 0x10, + HITLOC_L_FOOT = 0x11, + + HITLOC_COUNT, + }; + struct snd_alias_list_name { const char* soundName; diff --git a/src/Common/Game/IW4/GameIW4.cpp b/src/Common/Game/IW4/GameIW4.cpp index bb5de03b..1a26117e 100644 --- a/src/Common/Game/IW4/GameIW4.cpp +++ b/src/Common/Game/IW4/GameIW4.cpp @@ -21,24 +21,6 @@ const std::string& Game::GetShortName() const return shortName; } -void Game::AddZone(Zone* zone) -{ - m_zones.push_back(zone); -} - -void Game::RemoveZone(Zone* zone) -{ - const auto foundEntry = std::ranges::find(m_zones, zone); - - if (foundEntry != m_zones.end()) - m_zones.erase(foundEntry); -} - -const std::vector& Game::GetZones() const -{ - return m_zones; -} - const std::vector& Game::GetLanguagePrefixes() const { static std::vector prefixes; diff --git a/src/Common/Game/IW4/GameIW4.h b/src/Common/Game/IW4/GameIW4.h index 2f8e6d66..54967067 100644 --- a/src/Common/Game/IW4/GameIW4.h +++ b/src/Common/Game/IW4/GameIW4.h @@ -9,12 +9,6 @@ namespace IW4 [[nodiscard]] GameId GetId() const override; [[nodiscard]] const std::string& GetFullName() const override; [[nodiscard]] const std::string& GetShortName() const override; - void AddZone(Zone* zone) override; - void RemoveZone(Zone* zone) override; - [[nodiscard]] const std::vector& GetZones() const override; [[nodiscard]] const std::vector& GetLanguagePrefixes() const override; - - private: - std::vector m_zones; }; } // namespace IW4 diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 09431961..398f0f77 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -113,6 +113,8 @@ namespace IW4 struct VehicleDef; struct AddonMapEnts; + typedef unsigned short ScriptString; + union XAssetHeader { PhysPreset* physPreset; @@ -239,8 +241,8 @@ namespace IW4 struct Bounds { - float midPoint[3]; - float halfSize[3]; + vec3_t midPoint; + vec3_t halfSize; }; struct cplane_s @@ -325,7 +327,7 @@ namespace IW4 struct XAnimNotifyInfo { - uint16_t name; + ScriptString name; float time; }; @@ -448,7 +450,7 @@ namespace IW4 unsigned int indexCount; float framerate; float frequency; - uint16_t* names; + ScriptString* names; char* dataByte; int16_t* dataShort; int* dataInt; @@ -491,7 +493,7 @@ namespace IW4 struct type_align(16) GfxPackedVertex { - float xyz[3]; + vec3_t xyz; float binormalSign; GfxColor color; PackedTexCoords texCoord; @@ -536,7 +538,12 @@ namespace IW4 XSurfaceCollisionTree* collisionTree; }; - typedef tdef_align32(16) uint16_t r_index16_t; + struct XSurfaceTri + { + uint16_t i[3]; + }; + + typedef tdef_align32(16) XSurfaceTri XSurfaceTri16; struct XSurface { @@ -547,7 +554,7 @@ namespace IW4 char zoneHandle; uint16_t baseTriIndex; uint16_t baseVertIndex; - r_index16_t (*triIndices)[3]; + XSurfaceTri16* triIndices; XSurfaceVertexInfo vertInfo; GfxPackedVertex* verts0; unsigned int vertListCount; @@ -602,11 +609,16 @@ namespace IW4 struct DObjAnimMat { - float quat[4]; - float trans[3]; + vec4_t quat; + vec3_t trans; float transWeight; }; + struct XModelQuat + { + int16_t v[4]; + }; + struct XModel { const char* name; @@ -616,17 +628,17 @@ namespace IW4 char lodRampType; float scale; unsigned int noScalePartBits[6]; - uint16_t* boneNames; + ScriptString* boneNames; unsigned char* parentList; - int16_t (*quats)[4]; - float (*trans)[3]; + XModelQuat* quats; + float* trans; unsigned char* partClassification; DObjAnimMat* baseMat; Material** materialHandles; XModelLodInfo lodInfo[4]; char maxLoadedLod; unsigned char numLods; - unsigned char collLod; + char collLod; char flags; XModelCollSurf_s* collSurfs; int numCollSurfs; diff --git a/src/Common/Game/IW5/GameIW5.cpp b/src/Common/Game/IW5/GameIW5.cpp index 17581d9b..7cddaa87 100644 --- a/src/Common/Game/IW5/GameIW5.cpp +++ b/src/Common/Game/IW5/GameIW5.cpp @@ -21,24 +21,6 @@ const std::string& Game::GetShortName() const return shortName; } -void Game::AddZone(Zone* zone) -{ - m_zones.push_back(zone); -} - -void Game::RemoveZone(Zone* zone) -{ - const auto foundEntry = std::ranges::find(m_zones, zone); - - if (foundEntry != m_zones.end()) - m_zones.erase(foundEntry); -} - -const std::vector& Game::GetZones() const -{ - return m_zones; -} - const std::vector& Game::GetLanguagePrefixes() const { static std::vector prefixes; diff --git a/src/Common/Game/IW5/GameIW5.h b/src/Common/Game/IW5/GameIW5.h index 2ba464d6..a0ca6309 100644 --- a/src/Common/Game/IW5/GameIW5.h +++ b/src/Common/Game/IW5/GameIW5.h @@ -9,12 +9,6 @@ namespace IW5 [[nodiscard]] GameId GetId() const override; [[nodiscard]] const std::string& GetFullName() const override; [[nodiscard]] const std::string& GetShortName() const override; - void AddZone(Zone* zone) override; - void RemoveZone(Zone* zone) override; - [[nodiscard]] const std::vector& GetZones() const override; [[nodiscard]] const std::vector& GetLanguagePrefixes() const override; - - private: - std::vector m_zones; }; } // namespace IW5 diff --git a/src/Common/Game/T5/CommonT5.h b/src/Common/Game/T5/CommonT5.h index 9cb67e29..c5e4d762 100644 --- a/src/Common/Game/T5/CommonT5.h +++ b/src/Common/Game/T5/CommonT5.h @@ -11,6 +11,21 @@ namespace T5 static int Com_HashString(const char* str); static int Com_HashString(const char* str, int len); + static constexpr uint32_t R_HashString(const char* str, uint32_t hash) + { + for (const auto* pos = str; *pos; pos++) + { + hash = 33 * hash ^ (*pos | 0x20); + } + + return hash; + } + + static constexpr uint32_t R_HashString(const char* string) + { + return R_HashString(string, 0u); + } + static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); static GfxColor Vec4PackGfxColor(const float (&in)[4]); diff --git a/src/Common/Game/T5/GameT5.cpp b/src/Common/Game/T5/GameT5.cpp index 7448b026..2609df85 100644 --- a/src/Common/Game/T5/GameT5.cpp +++ b/src/Common/Game/T5/GameT5.cpp @@ -21,24 +21,6 @@ const std::string& Game::GetShortName() const return shortName; } -void Game::AddZone(Zone* zone) -{ - m_zones.push_back(zone); -} - -void Game::RemoveZone(Zone* zone) -{ - const auto foundEntry = std::ranges::find(m_zones, zone); - - if (foundEntry != m_zones.end()) - m_zones.erase(foundEntry); -} - -const std::vector& Game::GetZones() const -{ - return m_zones; -} - const std::vector& Game::GetLanguagePrefixes() const { static std::vector prefixes{ diff --git a/src/Common/Game/T5/GameT5.h b/src/Common/Game/T5/GameT5.h index 43ee7f6f..213037ba 100644 --- a/src/Common/Game/T5/GameT5.h +++ b/src/Common/Game/T5/GameT5.h @@ -9,12 +9,6 @@ namespace T5 [[nodiscard]] GameId GetId() const override; [[nodiscard]] const std::string& GetFullName() const override; [[nodiscard]] const std::string& GetShortName() const override; - void AddZone(Zone* zone) override; - void RemoveZone(Zone* zone) override; - [[nodiscard]] const std::vector& GetZones() const override; [[nodiscard]] const std::vector& GetLanguagePrefixes() const override; - - private: - std::vector m_zones; }; } // namespace T5 diff --git a/src/Common/Game/T5/T5_Assets.h b/src/Common/Game/T5/T5_Assets.h index c1145466..d06015db 100644 --- a/src/Common/Game/T5/T5_Assets.h +++ b/src/Common/Game/T5/T5_Assets.h @@ -540,6 +540,15 @@ namespace T5 XSurfaceCollisionTree* collisionTree; }; + enum XSurfaceFlag + { + XSURFACE_FLAG_QUANTIZED = 0x1, + XSURFACE_FLAG_SKINNED = 0x2, + XSURFACE_FLAG_CONSTANT_COLOR = 0x4, + XSURFACE_FLAG_DEFORMED = 0x80, + XSURFACE_FLAG_STREAMED = 0x8000, + }; + struct XSurfaceTri { uint16_t i[3]; @@ -739,14 +748,30 @@ namespace T5 gcc_align32(8) uint64_t packed; }; + enum MaterialGameFlags + { + MTL_GAMEFLAG_1 = 0x1, + MTL_GAMEFLAG_2 = 0x2, + MTL_GAMEFLAG_4 = 0x4, + MTL_GAMEFLAG_8 = 0x8, + MTL_GAMEFLAG_10 = 0x10, + MTL_GAMEFLAG_20 = 0x20, + + // Probably, seems to be this in T5 + MTL_GAMEFLAG_CASTS_SHADOW = 0x40, + MTL_GAMEFLAG_80 = 0x80, + MTL_GAMEFLAG_100 = 0x100, + MTL_GAMEFLAG_200 = 0x200, + }; + struct MaterialInfo { const char* name; unsigned int gameFlags; char pad; - char sortKey; - char textureAtlasRowCount; - char textureAtlasColumnCount; + unsigned char sortKey; + unsigned char textureAtlasRowCount; + unsigned char textureAtlasColumnCount; GfxDrawSurf drawSurf; unsigned int surfaceTypeBits; unsigned int layeredSurfaceTypes; @@ -787,14 +812,47 @@ namespace T5 water_t* water; }; + enum TextureFilter + { + TEXTURE_FILTER_DISABLED = 0x0, + TEXTURE_FILTER_NEAREST = 0x1, + TEXTURE_FILTER_LINEAR = 0x2, + TEXTURE_FILTER_ANISO2X = 0x3, + TEXTURE_FILTER_ANISO4X = 0x4, + + TEXTURE_FILTER_COUNT + }; + + enum SamplerStateBitsMipMap_e + { + SAMPLER_MIPMAP_ENUM_DISABLED, + SAMPLER_MIPMAP_ENUM_NEAREST, + SAMPLER_MIPMAP_ENUM_LINEAR, + + SAMPLER_MIPMAP_ENUM_COUNT + }; + + struct MaterialTextureDefSamplerState + { + unsigned char filter : 3; + unsigned char mipMap : 2; + unsigned char clampU : 1; + unsigned char clampV : 1; + unsigned char clampW : 1; + }; + +#ifndef __zonecodegenerator + static_assert(sizeof(MaterialTextureDefSamplerState) == 1u); +#endif + struct MaterialTextureDef { unsigned int nameHash; char nameStart; char nameEnd; - char samplerState; + MaterialTextureDefSamplerState samplerState; unsigned char semantic; // TextureSemantic - char isMatureContent; + bool isMatureContent; char pad[3]; MaterialTextureDefInfo u; }; @@ -803,7 +861,96 @@ namespace T5 { unsigned int nameHash; char name[12]; - float literal[4]; + vec4_t literal; + }; + + enum GfxBlend : unsigned int + { + GFXS_BLEND_DISABLED = 0x0, + GFXS_BLEND_ZERO = 0x1, + GFXS_BLEND_ONE = 0x2, + GFXS_BLEND_SRCCOLOR = 0x3, + GFXS_BLEND_INVSRCCOLOR = 0x4, + GFXS_BLEND_SRCALPHA = 0x5, + GFXS_BLEND_INVSRCALPHA = 0x6, + GFXS_BLEND_DESTALPHA = 0x7, + GFXS_BLEND_INVDESTALPHA = 0x8, + GFXS_BLEND_DESTCOLOR = 0x9, + GFXS_BLEND_INVDESTCOLOR = 0xA, + GFXS_BLEND_MASK = 0xF, + }; + + enum GfxBlendOp : unsigned int + { + GFXS_BLENDOP_DISABLED = 0x0, + GFXS_BLENDOP_ADD = 0x1, + GFXS_BLENDOP_SUBTRACT = 0x2, + GFXS_BLENDOP_REVSUBTRACT = 0x3, + GFXS_BLENDOP_MIN = 0x4, + GFXS_BLENDOP_MAX = 0x5, + GFXS_BLENDOP_MASK = 0x7, + }; + + enum GfxAlphaTest_e + { + GFXS_ALPHA_TEST_GT_0 = 1, + GFXS_ALPHA_TEST_GE_255 = 2, + GFXS_ALPHA_TEST_GE_128 = 3, + + GFXS_ALPHA_TEST_COUNT + }; + + enum GfxCullFace_e + { + GFXS_CULL_NONE = 1, + GFXS_CULL_BACK = 2, + GFXS_CULL_FRONT = 3, + }; + + enum GfxDepthTest_e + { + GFXS_DEPTHTEST_ALWAYS = 0, + GFXS_DEPTHTEST_LESS = 1, + GFXS_DEPTHTEST_EQUAL = 2, + GFXS_DEPTHTEST_LESSEQUAL = 3 + }; + + enum GfxPolygonOffset_e + { + GFXS_POLYGON_OFFSET_0 = 0, + GFXS_POLYGON_OFFSET_1 = 1, + GFXS_POLYGON_OFFSET_2 = 2, + GFXS_POLYGON_OFFSET_SHADOWMAP = 3 + }; + + enum GfxStencilOp : unsigned int + { + GFXS_STENCILOP_KEEP = 0x0, + GFXS_STENCILOP_ZERO = 0x1, + GFXS_STENCILOP_REPLACE = 0x2, + GFXS_STENCILOP_INCRSAT = 0x3, + GFXS_STENCILOP_DECRSAT = 0x4, + GFXS_STENCILOP_INVERT = 0x5, + GFXS_STENCILOP_INCR = 0x6, + GFXS_STENCILOP_DECR = 0x7, + + GFXS_STENCILOP_COUNT, + GFXS_STENCILOP_MASK = 0x7 + }; + + enum GfxStencilFunc : unsigned int + { + GFXS_STENCILFUNC_NEVER = 0x0, + GFXS_STENCILFUNC_LESS = 0x1, + GFXS_STENCILFUNC_EQUAL = 0x2, + GFXS_STENCILFUNC_LESSEQUAL = 0x3, + GFXS_STENCILFUNC_GREATER = 0x4, + GFXS_STENCILFUNC_NOTEQUAL = 0x5, + GFXS_STENCILFUNC_GREATEREQUAL = 0x6, + GFXS_STENCILFUNC_ALWAYS = 0x7, + + GFXS_STENCILFUNC_COUNT, + GFXS_STENCILFUNC_MASK = 0x7 }; enum GfxStateBitsEnum : unsigned int @@ -868,9 +1015,64 @@ namespace T5 GFXS1_STENCILOP_FRONTBACK_MASK = 0x1FF1FF00, }; + struct GfxStateBitsLoadBitsStructured + { + // Byte 0 + unsigned int srcBlendRgb : 4; // 0-3 + unsigned int dstBlendRgb : 4; // 4-7 + unsigned int blendOpRgb : 3; // 8-10 + unsigned int alphaTestDisabled : 1; // 11 + unsigned int alphaTest : 2; // 12-13 + unsigned int cullFace : 2; // 14-15 + unsigned int srcBlendAlpha : 4; // 16-19 + unsigned int dstBlendAlpha : 4; // 20-23 + unsigned int blendOpAlpha : 3; // 24-26 + unsigned int colorWriteRgb : 1; // 27 + unsigned int colorWriteAlpha : 1; // 28 + unsigned int unused0 : 2; // 29-30 + unsigned int polymodeLine : 1; // 31 + + // Byte 1 + unsigned int depthWrite : 1; // 0 + unsigned int depthTestDisabled : 1; // 1 + unsigned int depthTest : 2; // 2-3 + unsigned int polygonOffset : 2; // 4-5 + unsigned int stencilFrontEnabled : 1; // 6 + unsigned int stencilBackEnabled : 1; // 7 + unsigned int stencilFrontPass : 3; // 8-10 + unsigned int stencilFrontFail : 3; // 11-13 + unsigned int stencilFrontZFail : 3; // 14-16 + unsigned int stencilFrontFunc : 3; // 17-19 + unsigned int stencilBackPass : 3; // 20-22 + unsigned int stencilBackFail : 3; // 23-25 + unsigned int stencilBackZFail : 3; // 26-28 + unsigned int stencilBackFunc : 3; // 29-31 + }; + + union GfxStateBitsLoadBits + { + unsigned int raw[2]; + GfxStateBitsLoadBitsStructured structured; + }; + +#ifndef __zonecodegenerator + static_assert(sizeof(GfxStateBitsLoadBits) == 8); + static_assert(sizeof(GfxStateBitsLoadBitsStructured) == 8); +#endif + struct GfxStateBits { - unsigned int loadBits[2]; + GfxStateBitsLoadBits loadBits; + }; + + enum GfxCameraRegionType + { + CAMERA_REGION_LIT = 0x0, + CAMERA_REGION_DECAL = 0x1, + CAMERA_REGION_EMISSIVE = 0x2, + + CAMERA_REGION_COUNT, + CAMERA_REGION_NONE = CAMERA_REGION_COUNT, }; struct Material @@ -880,8 +1082,8 @@ namespace T5 unsigned char textureCount; unsigned char constantCount; unsigned char stateBitsCount; - char stateFlags; - char cameraRegion; + unsigned char stateFlags; + unsigned char cameraRegion; unsigned char maxStreamedMips; MaterialTechniqueSet* techniqueSet; MaterialTextureDef* textureTable; diff --git a/src/Common/Game/T6/GameT6.cpp b/src/Common/Game/T6/GameT6.cpp index 26edc8ce..c2ba1c4e 100644 --- a/src/Common/Game/T6/GameT6.cpp +++ b/src/Common/Game/T6/GameT6.cpp @@ -21,24 +21,6 @@ const std::string& Game::GetShortName() const return shortName; } -void Game::AddZone(Zone* zone) -{ - m_zones.push_back(zone); -} - -void Game::RemoveZone(Zone* zone) -{ - const auto foundEntry = std::ranges::find(m_zones, zone); - - if (foundEntry != m_zones.end()) - m_zones.erase(foundEntry); -} - -const std::vector& Game::GetZones() const -{ - return m_zones; -} - const std::vector& Game::GetLanguagePrefixes() const { static std::vector prefixes{ diff --git a/src/Common/Game/T6/GameT6.h b/src/Common/Game/T6/GameT6.h index 248f2aeb..0fbd4b7a 100644 --- a/src/Common/Game/T6/GameT6.h +++ b/src/Common/Game/T6/GameT6.h @@ -9,12 +9,6 @@ namespace T6 [[nodiscard]] GameId GetId() const override; [[nodiscard]] const std::string& GetFullName() const override; [[nodiscard]] const std::string& GetShortName() const override; - void AddZone(Zone* zone) override; - void RemoveZone(Zone* zone) override; - [[nodiscard]] const std::vector& GetZones() const override; [[nodiscard]] const std::vector& GetLanguagePrefixes() const override; - - private: - std::vector m_zones; }; } // namespace T6 diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index c830fc6a..52a25695 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -700,6 +700,8 @@ namespace T6 MTL_GAMEFLAG_400 = 0x400, MTL_GAMEFLAG_800 = 0x800, MTL_GAMEFLAG_1000 = 0x1000, + MTL_GAMEFLAG_2000 = 0x2000, + MTL_GAMEFLAG_4000 = 0x4000, }; struct type_align32(8) MaterialInfo @@ -2765,6 +2767,14 @@ namespace T6 float transWeight; }; + enum XSurfaceFlag + { + XSURFACE_FLAG_QUANTIZED = 0x1, + XSURFACE_FLAG_SKINNED = 0x2, + XSURFACE_FLAG_CONSTANT_COLOR = 0x4, + XSURFACE_FLAG_DEFORMED = 0x80, + }; + struct XSurfaceVertexInfo { int16_t vertCount[4]; diff --git a/src/ImageConverter/ImageConverter.cpp b/src/ImageConverter/ImageConverter.cpp index 7f180876..22fa6090 100644 --- a/src/ImageConverter/ImageConverter.cpp +++ b/src/ImageConverter/ImageConverter.cpp @@ -9,6 +9,7 @@ #include "Image/IwiWriter8.h" #include "Image/Texture.h" #include "ImageConverterArgs.h" +#include "Utils/Logging/Log.h" #include "Utils/StringUtils.h" #include @@ -61,7 +62,7 @@ namespace image_converter else if (extension == EXTENSION_DDS) ConvertDds(filePath); else - std::cerr << std::format("Unsupported extension {}\n", extension); + con::error("Unsupported extension {}", extension); } bool ConvertIwi(const fs::path& iwiPath) @@ -69,7 +70,7 @@ namespace image_converter std::ifstream file(iwiPath, std::ios::in | std::ios::binary); if (!file.is_open()) { - std::cerr << std::format("Failed to open input file {}\n", iwiPath.string()); + con::error("Failed to open input file {}", iwiPath.string()); return false; } @@ -83,7 +84,7 @@ namespace image_converter std::ofstream outFile(outPath, std::ios::out | std::ios::binary); if (!outFile.is_open()) { - std::cerr << std::format("Failed to open output file {}\n", outPath.string()); + con::error("Failed to open output file {}", outPath.string()); return false; } @@ -96,7 +97,7 @@ namespace image_converter std::ifstream file(ddsPath, std::ios::in | std::ios::binary); if (!file.is_open()) { - std::cerr << std::format("Failed to open input file {}\n", ddsPath.string()); + con::error("Failed to open input file {}", ddsPath.string()); return false; } @@ -113,7 +114,7 @@ namespace image_converter std::ofstream outFile(outPath, std::ios::out | std::ios::binary); if (!outFile.is_open()) { - std::cerr << std::format("Failed to open output file {}\n", outPath.string()); + con::error("Failed to open output file {}", outPath.string()); return false; } @@ -154,12 +155,12 @@ namespace image_converter bool ShowGameTui() { - std::cout << "Select the game to convert to:\n"; - std::cout << " 1 - Call Of Duty 4: Modern Warfare (IW3)\n"; - std::cout << " 2 - Call Of Duty: Modern Warfare 2 (IW4)\n"; - std::cout << " 3 - Call Of Duty: Modern Warfare 3 (IW5)\n"; - std::cout << " 4 - Call Of Duty: Black Ops (T5)\n"; - std::cout << " 5 - Call Of Duty: Black Ops 2 (T6)\n"; + con::info("Select the game to convert to:"); + con::info(" 1 - Call Of Duty 4: Modern Warfare (IW3)"); + con::info(" 2 - Call Of Duty: Modern Warfare 2 (IW4)"); + con::info(" 3 - Call Of Duty: Modern Warfare 3 (IW5)"); + con::info(" 4 - Call Of Duty: Black Ops (T5)"); + con::info(" 5 - Call Of Duty: Black Ops 2 (T6)"); unsigned num; std::cin >> num; @@ -182,7 +183,7 @@ namespace image_converter m_game_to_convert_to = Game::T6; break; default: - std::cerr << "Invalid input\n"; + con::error("Invalid input"); return false; } diff --git a/src/ImageConverter/ImageConverterArgs.cpp b/src/ImageConverter/ImageConverterArgs.cpp index 8b51a46f..1f9a7816 100644 --- a/src/ImageConverter/ImageConverterArgs.cpp +++ b/src/ImageConverter/ImageConverterArgs.cpp @@ -2,6 +2,7 @@ #include "GitVersion.h" #include "Utils/Arguments/UsageInformation.h" +#include "Utils/Logging/Log.h" #include #include @@ -28,6 +29,12 @@ const CommandLineOption* const OPTION_VERBOSE = .WithDescription("Outputs a lot more and more detailed messages.") .Build(); +const CommandLineOption* const OPTION_NO_COLOR = + CommandLineOption::Builder::Create() + .WithLongName("no-color") + .WithDescription("Disables colored terminal output.") + .Build(); + constexpr auto CATEGORY_GAME = "Game"; const CommandLineOption* const OPTION_GAME_IW3 = @@ -70,6 +77,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ OPTION_HELP, OPTION_VERSION, OPTION_VERBOSE, + OPTION_NO_COLOR, OPTION_GAME_IW3, OPTION_GAME_IW4, OPTION_GAME_IW5, @@ -78,8 +86,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ }; ImageConverterArgs::ImageConverterArgs() - : m_verbose(false), - m_game_to_convert_to(image_converter::Game::UNKNOWN), + : m_game_to_convert_to(image_converter::Game::UNKNOWN), m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v) { } @@ -101,12 +108,7 @@ void ImageConverterArgs::PrintUsage() void ImageConverterArgs::PrintVersion() { - std::cout << std::format("OpenAssetTools ImageConverter {}\n", GIT_VERSION); -} - -void ImageConverterArgs::SetVerbose(const bool isVerbose) -{ - m_verbose = isVerbose; + con::info("OpenAssetTools ImageConverter {}", GIT_VERSION); } bool ImageConverterArgs::ParseArgs(const int argc, const char** argv, bool& shouldContinue) @@ -143,7 +145,13 @@ bool ImageConverterArgs::ParseArgs(const int argc, const char** argv, bool& shou } // -v; --verbose - SetVerbose(m_argument_parser.IsOptionSpecified(OPTION_VERBOSE)); + if (m_argument_parser.IsOptionSpecified(OPTION_VERBOSE)) + con::globalLogLevel = con::LogLevel::DEBUG; + else + con::globalLogLevel = con::LogLevel::INFO; + + // --no-color + con::globalUseColor = !m_argument_parser.IsOptionSpecified(OPTION_NO_COLOR); return true; } diff --git a/src/ImageConverter/ImageConverterArgs.h b/src/ImageConverter/ImageConverterArgs.h index 6d39a8a4..2a221523 100644 --- a/src/ImageConverter/ImageConverterArgs.h +++ b/src/ImageConverter/ImageConverterArgs.h @@ -25,7 +25,6 @@ public: ImageConverterArgs(); bool ParseArgs(int argc, const char** argv, bool& shouldContinue); - bool m_verbose; std::vector m_files_to_convert; image_converter::Game m_game_to_convert_to; @@ -36,7 +35,5 @@ private: static void PrintUsage(); static void PrintVersion(); - void SetVerbose(bool isVerbose); - ArgumentParser m_argument_parser; }; diff --git a/src/Linker/Linker.cpp b/src/Linker/Linker.cpp index 941f639c..61050cf0 100644 --- a/src/Linker/Linker.cpp +++ b/src/Linker/Linker.cpp @@ -6,6 +6,7 @@ #include "ObjWriting.h" #include "SearchPath/OutputPathFilesystem.h" #include "SearchPath/SearchPaths.h" +#include "Utils/Logging/Log.h" #include "Utils/ObjFileStream.h" #include "Zone/AssetList/AssetList.h" #include "Zone/AssetList/AssetListReader.h" @@ -165,7 +166,7 @@ class LinkerImpl final : public Linker if (!definitionStream.IsOpen()) { if (logMissing) - std::cerr << std::format("Could not find zone definition file for target \"{}\".\n", targetName); + con::error("Could not find zone definition file for target \"{}\".", targetName); return nullptr; } @@ -175,7 +176,7 @@ class LinkerImpl final : public Linker if (!zoneDefinition) { - std::cerr << std::format("Failed to read zone definition file for target \"{}\".\n", targetName); + con::error("Failed to read zone definition file for target \"{}\".", targetName); return nullptr; } @@ -225,7 +226,7 @@ class LinkerImpl final : public Linker if (!ReadIgnoreEntries(paths, ignore, context.m_definition->m_game, context.m_ignored_assets)) { - std::cerr << std::format("Failed to read asset listing for ignoring assets of project \"{}\".\n", ignore); + con::error("Failed to read asset listing for ignoring assets of project \"{}\".", ignore); return false; } } @@ -239,7 +240,7 @@ class LinkerImpl final : public Linker const auto gdtFile = gdtSearchPath->Open(std::format("{}.gdt", gdtName)); if (!gdtFile.IsOpen()) { - std::cerr << std::format("Failed to open file for gdt \"{}\"\n", gdtName); + con::error("Failed to open file for gdt \"{}\"", gdtName); return false; } @@ -247,7 +248,7 @@ class LinkerImpl final : public Linker auto gdt = std::make_unique(); if (!gdtReader.Read(*gdt)) { - std::cerr << std::format("Failed to read gdt file \"{}\"\n", gdtName); + con::error("Failed to read gdt file \"{}\"", gdtName); return false; } @@ -274,19 +275,19 @@ class LinkerImpl final : public Linker const auto stream = outPath.Open(std::format("{}.ff", zone.m_name)); if (!stream) { - std::cerr << std::format("Failed to open file for zone: {}\n", zone.m_name); + con::error("Failed to open file for zone: {}", zone.m_name); return false; } - std::cout << std::format("Building zone \"{}\"\n", zone.m_name); + con::info("Building zone \"{}\"", zone.m_name); if (!ZoneWriting::WriteZone(*stream, zone)) { - std::cerr << "Writing zone failed.\n"; + con::error("Writing zone failed."); return false; } - std::cout << std::format("Created zone \"{}\"\n", zone.m_name); + con::info("Created zone \"{}\"", zone.m_name); return true; } @@ -339,7 +340,7 @@ class LinkerImpl final : public Linker if (alreadyBuiltTargets.find(referencedTarget) == alreadyBuiltTargets.end()) { targetsToBuild.emplace_back(referencedTarget); - std::cout << std::format("Building referenced target \"{}\"\n", referencedTarget); + con::info("Building referenced target \"{}\"", referencedTarget); } } } @@ -354,7 +355,7 @@ class LinkerImpl final : public Linker { if (!fs::is_regular_file(zonePath)) { - std::cerr << std::format("Could not find zone file to load \"{}\".\n", zonePath); + con::error("Could not find zone file to load \"{}\".", zonePath); return false; } @@ -366,14 +367,11 @@ class LinkerImpl final : public Linker auto zone = ZoneLoading::LoadZone(zonePath); if (!zone) { - std::cerr << std::format("Failed to load zone \"{}\".\n", zonePath); + con::error("Failed to load zone \"{}\".", zonePath); return false; } - if (m_args.m_verbose) - { - std::cout << std::format("Load zone \"{}\"\n", zone->m_name); - } + con::debug("Load zone \"{}\"", zone->m_name); m_loaded_zones.emplace_back(std::move(zone)); } @@ -390,8 +388,7 @@ class LinkerImpl final : public Linker loadedZone.reset(); - if (m_args.m_verbose) - std::cout << std::format("Unloaded zone \"{}\"\n", zoneName); + con::debug("Unloaded zone \"{}\"", zoneName); } m_loaded_zones.clear(); } @@ -406,7 +403,7 @@ class LinkerImpl final : public Linker } else if (projectSpecifier.find_first_of('/', targetNameSeparatorIndex + 1) != std::string::npos) { - std::cerr << std::format("Project specifier cannot have more than one target name: \"{}\"\n", projectSpecifier); + con::error("Project specifier cannot have more than one target name: \"{}\"", projectSpecifier); return false; } else @@ -417,13 +414,13 @@ class LinkerImpl final : public Linker if (projectName.empty()) { - std::cerr << std::format("Project name cannot be empty: \"{}\"\n", projectSpecifier); + con::error("Project name cannot be empty: \"{}\"", projectSpecifier); return false; } if (targetName.empty()) { - std::cerr << std::format("Target name cannot be empty: \"{}\"\n", projectSpecifier); + con::error("Target name cannot be empty: \"{}\"", projectSpecifier); return false; } diff --git a/src/Linker/LinkerArgs.cpp b/src/Linker/LinkerArgs.cpp index 30f3ff14..d363c75c 100644 --- a/src/Linker/LinkerArgs.cpp +++ b/src/Linker/LinkerArgs.cpp @@ -5,6 +5,7 @@ #include "ObjWriting.h" #include "Utils/Arguments/UsageInformation.h" #include "Utils/FileUtils.h" +#include "Utils/Logging/Log.h" #include "Utils/PathUtils.h" #include @@ -35,6 +36,12 @@ const CommandLineOption* const OPTION_VERBOSE = .WithDescription("Outputs a lot more and more detailed messages.") .Build(); +const CommandLineOption* const OPTION_NO_COLOR = + CommandLineOption::Builder::Create() + .WithLongName("no-color") + .WithDescription("Disables colored terminal output.") + .Build(); + const CommandLineOption* const OPTION_BASE_FOLDER = CommandLineOption::Builder::Create() .WithShortName("b") @@ -115,6 +122,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ OPTION_HELP, OPTION_VERSION, OPTION_VERBOSE, + OPTION_NO_COLOR, OPTION_BASE_FOLDER, OPTION_OUTPUT_FOLDER, OPTION_ADD_ASSET_SEARCH_PATH, @@ -128,8 +136,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ }; LinkerArgs::LinkerArgs() - : m_verbose(false), - m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v) + : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v) { } @@ -150,7 +157,7 @@ void LinkerArgs::PrintUsage() const void LinkerArgs::PrintVersion() { - std::cout << std::format("OpenAssetTools Linker {}\n", GIT_VERSION); + con::info("OpenAssetTools Linker {}", GIT_VERSION); } void LinkerArgs::SetBinFolder() @@ -159,13 +166,6 @@ void LinkerArgs::SetBinFolder() m_bin_folder = path.parent_path().string(); } -void LinkerArgs::SetVerbose(const bool isVerbose) -{ - m_verbose = isVerbose; - ObjLoading::Configuration.Verbose = isVerbose; - ObjWriting::Configuration.Verbose = isVerbose; -} - bool LinkerArgs::ParseArgs(const int argc, const char** argv, bool& shouldContinue) { shouldContinue = true; @@ -202,7 +202,13 @@ bool LinkerArgs::ParseArgs(const int argc, const char** argv, bool& shouldContin } // -v; --verbose - SetVerbose(m_argument_parser.IsOptionSpecified(OPTION_VERBOSE)); + if (m_argument_parser.IsOptionSpecified(OPTION_VERBOSE)) + con::globalLogLevel = con::LogLevel::DEBUG; + else + con::globalLogLevel = con::LogLevel::INFO; + + // --no-color + con::globalUseColor = !m_argument_parser.IsOptionSpecified(OPTION_NO_COLOR); // b; --base-folder if (m_argument_parser.IsOptionSpecified(OPTION_BASE_FOLDER)) diff --git a/src/Linker/LinkerArgs.h b/src/Linker/LinkerArgs.h index 4f779117..e9c93c15 100644 --- a/src/Linker/LinkerArgs.h +++ b/src/Linker/LinkerArgs.h @@ -17,8 +17,6 @@ public: LinkerArgs(); bool ParseArgs(int argc, const char** argv, bool& shouldContinue); - bool m_verbose; - std::vector m_zones_to_load; std::vector m_project_specifiers_to_build; @@ -38,7 +36,6 @@ private: static void PrintVersion(); void SetBinFolder(); - void SetVerbose(bool isVerbose); ArgumentParser m_argument_parser; }; diff --git a/src/Linker/LinkerPaths.cpp b/src/Linker/LinkerPaths.cpp index 37631e49..30fea265 100644 --- a/src/Linker/LinkerPaths.cpp +++ b/src/Linker/LinkerPaths.cpp @@ -3,6 +3,7 @@ #include "SearchPath/IWD.h" #include "SearchPath/SearchPathFilesystem.h" #include "SearchPath/SearchPaths.h" +#include "Utils/Logging/Log.h" #include "Utils/StringUtils.h" #include @@ -241,11 +242,11 @@ namespace if (!fs::is_directory(path)) { - std::cout << std::format("Adding {} search path (Not found): {}\n", m_type_name, path); + con::debug("Adding {} search path (Not found): {}", m_type_name, path); return false; } - std::cout << std::format("Adding {} search path: {}\n", m_type_name, path); + con::debug("Adding {} search path: {}", m_type_name, path); searchPaths.CommitSearchPath(std::make_unique(path)); return true; } diff --git a/src/Linker/ZoneCreation/ZoneCreator.cpp b/src/Linker/ZoneCreation/ZoneCreator.cpp index 29f35ed1..5d32c9a4 100644 --- a/src/Linker/ZoneCreation/ZoneCreator.cpp +++ b/src/Linker/ZoneCreation/ZoneCreator.cpp @@ -11,7 +11,7 @@ namespace { std::unique_ptr CreateZone(const ZoneCreationContext& context, const GameId gameId) { - return std::make_unique(context.m_definition->m_name, 0, IGame::GetGameById(gameId)); + return std::make_unique(context.m_definition->m_name, 0, gameId); } std::vector CreateGdtList(const ZoneCreationContext& context) diff --git a/src/ModMan.lua b/src/ModMan.lua new file mode 100644 index 00000000..c160f700 --- /dev/null +++ b/src/ModMan.lua @@ -0,0 +1,56 @@ +ModMan = {} + +function ModMan:include(includes) + if includes:handle(self:name()) then + includedirs { + path.join(ProjectFolder(), "ModMan") + } + end +end + +function ModMan:link(links) + +end + +function ModMan:use() + dependson(self:name()) +end + +function ModMan:name() + return "ModMan" +end + +function ModMan:project() + local folder = ProjectFolder() + local includes = Includes:create() + local links = Links:create() + + project(self:name()) + targetdir(TargetDirectoryBin) + location "%{wks.location}/src/%{prj.name}" + kind "WindowedApp" + language "C++" + + files { + path.join(folder, "ModMan/**.h"), + path.join(folder, "ModMan/**.cpp") + } + + includedirs { + "%{wks.location}/src/ModMan" + } + + filter { "system:linux", "action:gmake" } + buildoptions { "`pkg-config --cflags gtk4 webkitgtk-6.0`" } + linkoptions { "`pkg-config --libs gtk4 webkitgtk-6.0`" } + filter {} + + self:include(includes) + Utils:include(includes) + json:include(includes) + webview:include(includes) + + links:linkto(Utils) + links:linkto(webview) + links:linkall() +end diff --git a/src/ModMan/README.md b/src/ModMan/README.md new file mode 100644 index 00000000..af2839e2 --- /dev/null +++ b/src/ModMan/README.md @@ -0,0 +1,41 @@ +# ModMan + +ModMan is the experimental GUI for OpenAssetTools. + +## How do I test it + +Currently ModMan is not compiled by default. +To enable it, you have to generate with the appropriate premake5 flag: + +```shell +# On Windows +./generate.bat --modman + +# On Linux +./generate.sh --modman +``` + +**Before** building the C++ solution, the ui has to be built. +This will require NodeJS to be installed on your machine. + +```shell +# Download dependencies +npm --prefix src/ModManUi install + +# Build frontend +npm --prefix src/ModManUi run build + +# Optional: Dev Server for UI development +npm --prefix src/ModManUi run dev +``` + +## How does it work + +ModMan uses [`webview`](https://github.com/Laupetin/webview) for providing a web frontend as a native application. +Unlike frameworks like Electron this does not ship a browser engine alongside it, but instead relies on browser APIs of your OS. +On Windows, this makes use of [WebView2](https://learn.microsoft.com/en-us/microsoft-edge/webview2), on Linux it uses [WebKitGTK](https://webkitgtk.org). + +This adds the following dependencies: +* **Windows**: An up-to-date OS with at the very least Windows10. The WebView2 library for development is downloaded by premake. +* **Linux**: Developing and using ModMan requires the following dependencies to be installed: `gtk4 webkitgtk-6.0` + diff --git a/src/ModMan/Web/Edge/AssetHandlerEdge.cpp b/src/ModMan/Web/Edge/AssetHandlerEdge.cpp new file mode 100644 index 00000000..7cbdf961 --- /dev/null +++ b/src/ModMan/Web/Edge/AssetHandlerEdge.cpp @@ -0,0 +1,187 @@ +#include "AssetHandlerEdge.h" + +#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) + +#include "Web/UiAssets.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + constexpr auto LOCALHOST_PREFIX = "http://localhost:"; + + std::unordered_map assetLookup; + + std::string WideStringToString(const std::wstring& wideString) + { + if (wideString.empty()) + return ""; + + const auto sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, wideString.data(), static_cast(wideString.size()), nullptr, 0, nullptr, nullptr); + if (sizeNeeded <= 0) + throw std::runtime_error(std::format("WideCharToMultiByte() failed: {}", sizeNeeded)); + + std::string result(sizeNeeded, 0); + WideCharToMultiByte(CP_UTF8, 0, wideString.data(), static_cast(wideString.size()), result.data(), sizeNeeded, nullptr, nullptr); + return result; + } + + std::wstring StringToWideString(const std::string& string) + { + if (string.empty()) + return L""; + + const auto sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, string.data(), static_cast(string.size()), nullptr, 0); + if (sizeNeeded <= 0) + throw std::runtime_error(std::format("MultiByteToWideChar() failed: {}", sizeNeeded)); + + std::wstring result(sizeNeeded, 0); + MultiByteToWideChar(CP_UTF8, 0, string.data(), static_cast(string.size()), result.data(), sizeNeeded); + return result; + } + + std::wstring HeadersForAssetName(const std::string& assetName, const size_t contentLength) + { + std::wstringstream wss; + + wss << std::format(L"Content-Length: {}\n", contentLength); + wss << L"Content-Type: " << StringToWideString(ui::GetMimeTypeForFileName(assetName)); + + return wss.str(); + } + + HRESULT HandleResourceRequested(ICoreWebView2_22* core22, IUnknown* args) + { + Microsoft::WRL::ComPtr webResourceRequestArgs; + if (SUCCEEDED(args->QueryInterface(IID_PPV_ARGS(&webResourceRequestArgs)))) + { + COREWEBVIEW2_WEB_RESOURCE_REQUEST_SOURCE_KINDS requestSourceKind = COREWEBVIEW2_WEB_RESOURCE_REQUEST_SOURCE_KINDS_ALL; + if (!SUCCEEDED(webResourceRequestArgs->get_RequestedSourceKind(&requestSourceKind))) + { + std::cerr << "Failed to get requested source kind\n"; + return S_FALSE; + } + + Microsoft::WRL::ComPtr request; + if (!SUCCEEDED(webResourceRequestArgs->get_Request(&request))) + { + std::cerr << "Failed to get request\n"; + return S_FALSE; + } + + LPWSTR wUri; + if (!SUCCEEDED(request->get_Uri(&wUri))) + { + std::cerr << "Failed to get uri\n"; + return S_FALSE; + } + + Microsoft::WRL::ComPtr environment; + if (!SUCCEEDED(core22->get_Environment(&environment))) + { + std::cerr << "Failed to get environment\n"; + return S_FALSE; + } + + Microsoft::WRL::ComPtr response; + + const auto uri = WideStringToString(wUri); + bool fileFound = false; + +#ifdef _DEBUG + // Allow dev server access + if (uri.starts_with(LOCALHOST_PREFIX)) + return S_OK; +#endif + + if (uri.starts_with(edge::URL_PREFIX)) + { + const auto asset = uri.substr(std::char_traits::length(edge::URL_PREFIX) - 1); + + const auto foundUiFile = assetLookup.find(asset); + if (foundUiFile != assetLookup.end()) + { + const Microsoft::WRL::ComPtr responseStream = + SHCreateMemStream(static_cast(foundUiFile->second.data), foundUiFile->second.dataSize); + + const auto headers = HeadersForAssetName(asset, foundUiFile->second.dataSize); + if (!SUCCEEDED(environment->CreateWebResourceResponse(responseStream.Get(), 200, L"OK", headers.data(), &response))) + { + std::cerr << "Failed to create web resource\n"; + return S_FALSE; + } + + fileFound = true; + } + } + + if (!fileFound) + { + if (!SUCCEEDED(environment->CreateWebResourceResponse(nullptr, 404, L"Not found", L"", &response))) + { + std::cerr << "Failed to create web resource\n"; + return S_FALSE; + } + } + + if (!SUCCEEDED(webResourceRequestArgs->put_Response(response.Get()))) + { + std::cerr << "Failed to put response\n"; + return S_FALSE; + } + + return S_OK; + } + + return S_FALSE; + } +} // namespace + +namespace edge +{ + void InstallCustomProtocolHandler(webview::webview& wv) + { + assetLookup = ui::BuildUiFileLookup(); + + const auto controller = static_cast(wv.browser_controller().value()); + Microsoft::WRL::ComPtr core; + if (!SUCCEEDED(controller->get_CoreWebView2(&core))) + { + std::cerr << "Failed to get webview\n"; + return; + } + + Microsoft::WRL::ComPtr core22; + if (!SUCCEEDED(core->QueryInterface(IID_PPV_ARGS(&core22)))) + { + std::cerr << "Failed to get core22\n"; + return; + } + + if (!SUCCEEDED(core22->AddWebResourceRequestedFilterWithRequestSourceKinds( + L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL, COREWEBVIEW2_WEB_RESOURCE_REQUEST_SOURCE_KINDS_ALL))) + { + std::cerr << "Failed to install request filter\n"; + return; + } + + EventRegistrationToken token; + if (!SUCCEEDED(core->add_WebResourceRequested(Microsoft::WRL::Callback( + [core22](ICoreWebView2* sender, IUnknown* args) -> HRESULT + { + return HandleResourceRequested(core22.Get(), args); + }) + .Get(), + &token))) + { + std::cerr << "Failed to add resource requested filter\n"; + } + } +} // namespace edge + +#endif diff --git a/src/ModMan/Web/Edge/AssetHandlerEdge.h b/src/ModMan/Web/Edge/AssetHandlerEdge.h new file mode 100644 index 00000000..fb01197b --- /dev/null +++ b/src/ModMan/Web/Edge/AssetHandlerEdge.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) + +#include "Web/WebViewLib.h" + +namespace edge +{ + constexpr auto URL_PREFIX = "http://modman.local/"; + + void InstallCustomProtocolHandler(webview::webview& wv); +} // namespace edge + +#endif diff --git a/src/ModMan/Web/Gtk/AssetHandlerGtk.cpp b/src/ModMan/Web/Gtk/AssetHandlerGtk.cpp new file mode 100644 index 00000000..3cec4a45 --- /dev/null +++ b/src/ModMan/Web/Gtk/AssetHandlerGtk.cpp @@ -0,0 +1,51 @@ +#include "AssetHandlerGtk.h" + +#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) + +#include "Web/UiAssets.h" + +#include +#include + +namespace +{ + std::unordered_map assetLookup; + + void ModManUriSchemeRequestCb(WebKitURISchemeRequest* request, gpointer user_data) + { + const gchar* asset = webkit_uri_scheme_request_get_path(request); + + const auto foundUiFile = assetLookup.find(asset); + if (foundUiFile != assetLookup.end()) + { + gsize stream_length = foundUiFile->second.dataSize; + GInputStream* stream = g_memory_input_stream_new_from_data(foundUiFile->second.data, foundUiFile->second.dataSize, nullptr); + + webkit_uri_scheme_request_finish(request, stream, stream_length, ui::GetMimeTypeForFileName(foundUiFile->second.filename)); + g_object_unref(stream); + } + else + { + GError* error = g_error_new(G_SPAWN_ERROR, 123, "Could not find %s.", asset); + webkit_uri_scheme_request_finish_error(request, error); + g_error_free(error); + return; + } + } +} // namespace + +namespace gtk +{ + void InstallCustomProtocolHandler(webview::webview& wv) + { + const auto widget = static_cast(wv.browser_controller().value()); + const auto webView = WEBKIT_WEB_VIEW(widget); + const auto context = webkit_web_view_get_context(webView); + + assetLookup = ui::BuildUiFileLookup(); + + webkit_web_context_register_uri_scheme(context, "modman", ModManUriSchemeRequestCb, NULL, nullptr); + } +} // namespace gtk + +#endif diff --git a/src/ModMan/Web/Gtk/AssetHandlerGtk.h b/src/ModMan/Web/Gtk/AssetHandlerGtk.h new file mode 100644 index 00000000..8a399dff --- /dev/null +++ b/src/ModMan/Web/Gtk/AssetHandlerGtk.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) + +#include "Web/WebViewLib.h" + +namespace gtk +{ + constexpr auto URL_PREFIX = "modman://localhost/"; + + void InstallCustomProtocolHandler(webview::webview& wv); +} // namespace gtk + +#endif diff --git a/src/ModMan/Web/UiAssets.cpp b/src/ModMan/Web/UiAssets.cpp new file mode 100644 index 00000000..bf5c658d --- /dev/null +++ b/src/ModMan/Web/UiAssets.cpp @@ -0,0 +1,34 @@ +#include "UiAssets.h" + +#include + +namespace ui +{ + std::unordered_map BuildUiFileLookup() + { + std::unordered_map result; + + for (const auto& asset : MOD_MAN_UI_FILES) + { + result.emplace(std::format("/{}", asset.filename), asset); + } + + return result; + } + + const char* GetMimeTypeForFileName(const std::string& fileName) + { + const char* mimeType; + + if (fileName.ends_with(".html")) + mimeType = "text/html"; + else if (fileName.ends_with(".js")) + mimeType = "text/javascript"; + else if (fileName.ends_with(".css")) + mimeType = "text/css"; + else + mimeType = "application/octet-stream"; + + return mimeType; + } +} // namespace ui diff --git a/src/ModMan/Web/UiAssets.h b/src/ModMan/Web/UiAssets.h new file mode 100644 index 00000000..ea7e185b --- /dev/null +++ b/src/ModMan/Web/UiAssets.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Web/ViteAssets.h" + +#include +#include + +namespace ui +{ + std::unordered_map BuildUiFileLookup(); + const char* GetMimeTypeForFileName(const std::string& fileName); +} // namespace ui diff --git a/src/ObjCompiling/Game/T6/Image/ImageCompilerT6.cpp b/src/ModMan/Web/UiCommunication.cpp similarity index 100% rename from src/ObjCompiling/Game/T6/Image/ImageCompilerT6.cpp rename to src/ModMan/Web/UiCommunication.cpp diff --git a/src/ModMan/Web/UiCommunication.h b/src/ModMan/Web/UiCommunication.h new file mode 100644 index 00000000..df0935ba --- /dev/null +++ b/src/ModMan/Web/UiCommunication.h @@ -0,0 +1,142 @@ +#pragma once + +#include "Utils/Logging/Log.h" +#include "WebViewLib.h" + +#pragma warning(push, 0) +#include +#pragma warning(pop) + +namespace ui +{ + inline void Bind(webview::webview& wv, const std::string& name, std::function fn) + { + wv.bind(name, + [fn](const std::string& req) -> std::string + { + fn(); + return ""; + }); + } + + template void Bind(webview::webview& wv, const std::string& name, std::function fn) + { + wv.bind(name, + [fn](const std::string& req) -> std::string + { + TInput param; + try + { + const auto json = nlohmann::json::parse(req); + if (!json.is_array()) + { + con::error("Webview params are not an array: {}", req); + return ""; + } + param = json.at(0).get(); + } + catch (const nlohmann::json::exception& e) + { + con::error("Failed to parse json of webview call: {}", e.what()); + return ""; + } + + fn(std::move(param)); + + return ""; + }); + } + + template void BindRetOnly(webview::webview& wv, const std::string& name, std::function fn) + { + wv.bind(name, + [fn](const std::string& req) -> std::string + { + auto result = fn(); + + return nlohmann::json(result).dump(); + }); + } + + template void Bind(webview::webview& wv, const std::string& name, std::function fn) + { + wv.bind(name, + [fn](const std::string& req) -> std::string + { + TInput param; + try + { + const auto json = nlohmann::json::parse(req); + if (!json.is_array()) + { + con::error("Webview params are not an array: {}", req); + return ""; + } + param = json.at(0).get(); + } + catch (const nlohmann::json::exception& e) + { + con::error("Failed to parse json of webview call: {}", e.what()); + return ""; + } + + auto result = fn(std::move(param)); + + return nlohmann::json(result).dump(); + }); + } + + inline void BindAsync(webview::webview& wv, const std::string& name, std::function fn) + { + wv.bind( + name, + [fn](const std::string& id, const std::string& req, void* /* arg */) + { + fn(id); + }, + nullptr); + } + + template void BindAsync(webview::webview& wv, const std::string& name, std::function fn) + { + wv.bind( + name, + [fn](const std::string& id, const std::string& req, void* /* arg */) + { + TInput param; + try + { + const auto json = nlohmann::json::parse(req); + if (!json.is_array()) + { + con::error("Webview params are not an array: {}", req); + return ""; + } + param = json.at(0).get(); + } + catch (const nlohmann::json::exception& e) + { + con::error("Failed to parse json of webview call: {}", e.what()); + return ""; + } + + fn(id, std::move(param)); + }, + nullptr); + } + + template void PromiseResolve(webview::webview& wv, const std::string& id, const TPayload& payload) + { + wv.resolve(id, 0, nlohmann::json(payload).dump()); + } + + template void PromiseReject(webview::webview& wv, const std::string& id, const TPayload& payload) + { + wv.resolve(id, 1, nlohmann::json(payload).dump()); + } + + template void Notify(webview::webview& wv, const std::string& eventKey, const TPayload& payload) + { + wv.notify(eventKey, nlohmann::json(payload).dump()); + } +} // namespace ui diff --git a/src/ModMan/Web/WebViewLib.h b/src/ModMan/Web/WebViewLib.h new file mode 100644 index 00000000..5a8fd0b0 --- /dev/null +++ b/src/ModMan/Web/WebViewLib.h @@ -0,0 +1,20 @@ +#pragma once + +#ifdef _MSC_VER +#pragma warning(push, 0) +#else +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#else +#pragma GCC diagnostic pop +#endif + +#ifdef ERROR +#undef ERROR +#endif diff --git a/src/ModMan/main.cpp b/src/ModMan/main.cpp new file mode 100644 index 00000000..1929ef9c --- /dev/null +++ b/src/ModMan/main.cpp @@ -0,0 +1,140 @@ +#include "GitVersion.h" +#include "Web/Edge/AssetHandlerEdge.h" +#include "Web/Gtk/AssetHandlerGtk.h" +#include "Web/UiCommunication.h" +#include "Web/ViteAssets.h" +#include "Web/WebViewLib.h" + +#include +#include +#include +#include +#include +#include + +using namespace std::string_literals; + +namespace +{ +#ifdef _DEBUG + std::optional devToolWindow; + + void RunDevToolsWindow() + { + con::debug("Creating dev tools window"); + + try + { + auto& newWindow = devToolWindow.emplace(false, nullptr); + newWindow.set_title("Devtools"); + newWindow.set_size(640, 480, WEBVIEW_HINT_NONE); + newWindow.set_size(480, 320, WEBVIEW_HINT_MIN); + newWindow.navigate(std::format("http://localhost:{}/__devtools__/", VITE_DEV_SERVER_PORT)); + } + catch (const webview::exception& e) + { + std::cerr << e.what() << '\n'; + } + } +#endif + + int RunMainWindow() + { + con::debug("Creating main window"); + + try + { + webview::webview w( +#ifdef _DEBUG + true, +#else + false, +#endif + nullptr); + w.set_title("OpenAssetTools ModMan"); + w.set_size(1280, 640, WEBVIEW_HINT_NONE); + w.set_size(480, 320, WEBVIEW_HINT_MIN); + + // A binding that counts up or down and immediately returns the new value. + ui::Bind(w, + "greet", + [&w](std::string name) -> std::string + { + ui::Notify(w, "greeting", name); + return std::format("Hello from C++ {}!", name); + }); + + // A binding that counts up or down and immediately returns the new value. + ui::Bind(w, + "debug", + []() + { + con::info("Debug"); + }); + + // A binding that creates a new thread and returns the result at a later time. + ui::BindAsync(w, + "compute", + [&](const std::string& id) + { + // Create a thread and forget about it for the sake of simplicity. + std::thread( + [&, id] + { + // Simulate load. + std::this_thread::sleep_for(std::chrono::seconds(5)); + ui::PromiseResolve(w, id, 42); + }) + .detach(); + }); + +#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) + edge::InstallCustomProtocolHandler(w); + constexpr auto urlPrefix = edge::URL_PREFIX; +#elif defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) + gtk::InstallCustomProtocolHandler(w); + constexpr auto urlPrefix = gtk::URL_PREFIX; +#else +#error Unsupported platform +#endif + +#ifdef _DEBUG + w.navigate(VITE_DEV_SERVER ? std::format("http://localhost:{}", VITE_DEV_SERVER_PORT) : std::format("{}index.html", urlPrefix)); + + if (VITE_DEV_SERVER) + { + w.dispatch( + [] + { + RunDevToolsWindow(); + }); + } +#else + w.navigate(std::format("{}index.html", urlPrefix)); +#endif + w.run(); + } + catch (const webview::exception& e) + { + std::cerr << e.what() << '\n'; + return 1; + } + + return 0; + } +} // namespace + +#ifdef _WIN32 +int WINAPI WinMain(HINSTANCE /*hInst*/, HINSTANCE /*hPrevInst*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/) +{ +#else +int main() +{ +#endif + + con::info("Starting ModMan " GIT_VERSION); + + const auto result = RunMainWindow(); + + return result; +} diff --git a/src/ModManUi/.gitignore b/src/ModManUi/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/src/ModManUi/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/src/ModManUi/.prettierignore b/src/ModManUi/.prettierignore new file mode 100644 index 00000000..f36d5e9f --- /dev/null +++ b/src/ModManUi/.prettierignore @@ -0,0 +1 @@ +src-tauri diff --git a/src/ModManUi/.prettierrc.json b/src/ModManUi/.prettierrc.json new file mode 100644 index 00000000..22b66634 --- /dev/null +++ b/src/ModManUi/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "printWidth": 100 +} diff --git a/src/ModManUi/build/HeaderTransformationPlugin.ts b/src/ModManUi/build/HeaderTransformationPlugin.ts new file mode 100644 index 00000000..4d6fba3c --- /dev/null +++ b/src/ModManUi/build/HeaderTransformationPlugin.ts @@ -0,0 +1,171 @@ +import type { Plugin } from "vite"; +import type { OutputAsset, OutputChunk } from "rollup"; +import path from "node:path"; +import fs from "node:fs"; + +type MinimalOutputAsset = Pick; +type MinimalOutputChunk = Pick; +type MinimalOutputBundle = Record; + +function createVarName(fileName: string) { + return fileName.replaceAll(".", "_").toUpperCase(); +} + +function transformAsset(asset: MinimalOutputAsset) { + const varName = createVarName(asset.fileName); + + let bytes: string; + if (typeof asset.source === "string") { + bytes = [...asset.source].map((v) => String(v.charCodeAt(0))).join(","); + } else { + bytes = [...asset.source].map((v) => String(v)).join(","); + } + + return `constexpr const unsigned char ${varName}[] {${bytes}}; +`; +} + +function transformChunk(chunk: MinimalOutputChunk) { + const varName = createVarName(chunk.fileName); + const bytes = [...chunk.code].map((v) => String(v.charCodeAt(0))).join(","); + + return `constexpr const unsigned char ${varName}[] {${bytes}}; +`; +} + +function writeHeader( + bundle: MinimalOutputBundle, + outputDir?: string, + options?: HeaderTransformationPluginOptions, + devServerPort?: number, +) { + const outputPath = options?.outputPath ?? path.join(outputDir ?? "dist", "ViteAssets.h"); + const outputPathParentDir = path.dirname(outputPath); + + fs.mkdirSync(outputPathParentDir, { recursive: true }); + + const fd = fs.openSync(outputPath, "w"); + const includeFileEnumeration = options?.includeFileEnumeration ?? true; + + fs.writeSync( + fd, + `#pragma once + +`, + ); + + if (includeFileEnumeration) { + fs.writeSync( + fd, + `#include +#include + +`, + ); + } + + fs.writeSync( + fd, + `constexpr auto VITE_DEV_SERVER = ${devServerPort ? "true" : "false"}; +constexpr auto VITE_DEV_SERVER_PORT = ${devServerPort ? String(devServerPort) : "-1"}; +`, + ); + + for (const curBundle of Object.values(bundle)) { + if (curBundle.type === "asset") { + fs.writeSync(fd, transformAsset(curBundle)); + } else { + fs.writeSync(fd, transformChunk(curBundle)); + } + } + + if (includeFileEnumeration) { + fs.writeSync( + fd, + ` +struct UiFile +{ + const char* filename; + const void* data; + const size_t dataSize; +}; + +static inline const UiFile MOD_MAN_UI_FILES[] { +`, + ); + + let index = 0; + for (const curBundle of Object.values(bundle)) { + const fileName = curBundle.fileName; + const varName = createVarName(fileName); + + let prefix = " "; + if (index > 0) { + prefix = `, + `; + } + + fs.writeSync( + fd, + `${prefix}{ "${fileName}", ${varName}, std::extent_v }`, + ); + index++; + } + + fs.writeSync( + fd, + ` +}; +`, + ); + + fs.closeSync(fd); + } +} + +export interface HeaderTransformationPluginOptions { + outputPath?: string; + includeFileEnumeration?: boolean; +} + +export default function headerTransformationPlugin( + options?: HeaderTransformationPluginOptions, +): Plugin { + let writeServerActive = false; + let writeBundleActive = false; + + return { + name: "vite-plugin-header-transformation", + enforce: "post", + config(_userOptions, env) { + if (env.command === "serve") { + writeServerActive = true; + } else { + writeBundleActive = true; + } + }, + configureServer(server) { + if (!writeServerActive) { + return; + } + server.httpServer?.once("listening", () => { + writeHeader( + { + // We need at least one array entry for MSVC + dummyfile: { type: "chunk", fileName: "dummyfile", code: "dummy" }, + }, + server.config.build.outDir, + options, + server.config.server.port, + ); + }); + }, + writeBundle(outputOptions, bundle) { + if (!writeBundleActive) { + return; + } + + writeHeader(bundle, outputOptions.dir, options); + }, + }; +} diff --git a/src/ModManUi/env.d.ts b/src/ModManUi/env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/src/ModManUi/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/src/ModManUi/eslint.config.ts b/src/ModManUi/eslint.config.ts new file mode 100644 index 00000000..7298c96c --- /dev/null +++ b/src/ModManUi/eslint.config.ts @@ -0,0 +1,28 @@ +import { globalIgnores } from "eslint/config"; +import { defineConfigWithVueTs, vueTsConfigs } from "@vue/eslint-config-typescript"; +import pluginVue from "eslint-plugin-vue"; +import pluginVitest from "@vitest/eslint-plugin"; +import skipFormatting from "@vue/eslint-config-prettier/skip-formatting"; + +// To allow more languages other than `ts` in `.vue` files, uncomment the following lines: +// import { configureVueProject } from '@vue/eslint-config-typescript' +// configureVueProject({ scriptLangs: ['ts', 'tsx'] }) +// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup + +export default defineConfigWithVueTs( + { + name: "app/files-to-lint", + files: ["**/*.{ts,mts,tsx,vue}"], + }, + + globalIgnores(["**/dist/**", "**/dist-ssr/**", "**/coverage/**"]), + + pluginVue.configs["flat/essential"], + vueTsConfigs.recommended, + + { + ...pluginVitest.configs.recommended, + files: ["src/**/__tests__/*"], + }, + skipFormatting, +); diff --git a/src/ModManUi/index.html b/src/ModManUi/index.html new file mode 100644 index 00000000..27a13611 --- /dev/null +++ b/src/ModManUi/index.html @@ -0,0 +1,13 @@ + + + + + + + Tauri + Vue + Typescript App + + +
+ + + diff --git a/src/ModManUi/package-lock.json b/src/ModManUi/package-lock.json new file mode 100644 index 00000000..46c3d517 --- /dev/null +++ b/src/ModManUi/package-lock.json @@ -0,0 +1,7100 @@ +{ + "name": "openassettools", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "openassettools", + "version": "0.1.0", + "dependencies": { + "pinia": "3.0.3", + "vue": "3.5.22" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/jsdom": "^21.1.7", + "@types/node": "^22.18.6", + "@vitejs/plugin-vue": "6.0.1", + "@vitest/eslint-plugin": "^1.3.13", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.6.0", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.8.1", + "eslint": "^9.33.0", + "eslint-plugin-vue": "~10.4.0", + "jiti": "^2.5.1", + "jsdom": "^27.0.0", + "npm-run-all2": "^8.0.4", + "prettier": "3.6.2", + "sass": "1.93.2", + "typescript": "~5.9.3", + "vite": "7.1.7", + "vite-plugin-vue-devtools": "^8.0.2", + "vitest": "^3.2.4", + "vue-tsc": "3.1.0" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.5.tgz", + "integrity": "sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.1" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.5.7.tgz", + "integrity": "sha512-cvdTPsi2qC1c22UppvuVmx/PDwuc6+QQkwt9OnwQD6Uotbh//tb2XDF0OoK2V0F4b8d02LIwNp3BieaDMAhIhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.2" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.14.tgz", + "integrity": "sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@tsconfig/node22": { + "version": "22.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.2.tgz", + "integrity": "sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsdom": { + "version": "21.1.7", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", + "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.18.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.8.tgz", + "integrity": "sha512-pAZSHMiagDR7cARo/cch1f3rXy0AEXwsVsVH09FcyeJVAzCnGgmYis7P3JidtTUjyadhTeSo8TgRPswstghDaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/type-utils": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.45.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.45.0", + "@typescript-eslint/types": "^8.45.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitest/eslint-plugin": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.3.13.tgz", + "integrity": "sha512-QfzXd1+lCY3dIqPHOZlagA2bJYoWC5yAU3adv8Gks0rHAL6FpyXKYBiyMCuU6mRrbKUMphGqwDQobinOvYgJig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "^8.41.0", + "@typescript-eslint/utils": "^8.24.1" + }, + "peerDependencies": { + "eslint": ">= 8.57.0", + "typescript": ">= 5.0.0", + "vitest": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz", + "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.23" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz", + "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz", + "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", + "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz", + "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@vue/babel-helper-vue-transform-on": "1.5.0", + "@vue/babel-plugin-resolve-type": "1.5.0", + "@vue/shared": "^3.5.18" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz", + "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.0", + "@vue/compiler-sfc": "^3.5.18" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz", + "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.4", + "@vue/shared": "3.5.22", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz", + "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.22", + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz", + "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.4", + "@vue/compiler-core": "3.5.22", + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.19", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz", + "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.22", + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz", + "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.7" + } + }, + "node_modules/@vue/devtools-core": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.0.2.tgz", + "integrity": "sha512-V7eKTTHoS6KfK8PSGMLZMhGv/9yNDrmv6Qc3r71QILulnzPnqK2frsTyx3e2MrhdUZnENPEm6hcb4z0GZOqNhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.0.2", + "@vue/devtools-shared": "^8.0.2", + "mitt": "^3.0.1", + "nanoid": "^5.1.5", + "pathe": "^2.0.3", + "vite-hot-client": "^2.1.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-core/node_modules/@vue/devtools-kit": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.2.tgz", + "integrity": "sha512-yjZKdEmhJzQqbOh4KFBfTOQjDPMrjjBNCnHBvnTGJX+YLAqoUtY2J+cg7BE+EA8KUv8LprECq04ts75wCoIGWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.0.2", + "birpc": "^2.5.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^2.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-core/node_modules/@vue/devtools-shared": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.2.tgz", + "integrity": "sha512-mLU0QVdy5Lp40PMGSixDw/Kbd6v5dkQXltd2r+mdVQV7iUog2NlZuLxFZApFZ/mObUBDhoCpf0T3zF2FWWdeHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/devtools-core/node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@vue/devtools-core/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", + "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.7", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", + "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/eslint-config-prettier": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz", + "integrity": "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2" + }, + "peerDependencies": { + "eslint": ">= 8.21.0", + "prettier": ">= 3.0.0" + } + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "14.6.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.6.0.tgz", + "integrity": "sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.35.1", + "fast-glob": "^3.3.3", + "typescript-eslint": "^8.35.1", + "vue-eslint-parser": "^10.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0", + "eslint-plugin-vue": "^9.28.0 || ^10.0.0", + "typescript": ">=4.8.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.1.0.tgz", + "integrity": "sha512-a7ns+X9vTbdmk7QLrvnZs8s4E1wwtxG/sELzr6F2j4pU+r/OoAv6jJGSz+5tVTU6e4+3rjepGhSP8jDmBBcb3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.0.0", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.22.tgz", + "integrity": "sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.22.tgz", + "integrity": "sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.22", + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.22.tgz", + "integrity": "sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.22", + "@vue/runtime-core": "3.5.22", + "@vue/shared": "3.5.22", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.22.tgz", + "integrity": "sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22" + }, + "peerDependencies": { + "vue": "3.5.22" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz", + "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==", + "license": "MIT" + }, + "node_modules/@vue/test-utils": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz", + "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^2.0.0" + } + }, + "node_modules/@vue/tsconfig": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.8.1.tgz", + "integrity": "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/alien-signals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.0.0.tgz", + "integrity": "sha512-JHoRJf18Y6HN4/KZALr3iU+0vW9LKG+8FMThQlbn4+gv8utsLIkwpomjElGPccGeNwh0FI2HN6BLnyFLo6OyLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz", + "integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/birpc": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.6.1.tgz", + "integrity": "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001746", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz", + "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssstyle": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.1.tgz", + "integrity": "sha512-g5PC9Aiph9eiczFpcgUhd9S4UUO3F+LHGRIi5NUMZ+4xtoIYbHNZwZnWA2JsFGe8OU8nl4WyaEFiZuGuxlutJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^4.0.3", + "@csstools/css-syntax-patches-for-csstree": "^1.0.14", + "css-tree": "^3.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz", + "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.228", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz", + "integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.4.0.tgz", + "integrity": "sha512-K6tP0dW8FJVZLQxa2S7LcE1lLw3X8VvB3t887Q6CLrFVxHYBXGANbXvwNzYIu6Ughx1bSJ5BDT0YB3ybPT39lw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "vue-eslint-parser": "^10.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/parser": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.0.0.tgz", + "integrity": "sha512-lIHeR1qlIRrIN5VMccd8tI2Sgw6ieYXSVktcSHaNe3Z5nE/tcPQYQWOq00wxMvYOsz+73eAkNenVvmPC6bba9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/dom-selector": "^6.5.4", + "cssstyle": "^5.3.0", + "data-urls": "^6.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^7.3.0", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.0.0", + "ws": "^8.18.2", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz", + "integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "picomatch": "^4.0.2", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^20.5.0 || >=22.0.0", + "npm": ">= 10" + } + }, + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pinia": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.3.tgz", + "integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz", + "integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.16", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.16.tgz", + "integrity": "sha512-5bdPHSwbKTeHmXrgecID4Ljff8rQjv7g8zKQPkCozRo2HWWni+p310FSn5ImI+9kWw9kK4lzOB5q/a6iv0IJsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.16" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.16", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.16.tgz", + "integrity": "sha512-XHhPmHxphLi+LGbH0G/O7dmUH9V65OY20R7vH8gETHsp5AZCjBk9l8sqmRKLaGOxnETU7XNSDUPtewAy/K6jbA==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.45.0", + "@typescript-eslint/parser": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.0.tgz", + "integrity": "sha512-JLoggz+PvLVMJo+jZt97hdIIIZ2yTzGgft9e9q8iMrC4ewufl62ekeW7mixBghonn2gVb/ICjyvlmOCUBnJLQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz", + "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-dev-rpc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz", + "integrity": "sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==", + "dev": true, + "license": "MIT", + "dependencies": { + "birpc": "^2.4.0", + "vite-hot-client": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0" + } + }, + "node_modules/vite-hot-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.1.0.tgz", + "integrity": "sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-plugin-inspect": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.3.3.tgz", + "integrity": "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.1.0", + "debug": "^4.4.1", + "error-stack-parser-es": "^1.0.5", + "ohash": "^2.0.11", + "open": "^10.2.0", + "perfect-debounce": "^2.0.0", + "sirv": "^3.0.1", + "unplugin-utils": "^0.3.0", + "vite-dev-rpc": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-inspect/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite-plugin-vue-devtools": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.0.2.tgz", + "integrity": "sha512-1069qvMBcyAu3yXQlvYrkwoyLOk0lSSR/gTKy/vy+Det7TXnouGei6ZcKwr5TIe938v/14oLlp0ow6FSJkkORA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-core": "^8.0.2", + "@vue/devtools-kit": "^8.0.2", + "@vue/devtools-shared": "^8.0.2", + "execa": "^9.6.0", + "sirv": "^3.0.2", + "vite-plugin-inspect": "^11.3.3", + "vite-plugin-vue-inspector": "^5.3.2" + }, + "engines": { + "node": ">=v14.21.3" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/@vue/devtools-kit": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.2.tgz", + "integrity": "sha512-yjZKdEmhJzQqbOh4KFBfTOQjDPMrjjBNCnHBvnTGJX+YLAqoUtY2J+cg7BE+EA8KUv8LprECq04ts75wCoIGWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.0.2", + "birpc": "^2.5.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^2.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/@vue/devtools-shared": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.2.tgz", + "integrity": "sha512-mLU0QVdy5Lp40PMGSixDw/Kbd6v5dkQXltd2r+mdVQV7iUog2NlZuLxFZApFZ/mObUBDhoCpf0T3zF2FWWdeHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite-plugin-vue-inspector": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.3.2.tgz", + "integrity": "sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.0", + "@babel/plugin-proposal-decorators": "^7.23.0", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.22.15", + "@vue/babel-plugin-jsx": "^1.1.5", + "@vue/compiler-dom": "^3.3.4", + "kolorist": "^1.8.0", + "magic-string": "^0.30.4" + }, + "peerDependencies": { + "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz", + "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-sfc": "3.5.22", + "@vue/runtime-dom": "3.5.22", + "@vue/server-renderer": "3.5.22", + "@vue/shared": "3.5.22" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-component-type-helpers": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz", + "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-eslint-parser": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz", + "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.6.0", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-tsc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.1.0.tgz", + "integrity": "sha512-fbMynMG7kXSnqZTRBSCh9ROYaVpXfCZbEO0gY3lqOjLbp361uuS88n6BDajiUriDIF+SGLWoinjvf6stS2J3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.23", + "@vue/language-core": "3.1.0" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz", + "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/src/ModManUi/package.json b/src/ModManUi/package.json new file mode 100644 index 00000000..b182ae49 --- /dev/null +++ b/src/ModManUi/package.json @@ -0,0 +1,42 @@ +{ + "name": "openassettools", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "run-p type-check \"build-only {@}\" --", + "preview": "vite preview", + "build-only": "vite build", + "type-check": "vue-tsc --build", + "lint": "eslint . --fix", + "format": "prettier --write **/*.{js,ts,vue,html,json,yml,yaml,md}" + }, + "dependencies": { + "pinia": "3.0.3", + "vue": "3.5.22" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/jsdom": "^21.1.7", + "@types/node": "^22.18.6", + "@vitejs/plugin-vue": "6.0.1", + "@vitest/eslint-plugin": "^1.3.13", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.6.0", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.8.1", + "eslint": "^9.33.0", + "eslint-plugin-vue": "~10.4.0", + "jiti": "^2.5.1", + "jsdom": "^27.0.0", + "npm-run-all2": "^8.0.4", + "prettier": "3.6.2", + "sass": "1.93.2", + "typescript": "~5.9.3", + "vite": "7.1.7", + "vite-plugin-vue-devtools": "^8.0.2", + "vitest": "^3.2.4", + "vue-tsc": "3.1.0" + } +} diff --git a/src/ModManUi/public/favicon.ico b/src/ModManUi/public/favicon.ico new file mode 100644 index 00000000..df36fcfb Binary files /dev/null and b/src/ModManUi/public/favicon.ico differ diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue new file mode 100644 index 00000000..c906c7d3 --- /dev/null +++ b/src/ModManUi/src/App.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/ModManUi/src/main.scss b/src/ModManUi/src/main.scss new file mode 100644 index 00000000..20d4d9c6 --- /dev/null +++ b/src/ModManUi/src/main.scss @@ -0,0 +1,109 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color: #0f0f0f; + background-color: #f6f6f6; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +.container { + margin: 0; + padding-top: 10vh; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: 0.75s; +} + +.logo.tauri:hover { + filter: drop-shadow(0 0 2em #24c8db); +} + +.row { + display: flex; + justify-content: center; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +h1 { + text-align: center; +} + +input, +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + color: #0f0f0f; + background-color: #ffffff; + transition: border-color 0.25s; + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); +} + +button { + cursor: pointer; +} + +button:hover { + border-color: #396cd8; +} +button:active { + border-color: #396cd8; + background-color: #e8e8e8; +} + +input, +button { + outline: none; +} + +#greet-input { + margin-right: 5px; +} + +@media (prefers-color-scheme: dark) { + :root { + color: #f6f6f6; + background-color: #2f2f2f; + } + + a:hover { + color: #24c8db; + } + + input, + button { + color: #ffffff; + background-color: #0f0f0f98; + } + button:active { + background-color: #0f0f0f69; + } +} \ No newline at end of file diff --git a/src/ModManUi/src/main.ts b/src/ModManUi/src/main.ts new file mode 100644 index 00000000..d1a7b243 --- /dev/null +++ b/src/ModManUi/src/main.ts @@ -0,0 +1,13 @@ +import "../public/favicon.ico"; +import "./main.scss"; + +import { createApp } from "vue"; +import { createPinia } from "pinia"; + +import App from "./App.vue"; + +const app = createApp(App); + +app.use(createPinia()); + +app.mount("#app"); diff --git a/src/ModManUi/src/native.ts b/src/ModManUi/src/native.ts new file mode 100644 index 00000000..d2cc9a66 --- /dev/null +++ b/src/ModManUi/src/native.ts @@ -0,0 +1,25 @@ +export interface NativeMethods { + greet: (name: string) => Promise; +} + +interface NativeEventMap { + greeting: string; +} + +type WebViewExtensions = { + webviewBinds: NativeMethods; + webviewAddEventListener( + eventKey: K, + callback: (payload: NativeEventMap[K]) => void, + ): void; + webviewRemoveEventListener( + eventKey: K, + callback: (payload: NativeEventMap[K]) => void, + ): boolean; +}; + +const windowWithWebViewExtensions = window as typeof window & WebViewExtensions; + +export const webviewBinds = windowWithWebViewExtensions.webviewBinds; +export const webviewAddEventListener = windowWithWebViewExtensions.webviewAddEventListener; +export const webviewRemoveEventListener = windowWithWebViewExtensions.webviewRemoveEventListener; diff --git a/src/ModManUi/src/stores/counter.ts b/src/ModManUi/src/stores/counter.ts new file mode 100644 index 00000000..374b4d03 --- /dev/null +++ b/src/ModManUi/src/stores/counter.ts @@ -0,0 +1,12 @@ +import { ref, computed } from "vue"; +import { defineStore } from "pinia"; + +export const useCounterStore = defineStore("counter", () => { + const count = ref(0); + const doubleCount = computed(() => count.value * 2); + function increment() { + count.value++; + } + + return { count, doubleCount, increment }; +}); diff --git a/src/ModManUi/tsconfig.app.json b/src/ModManUi/tsconfig.app.json new file mode 100644 index 00000000..913b8f27 --- /dev/null +++ b/src/ModManUi/tsconfig.app.json @@ -0,0 +1,12 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/src/ModManUi/tsconfig.json b/src/ModManUi/tsconfig.json new file mode 100644 index 00000000..100cf6a8 --- /dev/null +++ b/src/ModManUi/tsconfig.json @@ -0,0 +1,14 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.vitest.json" + } + ] +} diff --git a/src/ModManUi/tsconfig.node.json b/src/ModManUi/tsconfig.node.json new file mode 100644 index 00000000..a23d4181 --- /dev/null +++ b/src/ModManUi/tsconfig.node.json @@ -0,0 +1,20 @@ +{ + "extends": "@tsconfig/node22/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*", + "eslint.config.*", + "build/**/*" + ], + "compilerOptions": { + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/src/ModManUi/tsconfig.vitest.json b/src/ModManUi/tsconfig.vitest.json new file mode 100644 index 00000000..7d1d8cef --- /dev/null +++ b/src/ModManUi/tsconfig.vitest.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.app.json", + "include": ["src/**/__tests__/*", "env.d.ts"], + "exclude": [], + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", + + "lib": [], + "types": ["node", "jsdom"] + } +} diff --git a/src/ModManUi/vite.config.ts b/src/ModManUi/vite.config.ts new file mode 100644 index 00000000..f5edcd61 --- /dev/null +++ b/src/ModManUi/vite.config.ts @@ -0,0 +1,35 @@ +import { fileURLToPath, URL } from "node:url"; + +import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; +import vueDevTools from "vite-plugin-vue-devtools"; +import headerTransformationPlugin from "./build/HeaderTransformationPlugin"; + +// https://vite.dev/config/ +export default defineConfig({ + build: { + copyPublicDir: false, + emptyOutDir: true, + rollupOptions: { + output: { + assetFileNames: "[name][extname]", + entryFileNames: "[name].js", + chunkFileNames: "[name].js", + }, + }, + }, + plugins: [ + vue(), + vueDevTools(), + headerTransformationPlugin({ + outputPath: fileURLToPath( + new URL("../../build/src/ModMan/Web/ViteAssets.h", import.meta.url), + ), + }), + ], + resolve: { + alias: { + "@": fileURLToPath(new URL("./src", import.meta.url)), + }, + }, +}); diff --git a/src/ModManUi/vitest.config.ts b/src/ModManUi/vitest.config.ts new file mode 100644 index 00000000..0ac4ebec --- /dev/null +++ b/src/ModManUi/vitest.config.ts @@ -0,0 +1,13 @@ +import { fileURLToPath } from "node:url"; +import { mergeConfig, defineConfig } from "vitest/config"; +import viteConfig from "./vite.config"; + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + environment: "jsdom", + root: fileURLToPath(new URL("./", import.meta.url)), + }, + }), +); diff --git a/src/ObjCommon.lua b/src/ObjCommon.lua index d1e28826..93bf6d44 100644 --- a/src/ObjCommon.lua +++ b/src/ObjCommon.lua @@ -54,4 +54,5 @@ function ObjCommon:project() self:include(includes) Utils:include(includes) + eigen:include(includes) end diff --git a/src/ObjCommon/FontIcon/FontIconCommon.cpp b/src/ObjCommon/FontIcon/FontIconCommon.cpp new file mode 100644 index 00000000..af9cbfb1 --- /dev/null +++ b/src/ObjCommon/FontIcon/FontIconCommon.cpp @@ -0,0 +1,23 @@ +#include "FontIconCommon.h" + +#include "Utils/StringUtils.h" + +#include +namespace fs = std::filesystem; + +namespace font_icon +{ + std::string GetJsonFileNameForAssetName(const std::string& assetName) + { + std::string originalFileName(assetName); + utils::MakeStringLowerCase(originalFileName); + if (originalFileName.ends_with(".csv")) + { + fs::path p(assetName); + p.replace_extension(".json"); + return p.string(); + } + + return assetName; + } +} // namespace font_icon diff --git a/src/ObjCommon/FontIcon/FontIconCommon.h b/src/ObjCommon/FontIcon/FontIconCommon.h new file mode 100644 index 00000000..e4c887bd --- /dev/null +++ b/src/ObjCommon/FontIcon/FontIconCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace font_icon +{ + std::string GetJsonFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/Game/IW5/XModel/JsonXModelIW5.h b/src/ObjCommon/Game/IW5/XModel/JsonXModelIW5.h deleted file mode 100644 index ce7424ff..00000000 --- a/src/ObjCommon/Game/IW5/XModel/JsonXModelIW5.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "Json/JsonCommon.h" -#include -#include -#include -#include -#include - -namespace IW5 -{ - class JsonXModelLod - { - public: - std::string file; - float distance; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file, distance); - - class JsonXModel - { - public: - std::vector lods; - std::optional collLod; - std::optional physPreset; - std::optional physCollmap; - uint8_t flags; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModel, lods, collLod, physPreset, physCollmap, flags); -} // namespace IW5 diff --git a/src/ObjCommon/Game/IW5/XModel/XModelConstantsIW5.h b/src/ObjCommon/Game/IW5/XModel/XModelConstantsIW5.h deleted file mode 100644 index a0a129a3..00000000 --- a/src/ObjCommon/Game/IW5/XModel/XModelConstantsIW5.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - inline const char* HITLOC_NAMES[]{ - // clang-format off - "none", - "helmet", - "head", - "neck", - "torso_upper", - "torso_lower", - "right_arm_upper", - "left_arm_upper", - "right_arm_lower", - "left_arm_lower", - "right_hand", - "left_hand", - "right_leg_upper", - "left_leg_upper", - "right_leg_lower", - "left_leg_lower", - "right_foot", - "left_foot", - "gun", - "shield", - // clang-format on - }; - static_assert(std::extent_v == HITLOC_COUNT); -} // namespace IW5 diff --git a/src/ObjCommon/Game/T5/XModel/JsonXModelT5.h b/src/ObjCommon/Game/T5/XModel/JsonXModelT5.h deleted file mode 100644 index 5b52ca9a..00000000 --- a/src/ObjCommon/Game/T5/XModel/JsonXModelT5.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "Json/JsonCommon.h" -#include -#include -#include -#include - -namespace T5 -{ - class JsonXModelLod - { - public: - std::string file; - float distance; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file, distance); - - class JsonXModel - { - public: - std::vector lods; - std::optional collLod; - std::optional physPreset; - std::optional physConstraints; - unsigned flags; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModel, lods, collLod, physPreset, physConstraints, flags); -} // namespace T5 diff --git a/src/ObjCommon/Game/T5/XModel/XModelConstantsT5.h b/src/ObjCommon/Game/T5/XModel/XModelConstantsT5.h deleted file mode 100644 index 0481cd27..00000000 --- a/src/ObjCommon/Game/T5/XModel/XModelConstantsT5.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "Game/T5/T5.h" - -namespace T5 -{ - inline const char* HITLOC_NAMES[]{ - // clang-format off - "none", - "helmet", - "head", - "neck", - "torso_upper", - "torso_lower", - "right_arm_upper", - "left_arm_upper", - "right_arm_lower", - "left_arm_lower", - "right_hand", - "left_hand", - "right_leg_upper", - "left_leg_upper", - "right_leg_lower", - "left_leg_lower", - "right_foot", - "left_foot", - "gun", - // clang-format on - }; - static_assert(std::extent_v == HITLOC_COUNT); -} // namespace T5 diff --git a/src/ObjCommon/Game/T6/FontIcon/JsonFontIconT6.h b/src/ObjCommon/Game/T6/FontIcon/JsonFontIconT6.h new file mode 100644 index 00000000..583f5ac9 --- /dev/null +++ b/src/ObjCommon/Game/T6/FontIcon/JsonFontIconT6.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Json/JsonCommon.h" +#include "Json/JsonExtension.h" +#include +#include +#include +#include +#include + +namespace T6 +{ + class JsonFontIconEntry + { + public: + std::string name; + std::string material; + unsigned size; + std::optional scale; + std::vector aliases; + std::optional> aliasHashes; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonFontIconEntry, name, material, size, scale, aliases, aliasHashes); + + class JsonFontIcon + { + public: + std::vector entries; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonFontIcon, entries); +} // namespace T6 diff --git a/src/ObjCommon/Game/T6/XModel/JsonXModelT6.h b/src/ObjCommon/Game/T6/XModel/JsonXModelT6.h deleted file mode 100644 index 79e6eb06..00000000 --- a/src/ObjCommon/Game/T6/XModel/JsonXModelT6.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "Json/JsonCommon.h" -#include -#include -#include -#include - -namespace T6 -{ - class JsonXModelLod - { - public: - std::string file; - float distance; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file, distance); - - class JsonXModel - { - public: - std::vector lods; - std::optional collLod; - std::optional physPreset; - std::optional physConstraints; - unsigned flags; - JsonVec3 lightingOriginOffset; - float lightingOriginRange; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModel, lods, collLod, physPreset, physConstraints, flags, lightingOriginOffset, lightingOriginRange); -} // namespace T6 diff --git a/src/ObjCommon/Game/T6/XModel/XModelConstantsT6.h b/src/ObjCommon/Game/T6/XModel/XModelConstantsT6.h deleted file mode 100644 index e7ae94cd..00000000 --- a/src/ObjCommon/Game/T6/XModel/XModelConstantsT6.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "Game/T6/T6.h" - -namespace T6 -{ - inline const char* HITLOC_NAMES[]{ - // clang-format off - "none", - "helmet", - "head", - "neck", - "torso_upper", - "torso_middle", - "torso_lower", - "right_arm_upper", - "left_arm_upper", - "right_arm_lower", - "left_arm_lower", - "right_hand", - "left_hand", - "right_leg_upper", - "left_leg_upper", - "right_leg_lower", - "left_leg_lower", - "right_foot", - "left_foot", - "gun", - "shield", - // clang-format on - }; - static_assert(std::extent_v == HITLOC_COUNT); -} // namespace T6 diff --git a/src/ObjCommon/Image/ImageCommon.cpp b/src/ObjCommon/Image/ImageCommon.cpp new file mode 100644 index 00000000..8a605bd7 --- /dev/null +++ b/src/ObjCommon/Image/ImageCommon.cpp @@ -0,0 +1,15 @@ +#include "ImageCommon.h" + +#include +#include + +namespace image +{ + std::string GetFileNameForAsset(std::string assetName, const std::string& extension) + { + std::string cleanAssetName(std::move(assetName)); + std::ranges::replace(cleanAssetName, '*', '_'); + + return std::format("images/{}{}", cleanAssetName, extension); + } +} // namespace image diff --git a/src/ObjCommon/Image/ImageCommon.h b/src/ObjCommon/Image/ImageCommon.h new file mode 100644 index 00000000..1a18b70e --- /dev/null +++ b/src/ObjCommon/Image/ImageCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace image +{ + std::string GetFileNameForAsset(std::string assetName, const std::string& extension); +} diff --git a/src/ObjCommon/InfoString/InfoString.cpp b/src/ObjCommon/InfoString/InfoString.cpp index 14efada8..c2c7a70e 100644 --- a/src/ObjCommon/InfoString/InfoString.cpp +++ b/src/ObjCommon/InfoString/InfoString.cpp @@ -1,5 +1,7 @@ #include "InfoString.h" +#include "Utils/Logging/Log.h" + #include #include #include @@ -172,13 +174,13 @@ bool InfoString::FromStream(const std::string& prefix, std::istream& stream) std::string readPrefix; if (!infoStream.NextField(readPrefix)) { - std::cerr << "Invalid info string: Empty\n"; + con::error("Invalid info string: Empty"); return false; } if (prefix != readPrefix) { - std::cerr << "Invalid info string: Prefix \"" << readPrefix << "\" did not match expected prefix \"" << prefix << "\"\n"; + con::error("Invalid info string: Prefix \"{}\" did not match expected prefix \"{}\"", readPrefix, prefix); return false; } @@ -188,9 +190,9 @@ bool InfoString::FromStream(const std::string& prefix, std::istream& stream) if (key.empty()) { if (m_keys_by_insertion.empty()) - std::cerr << "Invalid info string: Got empty key at the start of the info string\n"; + con::error("Invalid info string: Got empty key at the start of the info string"); else - std::cerr << "Invalid info string: Got empty key after key \"" << m_keys_by_insertion[m_keys_by_insertion.size() - 1] << "\"\n"; + con::error("Invalid info string: Got empty key after key \"{}\"", m_keys_by_insertion[m_keys_by_insertion.size() - 1]); return false; } @@ -198,7 +200,7 @@ bool InfoString::FromStream(const std::string& prefix, std::istream& stream) std::string value; if (!infoStream.NextField(value)) { - std::cerr << "Invalid info string: Unexpected eof, no value for key \"" << key << "\"\n"; + con::error("Invalid info string: Unexpected eof, no value for key \"{}\"", key); return false; } diff --git a/src/ObjCommon/Leaderboard/LeaderboardCommon.cpp b/src/ObjCommon/Leaderboard/LeaderboardCommon.cpp new file mode 100644 index 00000000..d3286f36 --- /dev/null +++ b/src/ObjCommon/Leaderboard/LeaderboardCommon.cpp @@ -0,0 +1,11 @@ +#include "LeaderboardCommon.h" + +#include + +namespace leaderboard +{ + std::string GetJsonFileNameForAsset(const std::string& assetName) + { + return std::format("leaderboards/{}.json", assetName); + } +} // namespace leaderboard diff --git a/src/ObjCommon/Leaderboard/LeaderboardCommon.h b/src/ObjCommon/Leaderboard/LeaderboardCommon.h new file mode 100644 index 00000000..ab6958b1 --- /dev/null +++ b/src/ObjCommon/Leaderboard/LeaderboardCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace leaderboard +{ + std::string GetJsonFileNameForAsset(const std::string& assetName); +} diff --git a/src/ObjCommon/LightDef/LightDefCommon.cpp b/src/ObjCommon/LightDef/LightDefCommon.cpp new file mode 100644 index 00000000..b43af370 --- /dev/null +++ b/src/ObjCommon/LightDef/LightDefCommon.cpp @@ -0,0 +1,11 @@ +#include "LightDefCommon.h" + +#include + +namespace light_def +{ + std::string GetFileNameForAsset(const std::string& assetName) + { + return std::format("lights/{}", assetName); + } +} // namespace light_def diff --git a/src/ObjCommon/LightDef/LightDefCommon.h b/src/ObjCommon/LightDef/LightDefCommon.h new file mode 100644 index 00000000..22002431 --- /dev/null +++ b/src/ObjCommon/LightDef/LightDefCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace light_def +{ + std::string GetFileNameForAsset(const std::string& assetName); +} diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template index 7c7f0663..b14bf10f 100644 --- a/src/ObjCommon/Material/JsonMaterial.h.template +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -1,13 +1,19 @@ -#options GAME (IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/Material/JsonMaterial" + GAME + ".h" -#if GAME == "IW4" +#if GAME == "IW3" +#define FEATURE_IW3 +#define HAS_WATER +#elif GAME == "IW4" #define FEATURE_IW4 #define HAS_WATER #elif GAME == "IW5" #define FEATURE_IW5 #define HAS_WATER +#elif GAME == "T5" +#define FEATURE_T5 +#define HAS_WATER #elif GAME == "T6" #define FEATURE_T6 #endif @@ -75,8 +81,10 @@ namespace GAME INVALID, DISABLED, GT0, -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) LT128, +#elif defined(FEATURE_T5) + GE255, #endif GE128 }; @@ -85,8 +93,10 @@ namespace GAME {JsonAlphaTest::INVALID, nullptr }, {JsonAlphaTest::DISABLED, "disabled"}, {JsonAlphaTest::GT0, "gt0" }, -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) {JsonAlphaTest::LT128, "lt128" }, +#elif defined(FEATURE_T5) + {JsonAlphaTest::GE255, "ge255" }, #endif {JsonAlphaTest::GE128, "ge128" } }); @@ -208,7 +218,7 @@ namespace GAME std::optional name; std::optional nameFragment; std::optional nameHash; - std::vector literal; + std::array literal; }; inline void to_json(nlohmann::json& out, const JsonConstant& in) @@ -315,52 +325,48 @@ namespace GAME #endif NLOHMANN_JSON_SERIALIZE_ENUM(TextureSemantic, { -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) {TS_2D, "2D" }, {TS_FUNCTION, "function" }, {TS_COLOR_MAP, "colorMap" }, +#if defined(FEATURE_IW3) || defined(FEATURE_T5) || defined(FEATURE_T6) + {TS_UNUSED_1, "unused1" }, +#else {TS_DETAIL_MAP, "detailMap" }, +#endif {TS_UNUSED_2, "unused2" }, {TS_NORMAL_MAP, "normalMap" }, {TS_UNUSED_3, "unused3" }, {TS_UNUSED_4, "unused4" }, {TS_SPECULAR_MAP, "specularMap" }, {TS_UNUSED_5, "unused5" }, +#ifdef FEATURE_T6 + {TS_OCCLUSION_MAP, "occlusionMap" }, +#endif {TS_UNUSED_6, "unused6" }, +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) || defined(FEATURE_T5) {TS_WATER_MAP, "waterMap" }, +#endif #ifdef FEATURE_IW5 {TS_DISPLACEMENT_MAP, "displacementMap"}, #endif -#elif defined(FEATURE_T6) - {TS_2D, "2D" }, - {TS_FUNCTION, "function" }, - {TS_COLOR_MAP, "colorMap" }, - {TS_UNUSED_1, "unused1" }, - {TS_UNUSED_2, "unused2" }, - {TS_NORMAL_MAP, "normalMap" }, - {TS_UNUSED_3, "unused3" }, - {TS_UNUSED_4, "unused4" }, - {TS_SPECULAR_MAP, "specularMap" }, - {TS_UNUSED_5, "unused5" }, - {TS_OCCLUSION_MAP, "occlusionMap"}, - {TS_UNUSED_6, "unused6" }, - {TS_COLOR0_MAP, "color0Map" }, - {TS_COLOR1_MAP, "color1Map" }, - {TS_COLOR2_MAP, "color2Map" }, - {TS_COLOR3_MAP, "color3Map" }, - {TS_COLOR4_MAP, "color4Map" }, - {TS_COLOR5_MAP, "color5Map" }, - {TS_COLOR6_MAP, "color6Map" }, - {TS_COLOR7_MAP, "color7Map" }, - {TS_COLOR8_MAP, "color8Map" }, - {TS_COLOR9_MAP, "color9Map" }, - {TS_COLOR10_MAP, "color10Map" }, - {TS_COLOR11_MAP, "color11Map" }, - {TS_COLOR12_MAP, "color12Map" }, - {TS_COLOR13_MAP, "color13Map" }, - {TS_COLOR14_MAP, "color14Map" }, - {TS_COLOR15_MAP, "color15Map" }, - {TS_THROW_MAP, "throwMap" }, +#if defined(FEATURE_T5) || defined(FEATURE_T6) + {TS_COLOR0_MAP, "color0Map" }, + {TS_COLOR1_MAP, "color1Map" }, + {TS_COLOR2_MAP, "color2Map" }, + {TS_COLOR3_MAP, "color3Map" }, + {TS_COLOR4_MAP, "color4Map" }, + {TS_COLOR5_MAP, "color5Map" }, + {TS_COLOR6_MAP, "color6Map" }, + {TS_COLOR7_MAP, "color7Map" }, + {TS_COLOR8_MAP, "color8Map" }, + {TS_COLOR9_MAP, "color9Map" }, + {TS_COLOR10_MAP, "color10Map" }, + {TS_COLOR11_MAP, "color11Map" }, + {TS_COLOR12_MAP, "color12Map" }, + {TS_COLOR13_MAP, "color13Map" }, + {TS_COLOR14_MAP, "color14Map" }, + {TS_COLOR15_MAP, "color15Map" }, + {TS_THROW_MAP, "throwMap" }, #endif }); @@ -372,7 +378,7 @@ namespace GAME std::optional nameStart; std::optional nameEnd; TextureSemantic semantic; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) bool isMatureContent; #endif JsonSamplerState samplerState; @@ -396,7 +402,7 @@ namespace GAME } out["semantic"] = in.semantic; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) out["isMatureContent"] = in.isMatureContent; #endif out["samplerState"] = in.samplerState; @@ -413,7 +419,7 @@ namespace GAME optional_from_json(in, "nameStart", out.nameStart); optional_from_json(in, "nameEnd", out.nameEnd); in.at("semantic").get_to(out.semantic); -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) in.at("isMatureContent").get_to(out.isMatureContent); #endif in.at("samplerState").get_to(out.samplerState); @@ -437,36 +443,43 @@ namespace GAME ); NLOHMANN_JSON_SERIALIZE_ENUM(MaterialGameFlags, { -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) - {MTL_GAMEFLAG_1, "1" }, - {MTL_GAMEFLAG_2, "2" }, - {MTL_GAMEFLAG_4, "4" }, - {MTL_GAMEFLAG_8, "8" }, - {MTL_GAMEFLAG_10, "10" }, - {MTL_GAMEFLAG_20, "20" }, - {MTL_GAMEFLAG_40, "40" }, - {MTL_GAMEFLAG_80, "80" }, -#elif defined(FEATURE_T6) {MTL_GAMEFLAG_1, "1" }, {MTL_GAMEFLAG_2, "2" }, +#ifdef FEATURE_T6 {MTL_GAMEFLAG_NO_MARKS, "NO_MARKS" }, {MTL_GAMEFLAG_NO_MARKS, "4" }, +#else + {MTL_GAMEFLAG_4, "4" }, +#endif {MTL_GAMEFLAG_8, "8" }, {MTL_GAMEFLAG_10, "10" }, {MTL_GAMEFLAG_20, "20" }, +#if defined(FEATURE_IW3) || defined(FEATURE_T5) || defined(FEATURE_T6) {MTL_GAMEFLAG_CASTS_SHADOW, "CASTS_SHADOW"}, {MTL_GAMEFLAG_CASTS_SHADOW, "40" }, +#else + {MTL_GAMEFLAG_40, "40" }, +#endif {MTL_GAMEFLAG_80, "80" }, +#if defined(FEATURE_T5) || defined(FEATURE_T6) {MTL_GAMEFLAG_100, "100" }, {MTL_GAMEFLAG_200, "200" }, +#if defined(FEATURE_T6) {MTL_GAMEFLAG_400, "400" }, {MTL_GAMEFLAG_800, "800" }, {MTL_GAMEFLAG_1000, "1000" }, + {MTL_GAMEFLAG_2000, "2000" }, + {MTL_GAMEFLAG_4000, "4000" }, +#endif #endif }); NLOHMANN_JSON_SERIALIZE_ENUM(GfxCameraRegionType, { -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_T5) + {CAMERA_REGION_LIT, "lit" }, + {CAMERA_REGION_DECAL, "decal" }, + {CAMERA_REGION_EMISSIVE, "emissive" }, +#elif defined(FEATURE_IW4) || defined(FEATURE_IW5) {CAMERA_REGION_LIT_OPAQUE, "litOpaque" }, {CAMERA_REGION_LIT_TRANS, "litTrans" }, {CAMERA_REGION_EMISSIVE, "emissive" }, @@ -474,7 +487,6 @@ namespace GAME #ifdef FEATURE_IW5 {CAMERA_REGION_LIGHT_MAP_OPAQUE, "lightMapOpaque"}, #endif - {CAMERA_REGION_NONE, "none" }, #elif defined(FEATURE_T6) {CAMERA_REGION_LIT_OPAQUE, "litOpaque" }, {CAMERA_REGION_LIT_TRANS, "litTrans" }, @@ -486,20 +498,20 @@ namespace GAME {CAMERA_REGION_DEPTH_HACK, "depthHack" }, {CAMERA_REGION_UNUSED, "unused" }, {CAMERA_REGION_SONAR, "sonar" }, - {CAMERA_REGION_NONE, "none" }, #endif + {CAMERA_REGION_NONE, "none" }, }); class JsonMaterial { public: -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) unsigned layeredSurfaceTypes; - unsigned hashIndex; +#if defined(FEATURE_T6) unsigned surfaceFlags; unsigned contents; - uint8_t probeMipBits; std::optional thermalMaterial; +#endif #endif std::vector gameFlags; unsigned sortKey; @@ -516,13 +528,13 @@ namespace GAME NLOHMANN_DEFINE_TYPE_EXTENSION( JsonMaterial, -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) layeredSurfaceTypes, - hashIndex, +#if defined(FEATURE_T6) surfaceFlags, contents, - probeMipBits, thermalMaterial, +#endif #endif gameFlags, sortKey, diff --git a/src/ObjCommon/Obj/Gdt/GdtStream.cpp b/src/ObjCommon/Obj/Gdt/GdtStream.cpp index 1416a6de..fc3f6139 100644 --- a/src/ObjCommon/Obj/Gdt/GdtStream.cpp +++ b/src/ObjCommon/Obj/Gdt/GdtStream.cpp @@ -1,5 +1,7 @@ #include "GdtStream.h" +#include "Utils/Logging/Log.h" + #include #include @@ -14,7 +16,7 @@ public: void GdtReader::PrintError(const std::string& message) const { - std::cout << "GDT Error at line " << m_line << ": " << message << "\n"; + con::error("GDT Error at line {}: {}", m_line, message); } int GdtReader::PeekChar() diff --git a/src/ObjCommon/PhysCollmap/PhysCollmapCommon.cpp b/src/ObjCommon/PhysCollmap/PhysCollmapCommon.cpp new file mode 100644 index 00000000..7414207e --- /dev/null +++ b/src/ObjCommon/PhysCollmap/PhysCollmapCommon.cpp @@ -0,0 +1,11 @@ +#include "PhysCollmapCommon.h" + +#include + +namespace phys_collmap +{ + std::string GetFileNameForAssetName(const std::string& assetName) + { + return std::format("phys_collmaps/{}.map", assetName); + } +} // namespace phys_collmap diff --git a/src/ObjCommon/PhysCollmap/PhysCollmapCommon.h b/src/ObjCommon/PhysCollmap/PhysCollmapCommon.h new file mode 100644 index 00000000..eaf0877e --- /dev/null +++ b/src/ObjCommon/PhysCollmap/PhysCollmapCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace phys_collmap +{ + std::string GetFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/PhysConstraints/PhysConstraintsCommon.cpp b/src/ObjCommon/PhysConstraints/PhysConstraintsCommon.cpp new file mode 100644 index 00000000..222a2615 --- /dev/null +++ b/src/ObjCommon/PhysConstraints/PhysConstraintsCommon.cpp @@ -0,0 +1,11 @@ +#include "PhysConstraintsCommon.h" + +#include + +namespace phys_constraints +{ + std::string GetFileNameForAssetName(const std::string& assetName) + { + return std::format("physconstraints/{}", assetName); + } +} // namespace phys_constraints diff --git a/src/ObjCommon/PhysConstraints/PhysConstraintsCommon.h b/src/ObjCommon/PhysConstraints/PhysConstraintsCommon.h new file mode 100644 index 00000000..90ccfb1c --- /dev/null +++ b/src/ObjCommon/PhysConstraints/PhysConstraintsCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace phys_constraints +{ + std::string GetFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/PhysPreset/PhysPresetCommon.cpp b/src/ObjCommon/PhysPreset/PhysPresetCommon.cpp new file mode 100644 index 00000000..66c08396 --- /dev/null +++ b/src/ObjCommon/PhysPreset/PhysPresetCommon.cpp @@ -0,0 +1,11 @@ +#include "PhysPresetCommon.h" + +#include + +namespace phys_preset +{ + std::string GetFileNameForAssetName(const std::string& assetName) + { + return std::format("physic/{}", assetName); + } +} // namespace phys_preset diff --git a/src/ObjCommon/PhysPreset/PhysPresetCommon.h b/src/ObjCommon/PhysPreset/PhysPresetCommon.h new file mode 100644 index 00000000..d627051c --- /dev/null +++ b/src/ObjCommon/PhysPreset/PhysPresetCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace phys_preset +{ + std::string GetFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/SearchPath/OutputPathFilesystem.cpp b/src/ObjCommon/SearchPath/OutputPathFilesystem.cpp index 038c5666..05945305 100644 --- a/src/ObjCommon/SearchPath/OutputPathFilesystem.cpp +++ b/src/ObjCommon/SearchPath/OutputPathFilesystem.cpp @@ -1,5 +1,7 @@ #include "OutputPathFilesystem.h" +#include "Utils/Logging/Log.h" + #include #include #include @@ -24,14 +26,14 @@ std::unique_ptr OutputPathFilesystem::Open(const std::string& file fs::create_directories(containingDirectory, ec); if (ec) { - std::cerr << std::format("Failed to create folder '{}' when try to open file '{}'\n", containingDirectory.string(), fileName); + con::error("Failed to create folder '{}' when try to open file '{}'", containingDirectory.string(), fileName); return nullptr; } std::ofstream stream(fullNewPath, std::ios::binary | std::ios::out); if (!stream.is_open()) { - std::cerr << std::format("Failed to open file '{}'\n", fileName); + con::error("Failed to open file '{}'", fileName); return nullptr; } diff --git a/src/ObjCommon/SearchPath/SearchPathFilesystem.cpp b/src/ObjCommon/SearchPath/SearchPathFilesystem.cpp index a87f972e..4cd54d62 100644 --- a/src/ObjCommon/SearchPath/SearchPathFilesystem.cpp +++ b/src/ObjCommon/SearchPath/SearchPathFilesystem.cpp @@ -1,5 +1,6 @@ #include "SearchPathFilesystem.h" +#include "Utils/Logging/Log.h" #include "Utils/ObjFileStream.h" #include @@ -59,6 +60,6 @@ void SearchPathFilesystem::Find(const SearchPathSearchOptions& options, const st } catch (std::filesystem::filesystem_error& e) { - std::cerr << std::format("Directory Iterator threw error when trying to find files: \"{}\"\n", e.what()); + con::error("Directory Iterator threw error when trying to find files: \"{}\"", e.what()); } } diff --git a/src/ObjCommon/Shader/ShaderCommon.cpp b/src/ObjCommon/Shader/ShaderCommon.cpp new file mode 100644 index 00000000..7dc6c7bb --- /dev/null +++ b/src/ObjCommon/Shader/ShaderCommon.cpp @@ -0,0 +1,16 @@ +#include "ShaderCommon.h" + +#include + +namespace shader +{ + std::string GetFileNameForPixelShaderAssetName(const std::string& assetName) + { + return std::format("shader_bin/ps_{}.cso", assetName); + } + + std::string GetFileNameForVertexShaderAssetName(const std::string& assetName) + { + return std::format("shader_bin/vs_{}.cso", assetName); + } +} // namespace shader diff --git a/src/ObjCommon/Shader/ShaderCommon.h b/src/ObjCommon/Shader/ShaderCommon.h new file mode 100644 index 00000000..0e3ef878 --- /dev/null +++ b/src/ObjCommon/Shader/ShaderCommon.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace shader +{ + std::string GetFileNameForPixelShaderAssetName(const std::string& assetName); + std::string GetFileNameForVertexShaderAssetName(const std::string& assetName); +} // namespace shader diff --git a/src/ObjCommon/Sound/FlacDecoder.cpp b/src/ObjCommon/Sound/FlacDecoder.cpp index 3ef8008c..a7c4fc0e 100644 --- a/src/ObjCommon/Sound/FlacDecoder.cpp +++ b/src/ObjCommon/Sound/FlacDecoder.cpp @@ -4,6 +4,7 @@ #include "Utils/ClassUtils.h" #include "Utils/Endianness.h" #include "Utils/FileUtils.h" +#include "Utils/Logging/Log.h" #include #include @@ -232,7 +233,7 @@ namespace flac } catch (const FlacReadingException& e) { - std::cerr << e.what() << "\n"; + con::error(e.what()); } return false; diff --git a/src/ObjCommon/Sound/SoundCurveCommon.cpp b/src/ObjCommon/Sound/SoundCurveCommon.cpp new file mode 100644 index 00000000..461a3f08 --- /dev/null +++ b/src/ObjCommon/Sound/SoundCurveCommon.cpp @@ -0,0 +1,11 @@ +#include "SoundCurveCommon.h" + +#include + +namespace sound_curve +{ + std::string GetFileNameForAssetName(const std::string& assetName) + { + return std::format("soundaliases/{}.vfcurve", assetName); + } +} // namespace sound_curve diff --git a/src/ObjCommon/Sound/SoundCurveCommon.h b/src/ObjCommon/Sound/SoundCurveCommon.h new file mode 100644 index 00000000..8ac96a84 --- /dev/null +++ b/src/ObjCommon/Sound/SoundCurveCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace sound_curve +{ + std::string GetFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/Techset/TechsetCommon.cpp b/src/ObjCommon/Techset/TechsetCommon.cpp new file mode 100644 index 00000000..f21097bd --- /dev/null +++ b/src/ObjCommon/Techset/TechsetCommon.cpp @@ -0,0 +1,21 @@ +#include "TechsetCommon.h" + +#include + +namespace techset +{ + std::string GetFileNameForStateMapName(const std::string& stateMapName) + { + return std::format("statemaps/{}.sm", stateMapName); + } + + std::string GetFileNameForTechniqueName(const std::string& assetName) + { + return std::format("techniques/{}.tech", assetName); + } + + std::string GetFileNameForTechsetName(const std::string& assetName) + { + return std::format("techsets/{}.techset", assetName); + } +} // namespace techset diff --git a/src/ObjCommon/Techset/TechsetCommon.h b/src/ObjCommon/Techset/TechsetCommon.h new file mode 100644 index 00000000..ad65b5df --- /dev/null +++ b/src/ObjCommon/Techset/TechsetCommon.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace techset +{ + std::string GetFileNameForStateMapName(const std::string& stateMapName); + std::string GetFileNameForTechniqueName(const std::string& assetName); + std::string GetFileNameForTechsetName(const std::string& assetName); +} // namespace techset diff --git a/src/ObjCommon/Tracer/TracerCommon.cpp b/src/ObjCommon/Tracer/TracerCommon.cpp new file mode 100644 index 00000000..d48a0740 --- /dev/null +++ b/src/ObjCommon/Tracer/TracerCommon.cpp @@ -0,0 +1,11 @@ +#include "TracerCommon.h" + +#include + +namespace tracer +{ + std::string GetFileNameForAssetName(const std::string& assetName) + { + return std::format("tracer/{}", assetName); + } +} // namespace tracer diff --git a/src/ObjCommon/Tracer/TracerCommon.h b/src/ObjCommon/Tracer/TracerCommon.h new file mode 100644 index 00000000..d95136b7 --- /dev/null +++ b/src/ObjCommon/Tracer/TracerCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace tracer +{ + std::string GetFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/Vehicle/VehicleCommon.cpp b/src/ObjCommon/Vehicle/VehicleCommon.cpp new file mode 100644 index 00000000..6b1ca27f --- /dev/null +++ b/src/ObjCommon/Vehicle/VehicleCommon.cpp @@ -0,0 +1,11 @@ +#include "VehicleCommon.h" + +#include + +namespace vehicle +{ + std::string GetFileNameForAssetName(const std::string& assetName) + { + return std::format("vehicles/{}", assetName); + } +} // namespace vehicle diff --git a/src/ObjCommon/Vehicle/VehicleCommon.h b/src/ObjCommon/Vehicle/VehicleCommon.h new file mode 100644 index 00000000..ecd96304 --- /dev/null +++ b/src/ObjCommon/Vehicle/VehicleCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace vehicle +{ + std::string GetFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/Weapon/AttachmentCommon.cpp b/src/ObjCommon/Weapon/AttachmentCommon.cpp new file mode 100644 index 00000000..1fd354b3 --- /dev/null +++ b/src/ObjCommon/Weapon/AttachmentCommon.cpp @@ -0,0 +1,16 @@ +#include "AttachmentCommon.h" + +#include + +namespace attachment +{ + std::string GetInfoStringFileNameForAssetName(const std::string& assetName) + { + return std::format("attachment/{}", assetName); + } + + std::string GetJsonFileNameForAssetName(const std::string& assetName) + { + return std::format("attachment/{}.json", assetName); + } +} // namespace attachment diff --git a/src/ObjCommon/Weapon/AttachmentCommon.h b/src/ObjCommon/Weapon/AttachmentCommon.h new file mode 100644 index 00000000..e9566b4f --- /dev/null +++ b/src/ObjCommon/Weapon/AttachmentCommon.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace attachment +{ + std::string GetInfoStringFileNameForAssetName(const std::string& assetName); + std::string GetJsonFileNameForAssetName(const std::string& assetName); +} // namespace attachment diff --git a/src/ObjCommon/Weapon/AttachmentUniqueCommon.cpp b/src/ObjCommon/Weapon/AttachmentUniqueCommon.cpp new file mode 100644 index 00000000..a1b520e3 --- /dev/null +++ b/src/ObjCommon/Weapon/AttachmentUniqueCommon.cpp @@ -0,0 +1,11 @@ +#include "AttachmentUniqueCommon.h" + +#include + +namespace attachment_unique +{ + std::string GetFileNameForAssetName(const std::string& assetName) + { + return std::format("attachmentunique/{}", assetName); + } +} // namespace attachment_unique diff --git a/src/ObjCommon/Weapon/AttachmentUniqueCommon.h b/src/ObjCommon/Weapon/AttachmentUniqueCommon.h new file mode 100644 index 00000000..dcc9accc --- /dev/null +++ b/src/ObjCommon/Weapon/AttachmentUniqueCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace attachment_unique +{ + std::string GetFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/Weapon/CamoCommon.cpp b/src/ObjCommon/Weapon/CamoCommon.cpp new file mode 100644 index 00000000..293794eb --- /dev/null +++ b/src/ObjCommon/Weapon/CamoCommon.cpp @@ -0,0 +1,11 @@ +#include "WeaponCommon.h" + +#include + +namespace camo +{ + std::string GetJsonFileNameForAssetName(const std::string& assetName) + { + return std::format("camo/{}.json", assetName); + } +} // namespace camo diff --git a/src/ObjCommon/Weapon/CamoCommon.h b/src/ObjCommon/Weapon/CamoCommon.h new file mode 100644 index 00000000..b0450767 --- /dev/null +++ b/src/ObjCommon/Weapon/CamoCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace camo +{ + std::string GetJsonFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/Weapon/WeaponCommon.cpp b/src/ObjCommon/Weapon/WeaponCommon.cpp new file mode 100644 index 00000000..704a388c --- /dev/null +++ b/src/ObjCommon/Weapon/WeaponCommon.cpp @@ -0,0 +1,11 @@ +#include "WeaponCommon.h" + +#include + +namespace weapon +{ + std::string GetFileNameForAssetName(const std::string& assetName) + { + return std::format("weapons/{}", assetName); + } +} // namespace weapon diff --git a/src/ObjCommon/Weapon/WeaponCommon.h b/src/ObjCommon/Weapon/WeaponCommon.h new file mode 100644 index 00000000..1d1a350a --- /dev/null +++ b/src/ObjCommon/Weapon/WeaponCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace weapon +{ + std::string GetFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/XModel/JsonXModel.h.template b/src/ObjCommon/XModel/JsonXModel.h.template new file mode 100644 index 00000000..08c28ef5 --- /dev/null +++ b/src/ObjCommon/XModel/JsonXModel.h.template @@ -0,0 +1,90 @@ +#options GAME (IW3, IW4, IW5, T5, T6) + +#filename "Game/" + GAME + "/XModel/JsonXModel" + GAME + ".h" + +#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" + +#if GAME == "IW3" +#define FEATURE_IW3 +#elif GAME == "IW4" +#define FEATURE_IW4 +#elif GAME == "IW5" +#define FEATURE_IW5 +#elif GAME == "T5" +#define FEATURE_T5 +#elif GAME == "T6" +#define FEATURE_T6 +#endif + +// This file was templated. +// See JsonXModel.h.template. +// Do not modify, changes will be lost. +#pragma once + +#include "Json/JsonCommon.h" +#include +#include +#include +#include + +namespace GAME +{ + class JsonXModelLod + { + public: + std::string file; + float distance; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file, distance); + + enum class JsonXModelType + { + RIGID, + ANIMATED, + VIEWHANDS + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonXModelType, { + {JsonXModelType::RIGID, "rigid" }, + {JsonXModelType::ANIMATED, "animated" }, + {JsonXModelType::VIEWHANDS, "viewhands" } + }); + + class JsonXModel + { + public: + std::optional type; + std::vector lods; + std::optional collLod; + std::optional physPreset; +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + std::optional physCollmap; +#elif defined(FEATURE_T5) || defined(FEATURE_T6) + std::optional physConstraints; +#endif +#if defined(FEATURE_T6) + JsonVec3 lightingOriginOffset; + float lightingOriginRange; +#endif + unsigned flags; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION( + JsonXModel, + type, + lods, + collLod, + physPreset, +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + physCollmap, +#elif defined(FEATURE_T5) || defined(FEATURE_T6) + physConstraints, +#endif +#if defined(FEATURE_T6) + lightingOriginOffset, + lightingOriginRange, +#endif + flags + ); +} // namespace GAME diff --git a/src/ObjCommon/XModel/XModelCommon.cpp b/src/ObjCommon/XModel/XModelCommon.cpp index a3cfb586..3a7fd151 100644 --- a/src/ObjCommon/XModel/XModelCommon.cpp +++ b/src/ObjCommon/XModel/XModelCommon.cpp @@ -1,6 +1,15 @@ #include "XModelCommon.h" +#pragma warning(push, 0) +// clang-format off: Order of includes is important +#include // Eigen uses std::bit_cast without including header themselves... +#include +// clang-format on +#pragma warning(pop) + +#include #include +#include #include #include @@ -47,6 +56,41 @@ void XModelMaterial::ApplyDefaults() phong = -1; } +void XModelCommon::CalculateBoneLocalsFromGlobals() +{ + const auto boneCount = m_bones.size(); + for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) + { + auto& bone = m_bones[boneIndex]; + + Eigen::Vector3f translation(bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]); + Eigen::Quaternionf rotation(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.y, bone.globalRotation.z); + + if (bone.parentIndex) + { + assert(boneIndex > *bone.parentIndex); + const auto& parentBone = m_bones[*bone.parentIndex]; + + const Eigen::Vector3f parentTranslation(parentBone.globalOffset[0], parentBone.globalOffset[1], parentBone.globalOffset[2]); + const Eigen::Quaternionf parentRotation( + parentBone.globalRotation.w, parentBone.globalRotation.x, parentBone.globalRotation.y, parentBone.globalRotation.z); + const auto inverseParentRotation = parentRotation.inverse(); + + translation -= parentTranslation; + translation = inverseParentRotation * translation; + rotation = inverseParentRotation * rotation; + } + + bone.localOffset[0] = translation.x(); + bone.localOffset[1] = translation.y(); + bone.localOffset[2] = translation.z(); + bone.localRotation.x = rotation.x(); + bone.localRotation.y = rotation.y(); + bone.localRotation.z = rotation.z(); + bone.localRotation.w = rotation.w(); + } +} + bool operator==(const VertexMergerPos& lhs, const VertexMergerPos& rhs) { const auto coordinatesMatch = std::fabs(lhs.x - rhs.x) < std::numeric_limits::epsilon() @@ -99,3 +143,11 @@ bool operator<(const VertexMergerPos& lhs, const VertexMergerPos& rhs) return false; } + +namespace xmodel +{ + std::string GetJsonFileNameForAssetName(const std::string& assetName) + { + return std::format("xmodel/{}.json", assetName); + } +} // namespace xmodel diff --git a/src/ObjCommon/XModel/XModelCommon.h b/src/ObjCommon/XModel/XModelCommon.h index 2caf68b3..46ebf3bb 100644 --- a/src/ObjCommon/XModel/XModelCommon.h +++ b/src/ObjCommon/XModel/XModelCommon.h @@ -111,6 +111,8 @@ struct XModelCommon std::vector m_vertices; std::vector m_vertex_bone_weights; XModelVertexBoneWeightCollection m_bone_weight_data; + + void CalculateBoneLocalsFromGlobals(); }; struct VertexMergerPos @@ -127,3 +129,8 @@ struct VertexMergerPos }; typedef DistinctMapper VertexMerger; + +namespace xmodel +{ + std::string GetJsonFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCommon/XModel/XModelConstants.h.template b/src/ObjCommon/XModel/XModelConstants.h.template new file mode 100644 index 00000000..29807b4f --- /dev/null +++ b/src/ObjCommon/XModel/XModelConstants.h.template @@ -0,0 +1,59 @@ +#options GAME (IW3, IW4, IW5, T5, T6) + +#filename "Game/" + GAME + "/XModel/XModelConstants" + GAME + ".h" + +#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" + +#if GAME == "IW3" +#define FEATURE_IW3 +#elif GAME == "IW4" +#define FEATURE_IW4 +#elif GAME == "IW5" +#define FEATURE_IW5 +#elif GAME == "T5" +#define FEATURE_T5 +#elif GAME == "T6" +#define FEATURE_T6 +#endif + +// This file was templated. +// See JsonXModel.h.template. +// Do not modify, changes will be lost. +#pragma once + +#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" +#include GAME_HEADER + +namespace GAME +{ + inline const char* HITLOC_NAMES[]{ + "none", + "helmet", + "head", + "neck", + "torso_upper", +#if defined(FEATURE_T6) + "torso_middle", +#endif + "torso_lower", + "right_arm_upper", + "left_arm_upper", + "right_arm_lower", + "left_arm_lower", + "right_hand", + "left_hand", + "right_leg_upper", + "left_leg_upper", + "right_leg_lower", + "left_leg_lower", + "right_foot", + "left_foot", +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) || defined(FEATURE_T5) || defined(FEATURE_T6) + "gun", +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) || defined(FEATURE_T6) + "shield", +#endif +#endif + }; + static_assert(std::extent_v == HITLOC_COUNT); +} // namespace GAME diff --git a/src/ObjCommon/ZBarrier/ZBarrierCommon.cpp b/src/ObjCommon/ZBarrier/ZBarrierCommon.cpp new file mode 100644 index 00000000..354ccaba --- /dev/null +++ b/src/ObjCommon/ZBarrier/ZBarrierCommon.cpp @@ -0,0 +1,11 @@ +#include "ZBarrierCommon.h" + +#include + +namespace z_barrier +{ + std::string GetFileNameForAssetName(const std::string& assetName) + { + return std::format("zbarrier/{}", assetName); + } +} // namespace z_barrier diff --git a/src/ObjCommon/ZBarrier/ZBarrierCommon.h b/src/ObjCommon/ZBarrier/ZBarrierCommon.h new file mode 100644 index 00000000..e647286c --- /dev/null +++ b/src/ObjCommon/ZBarrier/ZBarrierCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace z_barrier +{ + std::string GetFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCompiling/Game/IW3/ObjCompilerIW3.cpp b/src/ObjCompiling/Game/IW3/ObjCompilerIW3.cpp index 02e34655..29ea2ee1 100644 --- a/src/ObjCompiling/Game/IW3/ObjCompilerIW3.cpp +++ b/src/ObjCompiling/Game/IW3/ObjCompilerIW3.cpp @@ -25,8 +25,8 @@ namespace { auto& memory = zone.Memory(); - if (ImageIwdPostProcessor::AppliesToZoneDefinition(zoneDefinition)) - collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); + if (image::IwdPostProcessor::AppliesToZoneDefinition(zoneDefinition)) + collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); } } // namespace diff --git a/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp b/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp index 7b54ceaa..76897870 100644 --- a/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp +++ b/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp @@ -14,7 +14,9 @@ #include "StateMap/StateMapHandler.h" #include "Techset/TechniqueFileReader.h" #include "Techset/TechniqueStateMapCache.h" +#include "Techset/TechsetCommon.h" #include "Techset/TechsetDefinitionCache.h" +#include "Utils/Logging/Log.h" #include #include @@ -45,9 +47,9 @@ namespace m_search_path(searchPath), m_context(context), m_registration(registration), - m_state_map_cache(context.GetZoneAssetCreationState()), + m_state_map_cache(context.GetZoneAssetCreationState<::techset::TechniqueStateMapCache>()), m_base_state_bits{}, - m_techset_creator(CreateTechsetLoader(memory, searchPath)) + m_techset_creator(techset::CreateLoaderIW4(memory, searchPath)) { } @@ -247,7 +249,7 @@ namespace else throw GdtReadingException("ColorMap may not be blank in particle cloud materials"); - std::cout << std::format("Using particlecloud for \"{}\"\n", m_material.info.name); + con::info("Using particlecloud for \"{}\"", m_material.info.name); } void mtl_tools_template() @@ -793,7 +795,7 @@ namespace m_registration.AddDependency(techset); m_material.techniqueSet = techset->Asset(); - auto& definitionCache = m_context.GetZoneAssetCreationState(); + auto& definitionCache = m_context.GetZoneAssetCreationState<::techset::TechsetDefinitionCache>(); bool failure = false; const auto* techsetDefinition = m_techset_creator->LoadTechsetDefinition(techsetName, m_context, failure); @@ -806,7 +808,7 @@ namespace SetTechniqueSetCameraRegion(techsetDefinition); } - void SetTechniqueSetStateBits(const techset::TechsetDefinition* techsetDefinition) + void SetTechniqueSetStateBits(const ::techset::TechsetDefinition* techsetDefinition) { for (auto i = 0; i < TECHNIQUE_COUNT; i++) { @@ -854,19 +856,19 @@ namespace return stateBits; } - _NODISCARD const state_map::StateMapDefinition* GetStateMapForTechnique(const std::string& techniqueName) const + [[nodiscard]] const state_map::StateMapDefinition* GetStateMapForTechnique(const std::string& techniqueName) const { const auto* preloadedStateMap = m_state_map_cache.GetStateMapForTechnique(techniqueName); if (preloadedStateMap) return preloadedStateMap; - const auto techniqueFileName = GetTechniqueFileName(techniqueName); + const auto techniqueFileName = ::techset::GetFileNameForTechniqueName(techniqueName); const auto file = m_search_path.Open(techniqueFileName); if (!file.IsOpen()) return nullptr; state_map::StateMapFromTechniqueExtractor extractor; - const techset::TechniqueFileReader reader(*file.m_stream, techniqueFileName, &extractor); + const ::techset::TechniqueFileReader reader(*file.m_stream, techniqueFileName, &extractor); if (!reader.ReadTechniqueDefinition()) { m_state_map_cache.SetTechniqueUsesStateMap(techniqueName, nullptr); @@ -890,7 +892,7 @@ namespace return outBits; } - void SetTechniqueSetCameraRegion(const techset::TechsetDefinition* techsetDefinition) const + void SetTechniqueSetCameraRegion(const ::techset::TechsetDefinition* techsetDefinition) const { std::string tempName; if (techsetDefinition->GetTechniqueByIndex(TECHNIQUE_LIT, tempName)) @@ -1316,7 +1318,7 @@ namespace AssetCreationContext& m_context; AssetRegistration& m_registration; - techset::TechniqueStateMapCache& m_state_map_cache; + ::techset::TechniqueStateMapCache& m_state_map_cache; std::unordered_map m_state_bits_per_state_map; GfxStateBits m_base_state_bits; @@ -1324,7 +1326,7 @@ namespace std::vector m_textures; std::vector m_constants; - std::unique_ptr m_techset_creator; + std::unique_ptr m_techset_creator; }; class MaterialLoader final : public AssetCreator @@ -1361,7 +1363,7 @@ namespace } catch (const GdtReadingException& e) { - std::cerr << std::format("Error while trying to load material from gdt: {} @ GdtEntry \"{}\"\n", e.what(), entry->m_name); + con::error("Error while trying to load material from gdt: {} @ GdtEntry \"{}\"", e.what(), entry->m_name); } return AssetCreationResult::Failure(); @@ -1374,10 +1376,10 @@ namespace }; } // namespace -namespace IW4 +namespace material { - std::unique_ptr> CreateMaterialCompiler(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt) + std::unique_ptr> CreateCompilerIW4(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt) { return std::make_unique(memory, searchPath, gdt); } -} // namespace IW4 +} // namespace material diff --git a/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.h b/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.h index 52c7d3dd..7d3e216a 100644 --- a/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.h +++ b/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.h @@ -6,7 +6,7 @@ #include "SearchPath/ISearchPath.h" #include "Utils/MemoryManager.h" -namespace IW4 +namespace material { - std::unique_ptr> CreateMaterialCompiler(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt); -} // namespace IW4 + std::unique_ptr> CreateCompilerIW4(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt); +} // namespace material diff --git a/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp b/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp index f33dd566..3930a746 100644 --- a/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp +++ b/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp @@ -17,10 +17,10 @@ namespace auto& memory = zone.Memory(); #ifdef EXPERIMENTAL_MATERIAL_COMPILATION - collection.AddAssetCreator(CreateMaterialCompiler(memory, searchPath, gdt)); - collection.AddAssetCreator(CreateTechsetLoader(memory, searchPath)); + collection.AddAssetCreator(material::CreateCompilerIW4(memory, searchPath, gdt)); + collection.AddAssetCreator(techset::CreateLoaderIW4(memory, searchPath)); #endif - collection.AddAssetCreator(CreateVertexDeclLoader(memory)); + collection.AddAssetCreator(vertex_decl::CreateLoaderIW4(memory)); } void ConfigurePostProcessors(AssetCreatorCollection& collection, @@ -32,8 +32,8 @@ namespace { auto& memory = zone.Memory(); - if (ImageIwdPostProcessor::AppliesToZoneDefinition(zoneDefinition)) - collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); + if (image::IwdPostProcessor::AppliesToZoneDefinition(zoneDefinition)) + collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); } } // namespace diff --git a/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.cpp b/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.cpp index bb53a3d5..7627004b 100644 --- a/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.cpp +++ b/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.cpp @@ -5,12 +5,15 @@ #include "Game/IW4/Shader/LoaderVertexShaderIW4.h" #include "Game/IW4/TechsetConstantsIW4.h" #include "Shader/D3D9ShaderAnalyser.h" +#include "Shader/ShaderCommon.h" #include "StateMap/StateMapReader.h" #include "Techset/TechniqueFileReader.h" #include "Techset/TechniqueStateMapCache.h" +#include "Techset/TechsetCommon.h" #include "Techset/TechsetDefinitionCache.h" #include "Techset/TechsetFileReader.h" #include "Utils/Alignment.h" +#include "Utils/Logging/Log.h" #include #include @@ -24,6 +27,7 @@ #include using namespace IW4; +using namespace ::techset; using namespace std::string_literals; namespace @@ -61,7 +65,7 @@ namespace .first->second.get(); } - literal_t GetAllocatedLiteral(MemoryManager& memory, techset::ShaderArgumentLiteralSource source) + literal_t GetAllocatedLiteral(MemoryManager& memory, ShaderArgumentLiteralSource source) { const auto& existingEntry = m_allocated_literals.find(source); @@ -80,7 +84,7 @@ namespace private: std::unordered_map> m_loaded_techniques; - std::map m_allocated_literals; + std::map m_allocated_literals; }; class ShaderInfoFromFileSystemCacheState final : public IZoneAssetCreationState @@ -100,7 +104,7 @@ namespace if (shaderSize % sizeof(uint32_t) != 0) { - std::cerr << std::format("Invalid shader \"{}\": Size must be dividable by {}\n", fileName, sizeof(uint32_t)); + con::error("Invalid shader \"{}\": Size must be dividable by {}", fileName, sizeof(uint32_t)); return nullptr; } @@ -120,7 +124,7 @@ namespace std::unordered_map> m_cached_shader_info; }; - class TechniqueCreator final : public techset::ITechniqueDefinitionAcceptor + class TechniqueCreator final : public ITechniqueDefinitionAcceptor { public: class PassShaderArgument @@ -196,14 +200,17 @@ namespace std::vector m_arguments; }; - TechniqueCreator( - const std::string& techniqueName, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context, ITechsetCreator* techsetCreator) + TechniqueCreator(const std::string& techniqueName, + ISearchPath& searchPath, + MemoryManager& memory, + AssetCreationContext& context, + techset::ICreatorIW4* techsetCreator) : m_technique_name(techniqueName), m_search_path(searchPath), m_memory(memory), m_context(context), m_zone_state(context.GetZoneAssetCreationState()), - m_state_map_cache(context.GetZoneAssetCreationState()), + m_state_map_cache(context.GetZoneAssetCreationState()), m_shader_info_cache(context.GetZoneAssetCreationState()), m_techset_creator(techsetCreator) { @@ -229,7 +236,7 @@ namespace || constant.m_type == d3d9::ParameterType::SAMPLER_CUBE; } - bool AutoCreateShaderArgument(const techset::ShaderSelector shaderType, + bool AutoCreateShaderArgument(const ShaderSelector shaderType, const d3d9::ShaderConstant& shaderArgument, const size_t elementOffset, const size_t registerOffset) @@ -238,7 +245,7 @@ namespace auto& pass = m_passes.at(m_passes.size() - 1); const auto isSamplerArgument = IsSamplerArgument(shaderArgument); - if (shaderType == techset::ShaderSelector::VERTEX_SHADER && isSamplerArgument) + if (shaderType == ShaderSelector::VERTEX_SHADER && isSamplerArgument) return false; MaterialShaderArgument argument; @@ -269,7 +276,7 @@ namespace if (!constantSource) return false; - argument.type = shaderType == techset::ShaderSelector::VERTEX_SHADER ? MTL_ARG_CODE_VERTEX_CONST : MTL_ARG_CODE_PIXEL_CONST; + argument.type = shaderType == ShaderSelector::VERTEX_SHADER ? MTL_ARG_CODE_VERTEX_CONST : MTL_ARG_CODE_PIXEL_CONST; argument.u.codeConst.index = static_cast(constantSource->source + elementOffset); argument.u.codeConst.firstRow = 0u; argument.u.codeConst.rowCount = static_cast(shaderArgument.m_type_rows); @@ -300,7 +307,7 @@ namespace { if (!pass.m_handled_vertex_shader_arguments[argumentHandledIndex + elementIndex]) { - if (!AutoCreateShaderArgument(techset::ShaderSelector::VERTEX_SHADER, argument, elementIndex, registerIndex)) + if (!AutoCreateShaderArgument(ShaderSelector::VERTEX_SHADER, argument, elementIndex, registerIndex)) { std::string elementIndexStr; if (argument.m_type_elements > 1) @@ -335,7 +342,7 @@ namespace { if (!pass.m_handled_pixel_shader_arguments[argumentHandledIndex + elementIndex]) { - if (!AutoCreateShaderArgument(techset::ShaderSelector::PIXEL_SHADER, argument, elementIndex, registerIndex)) + if (!AutoCreateShaderArgument(ShaderSelector::PIXEL_SHADER, argument, elementIndex, registerIndex)) { std::ostringstream ss; ss << "Unassigned pixel shader \"" << pass.m_pixel_shader->m_name << "\" arg: " << argument.m_name; @@ -460,7 +467,8 @@ namespace if (pass.m_vertex_shader->Asset()->name && pass.m_vertex_shader->Asset()->name[0] == ',') { - pass.m_vertex_shader_info = m_shader_info_cache.LoadShaderInfoFromDisk(m_search_path, GetVertexShaderFileName(vertexShaderName)); + pass.m_vertex_shader_info = + m_shader_info_cache.LoadShaderInfoFromDisk(m_search_path, ::shader::GetFileNameForVertexShaderAssetName(vertexShaderName)); } else { @@ -495,7 +503,8 @@ namespace if (pass.m_pixel_shader->Asset()->name && pass.m_pixel_shader->Asset()->name[0] == ',') { - pass.m_pixel_shader_info = m_shader_info_cache.LoadShaderInfoFromDisk(m_search_path, GetPixelShaderFileName(pixelShaderName)); + pass.m_pixel_shader_info = + m_shader_info_cache.LoadShaderInfoFromDisk(m_search_path, ::shader::GetFileNameForPixelShaderAssetName(pixelShaderName)); } else { @@ -569,11 +578,8 @@ namespace return foundSource; } - static bool FindShaderArgument(const d3d9::ShaderInfo& shaderInfo, - const techset::ShaderArgument& argument, - size_t& constantIndex, - size_t& registerOffset, - std::string& errorMessage) + static bool FindShaderArgument( + const d3d9::ShaderInfo& shaderInfo, const ShaderArgument& argument, size_t& constantIndex, size_t& registerOffset, std::string& errorMessage) { const auto matchingShaderConstant = std::ranges::find_if(shaderInfo.m_constants, [argument](const d3d9::ShaderConstant& constant) @@ -619,7 +625,7 @@ namespace } static bool SetArgumentCodeConst(MaterialShaderArgument& argument, - const techset::ShaderArgumentCodeSource& source, + const ShaderArgumentCodeSource& source, const d3d9::ShaderConstant& shaderConstant, const unsigned sourceIndex, const unsigned arrayCount, @@ -657,7 +663,7 @@ namespace } static bool SetArgumentCodeSampler(MaterialShaderArgument& argument, - const techset::ShaderArgumentCodeSource& source, + const ShaderArgumentCodeSource& source, const d3d9::ShaderConstant& shaderConstant, const unsigned sourceIndex, const unsigned arrayCount, @@ -692,9 +698,7 @@ namespace return true; } - bool AcceptVertexShaderConstantArgument(const techset::ShaderArgument& shaderArgument, - const techset::ShaderArgumentCodeSource& source, - std::string& errorMessage) + bool AcceptVertexShaderConstantArgument(const ShaderArgument& shaderArgument, const ShaderArgumentCodeSource& source, std::string& errorMessage) { assert(!m_passes.empty()); auto& pass = m_passes.at(m_passes.size() - 1); @@ -742,8 +746,8 @@ namespace return true; } - bool AcceptPixelShaderCodeArgument(const techset::ShaderArgument& shaderArgument, - const techset::ShaderArgumentCodeSource& source, + bool AcceptPixelShaderCodeArgument(const ShaderArgument& shaderArgument, + const ShaderArgumentCodeSource& source, std::string& errorMessage, const bool isSampler) { @@ -823,36 +827,36 @@ namespace return true; } - bool AcceptShaderConstantArgument(const techset::ShaderSelector shader, - const techset::ShaderArgument shaderArgument, - const techset::ShaderArgumentCodeSource source, + bool AcceptShaderConstantArgument(const ShaderSelector shader, + const ShaderArgument shaderArgument, + const ShaderArgumentCodeSource source, std::string& errorMessage) override { - if (shader == techset::ShaderSelector::VERTEX_SHADER) + if (shader == ShaderSelector::VERTEX_SHADER) return AcceptVertexShaderConstantArgument(shaderArgument, source, errorMessage); - assert(shader == techset::ShaderSelector::PIXEL_SHADER); + assert(shader == ShaderSelector::PIXEL_SHADER); return AcceptPixelShaderCodeArgument(shaderArgument, source, errorMessage, false); } - bool AcceptShaderSamplerArgument(const techset::ShaderSelector shader, - const techset::ShaderArgument shaderArgument, - const techset::ShaderArgumentCodeSource source, + bool AcceptShaderSamplerArgument(const ShaderSelector shader, + const ShaderArgument shaderArgument, + const ShaderArgumentCodeSource source, std::string& errorMessage) override { - if (shader == techset::ShaderSelector::VERTEX_SHADER) + if (shader == ShaderSelector::VERTEX_SHADER) { errorMessage = "Vertex sampler are unsupported"; return false; } - assert(shader == techset::ShaderSelector::PIXEL_SHADER); + assert(shader == ShaderSelector::PIXEL_SHADER); return AcceptPixelShaderCodeArgument(shaderArgument, source, errorMessage, true); } - bool AcceptShaderLiteralArgument(const techset::ShaderSelector shader, - const techset::ShaderArgument shaderArgument, - const techset::ShaderArgumentLiteralSource source, + bool AcceptShaderLiteralArgument(const ShaderSelector shader, + const ShaderArgument shaderArgument, + const ShaderArgumentLiteralSource source, std::string& errorMessage) override { assert(!m_passes.empty()); @@ -861,14 +865,14 @@ namespace MaterialShaderArgument argument; const d3d9::ShaderInfo* shaderInfo; - if (shader == techset::ShaderSelector::VERTEX_SHADER) + if (shader == ShaderSelector::VERTEX_SHADER) { argument.type = MTL_ARG_LITERAL_VERTEX_CONST; shaderInfo = pass.m_vertex_shader_info; } else { - assert(shader == techset::ShaderSelector::PIXEL_SHADER); + assert(shader == ShaderSelector::PIXEL_SHADER); argument.type = MTL_ARG_LITERAL_PIXEL_CONST; shaderInfo = pass.m_pixel_shader_info; } @@ -889,7 +893,7 @@ namespace const auto argumentIsSampler = IsSamplerArgument(shaderConstant); if (argumentIsSampler) { - if (shader == techset::ShaderSelector::VERTEX_SHADER) + if (shader == ShaderSelector::VERTEX_SHADER) errorMessage = "Vertex shader argument expects sampler but got constant"; else errorMessage = "Pixel shader argument expects sampler but got constant"; @@ -901,7 +905,7 @@ namespace argument.u.literalConst = m_zone_state.GetAllocatedLiteral(m_memory, source); pass.m_arguments.emplace_back(argument); - if (shader == techset::ShaderSelector::VERTEX_SHADER) + if (shader == ShaderSelector::VERTEX_SHADER) pass.m_handled_vertex_shader_arguments[pass.m_vertex_shader_argument_handled_offset[shaderConstantIndex] + elementOffset] = true; else pass.m_handled_pixel_shader_arguments[pass.m_pixel_shader_argument_handled_offset[shaderConstantIndex] + elementOffset] = true; @@ -909,9 +913,9 @@ namespace return true; } - bool AcceptShaderMaterialArgument(const techset::ShaderSelector shader, - const techset::ShaderArgument shaderArgument, - const techset::ShaderArgumentMaterialSource source, + bool AcceptShaderMaterialArgument(const ShaderSelector shader, + const ShaderArgument shaderArgument, + const ShaderArgumentMaterialSource source, std::string& errorMessage) override { assert(!m_passes.empty()); @@ -920,13 +924,13 @@ namespace MaterialShaderArgument argument; const d3d9::ShaderInfo* shaderInfo; - if (shader == techset::ShaderSelector::VERTEX_SHADER) + if (shader == ShaderSelector::VERTEX_SHADER) { shaderInfo = pass.m_vertex_shader_info; } else { - assert(shader == techset::ShaderSelector::PIXEL_SHADER); + assert(shader == ShaderSelector::PIXEL_SHADER); shaderInfo = pass.m_pixel_shader_info; } @@ -944,7 +948,7 @@ namespace const auto elementOffset = shaderArgument.m_argument_index_specified ? shaderArgument.m_argument_index : 0u; const auto& shaderConstant = shaderInfo->m_constants[shaderConstantIndex]; const auto argumentIsSampler = IsSamplerArgument(shaderConstant); - if (shader == techset::ShaderSelector::VERTEX_SHADER) + if (shader == ShaderSelector::VERTEX_SHADER) { if (argumentIsSampler) { @@ -955,7 +959,7 @@ namespace } else { - assert(shader == techset::ShaderSelector::PIXEL_SHADER); + assert(shader == ShaderSelector::PIXEL_SHADER); argument.type = !argumentIsSampler ? MTL_ARG_MATERIAL_PIXEL_CONST : MTL_ARG_MATERIAL_PIXEL_SAMPLER; } @@ -967,7 +971,7 @@ namespace argument.dest = static_cast(shaderConstant.m_register_index + registerOffset); pass.m_arguments.emplace_back(argument); - if (shader == techset::ShaderSelector::VERTEX_SHADER) + if (shader == ShaderSelector::VERTEX_SHADER) pass.m_handled_vertex_shader_arguments[pass.m_vertex_shader_argument_handled_offset[shaderConstantIndex] + elementOffset] = true; else pass.m_handled_pixel_shader_arguments[pass.m_pixel_shader_argument_handled_offset[shaderConstantIndex] + elementOffset] = true; @@ -1021,15 +1025,15 @@ namespace MemoryManager& m_memory; AssetCreationContext& m_context; TechniqueZoneLoadingState& m_zone_state; - techset::TechniqueStateMapCache& m_state_map_cache; + TechniqueStateMapCache& m_state_map_cache; ShaderInfoFromFileSystemCacheState& m_shader_info_cache; - ITechsetCreator* m_techset_creator; + techset::ICreatorIW4* m_techset_creator; }; class TechniqueLoader { public: - TechniqueLoader(ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context, ITechsetCreator* techsetCreator) + TechniqueLoader(ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context, techset::ICreatorIW4* techsetCreator) : m_search_path(searchPath), m_memory(memory), m_context(context), @@ -1240,13 +1244,13 @@ namespace MaterialTechnique* LoadTechniqueFromRaw(const std::string& techniqueName, std::vector& dependencies) const { - const auto techniqueFileName = GetTechniqueFileName(techniqueName); + const auto techniqueFileName = GetFileNameForTechniqueName(techniqueName); const auto file = m_search_path.Open(techniqueFileName); if (!file.IsOpen()) return nullptr; TechniqueCreator creator(techniqueName, m_search_path, m_memory, m_context, m_techset_creator); - const techset::TechniqueFileReader reader(*file.m_stream, techniqueFileName, &creator); + const TechniqueFileReader reader(*file.m_stream, techniqueFileName, &creator); if (!reader.ReadTechniqueDefinition()) return nullptr; @@ -1257,10 +1261,10 @@ namespace MemoryManager& m_memory; AssetCreationContext& m_context; TechniqueZoneLoadingState& m_zone_state; - ITechsetCreator* m_techset_creator; + techset::ICreatorIW4* m_techset_creator; }; - class TechsetLoader final : public ITechsetCreator + class TechsetLoader final : public techset::ICreatorIW4 { public: TechsetLoader(MemoryManager& memory, ISearchPath& searchPath) @@ -1280,8 +1284,7 @@ namespace } private: - AssetCreationResult - CreateTechsetFromDefinition(const std::string& assetName, const techset::TechsetDefinition& definition, AssetCreationContext& context) + AssetCreationResult CreateTechsetFromDefinition(const std::string& assetName, const TechsetDefinition& definition, AssetCreationContext& context) { auto* techset = m_memory.Alloc(); techset->name = m_memory.Dup(assetName.c_str()); @@ -1309,20 +1312,20 @@ namespace return AssetCreationResult::Success(context.AddAsset(std::move(registration))); } - techset::TechsetDefinition* LoadTechsetDefinition(const std::string& assetName, AssetCreationContext& context, bool& failure) override + TechsetDefinition* LoadTechsetDefinition(const std::string& assetName, AssetCreationContext& context, bool& failure) override { failure = false; - auto& definitionCache = context.GetZoneAssetCreationState(); + auto& definitionCache = context.GetZoneAssetCreationState(); auto* cachedTechsetDefinition = definitionCache.GetCachedTechsetDefinition(assetName); if (cachedTechsetDefinition) return cachedTechsetDefinition; - const auto techsetFileName = GetTechsetFileName(assetName); + const auto techsetFileName = GetFileNameForTechsetName(assetName); const auto file = m_search_path.Open(techsetFileName); if (!file.IsOpen()) return nullptr; - const techset::TechsetFileReader reader(*file.m_stream, techsetFileName, techniqueTypeNames, std::extent_v); + const TechsetFileReader reader(*file.m_stream, techsetFileName, techniqueTypeNames, std::extent_v); auto techsetDefinition = reader.ReadTechsetDefinition(); if (!techsetDefinition) { @@ -1339,12 +1342,12 @@ namespace const state_map::StateMapDefinition* LoadStateMapDefinition(const std::string& stateMapName, AssetCreationContext& context) override { - auto& stateMapCache = context.GetZoneAssetCreationState(); + auto& stateMapCache = context.GetZoneAssetCreationState(); auto* cachedStateMap = stateMapCache.GetCachedStateMap(stateMapName); if (cachedStateMap) return cachedStateMap; - const auto stateMapFileName = GetStateMapFileName(stateMapName); + const auto stateMapFileName = GetFileNameForStateMapName(stateMapName); const auto file = m_search_path.Open(stateMapFileName); if (!file.IsOpen()) return nullptr; @@ -1367,25 +1370,10 @@ namespace }; } // namespace -namespace IW4 +namespace techset { - std::string GetTechsetFileName(const std::string& techsetAssetName) - { - return std::format("techsets/{}.techset", techsetAssetName); - } - - std::string GetTechniqueFileName(const std::string& techniqueName) - { - return std::format("techniques/{}.tech", techniqueName); - } - - std::string GetStateMapFileName(const std::string& stateMapName) - { - return std::format("statemaps/{}.sm", stateMapName); - } - - std::unique_ptr CreateTechsetLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace techset diff --git a/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.h b/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.h index 2f246031..09fd1375 100644 --- a/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.h +++ b/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.h @@ -10,21 +10,17 @@ #include #include -namespace IW4 +namespace techset { - [[nodiscard]] std::string GetTechsetFileName(const std::string& techsetAssetName); - [[nodiscard]] std::string GetTechniqueFileName(const std::string& techniqueName); - [[nodiscard]] std::string GetStateMapFileName(const std::string& stateMapName); - - class ITechsetCreator : public AssetCreator + class ICreatorIW4 : public AssetCreator { public: - ITechsetCreator() = default; - virtual ~ITechsetCreator() = default; + ICreatorIW4() = default; + virtual ~ICreatorIW4() = default; - virtual techset::TechsetDefinition* LoadTechsetDefinition(const std::string& assetName, AssetCreationContext& context, bool& failure) = 0; + virtual TechsetDefinition* LoadTechsetDefinition(const std::string& assetName, AssetCreationContext& context, bool& failure) = 0; virtual const state_map::StateMapDefinition* LoadStateMapDefinition(const std::string& stateMapName, AssetCreationContext& context) = 0; }; - std::unique_ptr CreateTechsetLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace techset diff --git a/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.cpp b/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.cpp index 391975ee..5c4144b8 100644 --- a/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.cpp +++ b/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.cpp @@ -2,6 +2,7 @@ #include "Game/IW4/IW4.h" #include "Game/IW4/TechsetConstantsIW4.h" +#include "Utils/Logging/Log.h" #include #include @@ -31,28 +32,28 @@ namespace { if (decl->streamCount >= std::extent_v) { - std::cerr << std::format("Failed to add vertex decl stream. Too many abbreviations: {}\n", assetName); + con::error("Failed to add vertex decl stream. Too many abbreviations: {}", assetName); return AssetCreationResult::Failure(); } std::string destinationAbbreviation; if (!NextAbbreviation(assetName, destinationAbbreviation, currentOffset)) { - std::cerr << std::format("Failed to detect vertex decl destination abbreviation: {}\n", assetName); + con::error("Failed to detect vertex decl destination abbreviation: {}", assetName); return AssetCreationResult::Failure(); } const auto foundSourceAbbreviation = std::ranges::find(materialStreamSourceAbbreviation, sourceAbbreviation); if (foundSourceAbbreviation == std::end(materialStreamSourceAbbreviation)) { - std::cerr << std::format("Unknown vertex decl source abbreviation: {}\n", sourceAbbreviation); + con::error("Unknown vertex decl source abbreviation: {}", sourceAbbreviation); return AssetCreationResult::Failure(); } const auto foundDestinationAbbreviation = std::ranges::find(materialStreamDestinationAbbreviation, destinationAbbreviation); if (foundDestinationAbbreviation == std::end(materialStreamDestinationAbbreviation)) { - std::cerr << std::format("Unknown vertex decl destination abbreviation: {}\n", destinationAbbreviation); + con::error("Unknown vertex decl destination abbreviation: {}", destinationAbbreviation); return AssetCreationResult::Failure(); } @@ -92,10 +93,10 @@ namespace }; } // namespace -namespace IW4 +namespace vertex_decl { - std::unique_ptr> CreateVertexDeclLoader(MemoryManager& memory) + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory) { return std::make_unique(memory); } -} // namespace IW4 +} // namespace vertex_decl diff --git a/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.h b/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.h index d4bd1b68..4713a652 100644 --- a/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.h +++ b/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.h @@ -7,7 +7,7 @@ #include -namespace IW4 +namespace vertex_decl { - std::unique_ptr> CreateVertexDeclLoader(MemoryManager& memory); -} // namespace IW4 + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory); +} // namespace vertex_decl diff --git a/src/ObjCompiling/Game/IW5/ObjCompilerIW5.cpp b/src/ObjCompiling/Game/IW5/ObjCompilerIW5.cpp index fe02ead8..5f9e9bf9 100644 --- a/src/ObjCompiling/Game/IW5/ObjCompilerIW5.cpp +++ b/src/ObjCompiling/Game/IW5/ObjCompilerIW5.cpp @@ -25,8 +25,8 @@ namespace { auto& memory = zone.Memory(); - if (ImageIwdPostProcessor::AppliesToZoneDefinition(zoneDefinition)) - collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); + if (image::IwdPostProcessor::AppliesToZoneDefinition(zoneDefinition)) + collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); } } // namespace diff --git a/src/ObjCompiling/Game/T5/ObjCompilerT5.cpp b/src/ObjCompiling/Game/T5/ObjCompilerT5.cpp index 3ca4d416..0442c1e8 100644 --- a/src/ObjCompiling/Game/T5/ObjCompilerT5.cpp +++ b/src/ObjCompiling/Game/T5/ObjCompilerT5.cpp @@ -25,8 +25,8 @@ namespace { auto& memory = zone.Memory(); - if (ImageIwdPostProcessor::AppliesToZoneDefinition(zoneDefinition)) - collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); + if (image::IwdPostProcessor::AppliesToZoneDefinition(zoneDefinition)) + collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); } } // namespace diff --git a/src/ObjCompiling/Game/T6/Image/ImageCompilerT6.h b/src/ObjCompiling/Game/T6/Image/ImageCompilerT6.h deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjCompiling/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6.cpp b/src/ObjCompiling/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6.cpp index 22da77ce..35724a68 100644 --- a/src/ObjCompiling/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6.cpp +++ b/src/ObjCompiling/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6.cpp @@ -18,7 +18,7 @@ namespace : m_memory(memory), m_zone(zone), m_zone_definition(zoneDefinition), - m_kvp_creator(zoneStates.GetZoneAssetCreationState()) + m_kvp_creator(zoneStates.GetZoneAssetCreationState()) { } @@ -67,15 +67,15 @@ namespace MemoryManager& m_memory; const Zone& m_zone; const ZoneDefinition& m_zone_definition; - KeyValuePairsCreator& m_kvp_creator; + key_value_pairs::Creator& m_kvp_creator; }; } // namespace -namespace T6 +namespace key_value_pairs { std::unique_ptr - CreateKeyValuePairsCompiler(MemoryManager& memory, const Zone& zone, const ZoneDefinition& zoneDefinition, ZoneAssetCreationStateContainer& zoneStates) + CreateCompilerT6(MemoryManager& memory, const Zone& zone, const ZoneDefinition& zoneDefinition, ZoneAssetCreationStateContainer& zoneStates) { return std::make_unique(memory, zone, zoneDefinition, zoneStates); } -} // namespace T6 +} // namespace key_value_pairs diff --git a/src/ObjCompiling/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6.h b/src/ObjCompiling/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6.h index c68c06b5..f5ce8b37 100644 --- a/src/ObjCompiling/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6.h +++ b/src/ObjCompiling/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6.h @@ -9,8 +9,8 @@ #include -namespace T6 +namespace key_value_pairs { std::unique_ptr - CreateKeyValuePairsCompiler(MemoryManager& memory, const Zone& zone, const ZoneDefinition& zoneDefinition, ZoneAssetCreationStateContainer& zoneStates); -} // namespace T6 + CreateCompilerT6(MemoryManager& memory, const Zone& zone, const ZoneDefinition& zoneDefinition, ZoneAssetCreationStateContainer& zoneStates); +} // namespace key_value_pairs diff --git a/src/ObjCompiling/Game/T6/ObjCompilerT6.cpp b/src/ObjCompiling/Game/T6/ObjCompilerT6.cpp index eae90387..8228234d 100644 --- a/src/ObjCompiling/Game/T6/ObjCompilerT6.cpp +++ b/src/ObjCompiling/Game/T6/ObjCompilerT6.cpp @@ -19,7 +19,7 @@ namespace { auto& memory = zone.Memory(); - collection.AddAssetCreator(CreateKeyValuePairsCompiler(memory, zone, zoneDefinition.m_zone_definition, zoneStates)); + collection.AddAssetCreator(key_value_pairs::CreateCompilerT6(memory, zone, zoneDefinition.m_zone_definition, zoneStates)); } void ConfigurePostProcessors(AssetCreatorCollection& collection, @@ -31,11 +31,11 @@ namespace { auto& memory = zone.Memory(); - if (ImageIPakPostProcessor::AppliesToZoneDefinition(zoneDefinition)) - collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); + if (image::IPakPostProcessor::AppliesToZoneDefinition(zoneDefinition)) + collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); - if (ImageIwdPostProcessor::AppliesToZoneDefinition(zoneDefinition)) - collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); + if (image::IwdPostProcessor::AppliesToZoneDefinition(zoneDefinition)) + collection.AddAssetPostProcessor(std::make_unique>(zoneDefinition, searchPath, zoneStates, outDir)); } } // namespace diff --git a/src/ObjCompiling/Image/IPak/IPakCreator.cpp b/src/ObjCompiling/Image/IPak/IPakCreator.cpp index 64134b2b..370074d2 100644 --- a/src/ObjCompiling/Image/IPak/IPakCreator.cpp +++ b/src/ObjCompiling/Image/IPak/IPakCreator.cpp @@ -4,6 +4,7 @@ #include "GitVersion.h" #include "ObjContainer/IPak/IPakTypes.h" #include "Utils/Alignment.h" +#include "Utils/Logging/Log.h" #include #include @@ -143,7 +144,7 @@ namespace const auto openFile = m_search_path.Open(fileName); if (!openFile.IsOpen()) { - std::cerr << std::format("Failed to open file for ipak: {}\n", fileName); + con::error("Failed to open file for ipak: {}", fileName); return nullptr; } @@ -371,68 +372,71 @@ namespace }; } // namespace -IPakToCreate::IPakToCreate(std::string name) - : m_name(std::move(name)) +namespace image { -} - -void IPakToCreate::AddImage(std::string imageName) -{ - m_image_names.emplace_back(std::move(imageName)); -} - -void IPakToCreate::Build(ISearchPath& searchPath, IOutputPath& outPath) -{ - const auto file = outPath.Open(std::format("{}.ipak", m_name)); - if (!file) + IPakToCreate::IPakToCreate(std::string name) + : m_name(std::move(name)) { - std::cerr << std::format("Failed to open file for ipak {}\n", m_name); - return; } - IPakWriter writer(*file, searchPath, m_image_names); - writer.Write(); + void IPakToCreate::AddImage(std::string imageName) + { + m_image_names.emplace_back(std::move(imageName)); + } - std::cout << std::format("Created ipak {} with {} entries\n", m_name, m_image_names.size()); -} + void IPakToCreate::Build(ISearchPath& searchPath, IOutputPath& outPath) + { + const auto file = outPath.Open(std::format("{}.ipak", m_name)); + if (!file) + { + con::error("Failed to open file for ipak {}", m_name); + return; + } -const std::vector& IPakToCreate::GetImageNames() const -{ - return m_image_names; -} + IPakWriter writer(*file, searchPath, m_image_names); + writer.Write(); -IPakCreator::IPakCreator() - : m_kvp_creator(nullptr) -{ -} + con::info("Created ipak {} with {} entries", m_name, m_image_names.size()); + } -void IPakCreator::Inject(ZoneAssetCreationInjection& inject) -{ - m_kvp_creator = &inject.m_zone_states.GetZoneAssetCreationState(); -} + const std::vector& IPakToCreate::GetImageNames() const + { + return m_image_names; + } -IPakToCreate* IPakCreator::GetOrAddIPak(const std::string& ipakName) -{ - const auto existingIPak = m_ipak_lookup.find(ipakName); - if (existingIPak != m_ipak_lookup.end()) - return existingIPak->second; + IPakCreator::IPakCreator() + : m_kvp_creator(nullptr) + { + } - auto newIPak = std::make_unique(ipakName); - auto* result = newIPak.get(); - m_ipak_lookup.emplace(ipakName, result); - m_ipaks.emplace_back(std::move(newIPak)); + void IPakCreator::Inject(ZoneAssetCreationInjection& inject) + { + m_kvp_creator = &inject.m_zone_states.GetZoneAssetCreationState(); + } - assert(m_kvp_creator); - m_kvp_creator->AddKeyValuePair(CommonKeyValuePair("ipak_read", ipakName)); + IPakToCreate* IPakCreator::GetOrAddIPak(const std::string& ipakName) + { + const auto existingIPak = m_ipak_lookup.find(ipakName); + if (existingIPak != m_ipak_lookup.end()) + return existingIPak->second; - return result; -} + auto newIPak = std::make_unique(ipakName); + auto* result = newIPak.get(); + m_ipak_lookup.emplace(ipakName, result); + m_ipaks.emplace_back(std::move(newIPak)); -void IPakCreator::Finalize(ISearchPath& searchPath, IOutputPath& outPath) -{ - for (const auto& ipakToCreate : m_ipaks) - ipakToCreate->Build(searchPath, outPath); + assert(m_kvp_creator); + m_kvp_creator->AddKeyValuePair(key_value_pairs::CommonKeyValuePair("ipak_read", ipakName)); - m_ipaks.clear(); - m_ipak_lookup.clear(); -} + return result; + } + + void IPakCreator::Finalize(ISearchPath& searchPath, IOutputPath& outPath) + { + for (const auto& ipakToCreate : m_ipaks) + ipakToCreate->Build(searchPath, outPath); + + m_ipaks.clear(); + m_ipak_lookup.clear(); + } +} // namespace image diff --git a/src/ObjCompiling/Image/IPak/IPakCreator.h b/src/ObjCompiling/Image/IPak/IPakCreator.h index 31eed0b2..b7891fbf 100644 --- a/src/ObjCompiling/Image/IPak/IPakCreator.h +++ b/src/ObjCompiling/Image/IPak/IPakCreator.h @@ -9,32 +9,35 @@ #include #include -class IPakToCreate +namespace image { -public: - explicit IPakToCreate(std::string name); + class IPakToCreate + { + public: + explicit IPakToCreate(std::string name); - void AddImage(std::string imageName); - void Build(ISearchPath& searchPath, IOutputPath& outPath); - [[nodiscard]] const std::vector& GetImageNames() const; + void AddImage(std::string imageName); + void Build(ISearchPath& searchPath, IOutputPath& outPath); + [[nodiscard]] const std::vector& GetImageNames() const; -private: - std::string m_name; - std::vector m_image_names; -}; + private: + std::string m_name; + std::vector m_image_names; + }; -class IPakCreator final : public IZoneAssetCreationState -{ -public: - IPakCreator(); + class IPakCreator final : public IZoneAssetCreationState + { + public: + IPakCreator(); - void Inject(ZoneAssetCreationInjection& inject) override; + void Inject(ZoneAssetCreationInjection& inject) override; - IPakToCreate* GetOrAddIPak(const std::string& ipakName); - void Finalize(ISearchPath& searchPath, IOutputPath& outPath); + IPakToCreate* GetOrAddIPak(const std::string& ipakName); + void Finalize(ISearchPath& searchPath, IOutputPath& outPath); -private: - KeyValuePairsCreator* m_kvp_creator; - std::unordered_map m_ipak_lookup; - std::vector> m_ipaks; -}; + private: + key_value_pairs::Creator* m_kvp_creator; + std::unordered_map m_ipak_lookup; + std::vector> m_ipaks; + }; +} // namespace image diff --git a/src/ObjCompiling/Image/ImageIPakPostProcessor.cpp b/src/ObjCompiling/Image/ImageIPakPostProcessor.cpp index 24c0ace2..2c05d22b 100644 --- a/src/ObjCompiling/Image/ImageIPakPostProcessor.cpp +++ b/src/ObjCompiling/Image/ImageIPakPostProcessor.cpp @@ -4,63 +4,66 @@ #include -AbstractImageIPakPostProcessor::AbstractImageIPakPostProcessor(const ZoneDefinitionContext& zoneDefinition, - ISearchPath& searchPath, - ZoneAssetCreationStateContainer& zoneStates, - IOutputPath& outDir) - : m_zone_definition(zoneDefinition), - m_search_path(searchPath), - m_ipak_creator(zoneStates.GetZoneAssetCreationState()), - m_out_dir(outDir), - m_obj_container_index(0u), - m_current_ipak(nullptr), - m_current_ipak_start_index(0u), - m_current_ipak_end_index(0u) +namespace image { - FindNextObjContainer(); -} - -bool AbstractImageIPakPostProcessor::AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition) -{ - return std::ranges::any_of(zoneDefinition.m_zone_definition.m_obj_containers, - [](const ZoneDefinitionObjContainer& objContainer) - { - return objContainer.m_type == ZoneDefinitionObjContainerType::IPAK; - }); -} - -void AbstractImageIPakPostProcessor::FindNextObjContainer() -{ - const auto objContainerCount = m_zone_definition.m_zone_definition.m_obj_containers.size(); - while (m_obj_container_index < objContainerCount) + AbstractIPakPostProcessor::AbstractIPakPostProcessor(const ZoneDefinitionContext& zoneDefinition, + ISearchPath& searchPath, + ZoneAssetCreationStateContainer& zoneStates, + IOutputPath& outDir) + : m_zone_definition(zoneDefinition), + m_search_path(searchPath), + m_ipak_creator(zoneStates.GetZoneAssetCreationState()), + m_out_dir(outDir), + m_obj_container_index(0u), + m_current_ipak(nullptr), + m_current_ipak_start_index(0u), + m_current_ipak_end_index(0u) { - const auto& objContainer = m_zone_definition.m_zone_definition.m_obj_containers[m_obj_container_index++]; - - if (objContainer.m_type != ZoneDefinitionObjContainerType::IPAK) - continue; - - m_current_ipak = m_ipak_creator.GetOrAddIPak(objContainer.m_name); - m_current_ipak_start_index = objContainer.m_asset_start; - m_current_ipak_end_index = objContainer.m_asset_end; - return; + FindNextObjContainer(); } - m_current_ipak = nullptr; -} + bool AbstractIPakPostProcessor::AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition) + { + return std::ranges::any_of(zoneDefinition.m_zone_definition.m_obj_containers, + [](const ZoneDefinitionObjContainer& objContainer) + { + return objContainer.m_type == ZoneDefinitionObjContainerType::IPAK; + }); + } -void AbstractImageIPakPostProcessor::PostProcessAsset(XAssetInfoGeneric& assetInfo, AssetCreationContext& context) -{ - if (assetInfo.m_name.empty() || assetInfo.m_name[0] == ',') - return; + void AbstractIPakPostProcessor::FindNextObjContainer() + { + const auto objContainerCount = m_zone_definition.m_zone_definition.m_obj_containers.size(); + while (m_obj_container_index < objContainerCount) + { + const auto& objContainer = m_zone_definition.m_zone_definition.m_obj_containers[m_obj_container_index++]; - while (m_current_ipak && m_zone_definition.m_asset_index_in_definition >= m_current_ipak_end_index) - FindNextObjContainer(); + if (objContainer.m_type != ZoneDefinitionObjContainerType::IPAK) + continue; - if (m_current_ipak && m_zone_definition.m_asset_index_in_definition >= m_current_ipak_start_index) - m_current_ipak->AddImage(assetInfo.m_name); -} + m_current_ipak = m_ipak_creator.GetOrAddIPak(objContainer.m_name); + m_current_ipak_start_index = objContainer.m_asset_start; + m_current_ipak_end_index = objContainer.m_asset_end; + return; + } -void AbstractImageIPakPostProcessor::FinalizeZone(AssetCreationContext& context) -{ - m_ipak_creator.Finalize(m_search_path, m_out_dir); -} + m_current_ipak = nullptr; + } + + void AbstractIPakPostProcessor::PostProcessAsset(XAssetInfoGeneric& assetInfo, AssetCreationContext& context) + { + if (assetInfo.m_name.empty() || assetInfo.m_name[0] == ',') + return; + + while (m_current_ipak && m_zone_definition.m_asset_index_in_definition >= m_current_ipak_end_index) + FindNextObjContainer(); + + if (m_current_ipak && m_zone_definition.m_asset_index_in_definition >= m_current_ipak_start_index) + m_current_ipak->AddImage(assetInfo.m_name); + } + + void AbstractIPakPostProcessor::FinalizeZone(AssetCreationContext& context) + { + m_ipak_creator.Finalize(m_search_path, m_out_dir); + } +} // namespace image diff --git a/src/ObjCompiling/Image/ImageIPakPostProcessor.h b/src/ObjCompiling/Image/ImageIPakPostProcessor.h index 675b79cf..b6a6ec2d 100644 --- a/src/ObjCompiling/Image/ImageIPakPostProcessor.h +++ b/src/ObjCompiling/Image/ImageIPakPostProcessor.h @@ -5,48 +5,51 @@ #include "Image/IPak/IPakCreator.h" #include "SearchPath/IOutputPath.h" -class AbstractImageIPakPostProcessor : public IAssetPostProcessor +namespace image { -public: - AbstractImageIPakPostProcessor(const ZoneDefinitionContext& zoneDefinition, - ISearchPath& searchPath, - ZoneAssetCreationStateContainer& zoneStates, - IOutputPath& outDir); - - static bool AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition); - - void PostProcessAsset(XAssetInfoGeneric& assetInfo, AssetCreationContext& context) override; - void FinalizeZone(AssetCreationContext& context) override; - -private: - void FindNextObjContainer(); - - const ZoneDefinitionContext& m_zone_definition; - ISearchPath& m_search_path; - IPakCreator& m_ipak_creator; - IOutputPath& m_out_dir; - - unsigned m_obj_container_index; - IPakToCreate* m_current_ipak; - unsigned m_current_ipak_start_index; - unsigned m_current_ipak_end_index; -}; - -template class ImageIPakPostProcessor final : public AbstractImageIPakPostProcessor -{ -public: - static_assert(std::is_base_of_v); - - ImageIPakPostProcessor(const ZoneDefinitionContext& zoneDefinition, - ISearchPath& searchPath, - ZoneAssetCreationStateContainer& zoneStates, - IOutputPath& outDir) - : AbstractImageIPakPostProcessor(zoneDefinition, searchPath, zoneStates, outDir) + class AbstractIPakPostProcessor : public IAssetPostProcessor { - } + public: + AbstractIPakPostProcessor(const ZoneDefinitionContext& zoneDefinition, + ISearchPath& searchPath, + ZoneAssetCreationStateContainer& zoneStates, + IOutputPath& outDir); - [[nodiscard]] asset_type_t GetHandlingAssetType() const override + static bool AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition); + + void PostProcessAsset(XAssetInfoGeneric& assetInfo, AssetCreationContext& context) override; + void FinalizeZone(AssetCreationContext& context) override; + + private: + void FindNextObjContainer(); + + const ZoneDefinitionContext& m_zone_definition; + ISearchPath& m_search_path; + IPakCreator& m_ipak_creator; + IOutputPath& m_out_dir; + + unsigned m_obj_container_index; + IPakToCreate* m_current_ipak; + unsigned m_current_ipak_start_index; + unsigned m_current_ipak_end_index; + }; + + template class IPakPostProcessor final : public AbstractIPakPostProcessor { - return AssetType::EnumEntry; - } -}; + public: + static_assert(std::is_base_of_v); + + IPakPostProcessor(const ZoneDefinitionContext& zoneDefinition, + ISearchPath& searchPath, + ZoneAssetCreationStateContainer& zoneStates, + IOutputPath& outDir) + : AbstractIPakPostProcessor(zoneDefinition, searchPath, zoneStates, outDir) + { + } + + [[nodiscard]] asset_type_t GetHandlingAssetType() const override + { + return AssetType::EnumEntry; + } + }; +} // namespace image diff --git a/src/ObjCompiling/Image/ImageIwdPostProcessor.cpp b/src/ObjCompiling/Image/ImageIwdPostProcessor.cpp index b329546c..3163ea81 100644 --- a/src/ObjCompiling/Image/ImageIwdPostProcessor.cpp +++ b/src/ObjCompiling/Image/ImageIwdPostProcessor.cpp @@ -1,67 +1,71 @@ #include "ImageIwdPostProcessor.h" +#include "Image/ImageCommon.h" #include "Iwd/IwdCreator.h" #include #include -AbstractImageIwdPostProcessor::AbstractImageIwdPostProcessor(const ZoneDefinitionContext& zoneDefinition, - ISearchPath& searchPath, - ZoneAssetCreationStateContainer& zoneStates, - IOutputPath& outDir) - : m_zone_definition(zoneDefinition), - m_search_path(searchPath), - m_iwd_creator(zoneStates.GetZoneAssetCreationState()), - m_out_dir(outDir), - m_obj_container_index(0u), - m_current_iwd(nullptr), - m_current_iwd_start_index(0u), - m_current_iwd_end_index(0u) +namespace image { - FindNextObjContainer(); -} - -bool AbstractImageIwdPostProcessor::AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition) -{ - return std::ranges::any_of(zoneDefinition.m_zone_definition.m_obj_containers, - [](const ZoneDefinitionObjContainer& objContainer) - { - return objContainer.m_type == ZoneDefinitionObjContainerType::IWD; - }); -} - -void AbstractImageIwdPostProcessor::FindNextObjContainer() -{ - const auto objContainerCount = m_zone_definition.m_zone_definition.m_obj_containers.size(); - while (m_obj_container_index < objContainerCount) + AbstractIwdPostProcessor::AbstractIwdPostProcessor(const ZoneDefinitionContext& zoneDefinition, + ISearchPath& searchPath, + ZoneAssetCreationStateContainer& zoneStates, + IOutputPath& outDir) + : m_zone_definition(zoneDefinition), + m_search_path(searchPath), + m_iwd_creator(zoneStates.GetZoneAssetCreationState()), + m_out_dir(outDir), + m_obj_container_index(0u), + m_current_iwd(nullptr), + m_current_iwd_start_index(0u), + m_current_iwd_end_index(0u) { - const auto& objContainer = m_zone_definition.m_zone_definition.m_obj_containers[m_obj_container_index++]; - - if (objContainer.m_type != ZoneDefinitionObjContainerType::IWD) - continue; - - m_current_iwd = m_iwd_creator.GetOrAddIwd(objContainer.m_name); - m_current_iwd_start_index = objContainer.m_asset_start; - m_current_iwd_end_index = objContainer.m_asset_end; - return; + FindNextObjContainer(); } - m_current_iwd = nullptr; -} + bool AbstractIwdPostProcessor::AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition) + { + return std::ranges::any_of(zoneDefinition.m_zone_definition.m_obj_containers, + [](const ZoneDefinitionObjContainer& objContainer) + { + return objContainer.m_type == ZoneDefinitionObjContainerType::IWD; + }); + } -void AbstractImageIwdPostProcessor::PostProcessAsset(XAssetInfoGeneric& assetInfo, AssetCreationContext& context) -{ - if (assetInfo.m_name.empty() || assetInfo.m_name[0] == ',') - return; + void AbstractIwdPostProcessor::FindNextObjContainer() + { + const auto objContainerCount = m_zone_definition.m_zone_definition.m_obj_containers.size(); + while (m_obj_container_index < objContainerCount) + { + const auto& objContainer = m_zone_definition.m_zone_definition.m_obj_containers[m_obj_container_index++]; - while (m_current_iwd && m_zone_definition.m_asset_index_in_definition >= m_current_iwd_end_index) - FindNextObjContainer(); + if (objContainer.m_type != ZoneDefinitionObjContainerType::IWD) + continue; - if (m_current_iwd && m_zone_definition.m_asset_index_in_definition >= m_current_iwd_start_index) - m_current_iwd->AddFile(std::format("images/{}.iwi", assetInfo.m_name)); -} + m_current_iwd = m_iwd_creator.GetOrAddIwd(objContainer.m_name); + m_current_iwd_start_index = objContainer.m_asset_start; + m_current_iwd_end_index = objContainer.m_asset_end; + return; + } -void AbstractImageIwdPostProcessor::FinalizeZone(AssetCreationContext& context) -{ - m_iwd_creator.Finalize(m_search_path, m_out_dir); -} + m_current_iwd = nullptr; + } + + void AbstractIwdPostProcessor::PostProcessAsset(XAssetInfoGeneric& assetInfo, AssetCreationContext& context) + { + if (assetInfo.m_name.empty() || assetInfo.m_name[0] == ',') + return; + + while (m_current_iwd && m_zone_definition.m_asset_index_in_definition >= m_current_iwd_end_index) + FindNextObjContainer(); + + if (m_current_iwd && m_zone_definition.m_asset_index_in_definition >= m_current_iwd_start_index) + m_current_iwd->AddFile(GetFileNameForAsset(assetInfo.m_name, ".iwi")); + } + + void AbstractIwdPostProcessor::FinalizeZone(AssetCreationContext& context) + { + m_iwd_creator.Finalize(m_search_path, m_out_dir); + } +} // namespace image diff --git a/src/ObjCompiling/Image/ImageIwdPostProcessor.h b/src/ObjCompiling/Image/ImageIwdPostProcessor.h index 8225c29f..a7bf657b 100644 --- a/src/ObjCompiling/Image/ImageIwdPostProcessor.h +++ b/src/ObjCompiling/Image/ImageIwdPostProcessor.h @@ -5,48 +5,48 @@ #include "Iwd/IwdCreator.h" #include "SearchPath/IOutputPath.h" -class AbstractImageIwdPostProcessor : public IAssetPostProcessor +namespace image { -public: - AbstractImageIwdPostProcessor(const ZoneDefinitionContext& zoneDefinition, - ISearchPath& searchPath, - ZoneAssetCreationStateContainer& zoneStates, - IOutputPath& outDir); - - static bool AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition); - - void PostProcessAsset(XAssetInfoGeneric& assetInfo, AssetCreationContext& context) override; - void FinalizeZone(AssetCreationContext& context) override; - -private: - void FindNextObjContainer(); - - const ZoneDefinitionContext& m_zone_definition; - ISearchPath& m_search_path; - IwdCreator& m_iwd_creator; - IOutputPath& m_out_dir; - - unsigned m_obj_container_index; - IwdToCreate* m_current_iwd; - unsigned m_current_iwd_start_index; - unsigned m_current_iwd_end_index; -}; - -template class ImageIwdPostProcessor final : public AbstractImageIwdPostProcessor -{ -public: - static_assert(std::is_base_of_v); - - ImageIwdPostProcessor(const ZoneDefinitionContext& zoneDefinition, - ISearchPath& searchPath, - ZoneAssetCreationStateContainer& zoneStates, - IOutputPath& outDir) - : AbstractImageIwdPostProcessor(zoneDefinition, searchPath, zoneStates, outDir) + class AbstractIwdPostProcessor : public IAssetPostProcessor { - } + public: + AbstractIwdPostProcessor(const ZoneDefinitionContext& zoneDefinition, + ISearchPath& searchPath, + ZoneAssetCreationStateContainer& zoneStates, + IOutputPath& outDir); - [[nodiscard]] asset_type_t GetHandlingAssetType() const override + static bool AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition); + + void PostProcessAsset(XAssetInfoGeneric& assetInfo, AssetCreationContext& context) override; + void FinalizeZone(AssetCreationContext& context) override; + + private: + void FindNextObjContainer(); + + const ZoneDefinitionContext& m_zone_definition; + ISearchPath& m_search_path; + IwdCreator& m_iwd_creator; + IOutputPath& m_out_dir; + + unsigned m_obj_container_index; + IwdToCreate* m_current_iwd; + unsigned m_current_iwd_start_index; + unsigned m_current_iwd_end_index; + }; + + template class IwdPostProcessor final : public AbstractIwdPostProcessor { - return AssetType::EnumEntry; - } -}; + public: + static_assert(std::is_base_of_v); + + IwdPostProcessor(const ZoneDefinitionContext& zoneDefinition, ISearchPath& searchPath, ZoneAssetCreationStateContainer& zoneStates, IOutputPath& outDir) + : AbstractIwdPostProcessor(zoneDefinition, searchPath, zoneStates, outDir) + { + } + + [[nodiscard]] asset_type_t GetHandlingAssetType() const override + { + return AssetType::EnumEntry; + } + }; +} // namespace image diff --git a/src/ObjCompiling/Iwd/IwdCreator.cpp b/src/ObjCompiling/Iwd/IwdCreator.cpp index 3715f480..aa79fce1 100644 --- a/src/ObjCompiling/Iwd/IwdCreator.cpp +++ b/src/ObjCompiling/Iwd/IwdCreator.cpp @@ -1,6 +1,7 @@ #include "IwdCreator.h" #include "Utils/FileToZlibWrapper.h" +#include "Utils/Logging/Log.h" #include #include @@ -24,7 +25,7 @@ void IwdToCreate::Build(ISearchPath& searchPath, IOutputPath& outPath) const auto file = outPath.Open(fileName); if (!file) { - std::cerr << std::format("Failed to open file for iwd {}\n", m_name); + con::error("Failed to open file for iwd {}", m_name); return; } @@ -33,7 +34,7 @@ void IwdToCreate::Build(ISearchPath& searchPath, IOutputPath& outPath) const auto zipFile = zipOpen2(fileName.c_str(), APPEND_STATUS_CREATE, nullptr, &functions); if (!zipFile) { - std::cerr << std::format("Failed to open file as zip for iwd {}\n", m_name); + con::error("Failed to open file as zip for iwd {}", m_name); return; } @@ -42,7 +43,7 @@ void IwdToCreate::Build(ISearchPath& searchPath, IOutputPath& outPath) auto readFile = searchPath.Open(filePath); if (!readFile.IsOpen()) { - std::cerr << std::format("Failed to open file for iwd: {}\n", filePath); + con::error("Failed to open file for iwd: {}", filePath); continue; } @@ -76,7 +77,7 @@ void IwdToCreate::Build(ISearchPath& searchPath, IOutputPath& outPath) zipClose(zipFile, nullptr); - std::cout << std::format("Created iwd {} with {} entries\n", m_name, m_file_paths.size()); + con::info("Created iwd {} with {} entries", m_name, m_file_paths.size()); } const std::vector& IwdToCreate::GetFilePaths() const @@ -100,7 +101,7 @@ IwdToCreate* IwdCreator::GetOrAddIwd(const std::string& iwdName) void IwdCreator::Finalize(ISearchPath& searchPath, IOutputPath& outPath) { - std::cout << std::format("Writing {} iwd files to disk\n", m_iwds.size()); + con::info("Writing {} iwd files to disk", m_iwds.size()); for (const auto& iwdToCreate : m_iwds) iwdToCreate->Build(searchPath, outPath); diff --git a/src/ObjCompiling/KeyValuePairs/KeyValuePairsCreator.cpp b/src/ObjCompiling/KeyValuePairs/KeyValuePairsCreator.cpp index 348a6177..26136f12 100644 --- a/src/ObjCompiling/KeyValuePairs/KeyValuePairsCreator.cpp +++ b/src/ObjCompiling/KeyValuePairs/KeyValuePairsCreator.cpp @@ -1,76 +1,81 @@ #include "KeyValuePairsCreator.h" +#include "Utils/Logging/Log.h" + #include #include #include #include -CommonKeyValuePair::CommonKeyValuePair(std::string keyStr, std::string value) - : m_key_str(std::move(keyStr)), - m_value(std::move(value)) +namespace key_value_pairs { -} - -CommonKeyValuePair::CommonKeyValuePair(const unsigned keyHash, std::string value) - : m_key_hash(keyHash), - m_value(std::move(value)) -{ -} - -void KeyValuePairsCreator::AddKeyValuePair(CommonKeyValuePair keyValuePair) -{ - m_key_value_pairs.emplace_back(std::move(keyValuePair)); -} - -void KeyValuePairsCreator::Finalize(const ZoneDefinition& zoneDefinition) -{ - for (const auto& metaData : zoneDefinition.m_properties.m_properties) + CommonKeyValuePair::CommonKeyValuePair(std::string keyStr, std::string value) + : m_key_str(std::move(keyStr)), + m_value(std::move(value)) { - if (metaData.first.rfind("level.", 0) == 0) - { - std::string strValue = metaData.first.substr(std::char_traits::length("level.")); - if (strValue.empty()) - continue; - - if (strValue[0] == '@') - { - char* endPtr; - const unsigned keyHash = strtoul(&strValue[1], &endPtr, 16); - - if (endPtr != &strValue[strValue.size()]) - { - std::cerr << std::format("Could not parse metadata key \"{}\" as hash\n", metaData.first); - continue; - } - - m_key_value_pairs.emplace_back(keyHash, metaData.second); - } - else - { - m_key_value_pairs.emplace_back(std::move(strValue), metaData.second); - } - } } - std::ranges::sort(m_key_value_pairs, - [](const CommonKeyValuePair& v0, const CommonKeyValuePair& v1) - { - if (v0.m_key_str.has_value()) + CommonKeyValuePair::CommonKeyValuePair(const unsigned keyHash, std::string value) + : m_key_hash(keyHash), + m_value(std::move(value)) + { + } + + void Creator::AddKeyValuePair(CommonKeyValuePair keyValuePair) + { + m_key_value_pairs.emplace_back(std::move(keyValuePair)); + } + + void Creator::Finalize(const ZoneDefinition& zoneDefinition) + { + for (const auto& metaData : zoneDefinition.m_properties.m_properties) + { + if (metaData.first.rfind("level.", 0) == 0) + { + std::string strValue = metaData.first.substr(std::char_traits::length("level.")); + if (strValue.empty()) + continue; + + if (strValue[0] == '@') + { + char* endPtr; + const unsigned keyHash = strtoul(&strValue[1], &endPtr, 16); + + if (endPtr != &strValue[strValue.size()]) + { + con::error("Could not parse metadata key \"{}\" as hash", metaData.first); + continue; + } + + m_key_value_pairs.emplace_back(keyHash, metaData.second); + } + else + { + m_key_value_pairs.emplace_back(std::move(strValue), metaData.second); + } + } + } + + std::ranges::sort(m_key_value_pairs, + [](const CommonKeyValuePair& v0, const CommonKeyValuePair& v1) { - if (!v1.m_key_str.has_value()) - return true; + if (v0.m_key_str.has_value()) + { + if (!v1.m_key_str.has_value()) + return true; - return *v0.m_key_str < *v1.m_key_str; - } + return *v0.m_key_str < *v1.m_key_str; + } - if (!v1.m_key_hash.has_value()) - return false; + if (!v1.m_key_hash.has_value()) + return false; - return *v0.m_key_hash < *v1.m_key_hash; - }); -} + return *v0.m_key_hash < *v1.m_key_hash; + }); + } -std::vector KeyValuePairsCreator::GetFinalKeyValuePairs() -{ - return std::move(m_key_value_pairs); -} + std::vector Creator::GetFinalKeyValuePairs() + { + return std::move(m_key_value_pairs); + } +} // namespace key_value_pairs diff --git a/src/ObjCompiling/KeyValuePairs/KeyValuePairsCreator.h b/src/ObjCompiling/KeyValuePairs/KeyValuePairsCreator.h index eb91d33f..dd35d260 100644 --- a/src/ObjCompiling/KeyValuePairs/KeyValuePairsCreator.h +++ b/src/ObjCompiling/KeyValuePairs/KeyValuePairsCreator.h @@ -7,24 +7,27 @@ #include #include -class CommonKeyValuePair +namespace key_value_pairs { -public: - CommonKeyValuePair(std::string keyStr, std::string value); - CommonKeyValuePair(unsigned keyHash, std::string value); + class CommonKeyValuePair + { + public: + CommonKeyValuePair(std::string keyStr, std::string value); + CommonKeyValuePair(unsigned keyHash, std::string value); - std::optional m_key_str; - std::optional m_key_hash; - std::string m_value; -}; + std::optional m_key_str; + std::optional m_key_hash; + std::string m_value; + }; -class KeyValuePairsCreator final : public IZoneAssetCreationState -{ -public: - void AddKeyValuePair(CommonKeyValuePair keyValuePair); - void Finalize(const ZoneDefinition& zoneDefinition); - std::vector GetFinalKeyValuePairs(); + class Creator final : public IZoneAssetCreationState + { + public: + void AddKeyValuePair(CommonKeyValuePair keyValuePair); + void Finalize(const ZoneDefinition& zoneDefinition); + std::vector GetFinalKeyValuePairs(); -private: - std::vector m_key_value_pairs; -}; + private: + std::vector m_key_value_pairs; + }; +} // namespace key_value_pairs diff --git a/src/ObjImage/Image/DdsLoader.cpp b/src/ObjImage/Image/DdsLoader.cpp index 6fbd617b..5bd4e4b4 100644 --- a/src/ObjImage/Image/DdsLoader.cpp +++ b/src/ObjImage/Image/DdsLoader.cpp @@ -3,6 +3,7 @@ #include "Image/DdsTypes.h" #include "Utils/ClassUtils.h" #include "Utils/FileUtils.h" +#include "Utils/Logging/Log.h" #include #include @@ -41,13 +42,13 @@ namespace dds m_stream.read(reinterpret_cast(&magic), sizeof(magic)); if (m_stream.gcount() != sizeof(magic)) { - std::cerr << "Failed to read dds data\n"; + con::error("Failed to read dds data"); return false; } if (magic != DDS_MAGIC) { - std::cerr << "Invalid magic for dds\n"; + con::error("Invalid magic for dds"); return false; } @@ -60,7 +61,7 @@ namespace dds m_stream.read(reinterpret_cast(&headerDx10), sizeof(headerDx10)); if (m_stream.gcount() != sizeof(headerDx10)) { - std::cerr << "Failed to read dds data\n"; + con::error("Failed to read dds data"); return false; } @@ -81,7 +82,7 @@ namespace dds } else { - std::cerr << std::format("Unsupported dds resourceDimension {}\n", static_cast(headerDx10.resourceDimension)); + con::error("Unsupported dds resourceDimension {}", static_cast(headerDx10.resourceDimension)); return false; } @@ -94,7 +95,7 @@ namespace dds } } - std::cerr << std::format("Unsupported dds dxgi format {}\n", static_cast(headerDx10.dxgiFormat)); + con::error("Unsupported dds dxgi format {}", static_cast(headerDx10.dxgiFormat)); return false; } @@ -118,7 +119,7 @@ namespace dds return ReadDxt10Header(); default: - std::cerr << std::format("Unknown dds FourCC {}\n", pf.dwFourCC); + con::error("Unknown dds FourCC {}", pf.dwFourCC); return false; } } @@ -169,8 +170,7 @@ namespace dds } } - std::cerr << std::format( - "Failed to find dds pixel format: R={:#x} G={:#x} B={:#x} A={:#x}\n", pf.dwRBitMask, pf.dwGBitMask, pf.dwBBitMask, pf.dwABitMask); + con::error("Failed to find dds pixel format: R={:#x} G={:#x} B={:#x} A={:#x}", pf.dwRBitMask, pf.dwGBitMask, pf.dwBBitMask, pf.dwABitMask); return false; } @@ -189,7 +189,7 @@ namespace dds m_stream.read(reinterpret_cast(&header), sizeof(header)); if (m_stream.gcount() != sizeof(header)) { - std::cerr << "Failed to read dds data\n"; + con::error("Failed to read dds data"); return false; } @@ -249,7 +249,7 @@ namespace dds if (m_stream.gcount() != mipSize) { - std::cerr << "Failed to read texture data from dds\n"; + con::error("Failed to read texture data from dds"); return nullptr; } } diff --git a/src/ObjImage/Image/IwiLoader.cpp b/src/ObjImage/Image/IwiLoader.cpp index 8d6bc028..4f0e5df5 100644 --- a/src/ObjImage/Image/IwiLoader.cpp +++ b/src/ObjImage/Image/IwiLoader.cpp @@ -1,6 +1,7 @@ #include "IwiLoader.h" #include "Image/IwiTypes.h" +#include "Utils/Logging/Log.h" #include #include @@ -36,10 +37,10 @@ namespace iwi case iwi6::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE_ALPHA: case iwi6::IwiFormat::IMG_FORMAT_WAVELET_LUMINANCE: case iwi6::IwiFormat::IMG_FORMAT_WAVELET_ALPHA: - std::cerr << std::format("Unsupported IWI format: {}\n", format); + con::error("Unsupported IWI format: {}", format); break; default: - std::cerr << std::format("Unknown IWI format: {}\n", format); + con::error("Unknown IWI format: {}", format); break; } @@ -84,14 +85,14 @@ namespace iwi if (currentMipLevel < static_cast(std::extent_v) && currentFileSize != header.fileSizeForPicmip[currentMipLevel]) { - std::cerr << std::format("Iwi has invalid file size for picmip {}\n", currentMipLevel); + con::error("Iwi has invalid file size for picmip {}", currentMipLevel); return nullptr; } stream.read(reinterpret_cast(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel); if (stream.gcount() != sizeOfMipLevel) { - std::cerr << std::format("Unexpected eof of iwi in mip level {}\n", currentMipLevel); + con::error("Unexpected eof of iwi in mip level {}", currentMipLevel); return nullptr; } } @@ -134,10 +135,10 @@ namespace iwi case iwi8::IwiFormat::IMG_FORMAT_DXN_AS_LUMINANCE_ALPHA: case iwi8::IwiFormat::IMG_FORMAT_DXT1_AS_LUMINANCE: case iwi8::IwiFormat::IMG_FORMAT_DXT1_AS_ALPHA: - std::cerr << std::format("Unsupported IWI format: {}\n", format); + con::error("Unsupported IWI format: {}", format); break; default: - std::cerr << std::format("Unknown IWI format: {}\n", format); + con::error("Unknown IWI format: {}", format); break; } @@ -176,12 +177,12 @@ namespace iwi } else if ((header.flags & iwi8::IwiFlags::IMG_FLAG_MAPTYPE_MASK) == iwi8::IwiFlags::IMG_FLAG_MAPTYPE_1D) { - std::cerr << "Iwi has unsupported map type 1D\n"; + con::error("Iwi has unsupported map type 1D"); return nullptr; } else { - std::cerr << "Iwi has unsupported map type\n"; + con::error("Iwi has unsupported map type"); return nullptr; } @@ -198,14 +199,14 @@ namespace iwi if (currentMipLevel < static_cast(std::extent_v) && currentFileSize != header.fileSizeForPicmip[currentMipLevel]) { - std::cerr << std::format("Iwi has invalid file size for picmip {}\n", currentMipLevel); + con::error("Iwi has invalid file size for picmip {}", currentMipLevel); return nullptr; } stream.read(reinterpret_cast(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel); if (stream.gcount() != sizeOfMipLevel) { - std::cerr << std::format("Unexpected eof of iwi in mip level {}\n", currentMipLevel); + con::error("Unexpected eof of iwi in mip level {}", currentMipLevel); return nullptr; } } @@ -245,10 +246,10 @@ namespace iwi case iwi13::IwiFormat::IMG_FORMAT_BITMAP_C8: case iwi13::IwiFormat::IMG_FORMAT_BITMAP_RGBA8: case iwi13::IwiFormat::IMG_FORMAT_A16B16G16R16F: - std::cerr << std::format("Unsupported IWI format: {}\n", format); + con::error("Unsupported IWI format: {}", format); break; default: - std::cerr << std::format("Unknown IWI format: {}\n", format); + con::error("Unknown IWI format: {}", format); break; } @@ -293,14 +294,14 @@ namespace iwi if (currentMipLevel < static_cast(std::extent_v) && currentFileSize != header.fileSizeForPicmip[currentMipLevel]) { - std::cerr << std::format("Iwi has invalid file size for picmip {}\n", currentMipLevel); + con::error("Iwi has invalid file size for picmip {}", currentMipLevel); return nullptr; } stream.read(reinterpret_cast(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel); if (stream.gcount() != sizeOfMipLevel) { - std::cerr << std::format("Unexpected eof of iwi in mip level {}\n", currentMipLevel); + con::error("Unexpected eof of iwi in mip level {}", currentMipLevel); return nullptr; } } @@ -342,10 +343,10 @@ namespace iwi case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGB5A3: case iwi27::IwiFormat::IMG_FORMAT_BITMAP_C8: case iwi27::IwiFormat::IMG_FORMAT_BITMAP_RGBA8: - std::cerr << std::format("Unsupported IWI format: {}\n", format); + con::error("Unsupported IWI format: {}", format); break; default: - std::cerr << std::format("Unknown IWI format: {}\n", format); + con::error("Unknown IWI format: {}", format); break; } @@ -390,14 +391,14 @@ namespace iwi if (currentMipLevel < static_cast(std::extent_v) && currentFileSize != header.fileSizeForPicmip[currentMipLevel]) { - std::cerr << std::format("Iwi has invalid file size for picmip {}\n", currentMipLevel); + con::error("Iwi has invalid file size for picmip {}", currentMipLevel); return nullptr; } stream.read(reinterpret_cast(texture->GetBufferForMipLevel(currentMipLevel)), sizeOfMipLevel); if (stream.gcount() != sizeOfMipLevel) { - std::cerr << std::format("Unexpected eof of iwi in mip level {}\n", currentMipLevel); + con::error("Unexpected eof of iwi in mip level {}", currentMipLevel); return nullptr; } } @@ -415,7 +416,7 @@ namespace iwi if (iwiVersion.tag[0] != 'I' || iwiVersion.tag[1] != 'W' || iwiVersion.tag[2] != 'i') { - std::cerr << "Invalid IWI magic\n"; + con::error("Invalid IWI magic"); return nullptr; } @@ -437,7 +438,7 @@ namespace iwi break; } - std::cerr << std::format("Unknown IWI version {}\n", iwiVersion.version); + con::error("Unknown IWI version {}", iwiVersion.version); return nullptr; } } // namespace iwi diff --git a/src/ObjLoading/Asset/AssetCreationContext.cpp b/src/ObjLoading/Asset/AssetCreationContext.cpp index deb08b5a..7624716c 100644 --- a/src/ObjLoading/Asset/AssetCreationContext.cpp +++ b/src/ObjLoading/Asset/AssetCreationContext.cpp @@ -1,5 +1,7 @@ #include "AssetCreationContext.h" +#include "Utils/Logging/Log.h" + #include #include #include @@ -65,7 +67,7 @@ std::unique_ptr GenericAssetRegistration::CreateXAssetInfo() AssetCreationContext::AssetCreationContext(Zone& zone, const AssetCreatorCollection* creators, const IgnoredAssetLookup* ignoredAssetLookup) : ZoneAssetCreationStateContainer(zone), m_zone(zone), - m_forced_asset_pools(ZoneAssetPools::CreateForGame(zone.m_game->GetId(), &zone, zone.m_priority)), + m_forced_asset_pools(ZoneAssetPools::CreateForGame(zone.m_game_id, &zone, zone.m_priority)), m_creators(creators), m_ignored_asset_lookup(ignoredAssetLookup), m_forced_load_depth(0u) @@ -87,7 +89,7 @@ XAssetInfoGeneric* AssetCreationContext::AddAssetGeneric(GenericAssetRegistratio addedAsset = m_zone.m_pools->AddAsset(std::move(xAssetInfo)); if (addedAsset == nullptr) - std::cerr << std::format("Failed to add asset of type \"{}\" to pool: \"{}\"\n", *m_zone.m_pools->GetAssetTypeName(assetType), pAssetName); + con::error("Failed to add asset of type \"{}\" to pool: \"{}\"", *m_zone.m_pools->GetAssetTypeName(assetType), pAssetName); return addedAsset; } @@ -97,7 +99,7 @@ XAssetInfoGeneric* AssetCreationContext::LoadDefaultAssetDependency(const asset_ if (result.HasTakenAction() && !result.HasFailed()) return result.GetAssetInfo(); - std::cerr << std::format("Failed to create default asset of type {}\n", *m_zone.m_pools->GetAssetTypeName(assetType)); + con::error("Failed to create default asset of type {}", *m_zone.m_pools->GetAssetTypeName(assetType)); return nullptr; } @@ -127,11 +129,11 @@ XAssetInfoGeneric* AssetCreationContext::LoadDependencyGeneric(const asset_type_ if (!result.HasFailed()) return result.GetAssetInfo(); - std::cerr << std::format("Could not load asset \"{}\" of type \"{}\"\n", assetName, *m_zone.m_pools->GetAssetTypeName(assetType)); + con::error("Could not load asset \"{}\" of type \"{}\"", assetName, *m_zone.m_pools->GetAssetTypeName(assetType)); } else { - std::cerr << std::format("Missing asset \"{}\" of type \"{}\"\n", assetName, *m_zone.m_pools->GetAssetTypeName(assetType)); + con::error("Missing asset \"{}\" of type \"{}\"", assetName, *m_zone.m_pools->GetAssetTypeName(assetType)); } return nullptr; @@ -149,7 +151,7 @@ IndirectAssetReference AssetCreationContext::LoadIndirectAssetReferenceGeneric(c const auto result = m_creators->CreateAsset(assetType, assetName, *this); if (!result.HasTakenAction() && !result.HasFailed()) { - std::cerr << std::format("Could not load indirectly referenced asset \"{}\" of type \"{}\"\n", assetName, *m_zone.m_pools->GetAssetTypeName(assetType)); + con::warn("Could not load indirectly referenced asset \"{}\" of type \"{}\"", assetName, *m_zone.m_pools->GetAssetTypeName(assetType)); } return IndirectAssetReference(assetType, assetName); } @@ -185,11 +187,11 @@ XAssetInfoGeneric* AssetCreationContext::ForceLoadDependencyGeneric(const asset_ if (!result.HasFailed()) return result.GetAssetInfo(); - std::cerr << std::format("Could not load asset \"{}\" of type \"{}\"\n", assetName, *m_zone.m_pools->GetAssetTypeName(assetType)); + con::error("Could not load asset \"{}\" of type \"{}\"", assetName, *m_zone.m_pools->GetAssetTypeName(assetType)); } else { - std::cerr << std::format("Missing asset \"{}\" of type \"{}\"\n", assetName, *m_zone.m_pools->GetAssetTypeName(assetType)); + con::error("Missing asset \"{}\" of type \"{}\"", assetName, *m_zone.m_pools->GetAssetTypeName(assetType)); } return nullptr; diff --git a/src/ObjLoading/Asset/IZoneAssetCreationState.h b/src/ObjLoading/Asset/IZoneAssetCreationState.h index c63b1861..5692f553 100644 --- a/src/ObjLoading/Asset/IZoneAssetCreationState.h +++ b/src/ObjLoading/Asset/IZoneAssetCreationState.h @@ -52,14 +52,15 @@ public: static_assert(std::is_base_of_v, "T must inherit IZoneAssetCreationState"); // T must also have a public default constructor - const auto foundEntry = m_zone_asset_creation_states.find(typeid(T)); + std::type_index typeId = typeid(T); + const auto foundEntry = m_zone_asset_creation_states.find(typeId); if (foundEntry != m_zone_asset_creation_states.end()) return *dynamic_cast(foundEntry->second.get()); auto newState = std::make_unique(); newState->Inject(m_injection); auto* newStatePtr = newState.get(); - m_zone_asset_creation_states.emplace(std::make_pair>(typeid(T), std::move(newState))); + m_zone_asset_creation_states.emplace(std::move(typeId), std::move(newState)); return *newStatePtr; } diff --git a/src/ObjLoading/Game/IW3/Image/AssetLoaderImageIW3.cpp b/src/ObjLoading/Game/IW3/Image/AssetLoaderImageIW3.cpp index a4d921c8..8e876432 100644 --- a/src/ObjLoading/Game/IW3/Image/AssetLoaderImageIW3.cpp +++ b/src/ObjLoading/Game/IW3/Image/AssetLoaderImageIW3.cpp @@ -4,6 +4,7 @@ #include "Image/DdsLoader.h" #include "Image/IwiTypes.h" #include "Pool/GlobalAssetPool.h" +#include "Utils/Logging/Log.h" #include #include @@ -40,7 +41,7 @@ namespace const auto texture = dds::LoadDds(*file.m_stream); if (!texture) { - std::cerr << std::format("Failed to load dds file for image asset \"{}\"\n", assetName); + con::error("Failed to load dds file for image asset \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -122,10 +123,10 @@ namespace }; } // namespace -namespace IW3 +namespace image { - std::unique_ptr> CreateImageLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW3 +} // namespace image diff --git a/src/ObjLoading/Game/IW3/Image/AssetLoaderImageIW3.h b/src/ObjLoading/Game/IW3/Image/AssetLoaderImageIW3.h index ce4dbb81..b8d60124 100644 --- a/src/ObjLoading/Game/IW3/Image/AssetLoaderImageIW3.h +++ b/src/ObjLoading/Game/IW3/Image/AssetLoaderImageIW3.h @@ -7,7 +7,7 @@ #include -namespace IW3 +namespace image { - std::unique_ptr> CreateImageLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW3 + std::unique_ptr> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath); +} // namespace image diff --git a/src/ObjLoading/Game/IW3/Localize/AssetLoaderLocalizeIW3.cpp b/src/ObjLoading/Game/IW3/Localize/AssetLoaderLocalizeIW3.cpp index c7f2572f..0afa5b6c 100644 --- a/src/ObjLoading/Game/IW3/Localize/AssetLoaderLocalizeIW3.cpp +++ b/src/ObjLoading/Game/IW3/Localize/AssetLoaderLocalizeIW3.cpp @@ -36,10 +36,10 @@ namespace }; } // namespace -namespace IW3 +namespace localize { - std::unique_ptr> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace IW3 +} // namespace localize diff --git a/src/ObjLoading/Game/IW3/Localize/AssetLoaderLocalizeIW3.h b/src/ObjLoading/Game/IW3/Localize/AssetLoaderLocalizeIW3.h index 5469d4ca..7d1c30ed 100644 --- a/src/ObjLoading/Game/IW3/Localize/AssetLoaderLocalizeIW3.h +++ b/src/ObjLoading/Game/IW3/Localize/AssetLoaderLocalizeIW3.h @@ -8,7 +8,7 @@ #include -namespace IW3 +namespace localize { - std::unique_ptr> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace IW3 + std::unique_ptr> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace localize diff --git a/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.cpp b/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.cpp new file mode 100644 index 00000000..5d911fa6 --- /dev/null +++ b/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.cpp @@ -0,0 +1,55 @@ +#include "LoaderMaterialIW3.h" + +#include "Game/IW3/IW3.h" +#include "Game/IW3/Material/JsonMaterialLoaderIW3.h" +#include "Material/MaterialCommon.h" +#include "Utils/Logging/Log.h" + +#include +#include + +using namespace IW3; + +namespace +{ + class MaterialLoader final : public AssetCreator + { + public: + MaterialLoader(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto file = m_search_path.Open(material::GetFileNameForAssetName(assetName)); + if (!file.IsOpen()) + return AssetCreationResult::NoAction(); + + auto* material = m_memory.Alloc(); + material->info.name = m_memory.Dup(assetName.c_str()); + + AssetRegistration registration(assetName, material); + if (!LoadMaterialAsJson(*file.m_stream, *material, m_memory, context, registration)) + { + con::error("Failed to load material \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; +} // namespace + +namespace material +{ + std::unique_ptr> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath) + { + return std::make_unique(memory, searchPath); + } +} // namespace material diff --git a/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.h b/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.h new file mode 100644 index 00000000..fd51d2c4 --- /dev/null +++ b/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/IW3/IW3.h" +#include "Gdt/IGdtQueryable.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +namespace material +{ + std::unique_ptr> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath); +} // namespace material diff --git a/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp b/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp index eeb2d3a8..fb2c1627 100644 --- a/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp +++ b/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp @@ -3,8 +3,10 @@ #include "Asset/GlobalAssetPoolsLoader.h" #include "Game/IW3/GameIW3.h" #include "Game/IW3/IW3.h" +#include "Game/IW3/XModel/LoaderXModelIW3.h" #include "Image/AssetLoaderImageIW3.h" #include "Localize/AssetLoaderLocalizeIW3.h" +#include "Material/LoaderMaterialIW3.h" #include "ObjLoading.h" #include "RawFile/AssetLoaderRawFileIW3.h" #include "StringTable/AssetLoaderStringTableIW3.h" @@ -89,10 +91,10 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - // collection.AddAssetCreator(std::make_unique(memory)); - // collection.AddAssetCreator(std::make_unique(memory)); + collection.AddAssetCreator(xmodel::CreateLoaderIW3(memory, searchPath, zone)); + collection.AddAssetCreator(material::CreateLoaderIW3(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateImageLoader(memory, searchPath)); + collection.AddAssetCreator(image::CreateLoaderIW3(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); @@ -106,13 +108,13 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateLocalizeLoader(memory, searchPath, zone)); + collection.AddAssetCreator(localize::CreateLoaderIW3(memory, searchPath, zone)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateRawFileLoader(memory, searchPath)); - collection.AddAssetCreator(CreateStringTableLoader(memory, searchPath)); + collection.AddAssetCreator(raw_file::CreateLoaderIW3(memory, searchPath)); + collection.AddAssetCreator(string_table::CreateLoaderIW3(memory, searchPath)); } } // namespace diff --git a/src/ObjLoading/Game/IW3/RawFile/AssetLoaderRawFileIW3.cpp b/src/ObjLoading/Game/IW3/RawFile/AssetLoaderRawFileIW3.cpp index 92a19b21..ffeaef61 100644 --- a/src/ObjLoading/Game/IW3/RawFile/AssetLoaderRawFileIW3.cpp +++ b/src/ObjLoading/Game/IW3/RawFile/AssetLoaderRawFileIW3.cpp @@ -44,10 +44,10 @@ namespace }; } // namespace -namespace IW3 +namespace raw_file { - std::unique_ptr> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW3 +} // namespace raw_file diff --git a/src/ObjLoading/Game/IW3/RawFile/AssetLoaderRawFileIW3.h b/src/ObjLoading/Game/IW3/RawFile/AssetLoaderRawFileIW3.h index cddae3ea..914c0529 100644 --- a/src/ObjLoading/Game/IW3/RawFile/AssetLoaderRawFileIW3.h +++ b/src/ObjLoading/Game/IW3/RawFile/AssetLoaderRawFileIW3.h @@ -7,7 +7,7 @@ #include -namespace IW3 +namespace raw_file { - std::unique_ptr> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW3 + std::unique_ptr> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath); +} // namespace raw_file diff --git a/src/ObjLoading/Game/IW3/StringTable/AssetLoaderStringTableIW3.cpp b/src/ObjLoading/Game/IW3/StringTable/AssetLoaderStringTableIW3.cpp index bb7303ec..e8146808 100644 --- a/src/ObjLoading/Game/IW3/StringTable/AssetLoaderStringTableIW3.cpp +++ b/src/ObjLoading/Game/IW3/StringTable/AssetLoaderStringTableIW3.cpp @@ -38,10 +38,10 @@ namespace }; } // namespace -namespace IW3 +namespace string_table { - std::unique_ptr> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW3 +} // namespace string_table diff --git a/src/ObjLoading/Game/IW3/StringTable/AssetLoaderStringTableIW3.h b/src/ObjLoading/Game/IW3/StringTable/AssetLoaderStringTableIW3.h index 76702596..5b5a1ded 100644 --- a/src/ObjLoading/Game/IW3/StringTable/AssetLoaderStringTableIW3.h +++ b/src/ObjLoading/Game/IW3/StringTable/AssetLoaderStringTableIW3.h @@ -7,7 +7,7 @@ #include -namespace IW3 +namespace string_table { - std::unique_ptr> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW3 + std::unique_ptr> CreateLoaderIW3(MemoryManager& memory, ISearchPath& searchPath); +} // namespace string_table diff --git a/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.cpp b/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.cpp index 112076a7..e9703fc2 100644 --- a/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.cpp +++ b/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.cpp @@ -1,5 +1,7 @@ #include "InfoStringToStructConverter.h" +#include "Utils/Logging/Log.h" + #include #include #include @@ -63,7 +65,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (fx == nullptr) { - std::cerr << std::format("Failed to load fx asset \"{}\"\n", value); + con::error("Failed to load fx asset \"{}\"", value); return false; } @@ -85,7 +87,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (xmodel == nullptr) { - std::cerr << std::format("Failed to load xmodel asset \"{}\"\n", value); + con::error("Failed to load xmodel asset \"{}\"", value); return false; } @@ -107,7 +109,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (material == nullptr) { - std::cerr << std::format("Failed to load material asset \"{}\"\n", value); + con::error("Failed to load material asset \"{}\"", value); return false; } @@ -129,7 +131,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (tracer == nullptr) { - std::cerr << std::format("Failed to load tracer asset \"{}\"\n", value); + con::error("Failed to load tracer asset \"{}\"", value); return false; } @@ -146,7 +148,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (endPtr != &value[value.size()]) { - std::cerr << std::format("Failed to parse value \"{}\" as mph\n", value); + con::error("Failed to parse value \"{}\" as mph", value); return false; } @@ -165,7 +167,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (collmap == nullptr) { - std::cerr << std::format("Failed to load collmap asset \"{}\"\n", value); + con::error("Failed to load collmap asset \"{}\"", value); return false; } diff --git a/src/ObjLoading/Game/IW4/Leaderboard/JsonLeaderboardDefLoader.cpp b/src/ObjLoading/Game/IW4/Leaderboard/JsonLeaderboardDefLoader.cpp deleted file mode 100644 index cff973f9..00000000 --- a/src/ObjLoading/Game/IW4/Leaderboard/JsonLeaderboardDefLoader.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "JsonLeaderboardDefLoader.h" - -#include "Game/IW4/CommonIW4.h" -#include "Game/IW4/Leaderboard/JsonLeaderboardDef.h" - -#include -#include -#include - -using namespace nlohmann; -using namespace IW4; - -namespace -{ - class JsonLoader - { - public: - JsonLoader(std::istream& stream, MemoryManager& memory) - : m_stream(stream), - m_memory(memory) - { - } - - bool Load(LeaderboardDef& leaderboardDef) const - { - const auto jRoot = json::parse(m_stream); - std::string type; - unsigned version; - - jRoot.at("_type").get_to(type); - jRoot.at("_version").get_to(version); - - if (type != "leaderboard" || version != 1u) - { - std::cerr << std::format("Tried to load leaderboard \"{}\" but did not find expected type leaderboard of version 1\n", leaderboardDef.name); - return false; - } - - try - { - const auto jLeaderboard = jRoot.get(); - return CreateLeaderboardFromJson(jLeaderboard, leaderboardDef); - } - catch (const json::exception& e) - { - std::cerr << std::format("Failed to parse json of leaderboard: {}\n", e.what()); - } - - return false; - } - - private: - bool CreateColumnDefFromJson(const JsonColumnDef& jColumn, LbColumnDef& lbColumnDef, LeaderboardDef& leaderboardDef) const - { - lbColumnDef.name = m_memory.Dup(jColumn.name.c_str()); - - lbColumnDef.id = jColumn.colId; - lbColumnDef.propertyId = jColumn.propertyId.value_or(0); - lbColumnDef.hidden = jColumn.hidden.value_or(false); - - if (jColumn.statName) - lbColumnDef.statName = m_memory.Dup(jColumn.statName->c_str()); - else - lbColumnDef.statName = nullptr; - - lbColumnDef.type = jColumn.type; - - lbColumnDef.precision = jColumn.precision.value_or(0); - lbColumnDef.agg = jColumn.aggregationFunction; - - return true; - } - - bool CreateLeaderboardFromJson(const JsonLeaderboardDef& jLeaderboardDef, LeaderboardDef& leaderboardDef) const - { - leaderboardDef.id = jLeaderboardDef.id; - - leaderboardDef.xpColId = jLeaderboardDef.xpColId.value_or(-1); - leaderboardDef.prestigeColId = jLeaderboardDef.prestigeColId.value_or(-1); - - if (!jLeaderboardDef.columns.empty()) - { - leaderboardDef.columnCount = static_cast(jLeaderboardDef.columns.size()); - leaderboardDef.columns = m_memory.Alloc(leaderboardDef.columnCount); - - for (auto i = 0; i < leaderboardDef.columnCount; i++) - { - if (!CreateColumnDefFromJson(jLeaderboardDef.columns[i], leaderboardDef.columns[i], leaderboardDef)) - return false; - } - } - else - { - leaderboardDef.columnCount = 0; - leaderboardDef.columns = nullptr; - } - - return true; - } - - std::istream& m_stream; - MemoryManager& m_memory; - }; -} // namespace - -namespace IW4 -{ - bool LoadLeaderboardAsJson(std::istream& stream, LeaderboardDef& leaderboard, MemoryManager* memory) - { - const JsonLoader loader(stream, *memory); - - return loader.Load(leaderboard); - } -} // namespace IW4 diff --git a/src/ObjLoading/Game/IW4/Leaderboard/JsonLeaderboardDefLoader.h b/src/ObjLoading/Game/IW4/Leaderboard/JsonLeaderboardDefLoader.h deleted file mode 100644 index 22f85756..00000000 --- a/src/ObjLoading/Game/IW4/Leaderboard/JsonLeaderboardDefLoader.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Game/IW4/IW4.h" -#include "Utils/MemoryManager.h" - -#include - -namespace IW4 -{ - bool LoadLeaderboardAsJson(std::istream& stream, LeaderboardDef& leaderboard, MemoryManager* memory); -} // namespace IW4 diff --git a/src/ObjLoading/Game/IW4/Leaderboard/LoaderLeaderboardIW4.cpp b/src/ObjLoading/Game/IW4/Leaderboard/LoaderLeaderboardIW4.cpp index 550472dc..9065aa75 100644 --- a/src/ObjLoading/Game/IW4/Leaderboard/LoaderLeaderboardIW4.cpp +++ b/src/ObjLoading/Game/IW4/Leaderboard/LoaderLeaderboardIW4.cpp @@ -1,16 +1,110 @@ #include "LoaderLeaderboardIW4.h" #include "Game/IW4/IW4.h" -#include "JsonLeaderboardDefLoader.h" +#include "Game/IW4/Leaderboard/JsonLeaderboardDef.h" +#include "Leaderboard/LeaderboardCommon.h" +#include "Utils/Logging/Log.h" #include #include #include +#include +using namespace nlohmann; using namespace IW4; namespace { + class JsonLoader + { + public: + JsonLoader(std::istream& stream, MemoryManager& memory) + : m_stream(stream), + m_memory(memory) + { + } + + bool Load(LeaderboardDef& leaderboardDef) const + { + try + { + const auto jRoot = json::parse(m_stream); + std::string type; + unsigned version; + + jRoot.at("_type").get_to(type); + jRoot.at("_version").get_to(version); + + if (type != "leaderboard" || version != 1u) + { + con::error("Tried to load leaderboard \"{}\" but did not find expected type leaderboard of version 1", leaderboardDef.name); + return false; + } + + const auto jLeaderboard = jRoot.get(); + return CreateLeaderboardFromJson(jLeaderboard, leaderboardDef); + } + catch (const json::exception& e) + { + con::error("Failed to parse json of leaderboard: {}", e.what()); + } + + return false; + } + + private: + bool CreateColumnDefFromJson(const JsonColumnDef& jColumn, LbColumnDef& lbColumnDef, LeaderboardDef& leaderboardDef) const + { + lbColumnDef.name = m_memory.Dup(jColumn.name.c_str()); + + lbColumnDef.id = jColumn.colId; + lbColumnDef.propertyId = jColumn.propertyId.value_or(0); + lbColumnDef.hidden = jColumn.hidden.value_or(false); + + if (jColumn.statName) + lbColumnDef.statName = m_memory.Dup(jColumn.statName->c_str()); + else + lbColumnDef.statName = nullptr; + + lbColumnDef.type = jColumn.type; + + lbColumnDef.precision = jColumn.precision.value_or(0); + lbColumnDef.agg = jColumn.aggregationFunction; + + return true; + } + + bool CreateLeaderboardFromJson(const JsonLeaderboardDef& jLeaderboardDef, LeaderboardDef& leaderboardDef) const + { + leaderboardDef.id = jLeaderboardDef.id; + + leaderboardDef.xpColId = jLeaderboardDef.xpColId.value_or(-1); + leaderboardDef.prestigeColId = jLeaderboardDef.prestigeColId.value_or(-1); + + if (!jLeaderboardDef.columns.empty()) + { + leaderboardDef.columnCount = static_cast(jLeaderboardDef.columns.size()); + leaderboardDef.columns = m_memory.Alloc(leaderboardDef.columnCount); + + for (auto i = 0; i < leaderboardDef.columnCount; i++) + { + if (!CreateColumnDefFromJson(jLeaderboardDef.columns[i], leaderboardDef.columns[i], leaderboardDef)) + return false; + } + } + else + { + leaderboardDef.columnCount = 0; + leaderboardDef.columns = nullptr; + } + + return true; + } + + std::istream& m_stream; + MemoryManager& m_memory; + }; + class LeaderboardLoader final : public AssetCreator { public: @@ -22,16 +116,17 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto file = m_search_path.Open(std::format("leaderboards/{}.json", assetName)); + const auto file = m_search_path.Open(leaderboard::GetJsonFileNameForAsset(assetName)); if (!file.IsOpen()) return AssetCreationResult::NoAction(); auto* leaderboardDef = m_memory.Alloc(); leaderboardDef->name = m_memory.Dup(assetName.c_str()); - if (!LoadLeaderboardAsJson(*file.m_stream, *leaderboardDef, &m_memory)) + const JsonLoader loader(*file.m_stream, m_memory); + if (!loader.Load(*leaderboardDef)) { - std::cerr << std::format("Failed to load leaderboard \"{}\"\n", assetName); + con::error("Failed to load leaderboard \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -44,10 +139,10 @@ namespace }; } // namespace -namespace IW4 +namespace leaderboard { - std::unique_ptr> CreateLeaderboardLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace leaderboard diff --git a/src/ObjLoading/Game/IW4/Leaderboard/LoaderLeaderboardIW4.h b/src/ObjLoading/Game/IW4/Leaderboard/LoaderLeaderboardIW4.h index 3f0a86ce..931a8fca 100644 --- a/src/ObjLoading/Game/IW4/Leaderboard/LoaderLeaderboardIW4.h +++ b/src/ObjLoading/Game/IW4/Leaderboard/LoaderLeaderboardIW4.h @@ -7,7 +7,7 @@ #include -namespace IW4 +namespace leaderboard { - std::unique_ptr> CreateLeaderboardLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace leaderboard diff --git a/src/ObjLoading/Game/IW4/LightDef/LoaderLightDefIW4.cpp b/src/ObjLoading/Game/IW4/LightDef/LightDefLoaderIW4.cpp similarity index 81% rename from src/ObjLoading/Game/IW4/LightDef/LoaderLightDefIW4.cpp rename to src/ObjLoading/Game/IW4/LightDef/LightDefLoaderIW4.cpp index 0563554e..f4722b58 100644 --- a/src/ObjLoading/Game/IW4/LightDef/LoaderLightDefIW4.cpp +++ b/src/ObjLoading/Game/IW4/LightDef/LightDefLoaderIW4.cpp @@ -1,6 +1,8 @@ -#include "LoaderLightDefIW4.h" +#include "LightDefLoaderIW4.h" #include "Game/IW4/IW4.h" +#include "LightDef/LightDefCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -23,7 +25,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto filename = GetAssetFilename(assetName); + const auto filename = light_def::GetFileNameForAsset(assetName); const auto file = m_search_path.Open(filename); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -48,7 +50,7 @@ namespace auto* imageDependency = context.LoadDependency(imageName); if (!imageDependency) { - std::cerr << std::format("Could not load GfxLightDef \"{}\" due to missing image \"{}\"\n", assetName, imageName); + con::error("Could not load GfxLightDef \"{}\" due to missing image \"{}\"", assetName, imageName); return AssetCreationResult::Failure(); } registration.AddDependency(imageDependency); @@ -61,20 +63,15 @@ namespace } private: - std::string GetAssetFilename(const std::string& assetName) - { - return std::format("lights/{}", assetName); - } - MemoryManager& m_memory; ISearchPath& m_search_path; }; } // namespace -namespace IW4 +namespace light_def { - std::unique_ptr> CreateLightDefLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace light_def diff --git a/src/ObjLoading/Game/IW4/LightDef/LoaderLightDefIW4.h b/src/ObjLoading/Game/IW4/LightDef/LightDefLoaderIW4.h similarity index 50% rename from src/ObjLoading/Game/IW4/LightDef/LoaderLightDefIW4.h rename to src/ObjLoading/Game/IW4/LightDef/LightDefLoaderIW4.h index 92eec1bb..7ac2880c 100644 --- a/src/ObjLoading/Game/IW4/LightDef/LoaderLightDefIW4.h +++ b/src/ObjLoading/Game/IW4/LightDef/LightDefLoaderIW4.h @@ -7,7 +7,7 @@ #include -namespace IW4 +namespace light_def { - std::unique_ptr> CreateLightDefLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace light_def diff --git a/src/ObjLoading/Game/IW4/Localize/LoaderLocalizeIW4.cpp b/src/ObjLoading/Game/IW4/Localize/LoaderLocalizeIW4.cpp index 795939ed..64ce403f 100644 --- a/src/ObjLoading/Game/IW4/Localize/LoaderLocalizeIW4.cpp +++ b/src/ObjLoading/Game/IW4/Localize/LoaderLocalizeIW4.cpp @@ -35,10 +35,10 @@ namespace }; } // namespace -namespace IW4 +namespace localize { - std::unique_ptr> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace IW4 +} // namespace localize diff --git a/src/ObjLoading/Game/IW4/Localize/LoaderLocalizeIW4.h b/src/ObjLoading/Game/IW4/Localize/LoaderLocalizeIW4.h index c7ae0d9d..e6abda46 100644 --- a/src/ObjLoading/Game/IW4/Localize/LoaderLocalizeIW4.h +++ b/src/ObjLoading/Game/IW4/Localize/LoaderLocalizeIW4.h @@ -8,7 +8,7 @@ #include -namespace IW4 +namespace localize { - std::unique_ptr> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace IW4 + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace localize diff --git a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp index be3bf8b0..9f921012 100644 --- a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp +++ b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp @@ -3,6 +3,7 @@ #include "Game/IW4/IW4.h" #include "Game/IW4/Material/JsonMaterialLoaderIW4.h" #include "Material/MaterialCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -32,7 +33,7 @@ namespace AssetRegistration registration(assetName, material); if (!LoadMaterialAsJson(*file.m_stream, *material, m_memory, context, registration)) { - std::cerr << std::format("Failed to load material \"{}\"\n", assetName); + con::error("Failed to load material \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -45,10 +46,10 @@ namespace }; } // namespace -namespace IW4 +namespace material { - std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace material diff --git a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h index efd0a610..6016cc17 100644 --- a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h +++ b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h @@ -6,7 +6,7 @@ #include "SearchPath/ISearchPath.h" #include "Utils/MemoryManager.h" -namespace IW4 +namespace material { - std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace material diff --git a/src/ObjLoading/Game/IW4/Menu/LoaderMenuListIW4.cpp b/src/ObjLoading/Game/IW4/Menu/LoaderMenuListIW4.cpp index 52e00c74..98ede01a 100644 --- a/src/ObjLoading/Game/IW4/Menu/LoaderMenuListIW4.cpp +++ b/src/ObjLoading/Game/IW4/Menu/LoaderMenuListIW4.cpp @@ -5,6 +5,7 @@ #include "Game/IW4/Menu/MenuConverterIW4.h" #include "ObjLoading.h" #include "Parsing/Menu/MenuFileReader.h" +#include "Utils/Logging/Log.h" #include #include @@ -88,7 +89,7 @@ namespace const auto alreadyLoadedMenuFile = conversionState.m_menus_by_filename.find(menuFilePath); if (alreadyLoadedMenuFile != conversionState.m_menus_by_filename.end()) { - std::cout << std::format("Already loaded \"{}\", skipping\n", menuFilePath); + con::debug("Already loaded \"{}\", skipping", menuFilePath); for (auto* menu : alreadyLoadedMenuFile->second) { menus.emplace_back(menu->Asset()); @@ -100,7 +101,7 @@ namespace const auto file = m_search_path.Open(menuFilePath); if (!file.IsOpen()) { - std::cerr << std::format("Could not open menu file \"{}\"\n", menuFilePath); + con::error("Could not open menu file \"{}\"", menuFilePath); return false; } @@ -109,12 +110,12 @@ namespace { ProcessParsedResults(menuFilePath, context, *menuFileResult, zoneState, conversionState, menus, registration); if (!menuFileResult->m_menus_to_load.empty()) - std::cout << std::format("WARNING: Menu file has menus to load even though it is not a menu list, ignoring: \"{}\"\n", menuFilePath); + con::warn("Menu file has menus to load even though it is not a menu list, ignoring: \"{}\"", menuFilePath); return true; } else - std::cerr << std::format("Could not read menu file \"{}\"\n", menuFilePath); + con::error("Could not read menu file \"{}\"", menuFilePath); return false; } @@ -134,12 +135,12 @@ namespace for (const auto& menu : parsingResult.m_menus) totalItemCount += menu->m_items.size(); - std::cout << std::format("Successfully read menu file \"{}\" ({} loads, {} menus, {} functions, {} items)\n", - fileName, - menuLoadCount, - menuCount, - functionCount, - totalItemCount); + con::info("Successfully read menu file \"{}\" ({} loads, {} menus, {} functions, {} items)", + fileName, + menuLoadCount, + menuCount, + functionCount, + totalItemCount); // Add all functions to the zone state to make them available for all menus to be converted for (auto& function : parsingResult.m_functions) @@ -160,7 +161,7 @@ namespace converter->ConvertMenu(*commonMenu, *menuAsset, menuRegistration); if (menuAsset == nullptr) { - std::cerr << std::format("Failed to convert menu file \"{}\"\n", commonMenu->m_name); + con::error("Failed to convert menu file \"{}\"", commonMenu->m_name); return false; } @@ -212,10 +213,10 @@ namespace }; } // namespace -namespace IW4 +namespace menu { - std::unique_ptr> CreateMenuListLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateMenuListLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace menu diff --git a/src/ObjLoading/Game/IW4/Menu/LoaderMenuListIW4.h b/src/ObjLoading/Game/IW4/Menu/LoaderMenuListIW4.h index 6a1d0499..a22d7b2c 100644 --- a/src/ObjLoading/Game/IW4/Menu/LoaderMenuListIW4.h +++ b/src/ObjLoading/Game/IW4/Menu/LoaderMenuListIW4.h @@ -7,7 +7,7 @@ #include -namespace IW4 +namespace menu { - std::unique_ptr> CreateMenuListLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr> CreateMenuListLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace menu diff --git a/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp b/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp index 021cc4b3..ad5b83c4 100644 --- a/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp +++ b/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp @@ -3,8 +3,9 @@ #include "Asset/GlobalAssetPoolsLoader.h" #include "Game/IW4/GameIW4.h" #include "Game/IW4/IW4.h" +#include "Game/IW4/XModel/LoaderXModelIW4.h" #include "Leaderboard/LoaderLeaderboardIW4.h" -#include "LightDef/LoaderLightDefIW4.h" +#include "LightDef/LightDefLoaderIW4.h" #include "Localize/LoaderLocalizeIW4.h" #include "Material/LoaderMaterialIW4.h" #include "Menu/LoaderMenuListIW4.h" @@ -118,19 +119,19 @@ namespace { auto& memory = zone.Memory(); - collection.AddAssetCreator(std::make_unique(memory, searchPath, zone)); - collection.AddAssetCreator(std::make_unique(memory, gdt, zone)); + collection.AddAssetCreator(phys_preset::CreateRawLoaderIW4(memory, searchPath, zone)); + collection.AddAssetCreator(phys_preset::CreateGdtLoaderIW4(memory, gdt, zone)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath)); - collection.AddAssetCreator(CreatePixelShaderLoader(memory, searchPath)); - collection.AddAssetCreator(CreateVertexShaderLoader(memory, searchPath)); + collection.AddAssetCreator(xmodel::CreateLoaderIW4(memory, searchPath, zone)); + collection.AddAssetCreator(material::CreateLoaderIW4(memory, searchPath)); + collection.AddAssetCreator(shader::CreatePixelShaderLoaderIW4(memory, searchPath)); + collection.AddAssetCreator(shader::CreateVertexShaderLoaderIW4(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateSoundCurveLoader(memory, searchPath)); + collection.AddAssetCreator(sound_curve::CreateLoaderIW4(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); @@ -139,19 +140,19 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateLightDefLoader(memory, searchPath)); + collection.AddAssetCreator(light_def::CreateLoaderIW4(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateMenuListLoader(memory, searchPath)); + collection.AddAssetCreator(menu::CreateMenuListLoaderIW4(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateLocalizeLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateRawWeaponLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateGdtWeaponLoader(memory, searchPath, gdt, zone)); + collection.AddAssetCreator(localize::CreateLoaderIW4(memory, searchPath, zone)); + collection.AddAssetCreator(weapon::CreateRawLoaderIW4(memory, searchPath, zone)); + collection.AddAssetCreator(weapon::CreateGdtLoaderIW4(memory, searchPath, gdt, zone)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateRawFileLoader(memory, searchPath)); - collection.AddAssetCreator(CreateStringTableLoader(memory, searchPath)); - collection.AddAssetCreator(CreateLeaderboardLoader(memory, searchPath)); - collection.AddAssetCreator(CreateStructuredDataDefLoader(memory, searchPath)); + collection.AddAssetCreator(raw_file::CreateLoaderIW4(memory, searchPath)); + collection.AddAssetCreator(string_table::CreateLoaderIW4(memory, searchPath)); + collection.AddAssetCreator(leaderboard::CreateLoaderIW4(memory, searchPath)); + collection.AddAssetCreator(structured_data_def::CreateLoaderIW4(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); diff --git a/src/ObjLoading/Game/IW4/PhysPreset/GdtLoaderPhysPresetIW4.cpp b/src/ObjLoading/Game/IW4/PhysPreset/GdtLoaderPhysPresetIW4.cpp index afd65d70..cc5a0525 100644 --- a/src/ObjLoading/Game/IW4/PhysPreset/GdtLoaderPhysPresetIW4.cpp +++ b/src/ObjLoading/Game/IW4/PhysPreset/GdtLoaderPhysPresetIW4.cpp @@ -4,32 +4,50 @@ #include "Game/IW4/ObjConstantsIW4.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderPhysPresetIW4.h" +#include "Utils/Logging/Log.h" #include #include using namespace IW4; -GdtLoaderPhysPreset::GdtLoaderPhysPreset(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone) - : m_memory(memory), - m_gdt(gdt), - m_zone(zone) +namespace { -} - -AssetCreationResult GdtLoaderPhysPreset::CreateAsset(const std::string& assetName, AssetCreationContext& context) -{ - auto* gdtEntry = m_gdt.GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_PHYS_PRESET, assetName); - if (gdtEntry == nullptr) - return AssetCreationResult::NoAction(); - - InfoString infoString; - if (!infoString.FromGdtProperties(*gdtEntry)) + class GdtLoaderPhysPreset final : public AssetCreator { - std::cerr << std::format("Failed to read phys preset gdt entry: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); - } + public: + GdtLoaderPhysPreset(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone) + : m_gdt(gdt), + m_info_string_loader(memory, zone) + { + } - InfoStringLoaderPhysPreset infoStringLoader(m_memory, m_zone); - return infoStringLoader.CreateAsset(assetName, infoString, context); -} + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto* gdtEntry = m_gdt.GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_PHYS_PRESET, assetName); + if (gdtEntry == nullptr) + return AssetCreationResult::NoAction(); + + InfoString infoString; + if (!infoString.FromGdtProperties(*gdtEntry)) + { + con::error("Failed to read phys preset gdt entry: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + return m_info_string_loader.CreateAsset(assetName, infoString, context); + } + + private: + IGdtQueryable& m_gdt; + phys_preset::InfoStringLoaderIW4 m_info_string_loader; + }; +} // namespace + +namespace phys_preset +{ + std::unique_ptr> CreateGdtLoaderIW4(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone) + { + return std::make_unique(memory, gdt, zone); + } +} // namespace phys_preset diff --git a/src/ObjLoading/Game/IW4/PhysPreset/GdtLoaderPhysPresetIW4.h b/src/ObjLoading/Game/IW4/PhysPreset/GdtLoaderPhysPresetIW4.h index 59b65bc1..c5e474f6 100644 --- a/src/ObjLoading/Game/IW4/PhysPreset/GdtLoaderPhysPresetIW4.h +++ b/src/ObjLoading/Game/IW4/PhysPreset/GdtLoaderPhysPresetIW4.h @@ -3,20 +3,12 @@ #include "Asset/IAssetCreator.h" #include "Game/IW4/IW4.h" #include "Gdt/IGdtQueryable.h" +#include "SearchPath/ISearchPath.h" #include "Utils/MemoryManager.h" -namespace IW4 +#include + +namespace phys_preset { - class GdtLoaderPhysPreset final : public AssetCreator - { - public: - GdtLoaderPhysPreset(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone); - - AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override; - - private: - MemoryManager& m_memory; - IGdtQueryable& m_gdt; - Zone& m_zone; - }; -} // namespace IW4 + std::unique_ptr> CreateGdtLoaderIW4(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone); +} // namespace phys_preset diff --git a/src/ObjLoading/Game/IW4/PhysPreset/InfoStringLoaderPhysPresetIW4.cpp b/src/ObjLoading/Game/IW4/PhysPreset/InfoStringLoaderPhysPresetIW4.cpp index 25c8cc89..813ff137 100644 --- a/src/ObjLoading/Game/IW4/PhysPreset/InfoStringLoaderPhysPresetIW4.cpp +++ b/src/ObjLoading/Game/IW4/PhysPreset/InfoStringLoaderPhysPresetIW4.cpp @@ -3,6 +3,7 @@ #include "Game/IW4/IW4.h" #include "Game/IW4/InfoString/InfoStringToStructConverter.h" #include "Game/IW4/PhysPreset/PhysPresetFields.h" +#include "Utils/Logging/Log.h" #include #include @@ -58,30 +59,33 @@ namespace } } // namespace -InfoStringLoaderPhysPreset::InfoStringLoaderPhysPreset(MemoryManager& memory, Zone& zone) - : m_memory(memory), - m_zone(zone) +namespace phys_preset { -} - -AssetCreationResult InfoStringLoaderPhysPreset::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) -{ - PhysPresetInfo presetInfo; - std::memset(&presetInfo, 0, sizeof(presetInfo)); - - auto* physPreset = m_memory.Alloc(); - AssetRegistration registration(assetName, physPreset); - - InfoStringToPhysPresetConverter converter( - infoString, &presetInfo, m_zone.m_script_strings, m_memory, context, registration, phys_preset_fields, std::extent_v); - if (!converter.Convert()) + InfoStringLoaderIW4::InfoStringLoaderIW4(MemoryManager& memory, Zone& zone) + : m_memory(memory), + m_zone(zone) { - std::cerr << std::format("Failed to parse phys preset: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - CopyFromPhysPresetInfo(presetInfo, *physPreset); - physPreset->name = m_memory.Dup(assetName.c_str()); + AssetCreationResult InfoStringLoaderIW4::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + PhysPresetInfo presetInfo; + std::memset(&presetInfo, 0, sizeof(presetInfo)); - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + auto* physPreset = m_memory.Alloc(); + AssetRegistration registration(assetName, physPreset); + + InfoStringToPhysPresetConverter converter( + infoString, &presetInfo, m_zone.m_script_strings, m_memory, context, registration, phys_preset_fields, std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse phys preset: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + CopyFromPhysPresetInfo(presetInfo, *physPreset); + physPreset->name = m_memory.Dup(assetName.c_str()); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace phys_preset diff --git a/src/ObjLoading/Game/IW4/PhysPreset/InfoStringLoaderPhysPresetIW4.h b/src/ObjLoading/Game/IW4/PhysPreset/InfoStringLoaderPhysPresetIW4.h index 0ca1cede..1618bd80 100644 --- a/src/ObjLoading/Game/IW4/PhysPreset/InfoStringLoaderPhysPresetIW4.h +++ b/src/ObjLoading/Game/IW4/PhysPreset/InfoStringLoaderPhysPresetIW4.h @@ -4,12 +4,12 @@ #include "Asset/AssetCreationResult.h" #include "InfoString/InfoString.h" -namespace IW4 +namespace phys_preset { - class InfoStringLoaderPhysPreset + class InfoStringLoaderIW4 { public: - InfoStringLoaderPhysPreset(MemoryManager& memory, Zone& zone); + InfoStringLoaderIW4(MemoryManager& memory, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); @@ -17,4 +17,4 @@ namespace IW4 MemoryManager& m_memory; Zone& m_zone; }; -} // namespace IW4 +} // namespace phys_preset diff --git a/src/ObjLoading/Game/IW4/PhysPreset/RawLoaderPhysPresetIW4.cpp b/src/ObjLoading/Game/IW4/PhysPreset/RawLoaderPhysPresetIW4.cpp index ada031ac..b9745054 100644 --- a/src/ObjLoading/Game/IW4/PhysPreset/RawLoaderPhysPresetIW4.cpp +++ b/src/ObjLoading/Game/IW4/PhysPreset/RawLoaderPhysPresetIW4.cpp @@ -4,33 +4,52 @@ #include "Game/IW4/ObjConstantsIW4.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderPhysPresetIW4.h" +#include "PhysPreset/PhysPresetCommon.h" +#include "Utils/Logging/Log.h" #include #include using namespace IW4; -RawLoaderPhysPreset::RawLoaderPhysPreset(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) +namespace { -} - -AssetCreationResult RawLoaderPhysPreset::CreateAsset(const std::string& assetName, AssetCreationContext& context) -{ - const auto fileName = std::format("physic/{}", assetName); - const auto file = m_search_path.Open(fileName); - if (!file.IsOpen()) - return AssetCreationResult::NoAction(); - - InfoString infoString; - if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET, *file.m_stream)) + class RawLoaderPhysPreset final : public AssetCreator { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); - return AssetCreationResult::Failure(); - } + public: + RawLoaderPhysPreset(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_search_path(searchPath), + m_info_string_loader(memory, zone) + { + } - InfoStringLoaderPhysPreset infoStringLoader(m_memory, m_zone); - return infoStringLoader.CreateAsset(assetName, infoString, context); -} + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto fileName = phys_preset::GetFileNameForAssetName(assetName); + const auto file = m_search_path.Open(fileName); + if (!file.IsOpen()) + return AssetCreationResult::NoAction(); + + InfoString infoString; + if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET, *file.m_stream)) + { + con::error("Could not parse as info string file: \"{}\"", fileName); + return AssetCreationResult::Failure(); + } + + return m_info_string_loader.CreateAsset(assetName, infoString, context); + } + + private: + ISearchPath& m_search_path; + phys_preset::InfoStringLoaderIW4 m_info_string_loader; + }; +} // namespace + +namespace phys_preset +{ + std::unique_ptr> CreateRawLoaderIW4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + { + return std::make_unique(memory, searchPath, zone); + } +} // namespace phys_preset diff --git a/src/ObjLoading/Game/IW4/PhysPreset/RawLoaderPhysPresetIW4.h b/src/ObjLoading/Game/IW4/PhysPreset/RawLoaderPhysPresetIW4.h index 1c3dd841..ab78361b 100644 --- a/src/ObjLoading/Game/IW4/PhysPreset/RawLoaderPhysPresetIW4.h +++ b/src/ObjLoading/Game/IW4/PhysPreset/RawLoaderPhysPresetIW4.h @@ -5,18 +5,9 @@ #include "SearchPath/ISearchPath.h" #include "Utils/MemoryManager.h" -namespace IW4 +#include + +namespace phys_preset { - class RawLoaderPhysPreset final : public AssetCreator - { - public: - RawLoaderPhysPreset(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); - - AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override; - - private: - MemoryManager& m_memory; - ISearchPath& m_search_path; - Zone& m_zone; - }; -} // namespace IW4 + std::unique_ptr> CreateRawLoaderIW4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace phys_preset diff --git a/src/ObjLoading/Game/IW4/RawFile/LoaderRawFileIW4.cpp b/src/ObjLoading/Game/IW4/RawFile/LoaderRawFileIW4.cpp index 5d708aa9..58c09255 100644 --- a/src/ObjLoading/Game/IW4/RawFile/LoaderRawFileIW4.cpp +++ b/src/ObjLoading/Game/IW4/RawFile/LoaderRawFileIW4.cpp @@ -1,6 +1,7 @@ #include "LoaderRawFileIW4.h" #include "Game/IW4/IW4.h" +#include "Utils/Logging/Log.h" #include #include @@ -58,7 +59,7 @@ namespace if (ret != Z_STREAM_END) { - std::cerr << std::format("Deflate failed for loading rawfile \"{}\"\n", assetName); + con::error("Deflate failed for loading rawfile \"{}\"", assetName); deflateEnd(&zs); return AssetCreationResult::Failure(); } @@ -82,10 +83,10 @@ namespace }; } // namespace -namespace IW4 +namespace raw_file { - std::unique_ptr> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace raw_file diff --git a/src/ObjLoading/Game/IW4/RawFile/LoaderRawFileIW4.h b/src/ObjLoading/Game/IW4/RawFile/LoaderRawFileIW4.h index e8163342..624b5b69 100644 --- a/src/ObjLoading/Game/IW4/RawFile/LoaderRawFileIW4.h +++ b/src/ObjLoading/Game/IW4/RawFile/LoaderRawFileIW4.h @@ -7,7 +7,7 @@ #include -namespace IW4 +namespace raw_file { - std::unique_ptr> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace raw_file diff --git a/src/ObjLoading/Game/IW4/Shader/LoaderPixelShaderIW4.cpp b/src/ObjLoading/Game/IW4/Shader/LoaderPixelShaderIW4.cpp index 670e6879..4450dc92 100644 --- a/src/ObjLoading/Game/IW4/Shader/LoaderPixelShaderIW4.cpp +++ b/src/ObjLoading/Game/IW4/Shader/LoaderPixelShaderIW4.cpp @@ -1,6 +1,8 @@ #include "LoaderPixelShaderIW4.h" #include "Game/IW4/IW4.h" +#include "Shader/ShaderCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -21,14 +23,14 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = GetPixelShaderFileName(assetName); + const auto fileName = shader::GetFileNameForPixelShaderAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); if (file.m_length % sizeof(uint32_t) != 0) { - std::cerr << std::format("Invalid pixel shader \"{}\": Size must be dividable by {}\n", assetName, sizeof(uint32_t)); + con::error("Invalid pixel shader \"{}\": Size must be dividable by {}", assetName, sizeof(uint32_t)); return AssetCreationResult::Failure(); } @@ -53,15 +55,10 @@ namespace }; } // namespace -namespace IW4 +namespace shader { - std::string GetPixelShaderFileName(const std::string& pixelShaderAssetName) - { - return std::format("shader_bin/ps_{}.cso", pixelShaderAssetName); - } - - std::unique_ptr> CreatePixelShaderLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreatePixelShaderLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace shader diff --git a/src/ObjLoading/Game/IW4/Shader/LoaderPixelShaderIW4.h b/src/ObjLoading/Game/IW4/Shader/LoaderPixelShaderIW4.h index d29d29ba..0bc212a5 100644 --- a/src/ObjLoading/Game/IW4/Shader/LoaderPixelShaderIW4.h +++ b/src/ObjLoading/Game/IW4/Shader/LoaderPixelShaderIW4.h @@ -7,9 +7,7 @@ #include -namespace IW4 +namespace shader { - [[nodiscard]] std::string GetPixelShaderFileName(const std::string& pixelShaderAssetName); - - std::unique_ptr> CreatePixelShaderLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr> CreatePixelShaderLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace shader diff --git a/src/ObjLoading/Game/IW4/Shader/LoaderVertexShaderIW4.cpp b/src/ObjLoading/Game/IW4/Shader/LoaderVertexShaderIW4.cpp index 6c1dd5bb..79fcff64 100644 --- a/src/ObjLoading/Game/IW4/Shader/LoaderVertexShaderIW4.cpp +++ b/src/ObjLoading/Game/IW4/Shader/LoaderVertexShaderIW4.cpp @@ -1,6 +1,8 @@ #include "LoaderVertexShaderIW4.h" #include "Game/IW4/IW4.h" +#include "Shader/ShaderCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -21,14 +23,14 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = GetVertexShaderFileName(assetName); + const auto fileName = shader::GetFileNameForVertexShaderAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); if (file.m_length % sizeof(uint32_t) != 0) { - std::cerr << std::format("Invalid vertex shader \"{}\": Size must be dividable by {}\n", assetName, sizeof(uint32_t)); + con::error("Invalid vertex shader \"{}\": Size must be dividable by {}", assetName, sizeof(uint32_t)); return AssetCreationResult::Failure(); } @@ -53,15 +55,10 @@ namespace }; } // namespace -namespace IW4 +namespace shader { - std::string GetVertexShaderFileName(const std::string& vertexShaderAssetName) - { - return std::format("shader_bin/vs_{}.cso", vertexShaderAssetName); - } - - std::unique_ptr> CreateVertexShaderLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateVertexShaderLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace shader diff --git a/src/ObjLoading/Game/IW4/Shader/LoaderVertexShaderIW4.h b/src/ObjLoading/Game/IW4/Shader/LoaderVertexShaderIW4.h index e8139af0..28076aaf 100644 --- a/src/ObjLoading/Game/IW4/Shader/LoaderVertexShaderIW4.h +++ b/src/ObjLoading/Game/IW4/Shader/LoaderVertexShaderIW4.h @@ -7,9 +7,7 @@ #include -namespace IW4 +namespace shader { - [[nodiscard]] std::string GetVertexShaderFileName(const std::string& vertexShaderAssetName); - - std::unique_ptr> CreateVertexShaderLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr> CreateVertexShaderLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace shader diff --git a/src/ObjLoading/Game/IW4/Sound/LoaderSoundCurveIW4.cpp b/src/ObjLoading/Game/IW4/Sound/LoaderSoundCurveIW4.cpp index 5f760754..e6aeeab4 100644 --- a/src/ObjLoading/Game/IW4/Sound/LoaderSoundCurveIW4.cpp +++ b/src/ObjLoading/Game/IW4/Sound/LoaderSoundCurveIW4.cpp @@ -4,6 +4,8 @@ #include "ObjLoading.h" #include "Parsing/Graph2D/Graph2DReader.h" #include "Pool/GlobalAssetPool.h" +#include "Sound/SoundCurveCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -25,7 +27,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("soundaliases/{}.vfcurve", assetName); + const auto fileName = sound_curve::GetFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -37,7 +39,7 @@ namespace if (sndCurveData->knots.size() > std::extent_v) { - std::cerr << std::format("Failed to load SndCurve \"{}\": Too many knots ({})\n", assetName, sndCurveData->knots.size()); + con::error("Failed to load SndCurve \"{}\": Too many knots ({})", assetName, sndCurveData->knots.size()); return AssetCreationResult::Failure(); } @@ -69,10 +71,10 @@ namespace }; } // namespace -namespace IW4 +namespace sound_curve { - std::unique_ptr> CreateSoundCurveLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace sound_curve diff --git a/src/ObjLoading/Game/IW4/Sound/LoaderSoundCurveIW4.h b/src/ObjLoading/Game/IW4/Sound/LoaderSoundCurveIW4.h index 19f1bb3c..b8b12cd3 100644 --- a/src/ObjLoading/Game/IW4/Sound/LoaderSoundCurveIW4.h +++ b/src/ObjLoading/Game/IW4/Sound/LoaderSoundCurveIW4.h @@ -7,7 +7,7 @@ #include -namespace IW4 +namespace sound_curve { - std::unique_ptr> CreateSoundCurveLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace sound_curve diff --git a/src/ObjLoading/Game/IW4/StringTable/LoaderStringTableIW4.cpp b/src/ObjLoading/Game/IW4/StringTable/LoaderStringTableIW4.cpp index d4530772..11483031 100644 --- a/src/ObjLoading/Game/IW4/StringTable/LoaderStringTableIW4.cpp +++ b/src/ObjLoading/Game/IW4/StringTable/LoaderStringTableIW4.cpp @@ -38,10 +38,10 @@ namespace }; } // namespace -namespace IW4 +namespace string_table { - std::unique_ptr> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace string_table diff --git a/src/ObjLoading/Game/IW4/StringTable/LoaderStringTableIW4.h b/src/ObjLoading/Game/IW4/StringTable/LoaderStringTableIW4.h index c9cbd55f..89c53c50 100644 --- a/src/ObjLoading/Game/IW4/StringTable/LoaderStringTableIW4.h +++ b/src/ObjLoading/Game/IW4/StringTable/LoaderStringTableIW4.h @@ -7,7 +7,7 @@ #include -namespace IW4 +namespace string_table { - std::unique_ptr> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace string_table diff --git a/src/ObjLoading/Game/IW4/StructuredDataDef/LoaderStructuredDataDefIW4.cpp b/src/ObjLoading/Game/IW4/StructuredDataDef/LoaderStructuredDataDefIW4.cpp index 6f662ab2..673d715d 100644 --- a/src/ObjLoading/Game/IW4/StructuredDataDef/LoaderStructuredDataDefIW4.cpp +++ b/src/ObjLoading/Game/IW4/StructuredDataDef/LoaderStructuredDataDefIW4.cpp @@ -207,10 +207,10 @@ namespace }; } // namespace -namespace IW4 +namespace structured_data_def { - std::unique_ptr> CreateStructuredDataDefLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW4 +} // namespace structured_data_def diff --git a/src/ObjLoading/Game/IW4/StructuredDataDef/LoaderStructuredDataDefIW4.h b/src/ObjLoading/Game/IW4/StructuredDataDef/LoaderStructuredDataDefIW4.h index 22d40f09..527a9389 100644 --- a/src/ObjLoading/Game/IW4/StructuredDataDef/LoaderStructuredDataDefIW4.h +++ b/src/ObjLoading/Game/IW4/StructuredDataDef/LoaderStructuredDataDefIW4.h @@ -7,7 +7,7 @@ #include -namespace IW4 +namespace structured_data_def { - std::unique_ptr> CreateStructuredDataDefLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW4 + std::unique_ptr> CreateLoaderIW4(MemoryManager& memory, ISearchPath& searchPath); +} // namespace structured_data_def diff --git a/src/ObjLoading/Game/IW4/Weapon/GdtLoaderWeaponIW4.cpp b/src/ObjLoading/Game/IW4/Weapon/GdtLoaderWeaponIW4.cpp index 9b585add..b8fbf7bf 100644 --- a/src/ObjLoading/Game/IW4/Weapon/GdtLoaderWeaponIW4.cpp +++ b/src/ObjLoading/Game/IW4/Weapon/GdtLoaderWeaponIW4.cpp @@ -4,6 +4,7 @@ #include "Game/IW4/ObjConstantsIW4.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderWeaponIW4.h" +#include "Utils/Logging/Log.h" #include #include @@ -31,7 +32,7 @@ namespace InfoString infoString; if (!infoString.FromGdtProperties(*gdtEntry)) { - std::cerr << std::format("Failed to read weapon gdt entry: \"{}\"\n", assetName); + con::error("Failed to read weapon gdt entry: \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -40,14 +41,14 @@ namespace private: IGdtQueryable& m_gdt; - InfoStringLoaderWeapon m_info_string_loader; + weapon::InfoStringLoaderIW4 m_info_string_loader; }; } // namespace -namespace IW4 +namespace weapon { - std::unique_ptr> CreateGdtWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + std::unique_ptr> CreateGdtLoaderIW4(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) { return std::make_unique(memory, searchPath, gdt, zone); } -} // namespace IW4 +} // namespace weapon diff --git a/src/ObjLoading/Game/IW4/Weapon/GdtLoaderWeaponIW4.h b/src/ObjLoading/Game/IW4/Weapon/GdtLoaderWeaponIW4.h index 44a699b8..6fcac0a4 100644 --- a/src/ObjLoading/Game/IW4/Weapon/GdtLoaderWeaponIW4.h +++ b/src/ObjLoading/Game/IW4/Weapon/GdtLoaderWeaponIW4.h @@ -8,7 +8,7 @@ #include -namespace IW4 +namespace weapon { - std::unique_ptr> CreateGdtWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); -} // namespace IW4 + std::unique_ptr> CreateGdtLoaderIW4(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); +} // namespace weapon diff --git a/src/ObjLoading/Game/IW4/Weapon/InfoStringLoaderWeaponIW4.cpp b/src/ObjLoading/Game/IW4/Weapon/InfoStringLoaderWeaponIW4.cpp index e44ef3c2..6bfe7f07 100644 --- a/src/ObjLoading/Game/IW4/Weapon/InfoStringLoaderWeaponIW4.cpp +++ b/src/ObjLoading/Game/IW4/Weapon/InfoStringLoaderWeaponIW4.cpp @@ -4,6 +4,7 @@ #include "Game/IW4/InfoString/EnumStrings.h" #include "Game/IW4/InfoString/InfoStringToStructConverter.h" #include "Game/IW4/Weapon/WeaponFields.h" +#include "Utils/Logging/Log.h" #include "Weapon/AccuracyGraphLoader.h" #include @@ -23,13 +24,13 @@ namespace std::vector valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse hide tags as array\n"; + con::error("Failed to parse hide tags as array"); return false; } if (valueArray.size() > std::extent_v) { - std::cerr << std::format("Cannot have more than {} hide tags!\n", std::extent_v); + con::error("Cannot have more than {} hide tags!", std::extent_v); return false; } @@ -85,14 +86,13 @@ namespace std::vector> pairs; if (!ParseAsArray(value, pairs)) { - std::cerr << std::format("Failed to parse notetrack{}map as pairs\n", mapName); + con::error("Failed to parse notetrack{}map as pairs", mapName); return false; } if (pairs.size() > std::extent_v) { - std::cerr << std::format( - "Cannot have more than {} notetrack{}map entries!\n", std::extent_v, mapName); + con::error("Cannot have more than {} notetrack{}map entries!", std::extent_v, mapName); return false; } @@ -267,28 +267,27 @@ namespace void CheckProjectileValues(const WeaponCompleteDef& weaponCompleteDef, const WeaponDef& weaponDef) { if (weaponDef.iProjectileSpeed <= 0) - std::cerr << std::format("Projectile speed for WeapType {} must be greater than 0.0", weaponCompleteDef.szDisplayName); + con::error("Projectile speed for WeapType {} must be greater than 0.0", weaponCompleteDef.szDisplayName); if (weaponDef.destabilizationCurvatureMax >= 1000000000.0f || weaponDef.destabilizationCurvatureMax < 0.0f) - std::cerr << std::format("Destabilization angle for for WeapType {} must be between 0 and 45 degrees", weaponCompleteDef.szDisplayName); + con::error("Destabilization angle for for WeapType {} must be between 0 and 45 degrees", weaponCompleteDef.szDisplayName); if (weaponDef.destabilizationRateTime < 0.0f) - std::cerr << std::format("Destabilization rate time for for WeapType {} must be non-negative", weaponCompleteDef.szDisplayName); + con::error("Destabilization rate time for for WeapType {} must be non-negative", weaponCompleteDef.szDisplayName); } void CheckTurretBarrelSpin(const WeaponCompleteDef& weaponCompleteDef, const WeaponDef& weaponDef) { if (weaponDef.weapClass != WEAPCLASS_TURRET) - std::cerr << std::format("Rotating barrel set for non-turret weapon {}.", weaponCompleteDef.szInternalName); + con::error("Rotating barrel set for non-turret weapon {}.", weaponCompleteDef.szInternalName); if (0.0f == weaponDef.turretBarrelSpinSpeed) { - std::cerr << std::format( - "Rotating barrel spin speed '{}' is invalid for weapon {}.", weaponDef.turretBarrelSpinSpeed, weaponCompleteDef.szInternalName); + con::error("Rotating barrel spin speed '{}' is invalid for weapon {}.", weaponDef.turretBarrelSpinSpeed, weaponCompleteDef.szInternalName); } if (0.0f < weaponDef.turretOverheatUpRate && 0.0f >= weaponDef.turretOverheatDownRate) { - std::cerr << std::format("Turret overheat Up rate is set, but the down rate '{}' is invalid for weapon {}.", - weaponDef.turretOverheatDownRate, - weaponCompleteDef.szInternalName); + con::error("Turret overheat Up rate is set, but the down rate '{}' is invalid for weapon {}.", + weaponDef.turretOverheatDownRate, + weaponCompleteDef.szInternalName); } } @@ -296,18 +295,18 @@ namespace { if (0.0f != weaponDef.fAdsZoomInFrac) { - std::cerr << std::format("Weapon {} ({}) has thermal scope set. ADS Zoom In frac should be 0 to prevent zoom-in blur ({}).\n", - weaponCompleteDef.szInternalName, - weaponCompleteDef.szDisplayName, - weaponDef.fAdsZoomInFrac); + con::error("Weapon {} ({}) has thermal scope set. ADS Zoom In frac should be 0 to prevent zoom-in blur ({}).", + weaponCompleteDef.szInternalName, + weaponCompleteDef.szDisplayName, + weaponDef.fAdsZoomInFrac); } if (0.0f != weaponDef.fAdsZoomOutFrac) { - std::cerr << std::format("Weapon {} ({}) has thermal scope set. ADS Zoom Out frac should be 0 to prevent zoom-out blur ({}).\n", - weaponCompleteDef.szInternalName, - weaponCompleteDef.szDisplayName, - weaponDef.fAdsZoomOutFrac); + con::error("Weapon {} ({}) has thermal scope set. ADS Zoom Out frac should be 0 to prevent zoom-out blur ({}).", + weaponCompleteDef.szInternalName, + weaponCompleteDef.szDisplayName, + weaponDef.fAdsZoomOutFrac); } } @@ -337,7 +336,7 @@ namespace weaponDef.fMinDamageRange = 999999.12f; if (weaponDef.enemyCrosshairRange > 15000.0f) - std::cerr << std::format("Enemy crosshair ranges should be less than {}\n", 15000.0f); + con::error("Enemy crosshair ranges should be less than {}", 15000.0f); if (weaponDef.weapType == WEAPTYPE_PROJECTILE) CheckProjectileValues(weaponCompleteDef, weaponDef); @@ -350,22 +349,21 @@ namespace if (weaponDef.offhandClass && !weaponDef.bClipOnly) { - std::cerr << std::format( - "Weapon {} ({}) is an offhand weapon but is not set to clip only, which is not supported since we can't reload the offhand slot.\n", - weaponCompleteDef.szInternalName, - weaponCompleteDef.szDisplayName); + con::error("Weapon {} ({}) is an offhand weapon but is not set to clip only, which is not supported since we can't reload the offhand slot.", + weaponCompleteDef.szInternalName, + weaponCompleteDef.szDisplayName); } if (weaponDef.weapType == WEAPTYPE_BULLET) { if (weaponDef.bulletExplDmgMult <= 0.0f) - std::cerr << std::format("Detected invalid bulletExplDmgMult of '{}' for weapon '{}'; please update weapon settings.\n", - weaponDef.bulletExplDmgMult, - weaponCompleteDef.szInternalName); + con::error("Detected invalid bulletExplDmgMult of '{}' for weapon '{}'; please update weapon settings.", + weaponDef.bulletExplDmgMult, + weaponCompleteDef.szInternalName); if (weaponDef.bulletExplRadiusMult <= 0.0f) - std::cerr << std::format("Detected invalid bulletExplRadiusMult of '{}' for weapon '{}'; please update weapon settings.\n", - weaponDef.bulletExplRadiusMult, - weaponCompleteDef.szInternalName); + con::error("Detected invalid bulletExplRadiusMult of '{}' for weapon '{}'; please update weapon settings.", + weaponDef.bulletExplRadiusMult, + weaponCompleteDef.szInternalName); } } @@ -426,33 +424,36 @@ namespace } } // namespace -InfoStringLoaderWeapon::InfoStringLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) +namespace weapon { -} - -AssetCreationResult InfoStringLoaderWeapon::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) -{ - auto* weaponFullDef = m_memory.Alloc(); - - InitWeaponFullDef(*weaponFullDef); - weaponFullDef->weapCompleteDef.szInternalName = m_memory.Dup(assetName.c_str()); - - AssetRegistration registration(assetName, &weaponFullDef->weapCompleteDef); - - InfoStringToWeaponConverter converter( - infoString, *weaponFullDef, m_zone.m_script_strings, m_memory, context, registration, weapon_fields, std::extent_v); - if (!converter.Convert()) + InfoStringLoaderIW4::InfoStringLoaderIW4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone) { - std::cerr << std::format("Failed to parse weapon: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - CalculateWeaponFields(*weaponFullDef, m_memory); + AssetCreationResult InfoStringLoaderIW4::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + auto* weaponFullDef = m_memory.Alloc(); - LoadAccuracyGraphs(*weaponFullDef, m_memory, m_search_path, context); + InitWeaponFullDef(*weaponFullDef); + weaponFullDef->weapCompleteDef.szInternalName = m_memory.Dup(assetName.c_str()); - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + AssetRegistration registration(assetName, &weaponFullDef->weapCompleteDef); + + InfoStringToWeaponConverter converter( + infoString, *weaponFullDef, m_zone.m_script_strings, m_memory, context, registration, weapon_fields, std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse weapon: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + CalculateWeaponFields(*weaponFullDef, m_memory); + + LoadAccuracyGraphs(*weaponFullDef, m_memory, m_search_path, context); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace weapon diff --git a/src/ObjLoading/Game/IW4/Weapon/InfoStringLoaderWeaponIW4.h b/src/ObjLoading/Game/IW4/Weapon/InfoStringLoaderWeaponIW4.h index 59204d85..1f82539b 100644 --- a/src/ObjLoading/Game/IW4/Weapon/InfoStringLoaderWeaponIW4.h +++ b/src/ObjLoading/Game/IW4/Weapon/InfoStringLoaderWeaponIW4.h @@ -4,12 +4,12 @@ #include "Asset/AssetCreationResult.h" #include "InfoString/InfoString.h" -namespace IW4 +namespace weapon { - class InfoStringLoaderWeapon + class InfoStringLoaderIW4 { public: - InfoStringLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + InfoStringLoaderIW4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); @@ -18,4 +18,4 @@ namespace IW4 ISearchPath& m_search_path; Zone& m_zone; }; -} // namespace IW4 +} // namespace weapon diff --git a/src/ObjLoading/Game/IW4/Weapon/RawLoaderWeaponIW4.cpp b/src/ObjLoading/Game/IW4/Weapon/RawLoaderWeaponIW4.cpp index a1b7b656..a2382743 100644 --- a/src/ObjLoading/Game/IW4/Weapon/RawLoaderWeaponIW4.cpp +++ b/src/ObjLoading/Game/IW4/Weapon/RawLoaderWeaponIW4.cpp @@ -4,6 +4,8 @@ #include "Game/IW4/ObjConstantsIW4.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderWeaponIW4.h" +#include "Utils/Logging/Log.h" +#include "Weapon/WeaponCommon.h" #include #include @@ -24,7 +26,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("weapons/{}", assetName); + const auto fileName = weapon::GetFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -32,7 +34,7 @@ namespace InfoString infoString; if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_WEAPON, *file.m_stream)) { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); + con::error("Could not parse as info string file: \"{}\"", fileName); return AssetCreationResult::Failure(); } @@ -41,14 +43,14 @@ namespace private: ISearchPath& m_search_path; - InfoStringLoaderWeapon m_info_string_loader; + weapon::InfoStringLoaderIW4 m_info_string_loader; }; } // namespace -namespace IW4 +namespace weapon { - std::unique_ptr> CreateRawWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateRawLoaderIW4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace IW4 +} // namespace weapon diff --git a/src/ObjLoading/Game/IW4/Weapon/RawLoaderWeaponIW4.h b/src/ObjLoading/Game/IW4/Weapon/RawLoaderWeaponIW4.h index 786d7ce3..208fc440 100644 --- a/src/ObjLoading/Game/IW4/Weapon/RawLoaderWeaponIW4.h +++ b/src/ObjLoading/Game/IW4/Weapon/RawLoaderWeaponIW4.h @@ -7,7 +7,7 @@ #include -namespace IW4 +namespace weapon { - std::unique_ptr> CreateRawWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace IW4 + std::unique_ptr> CreateRawLoaderIW4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace weapon diff --git a/src/ObjLoading/Game/IW5/Image/LoaderImageIW5.cpp b/src/ObjLoading/Game/IW5/Image/LoaderImageIW5.cpp index f30d27e4..a7f68cf4 100644 --- a/src/ObjLoading/Game/IW5/Image/LoaderImageIW5.cpp +++ b/src/ObjLoading/Game/IW5/Image/LoaderImageIW5.cpp @@ -1,7 +1,9 @@ #include "LoaderImageIW5.h" #include "Game/IW5/IW5.h" +#include "Image/ImageCommon.h" #include "Image/IwiLoader.h" +#include "Utils/Logging/Log.h" #include #include @@ -25,7 +27,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("images/{}.iwi", assetName); + const auto fileName = image::GetFileNameForAsset(assetName, ".iwi"); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -38,7 +40,7 @@ namespace const auto texture = iwi::LoadIwi(ss); if (!texture) { - std::cerr << std::format("Failed to load texture from: {}\n", fileName); + con::error("Failed to load texture from: {}", fileName); return AssetCreationResult::Failure(); } @@ -60,10 +62,10 @@ namespace }; } // namespace -namespace IW5 +namespace image { - std::unique_ptr> CreateImageLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW5 +} // namespace image diff --git a/src/ObjLoading/Game/IW5/Image/LoaderImageIW5.h b/src/ObjLoading/Game/IW5/Image/LoaderImageIW5.h index 66609137..b95a8f23 100644 --- a/src/ObjLoading/Game/IW5/Image/LoaderImageIW5.h +++ b/src/ObjLoading/Game/IW5/Image/LoaderImageIW5.h @@ -7,7 +7,7 @@ #include -namespace IW5 +namespace image { - std::unique_ptr> CreateImageLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW5 + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace image diff --git a/src/ObjLoading/Game/IW5/InfoString/InfoStringToStructConverter.cpp b/src/ObjLoading/Game/IW5/InfoString/InfoStringToStructConverter.cpp index 63be576b..0a07a98e 100644 --- a/src/ObjLoading/Game/IW5/InfoString/InfoStringToStructConverter.cpp +++ b/src/ObjLoading/Game/IW5/InfoString/InfoStringToStructConverter.cpp @@ -1,5 +1,7 @@ #include "InfoStringToStructConverter.h" +#include "Utils/Logging/Log.h" + #include #include #include @@ -63,7 +65,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (fx == nullptr) { - std::cerr << std::format("Failed to load fx asset \"{}\"\n", value); + con::error("Failed to load fx asset \"{}\"", value); return false; } @@ -85,7 +87,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (xmodel == nullptr) { - std::cerr << std::format("Failed to load xmodel asset \"{}\"\n", value); + con::error("Failed to load xmodel asset \"{}\"", value); return false; } @@ -107,7 +109,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (material == nullptr) { - std::cerr << std::format("Failed to load material asset \"{}\"\n", value); + con::error("Failed to load material asset \"{}\"", value); return false; } @@ -129,7 +131,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (tracer == nullptr) { - std::cerr << std::format("Failed to load tracer asset \"{}\"\n", value); + con::error("Failed to load tracer asset \"{}\"", value); return false; } @@ -146,7 +148,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (endPtr != &value[value.size()]) { - std::cout << "Failed to parse value \"" << value << "\" as mph\n"; + con::error("Failed to parse value \"{}\" as mph", value); return false; } @@ -165,7 +167,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (collmap == nullptr) { - std::cerr << std::format("Failed to load collmap asset \"{}\"\n", value); + con::error("Failed to load collmap asset \"{}\"", value); return false; } diff --git a/src/ObjLoading/Game/IW5/Leaderboard/JsonLeaderboardDefLoader.cpp b/src/ObjLoading/Game/IW5/Leaderboard/JsonLeaderboardDefLoader.cpp deleted file mode 100644 index 5c896d66..00000000 --- a/src/ObjLoading/Game/IW5/Leaderboard/JsonLeaderboardDefLoader.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "JsonLeaderboardDefLoader.h" - -#include "Game/IW5/CommonIW5.h" -#include "Game/IW5/Leaderboard/JsonLeaderboardDef.h" - -#include -#include -#include - -using namespace nlohmann; -using namespace IW5; - -namespace -{ - class JsonLoader - { - public: - JsonLoader(std::istream& stream, MemoryManager& memory) - : m_stream(stream), - m_memory(memory) - { - } - - bool Load(LeaderboardDef& leaderboardDef) const - { - const auto jRoot = json::parse(m_stream); - std::string type; - unsigned version; - - jRoot.at("_type").get_to(type); - jRoot.at("_version").get_to(version); - - if (type != "leaderboard" || version != 1u) - { - std::cerr << std::format("Tried to load leaderboard \"{}\" but did not find expected type leaderboard of version 1\n", leaderboardDef.name); - return false; - } - - try - { - const auto jLeaderboard = jRoot.get(); - return CreateLeaderboardFromJson(jLeaderboard, leaderboardDef); - } - catch (const json::exception& e) - { - std::cerr << std::format("Failed to parse json of leaderboard: {}\n", e.what()); - } - - return false; - } - - private: - static bool CreateTrackTypeFlagsFromJson(const JsonLeaderboardDef& jLeaderboardDef, int& trackTypeFlags) - { - for (const auto trackType : jLeaderboardDef.trackTypes) - trackTypeFlags |= 1 << trackType; - - return true; - } - - bool CreateColumnDefFromJson(const JsonColumnDef& jColumn, LbColumnDef& lbColumnDef, LeaderboardDef& leaderboardDef) const - { - lbColumnDef.name = m_memory.Dup(jColumn.name.c_str()); - - lbColumnDef.id = jColumn.colId; - lbColumnDef.propertyId = jColumn.propertyId.value_or(0); - lbColumnDef.hidden = jColumn.hidden.value_or(false); - - if (jColumn.statName) - lbColumnDef.statName = m_memory.Dup(jColumn.statName->c_str()); - else - lbColumnDef.statName = nullptr; - - lbColumnDef.type = jColumn.type; - - lbColumnDef.precision = jColumn.precision.value_or(0); - - lbColumnDef.agg = jColumn.aggregationFunction; - - lbColumnDef.uiCalColX = jColumn.uiCalColX.value_or(0); - lbColumnDef.uiCalColY = jColumn.uiCalColY.value_or(0); - - return true; - } - - bool CreateLeaderboardFromJson(const JsonLeaderboardDef& jLeaderboardDef, LeaderboardDef& leaderboardDef) const - { - leaderboardDef.id = jLeaderboardDef.id; - - leaderboardDef.xpColId = jLeaderboardDef.xpColId.value_or(-1); - leaderboardDef.prestigeColId = jLeaderboardDef.prestigeColId.value_or(-1); - - if (!jLeaderboardDef.columns.empty()) - { - leaderboardDef.columnCount = static_cast(jLeaderboardDef.columns.size()); - leaderboardDef.columns = m_memory.Alloc(leaderboardDef.columnCount); - - for (auto i = 0; i < leaderboardDef.columnCount; i++) - { - if (!CreateColumnDefFromJson(jLeaderboardDef.columns[i], leaderboardDef.columns[i], leaderboardDef)) - return false; - } - } - else - { - leaderboardDef.columnCount = 0; - leaderboardDef.columns = nullptr; - } - - leaderboardDef.updateType = jLeaderboardDef.updateType; - - if (!CreateTrackTypeFlagsFromJson(jLeaderboardDef, leaderboardDef.trackTypes)) - return false; - - return true; - } - - std::istream& m_stream; - MemoryManager& m_memory; - }; -} // namespace - -namespace IW5 -{ - bool LoadLeaderboardAsJson(std::istream& stream, LeaderboardDef& leaderboard, MemoryManager& memory) - { - const JsonLoader loader(stream, memory); - - return loader.Load(leaderboard); - } -} // namespace IW5 diff --git a/src/ObjLoading/Game/IW5/Leaderboard/JsonLeaderboardDefLoader.h b/src/ObjLoading/Game/IW5/Leaderboard/JsonLeaderboardDefLoader.h deleted file mode 100644 index 753a5db3..00000000 --- a/src/ObjLoading/Game/IW5/Leaderboard/JsonLeaderboardDefLoader.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Game/IW5/IW5.h" -#include "Utils/MemoryManager.h" - -#include - -namespace IW5 -{ - bool LoadLeaderboardAsJson(std::istream& stream, LeaderboardDef& leaderboard, MemoryManager& memory); -} // namespace IW5 diff --git a/src/ObjLoading/Game/IW5/Leaderboard/LoaderLeaderboardIW5.cpp b/src/ObjLoading/Game/IW5/Leaderboard/LoaderLeaderboardIW5.cpp index c18ed86b..293740be 100644 --- a/src/ObjLoading/Game/IW5/Leaderboard/LoaderLeaderboardIW5.cpp +++ b/src/ObjLoading/Game/IW5/Leaderboard/LoaderLeaderboardIW5.cpp @@ -1,16 +1,127 @@ #include "LoaderLeaderboardIW5.h" #include "Game/IW5/IW5.h" -#include "JsonLeaderboardDefLoader.h" +#include "Game/IW5/Leaderboard/JsonLeaderboardDef.h" +#include "Leaderboard/LeaderboardCommon.h" +#include "Utils/Logging/Log.h" #include #include #include +#include +using namespace nlohmann; using namespace IW5; namespace { + class JsonLoader + { + public: + JsonLoader(std::istream& stream, MemoryManager& memory) + : m_stream(stream), + m_memory(memory) + { + } + + bool Load(LeaderboardDef& leaderboardDef) const + { + try + { + const auto jRoot = json::parse(m_stream); + std::string type; + unsigned version; + + jRoot.at("_type").get_to(type); + jRoot.at("_version").get_to(version); + + if (type != "leaderboard" || version != 1u) + { + con::error("Tried to load leaderboard \"{}\" but did not find expected type leaderboard of version 1", leaderboardDef.name); + return false; + } + + const auto jLeaderboard = jRoot.get(); + return CreateLeaderboardFromJson(jLeaderboard, leaderboardDef); + } + catch (const json::exception& e) + { + con::error("Failed to parse json of leaderboard: {}", e.what()); + } + + return false; + } + + private: + static bool CreateTrackTypeFlagsFromJson(const JsonLeaderboardDef& jLeaderboardDef, int& trackTypeFlags) + { + for (const auto trackType : jLeaderboardDef.trackTypes) + trackTypeFlags |= 1 << trackType; + + return true; + } + + bool CreateColumnDefFromJson(const JsonColumnDef& jColumn, LbColumnDef& lbColumnDef, LeaderboardDef& leaderboardDef) const + { + lbColumnDef.name = m_memory.Dup(jColumn.name.c_str()); + + lbColumnDef.id = jColumn.colId; + lbColumnDef.propertyId = jColumn.propertyId.value_or(0); + lbColumnDef.hidden = jColumn.hidden.value_or(false); + + if (jColumn.statName) + lbColumnDef.statName = m_memory.Dup(jColumn.statName->c_str()); + else + lbColumnDef.statName = nullptr; + + lbColumnDef.type = jColumn.type; + + lbColumnDef.precision = jColumn.precision.value_or(0); + + lbColumnDef.agg = jColumn.aggregationFunction; + + lbColumnDef.uiCalColX = jColumn.uiCalColX.value_or(0); + lbColumnDef.uiCalColY = jColumn.uiCalColY.value_or(0); + + return true; + } + + bool CreateLeaderboardFromJson(const JsonLeaderboardDef& jLeaderboardDef, LeaderboardDef& leaderboardDef) const + { + leaderboardDef.id = jLeaderboardDef.id; + + leaderboardDef.xpColId = jLeaderboardDef.xpColId.value_or(-1); + leaderboardDef.prestigeColId = jLeaderboardDef.prestigeColId.value_or(-1); + + if (!jLeaderboardDef.columns.empty()) + { + leaderboardDef.columnCount = static_cast(jLeaderboardDef.columns.size()); + leaderboardDef.columns = m_memory.Alloc(leaderboardDef.columnCount); + + for (auto i = 0; i < leaderboardDef.columnCount; i++) + { + if (!CreateColumnDefFromJson(jLeaderboardDef.columns[i], leaderboardDef.columns[i], leaderboardDef)) + return false; + } + } + else + { + leaderboardDef.columnCount = 0; + leaderboardDef.columns = nullptr; + } + + leaderboardDef.updateType = jLeaderboardDef.updateType; + + if (!CreateTrackTypeFlagsFromJson(jLeaderboardDef, leaderboardDef.trackTypes)) + return false; + + return true; + } + + std::istream& m_stream; + MemoryManager& m_memory; + }; + class LeaderboardLoader final : public AssetCreator { public: @@ -22,16 +133,17 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto file = m_search_path.Open(std::format("leaderboards/{}.json", assetName)); + const auto file = m_search_path.Open(leaderboard::GetJsonFileNameForAsset(assetName)); if (!file.IsOpen()) return AssetCreationResult::NoAction(); auto* leaderboardDef = m_memory.Alloc(); leaderboardDef->name = m_memory.Dup(assetName.c_str()); - if (!LoadLeaderboardAsJson(*file.m_stream, *leaderboardDef, m_memory)) + const JsonLoader loader(*file.m_stream, m_memory); + if (!loader.Load(*leaderboardDef)) { - std::cerr << std::format("Failed to load leaderboard \"{}\"\n", assetName); + con::error("Failed to load leaderboard \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -44,10 +156,10 @@ namespace }; } // namespace -namespace IW5 +namespace leaderboard { - std::unique_ptr> CreateLeaderboardLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW5 +} // namespace leaderboard diff --git a/src/ObjLoading/Game/IW5/Leaderboard/LoaderLeaderboardIW5.h b/src/ObjLoading/Game/IW5/Leaderboard/LoaderLeaderboardIW5.h index 6c144840..d2841638 100644 --- a/src/ObjLoading/Game/IW5/Leaderboard/LoaderLeaderboardIW5.h +++ b/src/ObjLoading/Game/IW5/Leaderboard/LoaderLeaderboardIW5.h @@ -7,7 +7,7 @@ #include -namespace IW5 +namespace leaderboard { - std::unique_ptr> CreateLeaderboardLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW5 + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace leaderboard diff --git a/src/ObjLoading/Game/IW5/Localize/LoaderLocalizeIW5.cpp b/src/ObjLoading/Game/IW5/Localize/LoaderLocalizeIW5.cpp index a531c42f..a7512a26 100644 --- a/src/ObjLoading/Game/IW5/Localize/LoaderLocalizeIW5.cpp +++ b/src/ObjLoading/Game/IW5/Localize/LoaderLocalizeIW5.cpp @@ -35,10 +35,10 @@ namespace }; } // namespace -namespace IW5 +namespace localize { - std::unique_ptr> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace IW5 +} // namespace localize diff --git a/src/ObjLoading/Game/IW5/Localize/LoaderLocalizeIW5.h b/src/ObjLoading/Game/IW5/Localize/LoaderLocalizeIW5.h index 6c47a8d1..f161e6c6 100644 --- a/src/ObjLoading/Game/IW5/Localize/LoaderLocalizeIW5.h +++ b/src/ObjLoading/Game/IW5/Localize/LoaderLocalizeIW5.h @@ -8,7 +8,7 @@ #include -namespace IW5 +namespace localize { - std::unique_ptr> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace IW5 + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace localize diff --git a/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.cpp b/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.cpp index 189e368f..d41f0af2 100644 --- a/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.cpp +++ b/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.cpp @@ -3,6 +3,7 @@ #include "Game/IW5/IW5.h" #include "Game/IW5/Material/JsonMaterialLoaderIW5.h" #include "Material/MaterialCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -32,7 +33,7 @@ namespace AssetRegistration registration(assetName, material); if (!LoadMaterialAsJson(*file.m_stream, *material, m_memory, context, registration)) { - std::cerr << std::format("Failed to load material \"{}\"\n", assetName); + con::error("Failed to load material \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -45,10 +46,10 @@ namespace }; } // namespace -namespace IW5 +namespace material { - std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW5 +} // namespace material diff --git a/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.h b/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.h index 5488c1a7..ebfaf88d 100644 --- a/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.h +++ b/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.h @@ -7,7 +7,7 @@ #include -namespace IW5 +namespace material { - std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW5 + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace material diff --git a/src/ObjLoading/Game/IW5/Menu/LoaderMenuListIW5.cpp b/src/ObjLoading/Game/IW5/Menu/LoaderMenuListIW5.cpp index 46e6938c..b16d7fcf 100644 --- a/src/ObjLoading/Game/IW5/Menu/LoaderMenuListIW5.cpp +++ b/src/ObjLoading/Game/IW5/Menu/LoaderMenuListIW5.cpp @@ -5,6 +5,7 @@ #include "Game/IW5/Menu/MenuConverterIW5.h" #include "ObjLoading.h" #include "Parsing/Menu/MenuFileReader.h" +#include "Utils/Logging/Log.h" #include #include @@ -88,7 +89,7 @@ namespace const auto alreadyLoadedMenuFile = conversionState.m_menus_by_filename.find(menuFilePath); if (alreadyLoadedMenuFile != conversionState.m_menus_by_filename.end()) { - std::cout << std::format("Already loaded \"{}\", skipping\n", menuFilePath); + con::debug("Already loaded \"{}\", skipping", menuFilePath); for (auto* menu : alreadyLoadedMenuFile->second) { menus.emplace_back(menu->Asset()); @@ -100,7 +101,7 @@ namespace const auto file = m_search_path.Open(menuFilePath); if (!file.IsOpen()) { - std::cerr << std::format("Could not open menu file \"{}\"\n", menuFilePath); + con::error("Could not open menu file \"{}\"", menuFilePath); return false; } @@ -109,12 +110,12 @@ namespace { ProcessParsedResults(menuFilePath, context, *menuFileResult, zoneState, conversionState, menus, registration); if (!menuFileResult->m_menus_to_load.empty()) - std::cout << std::format("WARNING: Menu file has menus to load even though it is not a menu list, ignoring: \"{}\"\n", menuFilePath); + con::warn("Menu file has menus to load even though it is not a menu list, ignoring: \"{}\"", menuFilePath); return true; } else - std::cerr << std::format("Could not read menu file \"{}\"\n", menuFilePath); + con::error("Could not read menu file \"{}\"", menuFilePath); return false; } @@ -134,12 +135,12 @@ namespace for (const auto& menu : parsingResult.m_menus) totalItemCount += static_cast(menu->m_items.size()); - std::cout << std::format("Successfully read menu file \"{}\" ({} loads, {} menus, {} functions, {} items)\n", - fileName, - menuLoadCount, - menuCount, - functionCount, - totalItemCount); + con::info("Successfully read menu file \"{}\" ({} loads, {} menus, {} functions, {} items)", + fileName, + menuLoadCount, + menuCount, + functionCount, + totalItemCount); // Add all functions to the zone state to make them available for all menus to be converted for (auto& function : parsingResult.m_functions) @@ -160,7 +161,7 @@ namespace converter->ConvertMenu(*commonMenu, *menuAsset, menuRegistration); if (menuAsset == nullptr) { - std::cerr << std::format("Failed to convert menu file \"{}\"\n", commonMenu->m_name); + con::error("Failed to convert menu file \"{}\"", commonMenu->m_name); return false; } @@ -212,10 +213,10 @@ namespace }; } // namespace -namespace IW5 +namespace menu { - std::unique_ptr> CreateMenuListLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateMenuListLoaderIW5(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW5 +} // namespace menu diff --git a/src/ObjLoading/Game/IW5/Menu/LoaderMenuListIW5.h b/src/ObjLoading/Game/IW5/Menu/LoaderMenuListIW5.h index 823161b8..4a8f1c4b 100644 --- a/src/ObjLoading/Game/IW5/Menu/LoaderMenuListIW5.h +++ b/src/ObjLoading/Game/IW5/Menu/LoaderMenuListIW5.h @@ -7,7 +7,7 @@ #include -namespace IW5 +namespace menu { - std::unique_ptr> CreateMenuListLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW5 + std::unique_ptr> CreateMenuListLoaderIW5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace menu diff --git a/src/ObjLoading/Game/IW5/Menu/MenuConverterIW5.h b/src/ObjLoading/Game/IW5/Menu/MenuConverterIW5.h index 2f112270..c0b03845 100644 --- a/src/ObjLoading/Game/IW5/Menu/MenuConverterIW5.h +++ b/src/ObjLoading/Game/IW5/Menu/MenuConverterIW5.h @@ -14,7 +14,7 @@ namespace IW5 IMenuConverter() = default; virtual ~IMenuConverter() = default; - virtual void ConvertMenu(const menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration& registration) = 0; + virtual void ConvertMenu(const ::menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration& registration) = 0; static std::unique_ptr Create(bool disableOptimizations, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context); }; diff --git a/src/ObjLoading/Game/IW5/ObjLoaderIW5.cpp b/src/ObjLoading/Game/IW5/ObjLoaderIW5.cpp index 4755450f..cb3d96cd 100644 --- a/src/ObjLoading/Game/IW5/ObjLoaderIW5.cpp +++ b/src/ObjLoading/Game/IW5/ObjLoaderIW5.cpp @@ -125,13 +125,13 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateXModelLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath)); + collection.AddAssetCreator(xmodel::CreateLoaderIW5(memory, searchPath, zone)); + collection.AddAssetCreator(material::CreateLoaderIW5(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateImageLoader(memory, searchPath)); + collection.AddAssetCreator(image::CreateLoaderIW5(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); @@ -145,19 +145,19 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateMenuListLoader(memory, searchPath)); + collection.AddAssetCreator(menu::CreateMenuListLoaderIW5(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateLocalizeLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateAttachmentLoader(memory, searchPath)); - collection.AddAssetCreator(CreateRawWeaponLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateGdtWeaponLoader(memory, searchPath, gdt, zone)); + collection.AddAssetCreator(localize::CreateLoaderIW5(memory, searchPath, zone)); + collection.AddAssetCreator(attachment::CreateLoaderIW5(memory, searchPath)); + collection.AddAssetCreator(weapon::CreateRawLoaderIW5(memory, searchPath, zone)); + collection.AddAssetCreator(weapon::CreateGdtLoaderIW5(memory, searchPath, gdt, zone)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateRawFileLoader(memory, searchPath)); - collection.AddAssetCreator(CreateScriptLoader(memory, searchPath)); - collection.AddAssetCreator(CreateStringTableLoader(memory, searchPath)); - collection.AddAssetCreator(CreateLeaderboardLoader(memory, searchPath)); + collection.AddAssetCreator(raw_file::CreateLoaderIW5(memory, searchPath)); + collection.AddAssetCreator(script::CreateLoaderIW5(memory, searchPath)); + collection.AddAssetCreator(string_table::CreateLoaderIW5(memory, searchPath)); + collection.AddAssetCreator(leaderboard::CreateLoaderIW5(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); diff --git a/src/ObjLoading/Game/IW5/RawFile/LoaderRawFileIW5.cpp b/src/ObjLoading/Game/IW5/RawFile/LoaderRawFileIW5.cpp index 139e9ad3..e042ca7d 100644 --- a/src/ObjLoading/Game/IW5/RawFile/LoaderRawFileIW5.cpp +++ b/src/ObjLoading/Game/IW5/RawFile/LoaderRawFileIW5.cpp @@ -2,6 +2,7 @@ #include "Game/IW5/IW5.h" #include "Pool/GlobalAssetPool.h" +#include "Utils/Logging/Log.h" #include #include @@ -57,7 +58,7 @@ namespace if (ret != Z_STREAM_END) { - std::cerr << std::format("Deflate failed for loading rawfile \"{}\"\n", assetName); + con::error("Deflate failed for loading rawfile \"{}\"", assetName); deflateEnd(&zs); return AssetCreationResult::Failure(); } @@ -81,10 +82,10 @@ namespace }; } // namespace -namespace IW5 +namespace raw_file { - std::unique_ptr> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW5 +} // namespace raw_file diff --git a/src/ObjLoading/Game/IW5/RawFile/LoaderRawFileIW5.h b/src/ObjLoading/Game/IW5/RawFile/LoaderRawFileIW5.h index 3099146e..a8b2ad97 100644 --- a/src/ObjLoading/Game/IW5/RawFile/LoaderRawFileIW5.h +++ b/src/ObjLoading/Game/IW5/RawFile/LoaderRawFileIW5.h @@ -7,7 +7,7 @@ #include -namespace IW5 +namespace raw_file { - std::unique_ptr> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW5 + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace raw_file diff --git a/src/ObjLoading/Game/IW5/Script/LoaderScriptFileIW5.cpp b/src/ObjLoading/Game/IW5/Script/LoaderScriptFileIW5.cpp index 5ae68da9..ca9973fd 100644 --- a/src/ObjLoading/Game/IW5/Script/LoaderScriptFileIW5.cpp +++ b/src/ObjLoading/Game/IW5/Script/LoaderScriptFileIW5.cpp @@ -2,6 +2,7 @@ #include "Game/IW5/IW5.h" #include "Pool/GlobalAssetPool.h" +#include "Utils/Logging/Log.h" #include #include @@ -53,13 +54,13 @@ namespace if (scriptFile->compressedLen <= 0 || scriptFile->bytecodeLen <= 0) { - std::cerr << std::format("Error: Invalid length of the buffers in {} specified\n", assetName); + con::error("Invalid length of the buffers in {} specified", assetName); return AssetCreationResult::Failure(); } if (offset + static_cast(scriptFile->compressedLen + scriptFile->bytecodeLen) > static_cast(file.m_length)) { - std::cerr << std::format("Error: Specified length in {} GSC BIN structure exceeds the actual file size\n", assetName); + con::error("Specified length in {} GSC BIN structure exceeds the actual file size", assetName); return AssetCreationResult::Failure(); } @@ -79,10 +80,10 @@ namespace }; } // namespace -namespace IW5 +namespace script { - std::unique_ptr> CreateScriptLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW5 +} // namespace script diff --git a/src/ObjLoading/Game/IW5/Script/LoaderScriptFileIW5.h b/src/ObjLoading/Game/IW5/Script/LoaderScriptFileIW5.h index 457a0957..5d960798 100644 --- a/src/ObjLoading/Game/IW5/Script/LoaderScriptFileIW5.h +++ b/src/ObjLoading/Game/IW5/Script/LoaderScriptFileIW5.h @@ -7,7 +7,7 @@ #include -namespace IW5 +namespace script { - std::unique_ptr> CreateScriptLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW5 + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace script diff --git a/src/ObjLoading/Game/IW5/StringTable/LoaderStringTableIW5.cpp b/src/ObjLoading/Game/IW5/StringTable/LoaderStringTableIW5.cpp index 2ca18723..ba499cd1 100644 --- a/src/ObjLoading/Game/IW5/StringTable/LoaderStringTableIW5.cpp +++ b/src/ObjLoading/Game/IW5/StringTable/LoaderStringTableIW5.cpp @@ -40,10 +40,10 @@ namespace }; } // namespace -namespace IW5 +namespace string_table { - std::unique_ptr> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW5 +} // namespace string_table diff --git a/src/ObjLoading/Game/IW5/StringTable/LoaderStringTableIW5.h b/src/ObjLoading/Game/IW5/StringTable/LoaderStringTableIW5.h index e07bf9c7..52d88396 100644 --- a/src/ObjLoading/Game/IW5/StringTable/LoaderStringTableIW5.h +++ b/src/ObjLoading/Game/IW5/StringTable/LoaderStringTableIW5.h @@ -7,7 +7,7 @@ #include -namespace IW5 +namespace string_table { - std::unique_ptr> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW5 + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace string_table diff --git a/src/ObjLoading/Game/IW5/Weapon/GdtLoaderWeaponIW5.cpp b/src/ObjLoading/Game/IW5/Weapon/GdtLoaderWeaponIW5.cpp index 03897a0d..a9902318 100644 --- a/src/ObjLoading/Game/IW5/Weapon/GdtLoaderWeaponIW5.cpp +++ b/src/ObjLoading/Game/IW5/Weapon/GdtLoaderWeaponIW5.cpp @@ -4,6 +4,7 @@ #include "Game/IW5/ObjConstantsIW5.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderWeaponIW5.h" +#include "Utils/Logging/Log.h" #include #include @@ -31,7 +32,7 @@ namespace InfoString infoString; if (!infoString.FromGdtProperties(*gdtEntry)) { - std::cerr << std::format("Failed to read weapon gdt entry: \"{}\"\n", assetName); + con::error("Failed to read weapon gdt entry: \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -40,14 +41,14 @@ namespace private: IGdtQueryable& m_gdt; - InfoStringLoaderWeapon m_info_string_loader; + weapon::InfoStringLoaderIW5 m_info_string_loader; }; } // namespace -namespace IW5 +namespace weapon { - std::unique_ptr> CreateGdtWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + std::unique_ptr> CreateGdtLoaderIW5(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) { return std::make_unique(memory, searchPath, gdt, zone); } -} // namespace IW5 +} // namespace weapon diff --git a/src/ObjLoading/Game/IW5/Weapon/GdtLoaderWeaponIW5.h b/src/ObjLoading/Game/IW5/Weapon/GdtLoaderWeaponIW5.h index 0e86b336..f4b729c1 100644 --- a/src/ObjLoading/Game/IW5/Weapon/GdtLoaderWeaponIW5.h +++ b/src/ObjLoading/Game/IW5/Weapon/GdtLoaderWeaponIW5.h @@ -8,7 +8,7 @@ #include -namespace IW5 +namespace weapon { - std::unique_ptr> CreateGdtWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); -} // namespace IW5 + std::unique_ptr> CreateGdtLoaderIW5(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); +} // namespace weapon diff --git a/src/ObjLoading/Game/IW5/Weapon/InfoStringLoaderWeaponIW5.cpp b/src/ObjLoading/Game/IW5/Weapon/InfoStringLoaderWeaponIW5.cpp index 93f82995..7a6617fb 100644 --- a/src/ObjLoading/Game/IW5/Weapon/InfoStringLoaderWeaponIW5.cpp +++ b/src/ObjLoading/Game/IW5/Weapon/InfoStringLoaderWeaponIW5.cpp @@ -3,6 +3,7 @@ #include "Game/IW5/IW5.h" #include "Game/IW5/InfoString/InfoStringToStructConverter.h" #include "Game/IW5/Weapon/WeaponFields.h" +#include "Utils/Logging/Log.h" #include "Weapon/AccuracyGraphLoader.h" #include @@ -22,13 +23,13 @@ namespace std::vector valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse hide tags as array\n"; + con::error("Failed to parse hide tags as array"); return false; } if (valueArray.size() > std::extent_v) { - std::cerr << std::format("Cannot have more than {} hide tags!\n", std::extent_v); + con::error("Cannot have more than {} hide tags!", std::extent_v); return false; } @@ -83,14 +84,13 @@ namespace std::vector> pairs; if (!ParseAsArray(value, pairs)) { - std::cerr << std::format("Failed to parse notetrack{}map as pairs\n", mapName); + con::error("Failed to parse notetrack{}map as pairs", mapName); return false; } if (pairs.size() > std::extent_v) { - std::cerr << std::format( - "Cannot have more than {} notetrack{}map entries!\n", std::extent_v, mapName); + con::error("Cannot have more than {} notetrack{}map entries!", std::extent_v, mapName); return false; } @@ -146,7 +146,7 @@ namespace std::vector valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse attachments as array\n"; + con::error("Failed to parse attachments as array"); return false; } @@ -165,7 +165,7 @@ namespace { if (currentScope >= std::extent_v) { - std::cerr << std::format("Cannot have more than {} scopes\n", std::extent_v); + con::error("Cannot have more than {} scopes", std::extent_v); return false; } @@ -175,7 +175,7 @@ namespace { if (currentUnderBarrel >= std::extent_v) { - std::cerr << std::format("Cannot have more than {} under barrels\n", std::extent_v); + con::error("Cannot have more than {} under barrels", std::extent_v); return false; } @@ -185,7 +185,7 @@ namespace { if (currentOther >= std::extent_v) { - std::cerr << std::format("Cannot have more than {} other attachments\n", std::extent_v); + con::error("Cannot have more than {} other attachments", std::extent_v); return false; } @@ -201,7 +201,7 @@ namespace std::vector> valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse anim overrides as array\n"; + con::error("Failed to parse anim overrides as array"); return false; } @@ -242,7 +242,7 @@ namespace std::vector> valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse sound overrides as array\n"; + con::error("Failed to parse sound overrides as array"); return false; } @@ -277,7 +277,7 @@ namespace std::vector> valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse attachments as array\n"; + con::error("Failed to parse attachments as array"); return false; } @@ -315,7 +315,7 @@ namespace std::vector> valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse reload overrides as array\n"; + con::error("Failed to parse reload overrides as array"); return false; } @@ -349,7 +349,7 @@ namespace std::vector> valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse note track overrides as array\n"; + con::error("Failed to parse note track overrides as array"); return false; } @@ -378,7 +378,7 @@ namespace if (currentOverrideKeyOffset >= 24u) { - std::cerr << std::format("Cannot have more than {} note track overrides per attachment\n", 24u); + con::error("Cannot have more than {} note track overrides per attachment", 24u); return false; } @@ -433,7 +433,7 @@ namespace } } - std::cerr << std::format("Weapon does not have attachment \"{}\"\n", value); + con::error("Weapon does not have attachment \"{}\"", value); return false; } @@ -473,7 +473,7 @@ namespace auto* fxInfo = m_context.LoadDependency(value); if (!fxInfo) { - std::cerr << std::format("Failed to load fx for override \"{}\"\n", value); + con::error("Failed to load fx for override \"{}\"", value); return false; } @@ -494,7 +494,7 @@ namespace } } - std::cerr << std::format("Unknown anim file \"{}\"\n", value); + con::error("Unknown anim file \"{}\"", value); return false; } @@ -509,7 +509,7 @@ namespace } } - std::cerr << std::format("Unknown sound type \"{}\"\n", value); + con::error("Unknown sound type \"{}\"", value); return false; } @@ -524,7 +524,7 @@ namespace } } - std::cerr << std::format("Unknown fx type \"{}\"\n", value); + con::error("Unknown fx type \"{}\"", value); return false; } @@ -535,7 +535,7 @@ namespace if (size != value.size()) { - std::cerr << std::format("Invalid int value: \"{}\"\n", value); + con::error("Invalid int value: \"{}\"", value); return false; } @@ -697,28 +697,27 @@ namespace void CheckProjectileValues(const WeaponCompleteDef& weaponCompleteDef, const WeaponDef& weaponDef) { if (weaponDef.iProjectileSpeed <= 0) - std::cerr << std::format("Projectile speed for WeapType {} must be greater than 0.0", weaponCompleteDef.szDisplayName); + con::error("Projectile speed for WeapType {} must be greater than 0.0", weaponCompleteDef.szDisplayName); if (weaponDef.destabilizationCurvatureMax >= 1000000000.0f || weaponDef.destabilizationCurvatureMax < 0.0f) - std::cerr << std::format("Destabilization angle for for WeapType {} must be between 0 and 45 degrees", weaponCompleteDef.szDisplayName); + con::error("Destabilization angle for for WeapType {} must be between 0 and 45 degrees", weaponCompleteDef.szDisplayName); if (weaponDef.destabilizationRateTime < 0.0f) - std::cerr << std::format("Destabilization rate time for for WeapType {} must be non-negative", weaponCompleteDef.szDisplayName); + con::error("Destabilization rate time for for WeapType {} must be non-negative", weaponCompleteDef.szDisplayName); } void CheckTurretBarrelSpin(const WeaponCompleteDef& weaponCompleteDef, const WeaponDef& weaponDef) { if (weaponDef.weapClass != WEAPCLASS_TURRET) - std::cerr << std::format("Rotating barrel set for non-turret weapon {}.", weaponCompleteDef.szInternalName); + con::error("Rotating barrel set for non-turret weapon {}.", weaponCompleteDef.szInternalName); if (0.0f == weaponDef.turretBarrelSpinSpeed) { - std::cerr << std::format( - "Rotating barrel spin speed '{}' is invalid for weapon {}.", weaponDef.turretBarrelSpinSpeed, weaponCompleteDef.szInternalName); + con::error("Rotating barrel spin speed '{}' is invalid for weapon {}.", weaponDef.turretBarrelSpinSpeed, weaponCompleteDef.szInternalName); } if (0.0f < weaponDef.turretOverheatUpRate && 0.0f >= weaponDef.turretOverheatDownRate) { - std::cerr << std::format("Turret overheat Up rate is set, but the down rate '{}' is invalid for weapon {}.", - weaponDef.turretOverheatDownRate, - weaponCompleteDef.szInternalName); + con::error("Turret overheat Up rate is set, but the down rate '{}' is invalid for weapon {}.", + weaponDef.turretOverheatDownRate, + weaponCompleteDef.szInternalName); } } @@ -726,18 +725,18 @@ namespace { if (0.0f != weaponDef.fAdsZoomInFrac) { - std::cerr << std::format("Weapon {} ({}) has thermal scope set. ADS Zoom In frac should be 0 to prevent zoom-in blur ({}).\n", - weaponCompleteDef.szInternalName, - weaponCompleteDef.szDisplayName, - weaponDef.fAdsZoomInFrac); + con::error("Weapon {} ({}) has thermal scope set. ADS Zoom In frac should be 0 to prevent zoom-in blur ({}).", + weaponCompleteDef.szInternalName, + weaponCompleteDef.szDisplayName, + weaponDef.fAdsZoomInFrac); } if (0.0f != weaponDef.fAdsZoomOutFrac) { - std::cerr << std::format("Weapon {} ({}) has thermal scope set. ADS Zoom Out frac should be 0 to prevent zoom-out blur ({}).\n", - weaponCompleteDef.szInternalName, - weaponCompleteDef.szDisplayName, - weaponDef.fAdsZoomOutFrac); + con::error("Weapon {} ({}) has thermal scope set. ADS Zoom Out frac should be 0 to prevent zoom-out blur ({}).", + weaponCompleteDef.szInternalName, + weaponCompleteDef.szDisplayName, + weaponDef.fAdsZoomOutFrac); } } @@ -767,7 +766,7 @@ namespace weaponDef.fMinDamageRange = 999999.12f; if (weaponDef.enemyCrosshairRange > 15000.0f) - std::cerr << std::format("Enemy crosshair ranges should be less than {}\n", 15000.0f); + con::error("Enemy crosshair ranges should be less than {}", 15000.0f); if (weaponDef.weapType == WEAPTYPE_PROJECTILE) CheckProjectileValues(weaponCompleteDef, weaponDef); @@ -780,22 +779,21 @@ namespace if (weaponDef.offhandClass && !weaponDef.bClipOnly) { - std::cerr << std::format( - "Weapon {} ({}) is an offhand weapon but is not set to clip only, which is not supported since we can't reload the offhand slot.\n", - weaponCompleteDef.szInternalName, - weaponCompleteDef.szDisplayName); + con::error("Weapon {} ({}) is an offhand weapon but is not set to clip only, which is not supported since we can't reload the offhand slot.", + weaponCompleteDef.szInternalName, + weaponCompleteDef.szDisplayName); } if (weaponDef.weapType == WEAPTYPE_BULLET) { if (weaponDef.bulletExplDmgMult <= 0.0f) - std::cerr << std::format("Detected invalid bulletExplDmgMult of '{}' for weapon '{}'; please update weapon settings.\n", - weaponDef.bulletExplDmgMult, - weaponCompleteDef.szInternalName); + con::error("Detected invalid bulletExplDmgMult of '{}' for weapon '{}'; please update weapon settings.", + weaponDef.bulletExplDmgMult, + weaponCompleteDef.szInternalName); if (weaponDef.bulletExplRadiusMult <= 0.0f) - std::cerr << std::format("Detected invalid bulletExplRadiusMult of '{}' for weapon '{}'; please update weapon settings.\n", - weaponDef.bulletExplRadiusMult, - weaponCompleteDef.szInternalName); + con::error("Detected invalid bulletExplRadiusMult of '{}' for weapon '{}'; please update weapon settings.", + weaponDef.bulletExplRadiusMult, + weaponCompleteDef.szInternalName); } } @@ -856,33 +854,36 @@ namespace } } // namespace -InfoStringLoaderWeapon::InfoStringLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) +namespace weapon { -} - -AssetCreationResult InfoStringLoaderWeapon::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const -{ - auto* weaponFullDef = m_memory.Alloc(); - - InitWeaponFullDef(*weaponFullDef); - weaponFullDef->weapCompleteDef.szInternalName = m_memory.Dup(assetName.c_str()); - - AssetRegistration registration(assetName, &weaponFullDef->weapCompleteDef); - - InfoStringToWeaponConverter converter( - infoString, *weaponFullDef, m_zone.m_script_strings, m_memory, context, registration, weapon_fields, std::extent_v); - if (!converter.Convert()) + InfoStringLoaderIW5::InfoStringLoaderIW5(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone) { - std::cerr << std::format("Failed to parse weapon: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - CalculateWeaponFields(*weaponFullDef, m_memory); + AssetCreationResult InfoStringLoaderIW5::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const + { + auto* weaponFullDef = m_memory.Alloc(); - LoadAccuracyGraphs(*weaponFullDef, m_memory, m_search_path, context); + InitWeaponFullDef(*weaponFullDef); + weaponFullDef->weapCompleteDef.szInternalName = m_memory.Dup(assetName.c_str()); - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + AssetRegistration registration(assetName, &weaponFullDef->weapCompleteDef); + + InfoStringToWeaponConverter converter( + infoString, *weaponFullDef, m_zone.m_script_strings, m_memory, context, registration, weapon_fields, std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse weapon: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + CalculateWeaponFields(*weaponFullDef, m_memory); + + LoadAccuracyGraphs(*weaponFullDef, m_memory, m_search_path, context); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace weapon diff --git a/src/ObjLoading/Game/IW5/Weapon/InfoStringLoaderWeaponIW5.h b/src/ObjLoading/Game/IW5/Weapon/InfoStringLoaderWeaponIW5.h index 40f14367..d8935b8c 100644 --- a/src/ObjLoading/Game/IW5/Weapon/InfoStringLoaderWeaponIW5.h +++ b/src/ObjLoading/Game/IW5/Weapon/InfoStringLoaderWeaponIW5.h @@ -4,12 +4,12 @@ #include "Asset/AssetCreationResult.h" #include "InfoString/InfoString.h" -namespace IW5 +namespace weapon { - class InfoStringLoaderWeapon + class InfoStringLoaderIW5 { public: - InfoStringLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + InfoStringLoaderIW5(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const; @@ -18,4 +18,4 @@ namespace IW5 ISearchPath& m_search_path; Zone& m_zone; }; -} // namespace IW5 +} // namespace weapon diff --git a/src/ObjLoading/Game/IW5/Weapon/JsonWeaponAttachmentLoader.cpp b/src/ObjLoading/Game/IW5/Weapon/JsonWeaponAttachmentLoader.cpp deleted file mode 100644 index f6ef081e..00000000 --- a/src/ObjLoading/Game/IW5/Weapon/JsonWeaponAttachmentLoader.cpp +++ /dev/null @@ -1,645 +0,0 @@ -#include "JsonWeaponAttachmentLoader.h" - -#include "Game/IW5/CommonIW5.h" -#include "Game/IW5/Weapon/JsonWeaponAttachment.h" - -#include -#include -#include - -using namespace nlohmann; -using namespace IW5; - -namespace -{ - class JsonLoader - { - public: - JsonLoader(std::istream& stream, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) - : m_stream(stream), - m_memory(memory), - m_context(context), - m_registration(registration) - - { - } - - bool Load(WeaponAttachment& attachment) const - { - const auto jRoot = json::parse(m_stream); - std::string type; - unsigned version; - - jRoot.at("_type").get_to(type); - jRoot.at("_version").get_to(version); - - if (type != "attachment" || version != 1u) - { - std::cerr << "Tried to load attachment \"" << attachment.szInternalName << "\" but did not find expected type attachment of version 1\n"; - return false; - } - - try - { - const auto jAttachment = jRoot.get(); - return CreateWeaponAttachmentFromJson(jAttachment, attachment); - } - catch (const json::exception& e) - { - std::cerr << std::format("Failed to parse json of attachment: {}\n", e.what()); - } - - return false; - } - - private: - static void PrintError(const WeaponAttachment& attachment, const std::string& message) - { - std::cerr << "Cannot load attachment \"" << attachment.szInternalName << "\": " << message << "\n"; - } - - bool CreateWeaponAttachmentFromJson(const JsonWeaponAttachment& jAttachment, WeaponAttachment& attachment) const - { -#define CONVERT_XMODEL_ARRAY(propertyName, count) \ - CreateXModelArrayFromJson(jAttachment.propertyName, attachment.propertyName, #propertyName, count, attachment); - -#define CONVERT_ATTRIBUTE(attributeClass, attributeName) \ - if (jAttachment.attributeName) \ - { \ - using AttributeType = std::remove_pointer_t; \ - attachment.attributeName = m_memory.Alloc(); \ - if (!Create##attributeClass##FromJson(jAttachment.attributeName.value(), *attachment.attributeName, attachment)) \ - return false; \ - } \ - else \ - attachment.attributeName = nullptr; - - attachment.szDisplayName = m_memory.Dup(jAttachment.displayName.c_str()); - attachment.type = jAttachment.type; - attachment.weaponType = jAttachment.weaponType; - attachment.weapClass = jAttachment.weapClass; - - CONVERT_XMODEL_ARRAY(worldModels, ATTACHMENT_WORLD_MODEL_COUNT) - CONVERT_XMODEL_ARRAY(viewModels, ATTACHMENT_VIEW_MODEL_COUNT) - CONVERT_XMODEL_ARRAY(reticleViewModels, ATTACHMENT_RETICLE_VIEW_MODEL_COUNT) - - CONVERT_ATTRIBUTE(AttAmmoGeneral, ammoGeneral) - CONVERT_ATTRIBUTE(AttSight, sight) - CONVERT_ATTRIBUTE(AttReload, reload) - CONVERT_ATTRIBUTE(AttAddOns, addOns) - CONVERT_ATTRIBUTE(AttGeneral, general) - CONVERT_ATTRIBUTE(AttAimAssist, aimAssist) - CONVERT_ATTRIBUTE(AttAmmunition, ammunition) - CONVERT_ATTRIBUTE(AttDamage, damage) - CONVERT_ATTRIBUTE(AttLocationDamage, locationDamage) - CONVERT_ATTRIBUTE(AttIdleSettings, idleSettings) - CONVERT_ATTRIBUTE(AttADSSettings, adsSettings) - CONVERT_ATTRIBUTE(AttADSSettings, adsSettingsMain) - CONVERT_ATTRIBUTE(AttHipSpread, hipSpread) - CONVERT_ATTRIBUTE(AttGunKick, gunKick) - CONVERT_ATTRIBUTE(AttViewKick, viewKick) - CONVERT_ATTRIBUTE(AttADSOverlay, adsOverlay) - CONVERT_ATTRIBUTE(AttUI, ui) - CONVERT_ATTRIBUTE(AttRumbles, rumbles) - CONVERT_ATTRIBUTE(AttProjectile, projectile) - - attachment.ammunitionScale = jAttachment.ammunitionScale; - attachment.damageScale = jAttachment.damageScale; - attachment.damageScaleMin = jAttachment.damageScaleMin; - attachment.stateTimersScale = jAttachment.stateTimersScale; - attachment.fireTimersScale = jAttachment.fireTimersScale; - attachment.idleSettingsScale = jAttachment.idleSettingsScale; - attachment.adsSettingsScale = jAttachment.adsSettingsScale; - attachment.adsSettingsScaleMain = jAttachment.adsSettingsScaleMain; - attachment.hipSpreadScale = jAttachment.hipSpreadScale; - attachment.gunKickScale = jAttachment.gunKickScale; - attachment.viewKickScale = jAttachment.viewKickScale; - attachment.viewCenterScale = jAttachment.viewCenterScale; - attachment.loadIndex = jAttachment.loadIndex; - attachment.hideIronSightsWithThisAttachment = jAttachment.hideIronSightsWithThisAttachment; - attachment.shareAmmoWithAlt = jAttachment.shareAmmoWithAlt; - - return true; - } - - bool CreateTracerFromJson(const std::string& assetName, TracerDef*& tracerPtr, const WeaponAttachment& attachment) const - { - auto* tracer = m_context.LoadDependency(assetName); - if (!tracer) - { - PrintError(attachment, std::format("Could not find tracer {}", assetName)); - return false; - } - m_registration.AddDependency(tracer); - tracerPtr = tracer->Asset(); - - return true; - } - - bool CreateMaterialFromJson(const std::string& assetName, Material*& materialPtr, const WeaponAttachment& attachment) const - { - auto* material = m_context.LoadDependency(assetName); - if (!material) - { - PrintError(attachment, std::format("Could not find material {}", assetName)); - return false; - } - m_registration.AddDependency(material); - materialPtr = material->Asset(); - - return true; - } - - bool CreateFxFromJson(const std::string& assetName, FxEffectDef*& fxPtr, const WeaponAttachment& attachment) const - { - auto* fx = m_context.LoadDependency(assetName); - if (!fx) - { - PrintError(attachment, std::format("Could not find fx {}", assetName)); - return false; - } - m_registration.AddDependency(fx); - fxPtr = fx->Asset(); - - return true; - } - - bool CreateSoundFromJson(const std::string& assetName, SndAliasCustom& sndAliasCustom, const WeaponAttachment& attachment) const - { - m_registration.AddIndirectAssetReference(m_context.LoadIndirectAssetReference(assetName)); - sndAliasCustom.name = m_memory.Alloc(); - sndAliasCustom.name->soundName = m_memory.Dup(assetName.c_str()); - - return true; - } - - bool CreateXModelFromJson(const std::string& assetName, XModel*& xmodelPtr, const WeaponAttachment& attachment) const - { - auto* xmodel = m_context.LoadDependency(assetName); - if (!xmodel) - { - PrintError(attachment, std::format("Could not find xmodel {}", assetName)); - return false; - } - m_registration.AddDependency(xmodel); - xmodelPtr = xmodel->Asset(); - - return true; - } - - bool CreateXModelArrayFromJson(const std::vector& jXmodelArray, - XModel**& xmodelArray, - const char* propertyName, - size_t propertyCount, - const WeaponAttachment& attachment) const - { - if (!jXmodelArray.empty()) - { - const auto arraySize = jXmodelArray.size(); - if (arraySize > propertyCount) - { - PrintError(attachment, std::format("{} size cannot exceed {}", propertyName, propertyCount)); - return false; - } - xmodelArray = m_memory.Alloc(propertyCount); - memset(xmodelArray, 0, sizeof(void*) * propertyCount); - - for (auto i = 0u; i < arraySize; i++) - { - if (!CreateXModelFromJson(jXmodelArray[i], xmodelArray[i], attachment)) - return false; - } - } - else - { - xmodelArray = nullptr; - } - - return true; - } - - bool CreateAttAmmoGeneralFromJson(const JsonAttAmmoGeneral& jAmmoGeneral, AttAmmoGeneral& ammoGeneral, const WeaponAttachment& attachment) const - { - ammoGeneral.penetrateType = jAmmoGeneral.penetrateType; - ammoGeneral.penetrateMultiplier = jAmmoGeneral.penetrateMultiplier; - ammoGeneral.impactType = jAmmoGeneral.impactType; - ammoGeneral.fireType = jAmmoGeneral.fireType; - - if (jAmmoGeneral.tracerType) - { - if (!CreateTracerFromJson(jAmmoGeneral.tracerType.value(), ammoGeneral.tracerType, attachment)) - return false; - } - else - ammoGeneral.tracerType = nullptr; - - ammoGeneral.rifleBullet = jAmmoGeneral.rifleBullet; - ammoGeneral.armorPiercing = jAmmoGeneral.armorPiercing; - - return true; - } - - static bool CreateAttSightFromJson(const JsonAttSight& jSight, AttSight& sight, const WeaponAttachment& attachment) - { - sight.aimDownSight = jSight.aimDownSight; - sight.adsFire = jSight.adsFire; - sight.rechamberWhileAds = jSight.rechamberWhileAds; - sight.noAdsWhenMagEmpty = jSight.noAdsWhenMagEmpty; - sight.canHoldBreath = jSight.canHoldBreath; - sight.canVariableZoom = jSight.canVariableZoom; - sight.hideRailWithThisScope = jSight.hideRailWithThisScope; - - return true; - } - - static bool CreateAttReloadFromJson(const JsonAttReload& jReload, AttReload& reload, const WeaponAttachment& attachment) - { - reload.noPartialReload = jReload.noPartialReload; - reload.segmentedReload = jReload.segmentedReload; - - return true; - } - - static bool CreateAttAddOnsFromJson(const JsonAttAddOns& jAddOns, AttAddOns& addOns, const WeaponAttachment& attachment) - { - addOns.motionTracker = jAddOns.motionTracker; - addOns.silenced = jAddOns.silenced; - - return true; - } - - bool CreateAttGeneralFromJson(const JsonAttGeneral& jGeneral, AttGeneral& general, const WeaponAttachment& attachment) const - { - general.boltAction = jGeneral.boltAction; - general.inheritsPerks = jGeneral.inheritsPerks; - general.enemyCrosshairRange = jGeneral.enemyCrosshairRange; - - if (jGeneral.reticleCenter) - { - if (!CreateMaterialFromJson(jGeneral.reticleCenter.value(), general.reticleCenter, attachment)) - return false; - } - else - general.reticleCenter = nullptr; - - if (jGeneral.reticleSide) - { - if (!CreateMaterialFromJson(jGeneral.reticleSide.value(), general.reticleSide, attachment)) - return false; - } - else - general.reticleSide = nullptr; - - general.reticleCenterSize = jGeneral.reticleCenterSize; - general.reticleSideSize = jGeneral.reticleSideSize; - general.moveSpeedScale = jGeneral.moveSpeedScale; - general.adsMoveSpeedScale = jGeneral.adsMoveSpeedScale; - - return true; - } - - static bool CreateAttAimAssistFromJson(const JsonAttAimAssist& jAimAssist, AttAimAssist& aimAssist, const WeaponAttachment& attachment) - { - aimAssist.autoAimRange = jAimAssist.autoAimRange; - aimAssist.aimAssistRange = jAimAssist.aimAssistRange; - aimAssist.aimAssistRangeAds = jAimAssist.aimAssistRangeAds; - - return true; - } - - static bool CreateAttAmmunitionFromJson(const JsonAttAmmunition& jAmmunition, AttAmmunition& ammunition, const WeaponAttachment& attachment) - { - ammunition.maxAmmo = jAmmunition.maxAmmo; - ammunition.startAmmo = jAmmunition.startAmmo; - ammunition.clipSize = jAmmunition.clipSize; - ammunition.shotCount = jAmmunition.shotCount; - ammunition.reloadAmmoAdd = jAmmunition.reloadAmmoAdd; - ammunition.reloadStartAdd = jAmmunition.reloadStartAdd; - - return true; - } - - static bool CreateAttDamageFromJson(const JsonAttDamage& jDamage, AttDamage& damage, const WeaponAttachment& attachment) - { - damage.damage = jDamage.damage; - damage.minDamage = jDamage.minDamage; - damage.meleeDamage = jDamage.meleeDamage; - damage.maxDamageRange = jDamage.maxDamageRange; - damage.minDamageRange = jDamage.minDamageRange; - damage.playerDamage = jDamage.playerDamage; - damage.minPlayerDamage = jDamage.minPlayerDamage; - - return true; - } - - static bool - CreateAttLocationDamageFromJson(const JsonAttLocationDamage& jLocationDamage, AttLocationDamage& locationDamage, const WeaponAttachment& attachment) - { - locationDamage.locNone = jLocationDamage.locNone; - locationDamage.locHelmet = jLocationDamage.locHelmet; - locationDamage.locHead = jLocationDamage.locHead; - locationDamage.locNeck = jLocationDamage.locNeck; - locationDamage.locTorsoUpper = jLocationDamage.locTorsoUpper; - locationDamage.locTorsoLower = jLocationDamage.locTorsoLower; - locationDamage.locRightArmUpper = jLocationDamage.locRightArmUpper; - locationDamage.locRightArmLower = jLocationDamage.locRightArmLower; - locationDamage.locRightHand = jLocationDamage.locRightHand; - locationDamage.locLeftArmUpper = jLocationDamage.locLeftArmUpper; - locationDamage.locLeftArmLower = jLocationDamage.locLeftArmLower; - locationDamage.locLeftHand = jLocationDamage.locLeftHand; - locationDamage.locRightLegUpper = jLocationDamage.locRightLegUpper; - locationDamage.locRightLegLower = jLocationDamage.locRightLegLower; - locationDamage.locRightFoot = jLocationDamage.locRightFoot; - locationDamage.locLeftLegUpper = jLocationDamage.locLeftLegUpper; - locationDamage.locLeftLegLower = jLocationDamage.locLeftLegLower; - locationDamage.locLeftFoot = jLocationDamage.locLeftFoot; - locationDamage.locGun = jLocationDamage.locGun; - - return true; - } - - static bool CreateAttIdleSettingsFromJson(const JsonAttIdleSettings& jIdleSettings, AttIdleSettings& idleSettings, const WeaponAttachment& attachment) - { - idleSettings.hipIdleAmount = jIdleSettings.hipIdleAmount; - idleSettings.hipIdleSpeed = jIdleSettings.hipIdleSpeed; - idleSettings.idleCrouchFactor = jIdleSettings.idleCrouchFactor; - idleSettings.idleProneFactor = jIdleSettings.idleProneFactor; - idleSettings.adsIdleLerpStartTime = jIdleSettings.adsIdleLerpStartTime; - idleSettings.adsIdleLerpTime = jIdleSettings.adsIdleLerpTime; - - return true; - } - - static bool CreateAttADSSettingsFromJson(const JsonAttADSSettings& jAdsSettings, AttADSSettings& adsSettings, const WeaponAttachment& attachment) - { - adsSettings.adsSpread = jAdsSettings.adsSpread; - adsSettings.adsAimPitch = jAdsSettings.adsAimPitch; - adsSettings.adsTransInTime = jAdsSettings.adsTransInTime; - adsSettings.adsTransOutTime = jAdsSettings.adsTransOutTime; - adsSettings.adsReloadTransTime = jAdsSettings.adsReloadTransTime; - adsSettings.adsCrosshairInFrac = jAdsSettings.adsCrosshairInFrac; - adsSettings.adsCrosshairOutFrac = jAdsSettings.adsCrosshairOutFrac; - adsSettings.adsZoomFov = jAdsSettings.adsZoomFov; - adsSettings.adsZoomInFrac = jAdsSettings.adsZoomInFrac; - adsSettings.adsZoomOutFrac = jAdsSettings.adsZoomOutFrac; - adsSettings.adsBobFactor = jAdsSettings.adsBobFactor; - adsSettings.adsViewBobMult = jAdsSettings.adsViewBobMult; - adsSettings.adsViewErrorMin = jAdsSettings.adsViewErrorMin; - adsSettings.adsViewErrorMax = jAdsSettings.adsViewErrorMax; - - return true; - } - - static bool CreateAttHipSpreadFromJson(const JsonAttHipSpread& jHipSpread, AttHipSpread& hipSpread, const WeaponAttachment& attachment) - { - hipSpread.hipSpreadStandMin = jHipSpread.hipSpreadStandMin; - hipSpread.hipSpreadDuckedMin = jHipSpread.hipSpreadDuckedMin; - hipSpread.hipSpreadProneMin = jHipSpread.hipSpreadProneMin; - hipSpread.hipSpreadMax = jHipSpread.hipSpreadMax; - hipSpread.hipSpreadDuckedMax = jHipSpread.hipSpreadDuckedMax; - hipSpread.hipSpreadProneMax = jHipSpread.hipSpreadProneMax; - hipSpread.hipSpreadFireAdd = jHipSpread.hipSpreadFireAdd; - hipSpread.hipSpreadTurnAdd = jHipSpread.hipSpreadTurnAdd; - hipSpread.hipSpreadMoveAdd = jHipSpread.hipSpreadMoveAdd; - hipSpread.hipSpreadDecayRate = jHipSpread.hipSpreadDecayRate; - hipSpread.hipSpreadDuckedDecay = jHipSpread.hipSpreadDuckedDecay; - hipSpread.hipSpreadProneDecay = jHipSpread.hipSpreadProneDecay; - - return true; - } - - static bool CreateAttGunKickFromJson(const JsonAttGunKick& jGunKick, AttGunKick& gunKick, const WeaponAttachment& attachment) - { - gunKick.hipGunKickReducedKickBullets = jGunKick.hipGunKickReducedKickBullets; - gunKick.hipGunKickReducedKickPercent = jGunKick.hipGunKickReducedKickPercent; - gunKick.hipGunKickPitchMin = jGunKick.hipGunKickPitchMin; - gunKick.hipGunKickPitchMax = jGunKick.hipGunKickPitchMax; - gunKick.hipGunKickYawMin = jGunKick.hipGunKickYawMin; - gunKick.hipGunKickYawMax = jGunKick.hipGunKickYawMax; - gunKick.hipGunKickAccel = jGunKick.hipGunKickAccel; - gunKick.hipGunKickSpeedMax = jGunKick.hipGunKickSpeedMax; - gunKick.hipGunKickSpeedDecay = jGunKick.hipGunKickSpeedDecay; - gunKick.hipGunKickStaticDecay = jGunKick.hipGunKickStaticDecay; - gunKick.adsGunKickReducedKickBullets = jGunKick.adsGunKickReducedKickBullets; - gunKick.adsGunKickReducedKickPercent = jGunKick.adsGunKickReducedKickPercent; - gunKick.adsGunKickPitchMin = jGunKick.adsGunKickPitchMin; - gunKick.adsGunKickPitchMax = jGunKick.adsGunKickPitchMax; - gunKick.adsGunKickYawMin = jGunKick.adsGunKickYawMin; - gunKick.adsGunKickYawMax = jGunKick.adsGunKickYawMax; - gunKick.adsGunKickAccel = jGunKick.adsGunKickAccel; - gunKick.adsGunKickSpeedMax = jGunKick.adsGunKickSpeedMax; - gunKick.adsGunKickSpeedDecay = jGunKick.adsGunKickSpeedDecay; - gunKick.adsGunKickStaticDecay = jGunKick.adsGunKickStaticDecay; - - return true; - } - - static bool CreateAttViewKickFromJson(const JsonAttViewKick& jViewKick, AttViewKick& viewKick, const WeaponAttachment& attachment) - { - viewKick.hipViewKickPitchMin = jViewKick.hipViewKickPitchMin; - viewKick.hipViewKickPitchMax = jViewKick.hipViewKickPitchMax; - viewKick.hipViewKickYawMin = jViewKick.hipViewKickYawMin; - viewKick.hipViewKickYawMax = jViewKick.hipViewKickYawMax; - viewKick.hipViewKickCenterSpeed = jViewKick.hipViewKickCenterSpeed; - viewKick.adsViewKickPitchMin = jViewKick.adsViewKickPitchMin; - viewKick.adsViewKickPitchMax = jViewKick.adsViewKickPitchMax; - viewKick.adsViewKickYawMin = jViewKick.adsViewKickYawMin; - viewKick.adsViewKickYawMax = jViewKick.adsViewKickYawMax; - viewKick.adsViewKickCenterSpeed = jViewKick.adsViewKickCenterSpeed; - - return true; - } - - bool CreateAttADSOverlayFromJson(const JsonAttADSOverlay& jAdsOverlay, AttADSOverlay& adsOverlay, const WeaponAttachment& attachment) const - { - if (jAdsOverlay.shader) - { - if (!CreateMaterialFromJson(jAdsOverlay.shader.value(), adsOverlay.overlay.shader, attachment)) - return false; - } - else - adsOverlay.overlay.shader = nullptr; - - if (jAdsOverlay.shaderLowRes) - { - if (!CreateMaterialFromJson(jAdsOverlay.shaderLowRes.value(), adsOverlay.overlay.shaderLowRes, attachment)) - return false; - } - else - adsOverlay.overlay.shaderLowRes = nullptr; - - if (jAdsOverlay.shaderEMP) - { - if (!CreateMaterialFromJson(jAdsOverlay.shaderEMP.value(), adsOverlay.overlay.shaderEMP, attachment)) - return false; - } - else - adsOverlay.overlay.shaderEMP = nullptr; - - if (jAdsOverlay.shaderEMPLowRes) - { - if (!CreateMaterialFromJson(jAdsOverlay.shaderEMPLowRes.value(), adsOverlay.overlay.shaderEMPLowRes, attachment)) - return false; - } - else - adsOverlay.overlay.shaderEMPLowRes = nullptr; - - adsOverlay.overlay.reticle = jAdsOverlay.reticle; - adsOverlay.overlay.width = jAdsOverlay.width; - adsOverlay.overlay.height = jAdsOverlay.height; - adsOverlay.overlay.widthSplitscreen = jAdsOverlay.widthSplitscreen; - adsOverlay.overlay.heightSplitscreen = jAdsOverlay.heightSplitscreen; - adsOverlay.thermalScope = jAdsOverlay.thermalScope; - - return true; - } - - bool CreateAttUIFromJson(const JsonAttUI& jUi, AttUI& ui, const WeaponAttachment& attachment) const - { - if (jUi.dpadIcon) - { - if (!CreateMaterialFromJson(jUi.dpadIcon.value(), ui.dpadIcon, attachment)) - return false; - } - else - ui.dpadIcon = nullptr; - - if (jUi.ammoCounterIcon) - { - if (!CreateMaterialFromJson(jUi.ammoCounterIcon.value(), ui.ammoCounterIcon, attachment)) - return false; - } - else - ui.ammoCounterIcon = nullptr; - - ui.dpadIconRatio = jUi.dpadIconRatio; - ui.ammoCounterIconRatio = jUi.ammoCounterIconRatio; - ui.ammoCounterClip = jUi.ammoCounterClip; - - return true; - } - - bool CreateAttRumblesFromJson(const JsonAttRumbles& jRumbles, AttRumbles& rumbles, const WeaponAttachment& attachment) const - { - if (jRumbles.fireRumble) - rumbles.fireRumble = m_memory.Dup(jRumbles.fireRumble.value().c_str()); - else - rumbles.fireRumble = nullptr; - - if (jRumbles.meleeImpactRumble) - rumbles.meleeImpactRumble = m_memory.Dup(jRumbles.meleeImpactRumble.value().c_str()); - else - rumbles.meleeImpactRumble = nullptr; - - return true; - } - - bool CreateAttProjectileFromJson(const JsonAttProjectile& jProjectile, AttProjectile& projectile, const WeaponAttachment& attachment) const - { - projectile.explosionRadius = jProjectile.explosionRadius; - projectile.explosionInnerDamage = jProjectile.explosionInnerDamage; - projectile.explosionOuterDamage = jProjectile.explosionOuterDamage; - projectile.damageConeAngle = jProjectile.damageConeAngle; - projectile.projectileSpeed = jProjectile.projectileSpeed; - projectile.projectileSpeedUp = jProjectile.projectileSpeedUp; - projectile.projectileActivateDist = jProjectile.projectileActivateDist; - projectile.projectileLifetime = jProjectile.projectileLifetime; - - if (jProjectile.projectileModel) - { - if (!CreateXModelFromJson(jProjectile.projectileModel.value(), projectile.projectileModel, attachment)) - return false; - } - else - projectile.projectileModel = nullptr; - - projectile.projExplosionType = jProjectile.projExplosionType; - - if (jProjectile.projExplosionEffect) - { - if (!CreateFxFromJson(jProjectile.projExplosionEffect.value(), projectile.projExplosionEffect, attachment)) - return false; - } - else - projectile.projExplosionEffect = nullptr; - - projectile.projExplosionEffectForceNormalUp = jProjectile.projExplosionEffectForceNormalUp; - - if (jProjectile.projExplosionSound) - { - if (!CreateSoundFromJson(jProjectile.projExplosionSound.value(), projectile.projExplosionSound, attachment)) - return false; - } - else - projectile.projExplosionSound.name = nullptr; - - if (jProjectile.projDudEffect) - { - if (!CreateFxFromJson(jProjectile.projDudEffect.value(), projectile.projDudEffect, attachment)) - return false; - } - else - projectile.projDudEffect = nullptr; - - if (jProjectile.projDudSound) - { - if (!CreateSoundFromJson(jProjectile.projDudSound.value(), projectile.projDudSound, attachment)) - return false; - } - else - projectile.projDudSound.name = nullptr; - - projectile.projImpactExplode = jProjectile.projImpactExplode; - projectile.destabilizationRateTime = jProjectile.destabilizationRateTime; - projectile.destabilizationCurvatureMax = jProjectile.destabilizationCurvatureMax; - projectile.destabilizeDistance = jProjectile.destabilizeDistance; - - if (jProjectile.projTrailEffect) - { - if (!CreateFxFromJson(jProjectile.projTrailEffect.value(), projectile.projTrailEffect, attachment)) - return false; - } - else - projectile.projTrailEffect = nullptr; - - projectile.projIgnitionDelay = jProjectile.projIgnitionDelay; - - if (jProjectile.projIgnitionEffect) - { - if (!CreateFxFromJson(jProjectile.projIgnitionEffect.value(), projectile.projIgnitionEffect, attachment)) - return false; - } - else - projectile.projIgnitionEffect = nullptr; - - if (jProjectile.projIgnitionSound) - { - if (!CreateSoundFromJson(jProjectile.projIgnitionSound.value(), projectile.projIgnitionSound, attachment)) - return false; - } - else - projectile.projIgnitionSound.name = nullptr; - - return true; - } - - std::istream& m_stream; - MemoryManager& m_memory; - AssetCreationContext& m_context; - AssetRegistration& m_registration; - }; -} // namespace - -namespace IW5 -{ - bool LoadWeaponAttachmentAsJson(std::istream& stream, - WeaponAttachment& attachment, - MemoryManager& memory, - AssetCreationContext& context, - AssetRegistration& registration) - { - const JsonLoader loader(stream, memory, context, registration); - - return loader.Load(attachment); - } -} // namespace IW5 diff --git a/src/ObjLoading/Game/IW5/Weapon/JsonWeaponAttachmentLoader.h b/src/ObjLoading/Game/IW5/Weapon/JsonWeaponAttachmentLoader.h deleted file mode 100644 index 5b9d7b06..00000000 --- a/src/ObjLoading/Game/IW5/Weapon/JsonWeaponAttachmentLoader.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Asset/AssetCreationContext.h" -#include "Asset/AssetRegistration.h" -#include "Game/IW5/IW5.h" -#include "Utils/MemoryManager.h" - -#include - -namespace IW5 -{ - bool LoadWeaponAttachmentAsJson(std::istream& stream, - WeaponAttachment& attachment, - MemoryManager& memory, - AssetCreationContext& context, - AssetRegistration& registration); -} // namespace IW5 diff --git a/src/ObjLoading/Game/IW5/Weapon/LoaderAttachmentIW5.cpp b/src/ObjLoading/Game/IW5/Weapon/LoaderAttachmentIW5.cpp index ac7197c1..b04cdc78 100644 --- a/src/ObjLoading/Game/IW5/Weapon/LoaderAttachmentIW5.cpp +++ b/src/ObjLoading/Game/IW5/Weapon/LoaderAttachmentIW5.cpp @@ -1,16 +1,637 @@ #include "LoaderAttachmentIW5.h" #include "Game/IW5/IW5.h" -#include "JsonWeaponAttachmentLoader.h" +#include "Game/IW5/Weapon/JsonWeaponAttachment.h" +#include "Utils/Logging/Log.h" +#include "Weapon/AttachmentCommon.h" #include #include #include +#include +using namespace nlohmann; using namespace IW5; namespace { + class JsonLoader + { + public: + JsonLoader(std::istream& stream, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) + : m_stream(stream), + m_memory(memory), + m_context(context), + m_registration(registration) + + { + } + + bool Load(WeaponAttachment& attachment) const + { + try + { + const auto jRoot = json::parse(m_stream); + std::string type; + unsigned version; + + jRoot.at("_type").get_to(type); + jRoot.at("_version").get_to(version); + + if (type != "attachment" || version != 1u) + { + con::error("Tried to load attachment \"{}\" but did not find expected type attachment of version 1", attachment.szInternalName); + return false; + } + + const auto jAttachment = jRoot.get(); + return CreateWeaponAttachmentFromJson(jAttachment, attachment); + } + catch (const json::exception& e) + { + con::error("Failed to parse json of attachment: {}", e.what()); + } + + return false; + } + + private: + static void PrintError(const WeaponAttachment& attachment, const std::string& message) + { + con::error("Cannot load attachment \"{}\": {}", attachment.szInternalName, message); + } + + bool CreateWeaponAttachmentFromJson(const JsonWeaponAttachment& jAttachment, WeaponAttachment& attachment) const + { +#define CONVERT_XMODEL_ARRAY(propertyName, count) \ + CreateXModelArrayFromJson(jAttachment.propertyName, attachment.propertyName, #propertyName, count, attachment); + +#define CONVERT_ATTRIBUTE(attributeClass, attributeName) \ + if (jAttachment.attributeName) \ + { \ + using AttributeType = std::remove_pointer_t; \ + attachment.attributeName = m_memory.Alloc(); \ + if (!Create##attributeClass##FromJson(jAttachment.attributeName.value(), *attachment.attributeName, attachment)) \ + return false; \ + } \ + else \ + attachment.attributeName = nullptr; + + attachment.szDisplayName = m_memory.Dup(jAttachment.displayName.c_str()); + attachment.type = jAttachment.type; + attachment.weaponType = jAttachment.weaponType; + attachment.weapClass = jAttachment.weapClass; + + CONVERT_XMODEL_ARRAY(worldModels, ATTACHMENT_WORLD_MODEL_COUNT) + CONVERT_XMODEL_ARRAY(viewModels, ATTACHMENT_VIEW_MODEL_COUNT) + CONVERT_XMODEL_ARRAY(reticleViewModels, ATTACHMENT_RETICLE_VIEW_MODEL_COUNT) + + CONVERT_ATTRIBUTE(AttAmmoGeneral, ammoGeneral) + CONVERT_ATTRIBUTE(AttSight, sight) + CONVERT_ATTRIBUTE(AttReload, reload) + CONVERT_ATTRIBUTE(AttAddOns, addOns) + CONVERT_ATTRIBUTE(AttGeneral, general) + CONVERT_ATTRIBUTE(AttAimAssist, aimAssist) + CONVERT_ATTRIBUTE(AttAmmunition, ammunition) + CONVERT_ATTRIBUTE(AttDamage, damage) + CONVERT_ATTRIBUTE(AttLocationDamage, locationDamage) + CONVERT_ATTRIBUTE(AttIdleSettings, idleSettings) + CONVERT_ATTRIBUTE(AttADSSettings, adsSettings) + CONVERT_ATTRIBUTE(AttADSSettings, adsSettingsMain) + CONVERT_ATTRIBUTE(AttHipSpread, hipSpread) + CONVERT_ATTRIBUTE(AttGunKick, gunKick) + CONVERT_ATTRIBUTE(AttViewKick, viewKick) + CONVERT_ATTRIBUTE(AttADSOverlay, adsOverlay) + CONVERT_ATTRIBUTE(AttUI, ui) + CONVERT_ATTRIBUTE(AttRumbles, rumbles) + CONVERT_ATTRIBUTE(AttProjectile, projectile) + + attachment.ammunitionScale = jAttachment.ammunitionScale; + attachment.damageScale = jAttachment.damageScale; + attachment.damageScaleMin = jAttachment.damageScaleMin; + attachment.stateTimersScale = jAttachment.stateTimersScale; + attachment.fireTimersScale = jAttachment.fireTimersScale; + attachment.idleSettingsScale = jAttachment.idleSettingsScale; + attachment.adsSettingsScale = jAttachment.adsSettingsScale; + attachment.adsSettingsScaleMain = jAttachment.adsSettingsScaleMain; + attachment.hipSpreadScale = jAttachment.hipSpreadScale; + attachment.gunKickScale = jAttachment.gunKickScale; + attachment.viewKickScale = jAttachment.viewKickScale; + attachment.viewCenterScale = jAttachment.viewCenterScale; + attachment.loadIndex = jAttachment.loadIndex; + attachment.hideIronSightsWithThisAttachment = jAttachment.hideIronSightsWithThisAttachment; + attachment.shareAmmoWithAlt = jAttachment.shareAmmoWithAlt; + + return true; + } + + bool CreateTracerFromJson(const std::string& assetName, TracerDef*& tracerPtr, const WeaponAttachment& attachment) const + { + auto* tracer = m_context.LoadDependency(assetName); + if (!tracer) + { + PrintError(attachment, std::format("Could not find tracer {}", assetName)); + return false; + } + m_registration.AddDependency(tracer); + tracerPtr = tracer->Asset(); + + return true; + } + + bool CreateMaterialFromJson(const std::string& assetName, Material*& materialPtr, const WeaponAttachment& attachment) const + { + auto* material = m_context.LoadDependency(assetName); + if (!material) + { + PrintError(attachment, std::format("Could not find material {}", assetName)); + return false; + } + m_registration.AddDependency(material); + materialPtr = material->Asset(); + + return true; + } + + bool CreateFxFromJson(const std::string& assetName, FxEffectDef*& fxPtr, const WeaponAttachment& attachment) const + { + auto* fx = m_context.LoadDependency(assetName); + if (!fx) + { + PrintError(attachment, std::format("Could not find fx {}", assetName)); + return false; + } + m_registration.AddDependency(fx); + fxPtr = fx->Asset(); + + return true; + } + + bool CreateSoundFromJson(const std::string& assetName, SndAliasCustom& sndAliasCustom, const WeaponAttachment& attachment) const + { + m_registration.AddIndirectAssetReference(m_context.LoadIndirectAssetReference(assetName)); + sndAliasCustom.name = m_memory.Alloc(); + sndAliasCustom.name->soundName = m_memory.Dup(assetName.c_str()); + + return true; + } + + bool CreateXModelFromJson(const std::string& assetName, XModel*& xmodelPtr, const WeaponAttachment& attachment) const + { + auto* xmodel = m_context.LoadDependency(assetName); + if (!xmodel) + { + PrintError(attachment, std::format("Could not find xmodel {}", assetName)); + return false; + } + m_registration.AddDependency(xmodel); + xmodelPtr = xmodel->Asset(); + + return true; + } + + bool CreateXModelArrayFromJson(const std::vector& jXmodelArray, + XModel**& xmodelArray, + const char* propertyName, + size_t propertyCount, + const WeaponAttachment& attachment) const + { + if (!jXmodelArray.empty()) + { + const auto arraySize = jXmodelArray.size(); + if (arraySize > propertyCount) + { + PrintError(attachment, std::format("{} size cannot exceed {}", propertyName, propertyCount)); + return false; + } + xmodelArray = m_memory.Alloc(propertyCount); + memset(xmodelArray, 0, sizeof(void*) * propertyCount); + + for (auto i = 0u; i < arraySize; i++) + { + if (!CreateXModelFromJson(jXmodelArray[i], xmodelArray[i], attachment)) + return false; + } + } + else + { + xmodelArray = nullptr; + } + + return true; + } + + bool CreateAttAmmoGeneralFromJson(const JsonAttAmmoGeneral& jAmmoGeneral, AttAmmoGeneral& ammoGeneral, const WeaponAttachment& attachment) const + { + ammoGeneral.penetrateType = jAmmoGeneral.penetrateType; + ammoGeneral.penetrateMultiplier = jAmmoGeneral.penetrateMultiplier; + ammoGeneral.impactType = jAmmoGeneral.impactType; + ammoGeneral.fireType = jAmmoGeneral.fireType; + + if (jAmmoGeneral.tracerType) + { + if (!CreateTracerFromJson(jAmmoGeneral.tracerType.value(), ammoGeneral.tracerType, attachment)) + return false; + } + else + ammoGeneral.tracerType = nullptr; + + ammoGeneral.rifleBullet = jAmmoGeneral.rifleBullet; + ammoGeneral.armorPiercing = jAmmoGeneral.armorPiercing; + + return true; + } + + static bool CreateAttSightFromJson(const JsonAttSight& jSight, AttSight& sight, const WeaponAttachment& attachment) + { + sight.aimDownSight = jSight.aimDownSight; + sight.adsFire = jSight.adsFire; + sight.rechamberWhileAds = jSight.rechamberWhileAds; + sight.noAdsWhenMagEmpty = jSight.noAdsWhenMagEmpty; + sight.canHoldBreath = jSight.canHoldBreath; + sight.canVariableZoom = jSight.canVariableZoom; + sight.hideRailWithThisScope = jSight.hideRailWithThisScope; + + return true; + } + + static bool CreateAttReloadFromJson(const JsonAttReload& jReload, AttReload& reload, const WeaponAttachment& attachment) + { + reload.noPartialReload = jReload.noPartialReload; + reload.segmentedReload = jReload.segmentedReload; + + return true; + } + + static bool CreateAttAddOnsFromJson(const JsonAttAddOns& jAddOns, AttAddOns& addOns, const WeaponAttachment& attachment) + { + addOns.motionTracker = jAddOns.motionTracker; + addOns.silenced = jAddOns.silenced; + + return true; + } + + bool CreateAttGeneralFromJson(const JsonAttGeneral& jGeneral, AttGeneral& general, const WeaponAttachment& attachment) const + { + general.boltAction = jGeneral.boltAction; + general.inheritsPerks = jGeneral.inheritsPerks; + general.enemyCrosshairRange = jGeneral.enemyCrosshairRange; + + if (jGeneral.reticleCenter) + { + if (!CreateMaterialFromJson(jGeneral.reticleCenter.value(), general.reticleCenter, attachment)) + return false; + } + else + general.reticleCenter = nullptr; + + if (jGeneral.reticleSide) + { + if (!CreateMaterialFromJson(jGeneral.reticleSide.value(), general.reticleSide, attachment)) + return false; + } + else + general.reticleSide = nullptr; + + general.reticleCenterSize = jGeneral.reticleCenterSize; + general.reticleSideSize = jGeneral.reticleSideSize; + general.moveSpeedScale = jGeneral.moveSpeedScale; + general.adsMoveSpeedScale = jGeneral.adsMoveSpeedScale; + + return true; + } + + static bool CreateAttAimAssistFromJson(const JsonAttAimAssist& jAimAssist, AttAimAssist& aimAssist, const WeaponAttachment& attachment) + { + aimAssist.autoAimRange = jAimAssist.autoAimRange; + aimAssist.aimAssistRange = jAimAssist.aimAssistRange; + aimAssist.aimAssistRangeAds = jAimAssist.aimAssistRangeAds; + + return true; + } + + static bool CreateAttAmmunitionFromJson(const JsonAttAmmunition& jAmmunition, AttAmmunition& ammunition, const WeaponAttachment& attachment) + { + ammunition.maxAmmo = jAmmunition.maxAmmo; + ammunition.startAmmo = jAmmunition.startAmmo; + ammunition.clipSize = jAmmunition.clipSize; + ammunition.shotCount = jAmmunition.shotCount; + ammunition.reloadAmmoAdd = jAmmunition.reloadAmmoAdd; + ammunition.reloadStartAdd = jAmmunition.reloadStartAdd; + + return true; + } + + static bool CreateAttDamageFromJson(const JsonAttDamage& jDamage, AttDamage& damage, const WeaponAttachment& attachment) + { + damage.damage = jDamage.damage; + damage.minDamage = jDamage.minDamage; + damage.meleeDamage = jDamage.meleeDamage; + damage.maxDamageRange = jDamage.maxDamageRange; + damage.minDamageRange = jDamage.minDamageRange; + damage.playerDamage = jDamage.playerDamage; + damage.minPlayerDamage = jDamage.minPlayerDamage; + + return true; + } + + static bool + CreateAttLocationDamageFromJson(const JsonAttLocationDamage& jLocationDamage, AttLocationDamage& locationDamage, const WeaponAttachment& attachment) + { + locationDamage.locNone = jLocationDamage.locNone; + locationDamage.locHelmet = jLocationDamage.locHelmet; + locationDamage.locHead = jLocationDamage.locHead; + locationDamage.locNeck = jLocationDamage.locNeck; + locationDamage.locTorsoUpper = jLocationDamage.locTorsoUpper; + locationDamage.locTorsoLower = jLocationDamage.locTorsoLower; + locationDamage.locRightArmUpper = jLocationDamage.locRightArmUpper; + locationDamage.locRightArmLower = jLocationDamage.locRightArmLower; + locationDamage.locRightHand = jLocationDamage.locRightHand; + locationDamage.locLeftArmUpper = jLocationDamage.locLeftArmUpper; + locationDamage.locLeftArmLower = jLocationDamage.locLeftArmLower; + locationDamage.locLeftHand = jLocationDamage.locLeftHand; + locationDamage.locRightLegUpper = jLocationDamage.locRightLegUpper; + locationDamage.locRightLegLower = jLocationDamage.locRightLegLower; + locationDamage.locRightFoot = jLocationDamage.locRightFoot; + locationDamage.locLeftLegUpper = jLocationDamage.locLeftLegUpper; + locationDamage.locLeftLegLower = jLocationDamage.locLeftLegLower; + locationDamage.locLeftFoot = jLocationDamage.locLeftFoot; + locationDamage.locGun = jLocationDamage.locGun; + + return true; + } + + static bool CreateAttIdleSettingsFromJson(const JsonAttIdleSettings& jIdleSettings, AttIdleSettings& idleSettings, const WeaponAttachment& attachment) + { + idleSettings.hipIdleAmount = jIdleSettings.hipIdleAmount; + idleSettings.hipIdleSpeed = jIdleSettings.hipIdleSpeed; + idleSettings.idleCrouchFactor = jIdleSettings.idleCrouchFactor; + idleSettings.idleProneFactor = jIdleSettings.idleProneFactor; + idleSettings.adsIdleLerpStartTime = jIdleSettings.adsIdleLerpStartTime; + idleSettings.adsIdleLerpTime = jIdleSettings.adsIdleLerpTime; + + return true; + } + + static bool CreateAttADSSettingsFromJson(const JsonAttADSSettings& jAdsSettings, AttADSSettings& adsSettings, const WeaponAttachment& attachment) + { + adsSettings.adsSpread = jAdsSettings.adsSpread; + adsSettings.adsAimPitch = jAdsSettings.adsAimPitch; + adsSettings.adsTransInTime = jAdsSettings.adsTransInTime; + adsSettings.adsTransOutTime = jAdsSettings.adsTransOutTime; + adsSettings.adsReloadTransTime = jAdsSettings.adsReloadTransTime; + adsSettings.adsCrosshairInFrac = jAdsSettings.adsCrosshairInFrac; + adsSettings.adsCrosshairOutFrac = jAdsSettings.adsCrosshairOutFrac; + adsSettings.adsZoomFov = jAdsSettings.adsZoomFov; + adsSettings.adsZoomInFrac = jAdsSettings.adsZoomInFrac; + adsSettings.adsZoomOutFrac = jAdsSettings.adsZoomOutFrac; + adsSettings.adsBobFactor = jAdsSettings.adsBobFactor; + adsSettings.adsViewBobMult = jAdsSettings.adsViewBobMult; + adsSettings.adsViewErrorMin = jAdsSettings.adsViewErrorMin; + adsSettings.adsViewErrorMax = jAdsSettings.adsViewErrorMax; + + return true; + } + + static bool CreateAttHipSpreadFromJson(const JsonAttHipSpread& jHipSpread, AttHipSpread& hipSpread, const WeaponAttachment& attachment) + { + hipSpread.hipSpreadStandMin = jHipSpread.hipSpreadStandMin; + hipSpread.hipSpreadDuckedMin = jHipSpread.hipSpreadDuckedMin; + hipSpread.hipSpreadProneMin = jHipSpread.hipSpreadProneMin; + hipSpread.hipSpreadMax = jHipSpread.hipSpreadMax; + hipSpread.hipSpreadDuckedMax = jHipSpread.hipSpreadDuckedMax; + hipSpread.hipSpreadProneMax = jHipSpread.hipSpreadProneMax; + hipSpread.hipSpreadFireAdd = jHipSpread.hipSpreadFireAdd; + hipSpread.hipSpreadTurnAdd = jHipSpread.hipSpreadTurnAdd; + hipSpread.hipSpreadMoveAdd = jHipSpread.hipSpreadMoveAdd; + hipSpread.hipSpreadDecayRate = jHipSpread.hipSpreadDecayRate; + hipSpread.hipSpreadDuckedDecay = jHipSpread.hipSpreadDuckedDecay; + hipSpread.hipSpreadProneDecay = jHipSpread.hipSpreadProneDecay; + + return true; + } + + static bool CreateAttGunKickFromJson(const JsonAttGunKick& jGunKick, AttGunKick& gunKick, const WeaponAttachment& attachment) + { + gunKick.hipGunKickReducedKickBullets = jGunKick.hipGunKickReducedKickBullets; + gunKick.hipGunKickReducedKickPercent = jGunKick.hipGunKickReducedKickPercent; + gunKick.hipGunKickPitchMin = jGunKick.hipGunKickPitchMin; + gunKick.hipGunKickPitchMax = jGunKick.hipGunKickPitchMax; + gunKick.hipGunKickYawMin = jGunKick.hipGunKickYawMin; + gunKick.hipGunKickYawMax = jGunKick.hipGunKickYawMax; + gunKick.hipGunKickAccel = jGunKick.hipGunKickAccel; + gunKick.hipGunKickSpeedMax = jGunKick.hipGunKickSpeedMax; + gunKick.hipGunKickSpeedDecay = jGunKick.hipGunKickSpeedDecay; + gunKick.hipGunKickStaticDecay = jGunKick.hipGunKickStaticDecay; + gunKick.adsGunKickReducedKickBullets = jGunKick.adsGunKickReducedKickBullets; + gunKick.adsGunKickReducedKickPercent = jGunKick.adsGunKickReducedKickPercent; + gunKick.adsGunKickPitchMin = jGunKick.adsGunKickPitchMin; + gunKick.adsGunKickPitchMax = jGunKick.adsGunKickPitchMax; + gunKick.adsGunKickYawMin = jGunKick.adsGunKickYawMin; + gunKick.adsGunKickYawMax = jGunKick.adsGunKickYawMax; + gunKick.adsGunKickAccel = jGunKick.adsGunKickAccel; + gunKick.adsGunKickSpeedMax = jGunKick.adsGunKickSpeedMax; + gunKick.adsGunKickSpeedDecay = jGunKick.adsGunKickSpeedDecay; + gunKick.adsGunKickStaticDecay = jGunKick.adsGunKickStaticDecay; + + return true; + } + + static bool CreateAttViewKickFromJson(const JsonAttViewKick& jViewKick, AttViewKick& viewKick, const WeaponAttachment& attachment) + { + viewKick.hipViewKickPitchMin = jViewKick.hipViewKickPitchMin; + viewKick.hipViewKickPitchMax = jViewKick.hipViewKickPitchMax; + viewKick.hipViewKickYawMin = jViewKick.hipViewKickYawMin; + viewKick.hipViewKickYawMax = jViewKick.hipViewKickYawMax; + viewKick.hipViewKickCenterSpeed = jViewKick.hipViewKickCenterSpeed; + viewKick.adsViewKickPitchMin = jViewKick.adsViewKickPitchMin; + viewKick.adsViewKickPitchMax = jViewKick.adsViewKickPitchMax; + viewKick.adsViewKickYawMin = jViewKick.adsViewKickYawMin; + viewKick.adsViewKickYawMax = jViewKick.adsViewKickYawMax; + viewKick.adsViewKickCenterSpeed = jViewKick.adsViewKickCenterSpeed; + + return true; + } + + bool CreateAttADSOverlayFromJson(const JsonAttADSOverlay& jAdsOverlay, AttADSOverlay& adsOverlay, const WeaponAttachment& attachment) const + { + if (jAdsOverlay.shader) + { + if (!CreateMaterialFromJson(jAdsOverlay.shader.value(), adsOverlay.overlay.shader, attachment)) + return false; + } + else + adsOverlay.overlay.shader = nullptr; + + if (jAdsOverlay.shaderLowRes) + { + if (!CreateMaterialFromJson(jAdsOverlay.shaderLowRes.value(), adsOverlay.overlay.shaderLowRes, attachment)) + return false; + } + else + adsOverlay.overlay.shaderLowRes = nullptr; + + if (jAdsOverlay.shaderEMP) + { + if (!CreateMaterialFromJson(jAdsOverlay.shaderEMP.value(), adsOverlay.overlay.shaderEMP, attachment)) + return false; + } + else + adsOverlay.overlay.shaderEMP = nullptr; + + if (jAdsOverlay.shaderEMPLowRes) + { + if (!CreateMaterialFromJson(jAdsOverlay.shaderEMPLowRes.value(), adsOverlay.overlay.shaderEMPLowRes, attachment)) + return false; + } + else + adsOverlay.overlay.shaderEMPLowRes = nullptr; + + adsOverlay.overlay.reticle = jAdsOverlay.reticle; + adsOverlay.overlay.width = jAdsOverlay.width; + adsOverlay.overlay.height = jAdsOverlay.height; + adsOverlay.overlay.widthSplitscreen = jAdsOverlay.widthSplitscreen; + adsOverlay.overlay.heightSplitscreen = jAdsOverlay.heightSplitscreen; + adsOverlay.thermalScope = jAdsOverlay.thermalScope; + + return true; + } + + bool CreateAttUIFromJson(const JsonAttUI& jUi, AttUI& ui, const WeaponAttachment& attachment) const + { + if (jUi.dpadIcon) + { + if (!CreateMaterialFromJson(jUi.dpadIcon.value(), ui.dpadIcon, attachment)) + return false; + } + else + ui.dpadIcon = nullptr; + + if (jUi.ammoCounterIcon) + { + if (!CreateMaterialFromJson(jUi.ammoCounterIcon.value(), ui.ammoCounterIcon, attachment)) + return false; + } + else + ui.ammoCounterIcon = nullptr; + + ui.dpadIconRatio = jUi.dpadIconRatio; + ui.ammoCounterIconRatio = jUi.ammoCounterIconRatio; + ui.ammoCounterClip = jUi.ammoCounterClip; + + return true; + } + + bool CreateAttRumblesFromJson(const JsonAttRumbles& jRumbles, AttRumbles& rumbles, const WeaponAttachment& attachment) const + { + if (jRumbles.fireRumble) + rumbles.fireRumble = m_memory.Dup(jRumbles.fireRumble.value().c_str()); + else + rumbles.fireRumble = nullptr; + + if (jRumbles.meleeImpactRumble) + rumbles.meleeImpactRumble = m_memory.Dup(jRumbles.meleeImpactRumble.value().c_str()); + else + rumbles.meleeImpactRumble = nullptr; + + return true; + } + + bool CreateAttProjectileFromJson(const JsonAttProjectile& jProjectile, AttProjectile& projectile, const WeaponAttachment& attachment) const + { + projectile.explosionRadius = jProjectile.explosionRadius; + projectile.explosionInnerDamage = jProjectile.explosionInnerDamage; + projectile.explosionOuterDamage = jProjectile.explosionOuterDamage; + projectile.damageConeAngle = jProjectile.damageConeAngle; + projectile.projectileSpeed = jProjectile.projectileSpeed; + projectile.projectileSpeedUp = jProjectile.projectileSpeedUp; + projectile.projectileActivateDist = jProjectile.projectileActivateDist; + projectile.projectileLifetime = jProjectile.projectileLifetime; + + if (jProjectile.projectileModel) + { + if (!CreateXModelFromJson(jProjectile.projectileModel.value(), projectile.projectileModel, attachment)) + return false; + } + else + projectile.projectileModel = nullptr; + + projectile.projExplosionType = jProjectile.projExplosionType; + + if (jProjectile.projExplosionEffect) + { + if (!CreateFxFromJson(jProjectile.projExplosionEffect.value(), projectile.projExplosionEffect, attachment)) + return false; + } + else + projectile.projExplosionEffect = nullptr; + + projectile.projExplosionEffectForceNormalUp = jProjectile.projExplosionEffectForceNormalUp; + + if (jProjectile.projExplosionSound) + { + if (!CreateSoundFromJson(jProjectile.projExplosionSound.value(), projectile.projExplosionSound, attachment)) + return false; + } + else + projectile.projExplosionSound.name = nullptr; + + if (jProjectile.projDudEffect) + { + if (!CreateFxFromJson(jProjectile.projDudEffect.value(), projectile.projDudEffect, attachment)) + return false; + } + else + projectile.projDudEffect = nullptr; + + if (jProjectile.projDudSound) + { + if (!CreateSoundFromJson(jProjectile.projDudSound.value(), projectile.projDudSound, attachment)) + return false; + } + else + projectile.projDudSound.name = nullptr; + + projectile.projImpactExplode = jProjectile.projImpactExplode; + projectile.destabilizationRateTime = jProjectile.destabilizationRateTime; + projectile.destabilizationCurvatureMax = jProjectile.destabilizationCurvatureMax; + projectile.destabilizeDistance = jProjectile.destabilizeDistance; + + if (jProjectile.projTrailEffect) + { + if (!CreateFxFromJson(jProjectile.projTrailEffect.value(), projectile.projTrailEffect, attachment)) + return false; + } + else + projectile.projTrailEffect = nullptr; + + projectile.projIgnitionDelay = jProjectile.projIgnitionDelay; + + if (jProjectile.projIgnitionEffect) + { + if (!CreateFxFromJson(jProjectile.projIgnitionEffect.value(), projectile.projIgnitionEffect, attachment)) + return false; + } + else + projectile.projIgnitionEffect = nullptr; + + if (jProjectile.projIgnitionSound) + { + if (!CreateSoundFromJson(jProjectile.projIgnitionSound.value(), projectile.projIgnitionSound, attachment)) + return false; + } + else + projectile.projIgnitionSound.name = nullptr; + + return true; + } + + std::istream& m_stream; + MemoryManager& m_memory; + AssetCreationContext& m_context; + AssetRegistration& m_registration; + }; + class AttachmentLoader final : public AssetCreator { public: @@ -22,7 +643,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto file = m_search_path.Open(std::format("attachment/{}.json", assetName)); + const auto file = m_search_path.Open(attachment::GetJsonFileNameForAssetName(assetName)); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -30,9 +651,10 @@ namespace attachment->szInternalName = m_memory.Dup(assetName.c_str()); AssetRegistration registration(assetName, attachment); - if (!LoadWeaponAttachmentAsJson(*file.m_stream, *attachment, m_memory, context, registration)) + const JsonLoader loader(*file.m_stream, m_memory, context, registration); + if (!loader.Load(*attachment)) { - std::cerr << std::format("Failed to load attachment \"{}\"\n", assetName); + con::error("Failed to load attachment \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -45,10 +667,10 @@ namespace }; } // namespace -namespace IW5 +namespace attachment { - std::unique_ptr> CreateAttachmentLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace IW5 +} // namespace attachment diff --git a/src/ObjLoading/Game/IW5/Weapon/LoaderAttachmentIW5.h b/src/ObjLoading/Game/IW5/Weapon/LoaderAttachmentIW5.h index ec43ff96..5ba1c07e 100644 --- a/src/ObjLoading/Game/IW5/Weapon/LoaderAttachmentIW5.h +++ b/src/ObjLoading/Game/IW5/Weapon/LoaderAttachmentIW5.h @@ -7,7 +7,7 @@ #include -namespace IW5 +namespace attachment { - std::unique_ptr> CreateAttachmentLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace IW5 + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace attachment diff --git a/src/ObjLoading/Game/IW5/Weapon/RawLoaderWeaponIW5.cpp b/src/ObjLoading/Game/IW5/Weapon/RawLoaderWeaponIW5.cpp index 642efa2e..c2a7c16a 100644 --- a/src/ObjLoading/Game/IW5/Weapon/RawLoaderWeaponIW5.cpp +++ b/src/ObjLoading/Game/IW5/Weapon/RawLoaderWeaponIW5.cpp @@ -4,12 +4,15 @@ #include "Game/IW5/ObjConstantsIW5.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderWeaponIW5.h" +#include "Utils/Logging/Log.h" +#include "Weapon/WeaponCommon.h" #include #include #include using namespace IW5; +using namespace ::weapon; namespace { @@ -24,7 +27,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("weapons/{}", assetName); + const auto fileName = GetFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -32,7 +35,7 @@ namespace InfoString infoString; if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_WEAPON, *file.m_stream)) { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); + con::error("Could not parse as info string file: \"{}\"", fileName); return AssetCreationResult::Failure(); } @@ -41,14 +44,14 @@ namespace private: ISearchPath& m_search_path; - InfoStringLoaderWeapon m_info_string_loader; + weapon::InfoStringLoaderIW5 m_info_string_loader; }; } // namespace -namespace IW5 +namespace weapon { - std::unique_ptr> CreateRawWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateRawLoaderIW5(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace IW5 +} // namespace weapon diff --git a/src/ObjLoading/Game/IW5/Weapon/RawLoaderWeaponIW5.h b/src/ObjLoading/Game/IW5/Weapon/RawLoaderWeaponIW5.h index d5df5fcf..88b8053a 100644 --- a/src/ObjLoading/Game/IW5/Weapon/RawLoaderWeaponIW5.h +++ b/src/ObjLoading/Game/IW5/Weapon/RawLoaderWeaponIW5.h @@ -7,7 +7,7 @@ #include -namespace IW5 +namespace weapon { - std::unique_ptr> CreateRawWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace IW5 + std::unique_ptr> CreateRawLoaderIW5(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace weapon diff --git a/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.cpp b/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.cpp deleted file mode 100644 index 973d59f3..00000000 --- a/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// #include "LoaderXModelIW5.h" - -// #include "Game/IW5/IW5.h" -// #include "Game/IW5/XModel/XModelLoaderIW5.h" -// #include "Pool/GlobalAssetPool.h" - -// #include -// #include -// #include - -// using namespace IW5; - -// namespace -// { -// class XModelLoader final : public AssetCreator -// { -// public: -// XModelLoader(MemoryManager& memory, ISearchPath& searchPath) -// : m_memory(memory), -// m_search_path(searchPath) -// { -// } - -// AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override -// { -// const auto file = m_search_path.Open(std::format("xmodel/{}.json", assetName)); -// if (!file.IsOpen()) -// return AssetCreationResult::NoAction(); - -// auto* xmodel = m_memory.Alloc(); -// xmodel->name = m_memory.Dup(assetName.c_str()); - -// AssetRegistration registration(assetName, xmodel); -// if (!LoadXModel(*file.m_stream, *xmodel, m_memory, context, registration)) -// { -// std::cerr << std::format("Failed to load xmodel \"{}\"\n", assetName); -// return AssetCreationResult::Failure(); -// } - -// return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -// } - -// private: -// MemoryManager& m_memory; -// ISearchPath& m_search_path; -// }; -// } // namespace - -// namespace IW5 -// { -// std::unique_ptr> CreateXModelLoader(MemoryManager& memory, ISearchPath& searchPath) -// { -// return std::make_unique(memory, searchPath); -// } -// } // namespace IW5 diff --git a/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.h b/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.h deleted file mode 100644 index 3d656f21..00000000 --- a/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.h +++ /dev/null @@ -1,13 +0,0 @@ -// #pragma once - -// #include "Asset/IAssetCreator.h" -// #include "Game/IW5/IW5.h" -// #include "SearchPath/ISearchPath.h" -// #include "Utils/MemoryManager.h" - -// #include - -// namespace IW5 -// { -// std::unique_ptr> CreateXModelLoader(MemoryManager& memory, ISearchPath& searchPath); -// } // namespace IW5 diff --git a/src/ObjLoading/Game/T5/Localize/LoaderLocalizeT5.cpp b/src/ObjLoading/Game/T5/Localize/LoaderLocalizeT5.cpp index 175b71fb..c66a7f93 100644 --- a/src/ObjLoading/Game/T5/Localize/LoaderLocalizeT5.cpp +++ b/src/ObjLoading/Game/T5/Localize/LoaderLocalizeT5.cpp @@ -35,10 +35,10 @@ namespace }; } // namespace -namespace T5 +namespace localize { - std::unique_ptr> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateLoaderT5(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace T5 +} // namespace localize diff --git a/src/ObjLoading/Game/T5/Localize/LoaderLocalizeT5.h b/src/ObjLoading/Game/T5/Localize/LoaderLocalizeT5.h index fd71deb5..a6d14d5e 100644 --- a/src/ObjLoading/Game/T5/Localize/LoaderLocalizeT5.h +++ b/src/ObjLoading/Game/T5/Localize/LoaderLocalizeT5.h @@ -8,7 +8,7 @@ #include -namespace T5 +namespace localize { - std::unique_ptr> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace T5 + std::unique_ptr> CreateLoaderT5(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace localize diff --git a/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.cpp b/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.cpp new file mode 100644 index 00000000..44469186 --- /dev/null +++ b/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.cpp @@ -0,0 +1,55 @@ +#include "LoaderMaterialT5.h" + +#include "Game/T5/Material/JsonMaterialLoaderT5.h" +#include "Game/T5/T5.h" +#include "Material/MaterialCommon.h" +#include "Utils/Logging/Log.h" + +#include +#include + +using namespace T5; + +namespace +{ + class MaterialLoader final : public AssetCreator + { + public: + MaterialLoader(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto file = m_search_path.Open(material::GetFileNameForAssetName(assetName)); + if (!file.IsOpen()) + return AssetCreationResult::NoAction(); + + auto* material = m_memory.Alloc(); + material->info.name = m_memory.Dup(assetName.c_str()); + + AssetRegistration registration(assetName, material); + if (!LoadMaterialAsJson(*file.m_stream, *material, m_memory, context, registration)) + { + con::error("Failed to load material \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; +} // namespace + +namespace material +{ + std::unique_ptr> CreateLoaderT5(MemoryManager& memory, ISearchPath& searchPath) + { + return std::make_unique(memory, searchPath); + } +} // namespace material diff --git a/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.h b/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.h new file mode 100644 index 00000000..8b11165a --- /dev/null +++ b/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T5/T5.h" +#include "Gdt/IGdtQueryable.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +namespace material +{ + std::unique_ptr> CreateLoaderT5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace material diff --git a/src/ObjLoading/Game/T5/ObjLoaderT5.cpp b/src/ObjLoading/Game/T5/ObjLoaderT5.cpp index 1f4d8132..cc67cc5a 100644 --- a/src/ObjLoading/Game/T5/ObjLoaderT5.cpp +++ b/src/ObjLoading/Game/T5/ObjLoaderT5.cpp @@ -5,6 +5,7 @@ #include "Game/T5/T5.h" #include "Game/T5/XModel/LoaderXModelT5.h" #include "Localize/LoaderLocalizeT5.h" +#include "Material/LoaderMaterialT5.h" #include "ObjLoading.h" #include "RawFile/LoaderRawFileT5.h" #include "StringTable/LoaderStringTableT5.h" @@ -103,8 +104,8 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateXModelLoader(memory, searchPath, zone)); - // collection.AddAssetCreator(std::make_unique(memory)); + collection.AddAssetCreator(xmodel::CreateLoaderT5(memory, searchPath, zone)); + collection.AddAssetCreator(material::CreateLoaderT5(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); @@ -119,13 +120,13 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateLocalizeLoader(memory, searchPath, zone)); + collection.AddAssetCreator(localize::CreateLoaderT5(memory, searchPath, zone)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateRawFileLoader(memory, searchPath)); - collection.AddAssetCreator(CreateStringTableLoader(memory, searchPath)); + collection.AddAssetCreator(raw_file::CreateLoaderT5(memory, searchPath)); + collection.AddAssetCreator(string_table::CreateLoaderT5(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); diff --git a/src/ObjLoading/Game/T5/RawFile/LoaderRawFileT5.cpp b/src/ObjLoading/Game/T5/RawFile/LoaderRawFileT5.cpp index ec7ec152..4a49a980 100644 --- a/src/ObjLoading/Game/T5/RawFile/LoaderRawFileT5.cpp +++ b/src/ObjLoading/Game/T5/RawFile/LoaderRawFileT5.cpp @@ -1,6 +1,7 @@ #include "LoaderRawFileT5.h" #include "Game/T5/T5.h" +#include "Utils/Logging/Log.h" #include #include @@ -71,7 +72,7 @@ namespace if (ret != Z_STREAM_END) { - std::cerr << std::format("Deflate failed for loading gsc file \"{}\"\n", assetName); + con::error("Deflate failed for loading gsc file \"{}\"", assetName); deflateEnd(&zs); return AssetCreationResult::Failure(); } @@ -114,10 +115,10 @@ namespace }; } // namespace -namespace T5 +namespace raw_file { - std::unique_ptr> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderT5(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace T5 +} // namespace raw_file diff --git a/src/ObjLoading/Game/T5/RawFile/LoaderRawFileT5.h b/src/ObjLoading/Game/T5/RawFile/LoaderRawFileT5.h index 42e83ed3..ba06bd12 100644 --- a/src/ObjLoading/Game/T5/RawFile/LoaderRawFileT5.h +++ b/src/ObjLoading/Game/T5/RawFile/LoaderRawFileT5.h @@ -7,7 +7,7 @@ #include -namespace T5 +namespace raw_file { - std::unique_ptr> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T5 + std::unique_ptr> CreateLoaderT5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace raw_file diff --git a/src/ObjLoading/Game/T5/StringTable/LoaderStringTableT5.cpp b/src/ObjLoading/Game/T5/StringTable/LoaderStringTableT5.cpp index f50b423a..b1085572 100644 --- a/src/ObjLoading/Game/T5/StringTable/LoaderStringTableT5.cpp +++ b/src/ObjLoading/Game/T5/StringTable/LoaderStringTableT5.cpp @@ -37,10 +37,10 @@ namespace }; } // namespace -namespace T5 +namespace string_table { - std::unique_ptr> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderT5(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace T5 +} // namespace string_table diff --git a/src/ObjLoading/Game/T5/StringTable/LoaderStringTableT5.h b/src/ObjLoading/Game/T5/StringTable/LoaderStringTableT5.h index 3479639b..81146e31 100644 --- a/src/ObjLoading/Game/T5/StringTable/LoaderStringTableT5.h +++ b/src/ObjLoading/Game/T5/StringTable/LoaderStringTableT5.h @@ -7,7 +7,7 @@ #include -namespace T5 +namespace string_table { - std::unique_ptr> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T5 + std::unique_ptr> CreateLoaderT5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace string_table diff --git a/src/ObjLoading/Game/T6/FontIcon/LoaderFontIconT6.cpp b/src/ObjLoading/Game/T6/FontIcon/CsvLoaderFontIconT6.cpp similarity index 83% rename from src/ObjLoading/Game/T6/FontIcon/LoaderFontIconT6.cpp rename to src/ObjLoading/Game/T6/FontIcon/CsvLoaderFontIconT6.cpp index 43cde72c..0df26bc3 100644 --- a/src/ObjLoading/Game/T6/FontIcon/LoaderFontIconT6.cpp +++ b/src/ObjLoading/Game/T6/FontIcon/CsvLoaderFontIconT6.cpp @@ -1,8 +1,9 @@ -#include "LoaderFontIconT6.h" +#include "CsvLoaderFontIconT6.h" #include "Csv/CsvStream.h" #include "Game/T6/CommonT6.h" #include "Game/T6/T6.h" +#include "Utils/Logging/Log.h" #include #include @@ -30,10 +31,10 @@ namespace constexpr unsigned COL_COUNT_ALIAS = 4; constexpr unsigned COL_COUNT_MIN = std::min(COL_COUNT_ICON, COL_COUNT_ALIAS); - class FontIconLoader final : public AssetCreator + class CsvFontIconLoader final : public AssetCreator { public: - FontIconLoader(MemoryManager& memory, ISearchPath& searchPath) + CsvFontIconLoader(MemoryManager& memory, ISearchPath& searchPath) : m_memory(memory), m_search_path(searchPath) { @@ -65,14 +66,14 @@ namespace if (currentRow.size() < COL_COUNT_MIN) { - std::cerr << std::format("{} Column count lower than min column count ({})\n", ErrorPrefix(assetName, currentRowIndex), COL_COUNT_MIN); + con::error("{} Column count lower than min column count ({})", ErrorPrefix(assetName, currentRowIndex), COL_COUNT_MIN); return AssetCreationResult::Failure(); } int index; if (!ParseInt(index, currentRow[ROW_INDEX]) || index < 0) { - std::cerr << std::format("{} Failed to parse index\n", ErrorPrefix(assetName, currentRowIndex)); + con::error("{} Failed to parse index", ErrorPrefix(assetName, currentRowIndex)); return AssetCreationResult::Failure(); } @@ -118,7 +119,7 @@ namespace } else { - std::cerr << std::format("{} Unknown row type \"{}\"\n", ErrorPrefix(assetName, currentRowIndex), currentRow[ROW_TYPE]); + con::error("{} Unknown row type \"{}\"", ErrorPrefix(assetName, currentRowIndex), currentRow[ROW_TYPE]); return AssetCreationResult::Failure(); } } @@ -222,26 +223,26 @@ namespace { if (row.size() < COL_COUNT_ICON) { - std::cerr << std::format("{} Column count lower than min column count for entries ({})\n", ErrorPrefix(assetName, rowIndex), COL_COUNT_ICON); + con::error("{} Column count lower than min column count for entries ({})", ErrorPrefix(assetName, rowIndex), COL_COUNT_ICON); return false; } if (!ParseInt(icon.fontIconSize, row[ROW_ICON_SIZE])) { - std::cerr << std::format("{} Failed to parse size\n", ErrorPrefix(assetName, rowIndex)); + con::error("{} Failed to parse size", ErrorPrefix(assetName, rowIndex)); return false; } if (!ParseFloat(icon.xScale, row[ROW_ICON_XSCALE]) || !ParseFloat(icon.yScale, row[ROW_ICON_YSCALE])) { - std::cerr << std::format("{} Failed to parse scale\n", ErrorPrefix(assetName, rowIndex)); + con::error("{} Failed to parse scale", ErrorPrefix(assetName, rowIndex)); return false; } auto* materialDependency = context.LoadDependency(row[ROW_ICON_MATERIAL]); if (materialDependency == nullptr) { - std::cerr << std::format("{} Failed to load material \"{}\"\n", ErrorPrefix(assetName, rowIndex), row[ROW_ICON_MATERIAL]); + con::error("{} Failed to load material \"{}\"", ErrorPrefix(assetName, rowIndex), row[ROW_ICON_MATERIAL]); return false; } registration.AddDependency(materialDependency); @@ -258,19 +259,19 @@ namespace { if (row.size() < COL_COUNT_ALIAS) { - std::cerr << std::format("{} Column count lower than min column count for aliases ({})\n", ErrorPrefix(assetName, rowIndex), COL_COUNT_ALIAS); + con::error("{} Column count lower than min column count for aliases ({})", ErrorPrefix(assetName, rowIndex), COL_COUNT_ALIAS); return false; } if (!ParseHashStr(alias.aliasHash, row[ROW_ALIAS_NAME])) { - std::cerr << std::format("{} Failed to parse alias \"{}\"\n", ErrorPrefix(assetName, rowIndex), row[ROW_ALIAS_NAME]); + con::error("{} Failed to parse alias \"{}\"", ErrorPrefix(assetName, rowIndex), row[ROW_ALIAS_NAME]); return false; } if (!ParseHashStr(alias.buttonHash, row[ROW_ALIAS_BUTTON])) { - std::cerr << std::format("{} Failed to parse button \"{}\"\n", ErrorPrefix(assetName, rowIndex), row[ROW_ALIAS_BUTTON]); + con::error("{} Failed to parse button \"{}\"", ErrorPrefix(assetName, rowIndex), row[ROW_ALIAS_BUTTON]); return false; } @@ -282,10 +283,10 @@ namespace }; } // namespace -namespace T6 +namespace font_icon { - std::unique_ptr> CreateFontIconLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateCsvLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { - return std::make_unique(memory, searchPath); + return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace font_icon diff --git a/src/ObjLoading/Game/T6/FontIcon/CsvLoaderFontIconT6.h b/src/ObjLoading/Game/T6/FontIcon/CsvLoaderFontIconT6.h new file mode 100644 index 00000000..ea6d60a2 --- /dev/null +++ b/src/ObjLoading/Game/T6/FontIcon/CsvLoaderFontIconT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T6/T6.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace font_icon +{ + std::unique_ptr> CreateCsvLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace font_icon diff --git a/src/ObjLoading/Game/T6/FontIcon/JsonLoaderFontIconT6.cpp b/src/ObjLoading/Game/T6/FontIcon/JsonLoaderFontIconT6.cpp new file mode 100644 index 00000000..01dc5698 --- /dev/null +++ b/src/ObjLoading/Game/T6/FontIcon/JsonLoaderFontIconT6.cpp @@ -0,0 +1,151 @@ +#include "JsonLoaderFontIconT6.h" + +#include "FontIcon/FontIconCommon.h" +#include "Game/T6/CommonT6.h" +#include "Game/T6/FontIcon/JsonFontIconT6.h" +#include "Game/T6/T6.h" +#include "Utils/Logging/Log.h" + +#include +#include +#include +#include + +using namespace nlohmann; +using namespace T6; + +namespace +{ + class JsonFontIconLoader final : public AssetCreator + { + public: + JsonFontIconLoader(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto file = m_search_path.Open(font_icon::GetJsonFileNameForAssetName(assetName)); + if (!file.IsOpen()) + return AssetCreationResult::NoAction(); + + auto* fontIcon = m_memory.Alloc(); + fontIcon->name = m_memory.Dup(assetName.c_str()); + AssetRegistration registration(assetName, fontIcon); + + try + { + const auto jRoot = json::parse(*file.m_stream); + std::string type; + unsigned version; + + jRoot.at("_type").get_to(type); + jRoot.at("_version").get_to(version); + + if (type != "font-icon" || version != 1u) + { + con::error("Tried to load font icon \"{}\" but did not find expected type font-icon of version 1", assetName); + return AssetCreationResult::Failure(); + } + + const auto jFontIcon = jRoot.get(); + if (CreateFontIconFromJson(jFontIcon, *fontIcon, registration, context)) + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } + catch (const json::exception& e) + { + con::error("Failed to parse json of font icon: {}", e.what()); + } + + return AssetCreationResult::Failure(); + } + + private: + bool CreateFontIconFromJson(const JsonFontIcon& jFontIcon, + FontIcon& fontIcon, + AssetRegistration& registration, + AssetCreationContext& context) const + { + std::vector aliases; + + fontIcon.numEntries = static_cast(jFontIcon.entries.size()); + fontIcon.fontIconEntry = m_memory.Alloc(fontIcon.numEntries); + + for (auto entryIndex = 0u; entryIndex < fontIcon.numEntries; entryIndex++) + { + if (!CreateFontIconEntryFromJson(jFontIcon.entries[entryIndex], fontIcon.fontIconEntry[entryIndex], aliases, registration, context)) + return false; + } + + std::sort(fontIcon.fontIconEntry, + &fontIcon.fontIconEntry[fontIcon.numEntries], + [](const FontIconEntry& e0, const FontIconEntry& e1) -> bool + { + return e0.fontIconName.hash < e1.fontIconName.hash; + }); + + if (!aliases.empty()) + { + fontIcon.numAliasEntries = static_cast(aliases.size()); + fontIcon.fontIconAlias = m_memory.Alloc(fontIcon.numAliasEntries); + std::memcpy(fontIcon.fontIconAlias, aliases.data(), sizeof(FontIconAlias) * fontIcon.numAliasEntries); + + std::sort(fontIcon.fontIconAlias, + &fontIcon.fontIconAlias[fontIcon.numAliasEntries], + [](const FontIconAlias& a0, const FontIconAlias& a1) -> bool + { + return a0.aliasHash < a1.aliasHash; + }); + } + + return true; + } + + bool CreateFontIconEntryFromJson(const JsonFontIconEntry& jFontIconEntry, + FontIconEntry& fontIconEntry, + std::vector& aliases, + AssetRegistration& registration, + AssetCreationContext& context) const + { + fontIconEntry.fontIconName.string = m_memory.Dup(jFontIconEntry.name.c_str()); + fontIconEntry.fontIconName.hash = Common::Com_HashString(jFontIconEntry.name.c_str()); + + auto* materialDependency = context.LoadDependency(jFontIconEntry.material); + if (materialDependency == nullptr) + { + con::error("Failed to load material \"{}\" for font icon entry \"{}\"", jFontIconEntry.material, jFontIconEntry.name); + return false; + } + registration.AddDependency(materialDependency); + fontIconEntry.fontIconMaterialHandle = materialDependency->Asset(); + + fontIconEntry.fontIconSize = static_cast(jFontIconEntry.size); + fontIconEntry.xScale = jFontIconEntry.scale.has_value() ? jFontIconEntry.scale->x : 0; + fontIconEntry.yScale = jFontIconEntry.scale.has_value() ? jFontIconEntry.scale->y : 0; + + for (const auto& alias : jFontIconEntry.aliases) + aliases.emplace_back(Common::Com_HashString(alias.c_str()), fontIconEntry.fontIconName.hash); + + if (jFontIconEntry.aliasHashes.has_value()) + { + for (const auto aliasHash : *jFontIconEntry.aliasHashes) + aliases.emplace_back(aliasHash, fontIconEntry.fontIconName.hash); + } + + return true; + } + + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; +} // namespace + +namespace font_icon +{ + std::unique_ptr> CreateJsonLoaderT6(MemoryManager& memory, ISearchPath& searchPath) + { + return std::make_unique(memory, searchPath); + } +} // namespace font_icon diff --git a/src/ObjLoading/Game/T6/FontIcon/JsonLoaderFontIconT6.h b/src/ObjLoading/Game/T6/FontIcon/JsonLoaderFontIconT6.h new file mode 100644 index 00000000..262354df --- /dev/null +++ b/src/ObjLoading/Game/T6/FontIcon/JsonLoaderFontIconT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T6/T6.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace font_icon +{ + std::unique_ptr> CreateJsonLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace font_icon diff --git a/src/ObjLoading/Game/T6/FontIcon/LoaderFontIconT6.h b/src/ObjLoading/Game/T6/FontIcon/LoaderFontIconT6.h deleted file mode 100644 index e9f658e8..00000000 --- a/src/ObjLoading/Game/T6/FontIcon/LoaderFontIconT6.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Asset/IAssetCreator.h" -#include "Game/T6/T6.h" -#include "SearchPath/ISearchPath.h" -#include "Utils/MemoryManager.h" - -#include - -namespace T6 -{ - std::unique_ptr> CreateFontIconLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Image/LoaderImageT6.cpp b/src/ObjLoading/Game/T6/Image/LoaderImageT6.cpp index 4a27e276..e870761c 100644 --- a/src/ObjLoading/Game/T6/Image/LoaderImageT6.cpp +++ b/src/ObjLoading/Game/T6/Image/LoaderImageT6.cpp @@ -2,8 +2,9 @@ #include "Game/T6/CommonT6.h" #include "Game/T6/T6.h" +#include "Image/ImageCommon.h" #include "Image/IwiLoader.h" -#include "Image/IwiTypes.h" +#include "Utils/Logging/Log.h" #include #include @@ -26,12 +27,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - auto fileName = std::format("images/{}.iwi", assetName); - - for (size_t i = 0; i < fileName.size(); i++) - if (fileName[i] == '*') - fileName[i] = '_'; - + const auto fileName = image::GetFileNameForAsset(assetName, ".iwi"); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -45,7 +41,7 @@ namespace const auto texture = iwi::LoadIwi(ss); if (!texture) { - std::cerr << std::format("Failed to load texture from: {}\n", fileName); + con::error("Failed to load texture from: {}", fileName); return AssetCreationResult::Failure(); } @@ -59,47 +55,11 @@ namespace image->height = static_cast(texture->GetHeight()); image->depth = static_cast(texture->GetDepth()); - if (texture->GetTextureType() == TextureType::T_2D) - image->mapType = 3; - else if (texture->GetTextureType() == TextureType::T_3D) - image->mapType = 4; - else if (texture->GetTextureType() == TextureType::T_CUBE) - image->mapType = 5; - else - _ASSERT(false); - - //image->streaming = 1; - //image->streamedParts[0].levelCount = 1; - //image->streamedParts[0].levelSize = static_cast(fileSize); - //image->streamedParts[0].hash = dataHash & 0x1FFFFFFF; - //image->streamedPartCount = 1; - - int mipMapCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1; - size_t textureSize = 0; - for (int previousMipLevel = 0; previousMipLevel < mipMapCount; previousMipLevel++) - { - textureSize += texture->GetSizeOfMipLevel(previousMipLevel) * texture->GetFaceCount(); - } - - image->streaming = 0; - image->texture.loadDef = (GfxImageLoadDef*)malloc(sizeof(GfxImageLoadDef) + textureSize); - memset(image->texture.loadDef, 0, sizeof(GfxImageLoadDef) + textureSize); - image->texture.loadDef->format = texture->GetFormat()->GetDxgiFormat(); - image->texture.loadDef->levelCount = 1; - image->texture.loadDef->resourceSize = textureSize; - memcpy(image->texture.loadDef->data, - texture->GetBufferForMipLevel(0), - textureSize); // GetBufferForMipLevel(0) returns a pointer to the start of the image - - image->texture.loadDef->flags = 0; - if (image->noPicmip) - image->texture.loadDef->flags |= iwi27::IMG_FLAG_NOMIPMAPS; - - if (texture->GetTextureType() == TextureType::T_3D) - image->texture.loadDef->flags |= iwi27::IMG_FLAG_VOLMAP; - - if (texture->GetTextureType() == TextureType::T_CUBE) - image->texture.loadDef->flags |= iwi27::IMG_FLAG_CUBEMAP; + image->streaming = 1; + image->streamedParts[0].levelCount = 1; + image->streamedParts[0].levelSize = static_cast(fileSize); + image->streamedParts[0].hash = dataHash & 0x1FFFFFFF; + image->streamedPartCount = 1; return AssetCreationResult::Success(context.AddAsset(assetName, image)); } @@ -110,10 +70,10 @@ namespace }; } // namespace -namespace T6 +namespace image { - std::unique_ptr> CreateImageLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace image diff --git a/src/ObjLoading/Game/T6/Image/LoaderImageT6.h b/src/ObjLoading/Game/T6/Image/LoaderImageT6.h index 6c1bfb1f..610af102 100644 --- a/src/ObjLoading/Game/T6/Image/LoaderImageT6.h +++ b/src/ObjLoading/Game/T6/Image/LoaderImageT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace image { - std::unique_ptr> CreateImageLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace image diff --git a/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.cpp b/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.cpp index d7fd2678..1338bb2c 100644 --- a/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.cpp +++ b/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.cpp @@ -1,6 +1,7 @@ #include "InfoStringToStructConverter.h" #include "Game/T6/CommonT6.h" +#include "Utils/Logging/Log.h" #include #include @@ -83,7 +84,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (fx == nullptr) { - std::cerr << std::format("Failed to load fx asset \"{}\"\n", value); + con::error("Failed to load fx asset \"{}\"", value); return false; } @@ -105,7 +106,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (xmodel == nullptr) { - std::cerr << std::format("Failed to load xmodel asset \"{}\"\n", value); + con::error("Failed to load xmodel asset \"{}\"", value); return false; } @@ -128,7 +129,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (material == nullptr) { - std::cerr << std::format("Failed to load material asset \"{}\"\n", value); + con::error("Failed to load material asset \"{}\"", value); return false; } @@ -150,7 +151,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (physPreset == nullptr) { - std::cerr << std::format("Failed to load physpreset asset \"{}\"\n", value); + con::error("Failed to load physpreset asset \"{}\"", value); return false; } @@ -175,7 +176,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons if (tracer == nullptr) { - std::cerr << std::format("Failed to load tracer asset \"{}\"\n", value); + con::error("Failed to load tracer asset \"{}\"", value); return false; } @@ -190,7 +191,7 @@ bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, cons unsigned int soundAliasHash; if (!GetHashValue(value, soundAliasHash)) { - std::cerr << std::format("Failed to parse value \"{}\" as hash\n", value); + con::error("Failed to parse value \"{}\" as hash", value); return false; } diff --git a/src/ObjLoading/Game/T6/Leaderboard/JsonLeaderboardDefLoader.h b/src/ObjLoading/Game/T6/Leaderboard/JsonLeaderboardDefLoader.h deleted file mode 100644 index 6145d04d..00000000 --- a/src/ObjLoading/Game/T6/Leaderboard/JsonLeaderboardDefLoader.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Game/T6/T6.h" -#include "Utils/MemoryManager.h" - -#include - -namespace T6 -{ - bool LoadLeaderboardAsJson(std::istream& stream, LeaderboardDef& leaderboard, MemoryManager& memory); -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Leaderboard/JsonLeaderboardDefLoader.cpp b/src/ObjLoading/Game/T6/Leaderboard/JsonLoaderLeaderboardT6.cpp similarity index 63% rename from src/ObjLoading/Game/T6/Leaderboard/JsonLeaderboardDefLoader.cpp rename to src/ObjLoading/Game/T6/Leaderboard/JsonLoaderLeaderboardT6.cpp index 6e8686c3..65c71ce3 100644 --- a/src/ObjLoading/Game/T6/Leaderboard/JsonLeaderboardDefLoader.cpp +++ b/src/ObjLoading/Game/T6/Leaderboard/JsonLoaderLeaderboardT6.cpp @@ -1,8 +1,12 @@ -#include "JsonLeaderboardDefLoader.h" +#include "JsonLoaderLeaderboardT6.h" #include "Game/T6/CommonT6.h" #include "Game/T6/Leaderboard/JsonLeaderboardDef.h" +#include "Game/T6/T6.h" +#include "Leaderboard/LeaderboardCommon.h" +#include "Utils/Logging/Log.h" +#include #include #include #include @@ -23,27 +27,27 @@ namespace bool Load(LeaderboardDef& leaderboardDef) const { - const auto jRoot = json::parse(m_stream); - std::string type; - unsigned version; - - jRoot.at("_type").get_to(type); - jRoot.at("_version").get_to(version); - - if (type != "leaderboard" || version != 1u) - { - std::cerr << std::format("Tried to load leaderboard \"{}\" but did not find expected type leaderboard of version 1\n", leaderboardDef.name); - return false; - } - try { + const auto jRoot = json::parse(m_stream); + std::string type; + unsigned version; + + jRoot.at("_type").get_to(type); + jRoot.at("_version").get_to(version); + + if (type != "leaderboard" || version != 1u) + { + con::error("Tried to load leaderboard \"{}\" but did not find expected type leaderboard of version 1", leaderboardDef.name); + return false; + } + const auto jLeaderboard = jRoot.get(); return CreateLeaderboardFromJson(jLeaderboard, leaderboardDef); } catch (const json::exception& e) { - std::cerr << std::format("Failed to parse json of leaderboard: {}\n", e.what()); + con::error("Failed to parse json of leaderboard: {}", e.what()); } return false; @@ -124,13 +128,45 @@ namespace std::istream& m_stream; MemoryManager& m_memory; }; + + class LeaderboardLoader final : public AssetCreator + { + public: + LeaderboardLoader(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto file = m_search_path.Open(leaderboard::GetJsonFileNameForAsset(assetName)); + if (!file.IsOpen()) + return AssetCreationResult::NoAction(); + + auto* leaderboardDef = m_memory.Alloc(); + leaderboardDef->name = m_memory.Dup(assetName.c_str()); + + const JsonLoader loader(*file.m_stream, m_memory); + if (!loader.Load(*leaderboardDef)) + { + con::error("Failed to load leaderboard \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + return AssetCreationResult::Success(context.AddAsset(assetName, leaderboardDef)); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; } // namespace -namespace T6 +namespace leaderboard { - bool LoadLeaderboardAsJson(std::istream& stream, LeaderboardDef& leaderboard, MemoryManager& memory) + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { - const JsonLoader loader(stream, memory); - return loader.Load(leaderboard); + return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace leaderboard diff --git a/src/ObjLoading/Game/T6/Leaderboard/JsonLoaderLeaderboardT6.h b/src/ObjLoading/Game/T6/Leaderboard/JsonLoaderLeaderboardT6.h new file mode 100644 index 00000000..cbe7c604 --- /dev/null +++ b/src/ObjLoading/Game/T6/Leaderboard/JsonLoaderLeaderboardT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T6/T6.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace leaderboard +{ + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace leaderboard diff --git a/src/ObjLoading/Game/T6/Leaderboard/LoaderLeaderboardT6.cpp b/src/ObjLoading/Game/T6/Leaderboard/LoaderLeaderboardT6.cpp deleted file mode 100644 index ae08c020..00000000 --- a/src/ObjLoading/Game/T6/Leaderboard/LoaderLeaderboardT6.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "LoaderLeaderboardT6.h" - -#include "Game/T6/CommonT6.h" -#include "Game/T6/T6.h" -#include "JsonLeaderboardDefLoader.h" - -#include -#include -#include - -using namespace T6; - -namespace -{ - class LeaderboardLoader final : public AssetCreator - { - public: - LeaderboardLoader(MemoryManager& memory, ISearchPath& searchPath) - : m_memory(memory), - m_search_path(searchPath) - { - } - - AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override - { - const auto file = m_search_path.Open(std::format("leaderboards/{}.json", assetName)); - if (!file.IsOpen()) - return AssetCreationResult::NoAction(); - - auto* leaderboardDef = m_memory.Alloc(); - leaderboardDef->name = m_memory.Dup(assetName.c_str()); - - if (!LoadLeaderboardAsJson(*file.m_stream, *leaderboardDef, m_memory)) - { - std::cerr << std::format("Failed to load leaderboard \"{}\"\n", assetName); - return AssetCreationResult::Failure(); - } - - return AssetCreationResult::Success(context.AddAsset(assetName, leaderboardDef)); - } - - private: - MemoryManager& m_memory; - ISearchPath& m_search_path; - }; -} // namespace - -namespace T6 -{ - std::unique_ptr> CreateLeaderboardLoader(MemoryManager& memory, ISearchPath& searchPath) - { - return std::make_unique(memory, searchPath); - } -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Leaderboard/LoaderLeaderboardT6.h b/src/ObjLoading/Game/T6/Leaderboard/LoaderLeaderboardT6.h deleted file mode 100644 index df060b90..00000000 --- a/src/ObjLoading/Game/T6/Leaderboard/LoaderLeaderboardT6.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Asset/IAssetCreator.h" -#include "Game/T6/T6.h" -#include "SearchPath/ISearchPath.h" -#include "Utils/MemoryManager.h" - -#include - -namespace T6 -{ - std::unique_ptr> CreateLeaderboardLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Localize/LoaderLocalizeT6.cpp b/src/ObjLoading/Game/T6/Localize/LocalizeLoaderT6.cpp similarity index 86% rename from src/ObjLoading/Game/T6/Localize/LoaderLocalizeT6.cpp rename to src/ObjLoading/Game/T6/Localize/LocalizeLoaderT6.cpp index c4a3b77d..a51abb2f 100644 --- a/src/ObjLoading/Game/T6/Localize/LoaderLocalizeT6.cpp +++ b/src/ObjLoading/Game/T6/Localize/LocalizeLoaderT6.cpp @@ -1,4 +1,4 @@ -#include "LoaderLocalizeT6.h" +#include "LocalizeLoaderT6.h" #include "Localize/CommonLocalizeLoader.h" @@ -35,10 +35,10 @@ namespace }; } // namespace -namespace T6 +namespace localize { - std::unique_ptr> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace T6 +} // namespace localize diff --git a/src/ObjLoading/Game/T6/Localize/LoaderLocalizeT6.h b/src/ObjLoading/Game/T6/Localize/LocalizeLoaderT6.h similarity index 51% rename from src/ObjLoading/Game/T6/Localize/LoaderLocalizeT6.h rename to src/ObjLoading/Game/T6/Localize/LocalizeLoaderT6.h index 991f53b1..dc7f49eb 100644 --- a/src/ObjLoading/Game/T6/Localize/LoaderLocalizeT6.h +++ b/src/ObjLoading/Game/T6/Localize/LocalizeLoaderT6.h @@ -8,7 +8,7 @@ #include -namespace T6 +namespace localize { - std::unique_ptr> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace localize diff --git a/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.cpp b/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.cpp index 5458b96e..8f6bd9c4 100644 --- a/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.cpp +++ b/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.cpp @@ -3,6 +3,7 @@ #include "Game/T6/Material/JsonMaterialLoaderT6.h" #include "Game/T6/T6.h" #include "Material/MaterialCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -32,7 +33,7 @@ namespace AssetRegistration registration(assetName, material); if (!LoadMaterialAsJson(*file.m_stream, *material, m_memory, context, registration)) { - std::cerr << std::format("Failed to load material \"{}\"\n", assetName); + con::error("Failed to load material \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -45,10 +46,10 @@ namespace }; } // namespace -namespace T6 +namespace material { - std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace material diff --git a/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.h b/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.h index 22cd78dc..af1a1f92 100644 --- a/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.h +++ b/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace material { - std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace material diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index 18f79214..796278d6 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -1,7 +1,8 @@ #include "ObjLoaderT6.h" #include "Asset/GlobalAssetPoolsLoader.h" -#include "FontIcon/LoaderFontIconT6.h" +#include "FontIcon/CsvLoaderFontIconT6.h" +#include "FontIcon/JsonLoaderFontIconT6.h" #include "Game/T6/CommonT6.h" #include "Game/T6/GameAssetPoolT6.h" #include "Game/T6/GameT6.h" @@ -12,8 +13,8 @@ #include "Image/IwiTypes.h" #include "Image/LoaderImageT6.h" #include "Image/Texture.h" -#include "Leaderboard/LoaderLeaderboardT6.h" -#include "Localize/LoaderLocalizeT6.h" +#include "Leaderboard/JsonLoaderLeaderboardT6.h" +#include "Localize/LocalizeLoaderT6.h" #include "Material/LoaderMaterialT6.h" #include "ObjContainer/IPak/IPak.h" #include "ObjLoading.h" @@ -29,19 +30,18 @@ #include "StringTable/LoaderStringTableT6.h" #include "Tracer/GdtLoaderTracerT6.h" #include "Tracer/RawLoaderTracerT6.h" +#include "Utils/Logging/Log.h" #include "Vehicle/GdtLoaderVehicleT6.h" #include "Vehicle/RawLoaderVehicleT6.h" -#include "Weapon/GdtLoaderAttachmentT6.h" -#include "Weapon/GdtLoaderAttachmentUniqueT6.h" -#include "Weapon/GdtLoaderWeaponT6.h" -#include "Weapon/LoaderWeaponCamoT6.h" -#include "Weapon/RawLoaderAttachmentT6.h" -#include "Weapon/RawLoaderAttachmentUniqueT6.h" -#include "Weapon/RawLoaderWeaponT6.h" +#include "Weapon/AttachmentGdtLoaderT6.h" +#include "Weapon/AttachmentRawLoaderT6.h" +#include "Weapon/AttachmentUniqueGdtLoaderT6.h" +#include "Weapon/AttachmentUniqueRawLoaderT6.h" +#include "Weapon/CamoJsonLoaderT6.h" +#include "Weapon/WeaponGdtLoaderT6.h" +#include "Weapon/WeaponRawLoaderT6.h" #include "ZBarrier/GdtLoaderZBarrierT6.h" #include "ZBarrier/RawLoaderZBarrierT6.h" -#include "CustomMap/LoaderCustomMapT6.h" -#include "TechniqueSet/LoaderTechniqueSetT6.h" #include #include @@ -63,14 +63,12 @@ namespace T6 SoundBank* ObjLoader::LoadSoundBankForZone(ISearchPath& searchPath, const std::string& soundBankFileName, Zone& zone) { - if (ObjLoading::Configuration.Verbose) - std::cout << std::format("Trying to load sound bank '{}' for zone '{}'\n", soundBankFileName, zone.m_name); + con::debug("Trying to load sound bank '{}' for zone '{}'", soundBankFileName, zone.m_name); auto* existingSoundBank = SoundBank::Repository.GetContainerByName(soundBankFileName); if (existingSoundBank != nullptr) { - if (ObjLoading::Configuration.Verbose) - std::cout << std::format("Referencing loaded sound bank '{}'.\n", soundBankFileName); + con::debug("Referencing loaded sound bank '{}'.", soundBankFileName); SoundBank::Repository.AddContainerReference(existingSoundBank, &zone); return existingSoundBank; @@ -84,19 +82,18 @@ namespace T6 if (!sndBank->Initialize()) { - std::cerr << std::format("Failed to load sound bank '{}'\n", soundBankFileName); + con::error("Failed to load sound bank '{}'", soundBankFileName); return nullptr; } SoundBank::Repository.AddContainer(std::move(sndBank), &zone); - if (ObjLoading::Configuration.Verbose) - std::cout << std::format("Found and loaded sound bank '{}'\n", soundBankFileName); + con::debug("Found and loaded sound bank '{}'", soundBankFileName); return sndBankPtr; } - std::cerr << std::format("Failed to load sound bank '{}'\n", soundBankFileName); + con::error("Failed to load sound bank '{}'", soundBankFileName); return nullptr; } @@ -114,7 +111,7 @@ namespace T6 if (soundBank) { if (!VerifySoundBankChecksum(*soundBank, sndBankLinkedInfo)) - std::cout << std::format("Checksum of sound bank does not match link time checksum for '{}'\n", soundBankFileName); + con::warn("Checksum of sound bank does not match link time checksum for '{}'", soundBankFileName); loadedBanksForZone.emplace(soundBankFileName); @@ -164,14 +161,12 @@ namespace T6 void ObjLoader::LoadIPakForZone(ISearchPath& searchPath, const std::string& ipakName, Zone& zone) { - if (ObjLoading::Configuration.Verbose) - std::cout << std::format("Trying to load ipak '{}' for zone '{}'\n", ipakName, zone.m_name); + con::debug("Trying to load ipak '{}' for zone '{}'", ipakName, zone.m_name); auto* existingIPak = IIPak::Repository.GetContainerByName(ipakName); if (existingIPak != nullptr) { - if (ObjLoading::Configuration.Verbose) - std::cout << std::format("Referencing loaded ipak '{}'.\n", ipakName); + con::debug("Referencing loaded ipak '{}'.", ipakName); IIPak::Repository.AddContainerReference(existingIPak, &zone); return; @@ -188,12 +183,11 @@ namespace T6 { IIPak::Repository.AddContainer(std::move(ipak), &zone); - if (ObjLoading::Configuration.Verbose) - std::cout << std::format("Found and loaded ipak '{}'.\n", ipakFilename); + con::debug("Found and loaded ipak '{}'.", ipakFilename); } else { - std::cerr << std::format("Failed to load ipak '{}'!\n", ipakFilename); + con::error("Failed to load ipak '{}'!", ipakFilename); } } } @@ -210,8 +204,7 @@ namespace T6 void ObjLoader::LoadCommonIPaks(ISearchPath& searchPath, Zone& zone) { - if (ObjLoading::Configuration.Verbose) - std::cout << std::format("Loading common ipaks for zone \"{}\"\n", zone.m_name); + con::debug("Loading common ipaks for zone \"{}\"", zone.m_name); LoadIPakForZone(searchPath, "base", zone); const auto& languagePrefixes = IGame::GetGameById(GameId::T6)->GetLanguagePrefixes(); @@ -220,23 +213,20 @@ namespace T6 if (IsMpZone(zone)) { - if (ObjLoading::Configuration.Verbose) - std::cout << std::format("Loading multiplayer ipaks for zone \"{}\"\n", zone.m_name); + con::debug("Loading multiplayer ipaks for zone \"{}\"", zone.m_name); LoadIPakForZone(searchPath, "mp", zone); LoadIPakForZone(searchPath, "so", zone); } else if (IsZmZone(zone)) { - if (ObjLoading::Configuration.Verbose) - std::cout << std::format("Loading zombie ipak for zone \"{}\"\n", zone.m_name); + con::debug("Loading zombie ipak for zone \"{}\"", zone.m_name); LoadIPakForZone(searchPath, "zm", zone); } else { - if (ObjLoading::Configuration.Verbose) - std::cout << std::format("Loading singleplayer ipak for zone \"{}\"\n", zone.m_name); + con::debug("Loading singleplayer ipak for zone \"{}\"", zone.m_name); LoadIPakForZone(searchPath, "sp", zone); } @@ -394,17 +384,17 @@ namespace T6 { auto& memory = zone.Memory(); - collection.AddAssetCreator(CreateRawPhysPresetLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateGdtPhysPresetLoader(memory, searchPath, gdt, zone)); - collection.AddAssetCreator(CreateRawPhysConstraintsLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateGdtPhysConstraintsLoader(memory, searchPath, gdt, zone)); + collection.AddAssetCreator(phys_preset::CreateRawLoaderT6(memory, searchPath, zone)); + collection.AddAssetCreator(phys_preset::CreateGdtLoaderT6(memory, gdt, zone)); + collection.AddAssetCreator(phys_constraints::CreateRawLoaderT6(memory, searchPath, zone)); + collection.AddAssetCreator(phys_constraints::CreateGdtLoaderT6(memory, searchPath, gdt, zone)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateXModelLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath)); - collection.AddAssetCreator(CreateTechniqueSetLoader(memory, searchPath)); - collection.AddAssetCreator(CreateImageLoader(memory, searchPath)); - collection.AddAssetCreator(CreateSoundBankLoader(memory, searchPath)); + collection.AddAssetCreator(xmodel::CreateLoaderT6(memory, searchPath, zone)); + collection.AddAssetCreator(material::CreateLoaderT6(memory, searchPath)); + // collection.AddAssetCreator(std::make_unique(memory)); + collection.AddAssetCreator(image::CreateLoaderT6(memory, searchPath)); + collection.AddAssetCreator(sound::CreateSoundBankLoaderT6(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); @@ -414,43 +404,41 @@ namespace T6 // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateFontIconLoader(memory, searchPath)); + collection.AddAssetCreator(font_icon::CreateCsvLoaderT6(memory, searchPath)); + collection.AddAssetCreator(font_icon::CreateJsonLoaderT6(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateLocalizeLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateRawWeaponLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateGdtWeaponLoader(memory, searchPath, gdt, zone)); - collection.AddAssetCreator(CreateRawAttachmentLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateGdtAttachmentLoader(memory, searchPath, gdt, zone)); - collection.AddAssetCreator(CreateRawAttachmentUniqueLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateGdtAttachmentUniqueLoader(memory, searchPath, gdt, zone)); - collection.AddAssetCreator(CreateWeaponCamoLoader(memory, searchPath)); + collection.AddAssetCreator(localize::CreateLoaderT6(memory, searchPath, zone)); + collection.AddAssetCreator(weapon::CreateRawLoaderT6(memory, searchPath, zone)); + collection.AddAssetCreator(weapon::CreateGdtLoaderT6(memory, searchPath, gdt, zone)); + collection.AddAssetCreator(attachment::CreateRawLoaderT6(memory, searchPath, zone)); + collection.AddAssetCreator(attachment::CreateGdtLoaderT6(memory, searchPath, gdt, zone)); + collection.AddAssetCreator(attachment_unique::CreateRawLoaderT6(memory, searchPath, zone)); + collection.AddAssetCreator(attachment_unique::CreateGdtLoaderT6(memory, searchPath, gdt, zone)); + collection.AddAssetCreator(camo::CreateJsonLoaderT6(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateRawFileLoader(memory, searchPath)); - collection.AddAssetCreator(CreateStringTableLoader(memory, searchPath)); - collection.AddAssetCreator(CreateLeaderboardLoader(memory, searchPath)); + collection.AddAssetCreator(raw_file::CreateLoaderT6(memory, searchPath)); + collection.AddAssetCreator(string_table::CreateLoaderT6(memory, searchPath)); + collection.AddAssetCreator(leaderboard::CreateLoaderT6(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateScriptLoader(memory, searchPath)); - collection.AddAssetCreator(CreateRawVehicleLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateGdtVehicleLoader(memory, searchPath, gdt, zone)); + collection.AddAssetCreator(script::CreateLoaderT6(memory, searchPath)); + collection.AddAssetCreator(vehicle::CreateRawLoaderT6(memory, searchPath, zone)); + collection.AddAssetCreator(vehicle::CreateGdtLoaderT6(memory, searchPath, gdt, zone)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateQdbLoader(memory, searchPath)); - collection.AddAssetCreator(CreateSlugLoader(memory, searchPath)); + collection.AddAssetCreator(qdb::CreateLoaderT6(memory, searchPath)); + collection.AddAssetCreator(slug::CreateLoaderT6(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateRawZBarrierLoader(memory, searchPath, zone)); - collection.AddAssetCreator(CreateGdtZBarrierLoader(memory, searchPath, gdt, zone)); - - // The Custom Map Loader uses gfxworld as the starting asset - collection.AddAssetCreator(CreateCustomMapLoader(memory, searchPath, zone)); + collection.AddAssetCreator(z_barrier::CreateRawLoaderT6(memory, searchPath, zone)); + collection.AddAssetCreator(z_barrier::CreateGdtLoaderT6(memory, searchPath, gdt, zone)); } } // namespace diff --git a/src/ObjLoading/Game/T6/PhysConstraints/GdtLoaderPhysConstraintsT6.cpp b/src/ObjLoading/Game/T6/PhysConstraints/GdtLoaderPhysConstraintsT6.cpp index d6c35c5d..f6aa1df0 100644 --- a/src/ObjLoading/Game/T6/PhysConstraints/GdtLoaderPhysConstraintsT6.cpp +++ b/src/ObjLoading/Game/T6/PhysConstraints/GdtLoaderPhysConstraintsT6.cpp @@ -4,6 +4,7 @@ #include "Game/T6/T6.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderPhysConstraintsT6.h" +#include "Utils/Logging/Log.h" #include #include @@ -31,7 +32,7 @@ namespace InfoString infoString; if (!infoString.FromGdtProperties(*gdtEntry)) { - std::cerr << std::format("Failed to read phys constraints gdt entry: \"{}\"\n", assetName); + con::error("Failed to read phys constraints gdt entry: \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -40,15 +41,14 @@ namespace private: IGdtQueryable& m_gdt; - InfoStringLoaderPhysConstraints m_info_string_loader; + phys_constraints::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace phys_constraints { - std::unique_ptr> - CreateGdtPhysConstraintsLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) { return std::make_unique(memory, searchPath, gdt, zone); } -} // namespace T6 +} // namespace phys_constraints diff --git a/src/ObjLoading/Game/T6/PhysConstraints/GdtLoaderPhysConstraintsT6.h b/src/ObjLoading/Game/T6/PhysConstraints/GdtLoaderPhysConstraintsT6.h index 45b70188..c35e49bd 100644 --- a/src/ObjLoading/Game/T6/PhysConstraints/GdtLoaderPhysConstraintsT6.h +++ b/src/ObjLoading/Game/T6/PhysConstraints/GdtLoaderPhysConstraintsT6.h @@ -8,8 +8,7 @@ #include -namespace T6 +namespace phys_constraints { - std::unique_ptr> - CreateGdtPhysConstraintsLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); +} // namespace phys_constraints diff --git a/src/ObjLoading/Game/T6/PhysConstraints/InfoStringLoaderPhysConstraintsT6.cpp b/src/ObjLoading/Game/T6/PhysConstraints/InfoStringLoaderPhysConstraintsT6.cpp index ab29df7e..69d071be 100644 --- a/src/ObjLoading/Game/T6/PhysConstraints/InfoStringLoaderPhysConstraintsT6.cpp +++ b/src/ObjLoading/Game/T6/PhysConstraints/InfoStringLoaderPhysConstraintsT6.cpp @@ -3,6 +3,7 @@ #include "Game/T6/InfoString/InfoStringToStructConverter.h" #include "Game/T6/PhysConstraints/PhysConstraintsFields.h" #include "Game/T6/T6.h" +#include "Utils/Logging/Log.h" #include #include @@ -76,35 +77,38 @@ namespace } } // namespace -InfoStringLoaderPhysConstraints::InfoStringLoaderPhysConstraints(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) +namespace phys_constraints { -} - -AssetCreationResult InfoStringLoaderPhysConstraints::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) -{ - auto* physConstraints = m_memory.Alloc(); - physConstraints->name = m_memory.Dup(assetName.c_str()); - - AssetRegistration registration(assetName, physConstraints); - InfoStringToPhysConstraintsConverter converter(infoString, - *physConstraints, - m_zone.m_script_strings, - m_memory, - context, - registration, - phys_constraints_fields, - std::extent_v); - if (!converter.Convert()) + InfoStringLoaderT6::InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone) { - std::cerr << std::format("Failed to parse phys constraints: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - CalculatePhysConstraintsFields(*physConstraints, m_zone.m_script_strings); - registration.AddScriptString(m_zone.m_script_strings.AddOrGetScriptString("")); + AssetCreationResult InfoStringLoaderT6::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + auto* physConstraints = m_memory.Alloc(); + physConstraints->name = m_memory.Dup(assetName.c_str()); - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + AssetRegistration registration(assetName, physConstraints); + InfoStringToPhysConstraintsConverter converter(infoString, + *physConstraints, + m_zone.m_script_strings, + m_memory, + context, + registration, + phys_constraints_fields, + std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse phys constraints: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + CalculatePhysConstraintsFields(*physConstraints, m_zone.m_script_strings); + registration.AddScriptString(m_zone.m_script_strings.AddOrGetScriptString("")); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace phys_constraints diff --git a/src/ObjLoading/Game/T6/PhysConstraints/InfoStringLoaderPhysConstraintsT6.h b/src/ObjLoading/Game/T6/PhysConstraints/InfoStringLoaderPhysConstraintsT6.h index dbebb186..134b3500 100644 --- a/src/ObjLoading/Game/T6/PhysConstraints/InfoStringLoaderPhysConstraintsT6.h +++ b/src/ObjLoading/Game/T6/PhysConstraints/InfoStringLoaderPhysConstraintsT6.h @@ -4,12 +4,12 @@ #include "Asset/AssetCreationResult.h" #include "InfoString/InfoString.h" -namespace T6 +namespace phys_constraints { - class InfoStringLoaderPhysConstraints + class InfoStringLoaderT6 { public: - InfoStringLoaderPhysConstraints(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); @@ -18,4 +18,4 @@ namespace T6 ISearchPath& m_search_path; Zone& m_zone; }; -} // namespace T6 +} // namespace phys_constraints diff --git a/src/ObjLoading/Game/T6/PhysConstraints/RawLoaderPhysConstraintsT6.cpp b/src/ObjLoading/Game/T6/PhysConstraints/RawLoaderPhysConstraintsT6.cpp index 607a3c82..f26a8f45 100644 --- a/src/ObjLoading/Game/T6/PhysConstraints/RawLoaderPhysConstraintsT6.cpp +++ b/src/ObjLoading/Game/T6/PhysConstraints/RawLoaderPhysConstraintsT6.cpp @@ -4,6 +4,8 @@ #include "Game/T6/T6.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderPhysConstraintsT6.h" +#include "PhysConstraints/PhysConstraintsCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -24,7 +26,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("physconstraints/{}", assetName); + const auto fileName = phys_constraints::GetFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -32,7 +34,7 @@ namespace InfoString infoString; if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_PHYS_CONSTRAINTS, *file.m_stream)) { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); + con::error("Could not parse as info string file: \"{}\"", fileName); return AssetCreationResult::Failure(); } @@ -41,14 +43,14 @@ namespace private: ISearchPath& m_search_path; - InfoStringLoaderPhysConstraints m_info_string_loader; + phys_constraints::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace phys_constraints { - std::unique_ptr> CreateRawPhysConstraintsLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace T6 +} // namespace phys_constraints diff --git a/src/ObjLoading/Game/T6/PhysConstraints/RawLoaderPhysConstraintsT6.h b/src/ObjLoading/Game/T6/PhysConstraints/RawLoaderPhysConstraintsT6.h index dc88bc45..33342687 100644 --- a/src/ObjLoading/Game/T6/PhysConstraints/RawLoaderPhysConstraintsT6.h +++ b/src/ObjLoading/Game/T6/PhysConstraints/RawLoaderPhysConstraintsT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace phys_constraints { - std::unique_ptr> CreateRawPhysConstraintsLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace phys_constraints diff --git a/src/ObjLoading/Game/T6/PhysPreset/GdtLoaderPhysPresetT6.cpp b/src/ObjLoading/Game/T6/PhysPreset/GdtLoaderPhysPresetT6.cpp index 9d7f179f..c8e6493e 100644 --- a/src/ObjLoading/Game/T6/PhysPreset/GdtLoaderPhysPresetT6.cpp +++ b/src/ObjLoading/Game/T6/PhysPreset/GdtLoaderPhysPresetT6.cpp @@ -4,6 +4,7 @@ #include "Game/T6/T6.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderPhysPresetT6.h" +#include "Utils/Logging/Log.h" #include #include @@ -16,22 +17,22 @@ namespace class GdtLoaderPhysPreset final : public AssetCreator { public: - GdtLoaderPhysPreset(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + GdtLoaderPhysPreset(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone) : m_gdt(gdt), - m_info_string_loader(memory, searchPath, zone) + m_info_string_loader(memory, zone) { } AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto* gdtEntry = m_gdt.GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_WEAPON, assetName); + const auto* gdtEntry = m_gdt.GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_PHYS_PRESET, assetName); if (gdtEntry == nullptr) return AssetCreationResult::NoAction(); InfoString infoString; if (!infoString.FromGdtProperties(*gdtEntry)) { - std::cerr << std::format("Failed to read phys preset gdt entry: \"{}\"\n", assetName); + con::error("Failed to read phys preset gdt entry: \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -40,14 +41,14 @@ namespace private: IGdtQueryable& m_gdt; - InfoStringLoaderPhysPreset m_info_string_loader; + phys_preset::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace phys_preset { - std::unique_ptr> CreateGdtPhysPresetLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone) { - return std::make_unique(memory, searchPath, gdt, zone); + return std::make_unique(memory, gdt, zone); } -} // namespace T6 +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T6/PhysPreset/GdtLoaderPhysPresetT6.h b/src/ObjLoading/Game/T6/PhysPreset/GdtLoaderPhysPresetT6.h index c2fdbe8d..bc167205 100644 --- a/src/ObjLoading/Game/T6/PhysPreset/GdtLoaderPhysPresetT6.h +++ b/src/ObjLoading/Game/T6/PhysPreset/GdtLoaderPhysPresetT6.h @@ -8,7 +8,7 @@ #include -namespace T6 +namespace phys_preset { - std::unique_ptr> CreateGdtPhysPresetLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone); +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T6/PhysPreset/InfoStringLoaderPhysPresetT6.cpp b/src/ObjLoading/Game/T6/PhysPreset/InfoStringLoaderPhysPresetT6.cpp index fbd2b9d2..647f2353 100644 --- a/src/ObjLoading/Game/T6/PhysPreset/InfoStringLoaderPhysPresetT6.cpp +++ b/src/ObjLoading/Game/T6/PhysPreset/InfoStringLoaderPhysPresetT6.cpp @@ -3,6 +3,7 @@ #include "Game/T6/InfoString/InfoStringToStructConverter.h" #include "Game/T6/PhysPreset/PhysPresetFields.h" #include "Game/T6/T6.h" +#include "Utils/Logging/Log.h" #include #include @@ -60,31 +61,39 @@ namespace } } // namespace -InfoStringLoaderPhysPreset::InfoStringLoaderPhysPreset(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) +namespace phys_preset { -} - -AssetCreationResult InfoStringLoaderPhysPreset::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) -{ - auto* physPreset = m_memory.Alloc(); - physPreset->name = m_memory.Dup(assetName.c_str()); - - AssetRegistration registration(assetName, physPreset); - - PhysPresetInfo physPresetInfo; - memset(&physPresetInfo, 0, sizeof(physPresetInfo)); - InfoStringToPhysPresetConverter converter( - infoString, physPresetInfo, m_zone.m_script_strings, m_memory, context, registration, phys_preset_fields, std::extent_v); - if (!converter.Convert()) + InfoStringLoaderT6::InfoStringLoaderT6(MemoryManager& memory, Zone& zone) + : m_memory(memory), + m_zone(zone) { - std::cerr << std::format("Failed to parse phys preset: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - CopyFromPhysPresetInfo(physPresetInfo, *physPreset); + AssetCreationResult InfoStringLoaderT6::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + auto* physPreset = m_memory.Alloc(); + physPreset->name = m_memory.Dup(assetName.c_str()); - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + AssetRegistration registration(assetName, physPreset); + + PhysPresetInfo physPresetInfo; + memset(&physPresetInfo, 0, sizeof(physPresetInfo)); + InfoStringToPhysPresetConverter converter(infoString, + physPresetInfo, + m_zone.m_script_strings, + m_memory, + context, + registration, + phys_preset_fields, + std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse phys preset: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + CopyFromPhysPresetInfo(physPresetInfo, *physPreset); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T6/PhysPreset/InfoStringLoaderPhysPresetT6.h b/src/ObjLoading/Game/T6/PhysPreset/InfoStringLoaderPhysPresetT6.h index 3e214d60..06e10b2f 100644 --- a/src/ObjLoading/Game/T6/PhysPreset/InfoStringLoaderPhysPresetT6.h +++ b/src/ObjLoading/Game/T6/PhysPreset/InfoStringLoaderPhysPresetT6.h @@ -4,18 +4,17 @@ #include "Asset/AssetCreationResult.h" #include "InfoString/InfoString.h" -namespace T6 +namespace phys_preset { - class InfoStringLoaderPhysPreset + class InfoStringLoaderT6 { public: - InfoStringLoaderPhysPreset(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + InfoStringLoaderT6(MemoryManager& memory, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); private: MemoryManager& m_memory; - ISearchPath& m_search_path; Zone& m_zone; }; -} // namespace T6 +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T6/PhysPreset/RawLoaderPhysPresetT6.cpp b/src/ObjLoading/Game/T6/PhysPreset/RawLoaderPhysPresetT6.cpp index 4fe78db8..bfd2e12c 100644 --- a/src/ObjLoading/Game/T6/PhysPreset/RawLoaderPhysPresetT6.cpp +++ b/src/ObjLoading/Game/T6/PhysPreset/RawLoaderPhysPresetT6.cpp @@ -4,6 +4,8 @@ #include "Game/T6/T6.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderPhysPresetT6.h" +#include "PhysPreset/PhysPresetCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -18,13 +20,13 @@ namespace public: RawLoaderPhysPreset(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) : m_search_path(searchPath), - m_info_string_loader(memory, searchPath, zone) + m_info_string_loader(memory, zone) { } AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("physic/{}", assetName); + const auto fileName = phys_preset::GetFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -32,7 +34,7 @@ namespace InfoString infoString; if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET, *file.m_stream)) { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); + con::error("Could not parse as info string file: \"{}\"", fileName); return AssetCreationResult::Failure(); } @@ -41,14 +43,14 @@ namespace private: ISearchPath& m_search_path; - InfoStringLoaderPhysPreset m_info_string_loader; + phys_preset::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace phys_preset { - std::unique_ptr> CreateRawPhysPresetLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace T6 +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T6/PhysPreset/RawLoaderPhysPresetT6.h b/src/ObjLoading/Game/T6/PhysPreset/RawLoaderPhysPresetT6.h index 44070d9b..f319a936 100644 --- a/src/ObjLoading/Game/T6/PhysPreset/RawLoaderPhysPresetT6.h +++ b/src/ObjLoading/Game/T6/PhysPreset/RawLoaderPhysPresetT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace phys_preset { - std::unique_ptr> CreateRawPhysPresetLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T6/Qdb/LoaderQdbT6.cpp b/src/ObjLoading/Game/T6/Qdb/LoaderQdbT6.cpp index e00e88ac..e1327edc 100644 --- a/src/ObjLoading/Game/T6/Qdb/LoaderQdbT6.cpp +++ b/src/ObjLoading/Game/T6/Qdb/LoaderQdbT6.cpp @@ -44,10 +44,10 @@ namespace }; } // namespace -namespace T6 +namespace qdb { - std::unique_ptr> CreateQdbLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace qdb diff --git a/src/ObjLoading/Game/T6/Qdb/LoaderQdbT6.h b/src/ObjLoading/Game/T6/Qdb/LoaderQdbT6.h index a8d5f9c9..17a44611 100644 --- a/src/ObjLoading/Game/T6/Qdb/LoaderQdbT6.h +++ b/src/ObjLoading/Game/T6/Qdb/LoaderQdbT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace qdb { - std::unique_ptr> CreateQdbLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace qdb diff --git a/src/ObjLoading/Game/T6/RawFile/LoaderRawFileT6.cpp b/src/ObjLoading/Game/T6/RawFile/LoaderRawFileT6.cpp index f3352243..37a6cf31 100644 --- a/src/ObjLoading/Game/T6/RawFile/LoaderRawFileT6.cpp +++ b/src/ObjLoading/Game/T6/RawFile/LoaderRawFileT6.cpp @@ -2,6 +2,7 @@ #include "Game/T6/T6.h" #include "Pool/GlobalAssetPool.h" +#include "Utils/Logging/Log.h" #include #include @@ -73,7 +74,7 @@ namespace if (ret != Z_STREAM_END) { - std::cerr << std::format("Deflate failed for loading animtree file \"{}\"\n", assetName); + con::error("Deflate failed for loading animtree file \"{}\"", assetName); deflateEnd(&zs); return AssetCreationResult::Failure(); } @@ -114,10 +115,10 @@ namespace }; } // namespace -namespace T6 +namespace raw_file { - std::unique_ptr> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace raw_file diff --git a/src/ObjLoading/Game/T6/RawFile/LoaderRawFileT6.h b/src/ObjLoading/Game/T6/RawFile/LoaderRawFileT6.h index db44f9a5..1ef99df6 100644 --- a/src/ObjLoading/Game/T6/RawFile/LoaderRawFileT6.h +++ b/src/ObjLoading/Game/T6/RawFile/LoaderRawFileT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace raw_file { - std::unique_ptr> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace raw_file diff --git a/src/ObjLoading/Game/T6/Script/LoaderScriptT6.cpp b/src/ObjLoading/Game/T6/Script/LoaderScriptT6.cpp index 5d30b473..a7284bc7 100644 --- a/src/ObjLoading/Game/T6/Script/LoaderScriptT6.cpp +++ b/src/ObjLoading/Game/T6/Script/LoaderScriptT6.cpp @@ -44,10 +44,10 @@ namespace }; } // namespace -namespace T6 +namespace script { - std::unique_ptr> CreateScriptLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace script diff --git a/src/ObjLoading/Game/T6/Script/LoaderScriptT6.h b/src/ObjLoading/Game/T6/Script/LoaderScriptT6.h index 754da2ec..ebaab5da 100644 --- a/src/ObjLoading/Game/T6/Script/LoaderScriptT6.h +++ b/src/ObjLoading/Game/T6/Script/LoaderScriptT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace script { - std::unique_ptr> CreateScriptLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace script diff --git a/src/ObjLoading/Game/T6/Slug/LoaderSlugT6.cpp b/src/ObjLoading/Game/T6/Slug/LoaderSlugT6.cpp index f527a521..cce61264 100644 --- a/src/ObjLoading/Game/T6/Slug/LoaderSlugT6.cpp +++ b/src/ObjLoading/Game/T6/Slug/LoaderSlugT6.cpp @@ -44,10 +44,10 @@ namespace }; } // namespace -namespace T6 +namespace slug { - std::unique_ptr> CreateSlugLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace slug diff --git a/src/ObjLoading/Game/T6/Slug/LoaderSlugT6.h b/src/ObjLoading/Game/T6/Slug/LoaderSlugT6.h index 49da9e84..fefc89c1 100644 --- a/src/ObjLoading/Game/T6/Slug/LoaderSlugT6.h +++ b/src/ObjLoading/Game/T6/Slug/LoaderSlugT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace slug { - std::unique_ptr> CreateSlugLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace slug diff --git a/src/ObjLoading/Game/T6/Sound/LoaderSoundBankT6.cpp b/src/ObjLoading/Game/T6/Sound/LoaderSoundBankT6.cpp index f72d2a77..3626b210 100644 --- a/src/ObjLoading/Game/T6/Sound/LoaderSoundBankT6.cpp +++ b/src/ObjLoading/Game/T6/Sound/LoaderSoundBankT6.cpp @@ -7,6 +7,7 @@ #include "Game/T6/T6.h" #include "ObjContainer/SoundBank/SoundBankWriter.h" #include "Pool/GlobalAssetPool.h" +#include "Utils/Logging/Log.h" #include "Utils/StringUtils.h" #include @@ -96,14 +97,14 @@ namespace if (!cell.AsFloat(dbsplValue)) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - Must be a float\n", rowIndex + 1, colName); + con::error("Invalid value for row {} col '{}' - Must be a float", rowIndex + 1, colName); return false; } if (dbsplValue < 0.0f || dbsplValue > 100.0f) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - {} [0.0, 100.0]\n", rowIndex + 1, colName, dbsplValue); + con::error("Invalid value for row {} col '{}' - {} [0.0, 100.0]", rowIndex + 1, colName, dbsplValue); return false; } @@ -121,14 +122,14 @@ namespace if (!cell.AsFloat(centsValue)) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - Must be a float\n", rowIndex + 1, colName); + con::error("Invalid value for row {} col '{}' - Must be a float", rowIndex + 1, colName); return false; } if (centsValue < -2400.0f || centsValue > 1200.0f) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - {} [-2400.0, 1200.0]\n", rowIndex + 1, colName, centsValue); + con::error("Invalid value for row {} col '{}' - {} [-2400.0, 1200.0]", rowIndex + 1, colName, centsValue); return false; } @@ -151,14 +152,14 @@ namespace if (!cell.AsUInt32(value32)) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - Must be a uint\n", rowIndex + 1, colName); + con::error("Invalid value for row {} col '{}' - Must be a uint", rowIndex + 1, colName); return false; } if (value32 < min || value32 > max) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - {} [{}, {}]\n", rowIndex + 1, colName, value32, min, max); + con::error("Invalid value for row {} col '{}' - {} [{}, {}]", rowIndex + 1, colName, value32, min, max); return false; } @@ -181,14 +182,14 @@ namespace if (!cell.AsInt32(value32)) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - Must be a int\n", rowIndex + 1, colName); + con::error("Invalid value for row {} col '{}' - Must be a int", rowIndex + 1, colName); return false; } if (value32 < min || value32 > max) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - {} [{}, {}]\n", rowIndex + 1, colName, value32, min, max); + con::error("Invalid value for row {} col '{}' - {} [{}, {}]", rowIndex + 1, colName, value32, min, max); return false; } @@ -211,14 +212,14 @@ namespace if (!cell.AsUInt32(value32)) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - Must be a uint\n", rowIndex + 1, colName); + con::error("Invalid value for row {} col '{}' - Must be a uint", rowIndex + 1, colName); return false; } if (value32 < min || value32 > max) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - {} [{}, {}]\n", rowIndex + 1, colName, value32, min, max); + con::error("Invalid value for row {} col '{}' - {} [{}, {}]", rowIndex + 1, colName, value32, min, max); return false; } @@ -235,14 +236,14 @@ namespace if (!cell.AsFloat(valueFloat)) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - Must be a float\n", rowIndex + 1, colName); + con::error("Invalid value for row {} col '{}' - Must be a float", rowIndex + 1, colName); return false; } if (valueFloat < 0.0f || valueFloat > 1.0f) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - {} [0.0, 1.0]\n", rowIndex + 1, colName, valueFloat); + con::error("Invalid value for row {} col '{}' - {} [0.0, 1.0]", rowIndex + 1, colName, valueFloat); return false; } @@ -290,9 +291,9 @@ namespace } const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - {}. Must be one of:\n", rowIndex + 1, colName, cell.m_value); + con::error("Invalid value for row {} col '{}' - {}. Must be one of:", rowIndex + 1, colName, cell.m_value); for (auto i = 0u; i < enumValueCount; i++) - std::cerr << std::format(" {}\n", enumValues[i]); + con::error(" {}", enumValues[i]); return false; } @@ -341,9 +342,9 @@ namespace if (!foundValue) { const auto& colName = headerRow.HeaderNameForColumn(columnIndex); - std::cerr << std::format("Invalid value for row {} col '{}' - {}. Must be one of:\n", rowIndex + 1, colName, entry); + con::error("Invalid value for row {} col '{}' - {}. Must be one of:", rowIndex + 1, colName, entry); for (auto i = 0u; i < enumValueCount; i++) - std::cerr << std::format(" {}\n", enumValues[i]); + con::error(" {}", enumValues[i]); return false; } } @@ -648,7 +649,7 @@ namespace SoundAliasHeaders headers; if (!headerRow.Read(csv) || !headers.From(headerRow)) { - std::cerr << std::format("Invalid headers for aliases of sound bank {}\n", sndBank->name); + con::error("Invalid headers for aliases of sound bank {}", sndBank->name); return false; } @@ -740,7 +741,7 @@ namespace if (freeIdx == std::numeric_limits::max()) { - std::cerr << "Unable to allocate sound bank alias index list\n"; + con::error("Unable to allocate sound bank alias index list"); return false; } @@ -807,7 +808,7 @@ namespace RadverbHeaders headers; if (!csvHeaders.Read(csv) || !headers.From(csvHeaders)) { - std::cerr << std::format("Invalid headers for radverbs of sound bank {}\n", sndBank->name); + con::error("Invalid headers for radverbs of sound bank {}", sndBank->name); return false; } @@ -867,7 +868,7 @@ namespace const auto nameRow = headerRow.GetIndexForHeader("name"); if (!nameRow) { - std::cerr << std::format("Missing name header for ducks of sound bank {}\n", sndBank->name); + con::error("Missing name header for ducks of sound bank {}", sndBank->name); return false; } @@ -882,7 +883,7 @@ namespace const auto duckFile = searchPath.Open(std::format("soundbank/ducks/{}.duk", name)); if (!duckFile.IsOpen()) { - std::cerr << std::format("Unable to find .duk file for {} in ducklist for sound bank {}\n", name, sndBank->name); + con::error("Unable to find .duk file for {} in ducklist for sound bank {}", name, sndBank->name); return false; } @@ -944,7 +945,7 @@ namespace { if (assetName.find('.') == std::string::npos) { - std::cerr << "A language must be specific in the sound bank asset name! (Ex: mpl_common.all)\n"; + con::error("A language must be specific in the sound bank asset name! (Ex: mpl_common.all)"); return AssetCreationResult::Failure(); } @@ -969,7 +970,7 @@ namespace { if (!LoadSoundRadverbs(m_memory, sndBank, radverbFile)) { - std::cerr << std::format("Sound bank reverbs file for {} is invalid\n", assetName); + con::error("Sound bank reverbs file for {} is invalid", assetName); return AssetCreationResult::Failure(); } } @@ -979,7 +980,7 @@ namespace { if (!LoadSoundDuckList(m_search_path, m_memory, sndBank, duckListFile)) { - std::cerr << std::format("Sound bank ducklist file for {} is invalid\n", assetName); + con::error("Sound bank ducklist file for {} is invalid", assetName); return AssetCreationResult::Failure(); } } @@ -1051,7 +1052,7 @@ namespace } else { - std::cerr << std::format("Loaded sound bank for {} failed to generate. Please check your build files.\n", assetName); + con::error("Loaded sound bank for {} failed to generate. Please check your build files.", assetName); return AssetCreationResult::Failure(); } } @@ -1065,7 +1066,7 @@ namespace if (!result) { - std::cerr << std::format("Streamed sound bank for {} failed to generate. Please check your build files.\n", assetName); + con::error("Streamed sound bank for {} failed to generate. Please check your build files.", assetName); return AssetCreationResult::Failure(); } } @@ -1079,10 +1080,10 @@ namespace }; } // namespace -namespace T6 +namespace sound { - std::unique_ptr> CreateSoundBankLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateSoundBankLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace sound diff --git a/src/ObjLoading/Game/T6/Sound/LoaderSoundBankT6.h b/src/ObjLoading/Game/T6/Sound/LoaderSoundBankT6.h index 6e99e962..358b7710 100644 --- a/src/ObjLoading/Game/T6/Sound/LoaderSoundBankT6.h +++ b/src/ObjLoading/Game/T6/Sound/LoaderSoundBankT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace sound { - std::unique_ptr> CreateSoundBankLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 + std::unique_ptr> CreateSoundBankLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace sound diff --git a/src/ObjLoading/Game/T6/StringTable/LoaderStringTableT6.cpp b/src/ObjLoading/Game/T6/StringTable/LoaderStringTableT6.cpp index d9560241..d5e092d6 100644 --- a/src/ObjLoading/Game/T6/StringTable/LoaderStringTableT6.cpp +++ b/src/ObjLoading/Game/T6/StringTable/LoaderStringTableT6.cpp @@ -38,10 +38,10 @@ namespace }; } // namespace -namespace T6 +namespace string_table { - std::unique_ptr> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath) + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace string_table diff --git a/src/ObjLoading/Game/T6/StringTable/LoaderStringTableT6.h b/src/ObjLoading/Game/T6/StringTable/LoaderStringTableT6.h index 7db144c6..bebd85b2 100644 --- a/src/ObjLoading/Game/T6/StringTable/LoaderStringTableT6.h +++ b/src/ObjLoading/Game/T6/StringTable/LoaderStringTableT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace string_table { - std::unique_ptr> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 + std::unique_ptr> CreateLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace string_table diff --git a/src/ObjLoading/Game/T6/Tracer/GdtLoaderTracerT6.cpp b/src/ObjLoading/Game/T6/Tracer/GdtLoaderTracerT6.cpp index ee7376ed..fa4ba6a4 100644 --- a/src/ObjLoading/Game/T6/Tracer/GdtLoaderTracerT6.cpp +++ b/src/ObjLoading/Game/T6/Tracer/GdtLoaderTracerT6.cpp @@ -4,6 +4,7 @@ #include "Game/T6/T6.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderTracerT6.h" +#include "Utils/Logging/Log.h" #include #include @@ -31,7 +32,7 @@ namespace InfoString infoString; if (!infoString.FromGdtProperties(*gdtEntry)) { - std::cerr << std::format("Failed to read tracer gdt entry: \"{}\"\n", assetName); + con::error("Failed to read tracer gdt entry: \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -40,14 +41,14 @@ namespace private: IGdtQueryable& m_gdt; - InfoStringLoaderTracer m_info_string_loader; + tracer::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace tracer { - std::unique_ptr> CreateGdtTracerLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) { return std::make_unique(memory, searchPath, gdt, zone); } -} // namespace T6 +} // namespace tracer diff --git a/src/ObjLoading/Game/T6/Tracer/GdtLoaderTracerT6.h b/src/ObjLoading/Game/T6/Tracer/GdtLoaderTracerT6.h index d3059ce3..e48dd81b 100644 --- a/src/ObjLoading/Game/T6/Tracer/GdtLoaderTracerT6.h +++ b/src/ObjLoading/Game/T6/Tracer/GdtLoaderTracerT6.h @@ -8,7 +8,7 @@ #include -namespace T6 +namespace tracer { - std::unique_ptr> CreateGdtTracerLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); +} // namespace tracer diff --git a/src/ObjLoading/Game/T6/Tracer/InfoStringLoaderTracerT6.cpp b/src/ObjLoading/Game/T6/Tracer/InfoStringLoaderTracerT6.cpp index 00c21757..bcd0ea47 100644 --- a/src/ObjLoading/Game/T6/Tracer/InfoStringLoaderTracerT6.cpp +++ b/src/ObjLoading/Game/T6/Tracer/InfoStringLoaderTracerT6.cpp @@ -3,6 +3,8 @@ #include "Game/T6/InfoString/InfoStringToStructConverter.h" #include "Game/T6/T6.h" #include "Game/T6/Tracer/TracerFields.h" +#include "Tracer/TracerCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -46,27 +48,31 @@ namespace }; } // namespace -InfoStringLoaderTracer::InfoStringLoaderTracer(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) +namespace tracer { -} - -AssetCreationResult InfoStringLoaderTracer::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) -{ - auto* tracer = m_memory.Alloc(); - tracer->name = m_memory.Dup(assetName.c_str()); - - AssetRegistration registration(assetName, tracer); - - InfoStringToTracerConverter converter( - infoString, *tracer, m_zone.m_script_strings, m_memory, context, registration, tracer_fields, std::extent_v); - if (!converter.Convert()) + InfoStringLoaderT6::InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone) { - std::cerr << std::format("Failed to parse tracer: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + AssetCreationResult InfoStringLoaderT6::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + auto* tracer = m_memory.Alloc(); + tracer->name = m_memory.Dup(assetName.c_str()); + + AssetRegistration registration(assetName, tracer); + + InfoStringToTracerConverter converter( + infoString, *tracer, m_zone.m_script_strings, m_memory, context, registration, tracer_fields, std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse tracer: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } + +} // namespace tracer diff --git a/src/ObjLoading/Game/T6/Tracer/InfoStringLoaderTracerT6.h b/src/ObjLoading/Game/T6/Tracer/InfoStringLoaderTracerT6.h index 141c8216..d5ace0a6 100644 --- a/src/ObjLoading/Game/T6/Tracer/InfoStringLoaderTracerT6.h +++ b/src/ObjLoading/Game/T6/Tracer/InfoStringLoaderTracerT6.h @@ -4,12 +4,12 @@ #include "Asset/AssetCreationResult.h" #include "InfoString/InfoString.h" -namespace T6 +namespace tracer { - class InfoStringLoaderTracer + class InfoStringLoaderT6 { public: - InfoStringLoaderTracer(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); @@ -18,4 +18,4 @@ namespace T6 ISearchPath& m_search_path; Zone& m_zone; }; -} // namespace T6 +} // namespace tracer diff --git a/src/ObjLoading/Game/T6/Tracer/RawLoaderTracerT6.cpp b/src/ObjLoading/Game/T6/Tracer/RawLoaderTracerT6.cpp index 16eb7ea9..7de94228 100644 --- a/src/ObjLoading/Game/T6/Tracer/RawLoaderTracerT6.cpp +++ b/src/ObjLoading/Game/T6/Tracer/RawLoaderTracerT6.cpp @@ -4,6 +4,8 @@ #include "Game/T6/T6.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderTracerT6.h" +#include "Tracer/TracerCommon.h" +#include "Utils/Logging/Log.h" #include #include @@ -24,7 +26,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("tracer/{}", assetName); + const auto fileName = tracer::GetFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -32,7 +34,7 @@ namespace InfoString infoString; if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_TRACER, *file.m_stream)) { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); + con::error("Could not parse as info string file: \"{}\"", fileName); return AssetCreationResult::Failure(); } @@ -41,14 +43,14 @@ namespace private: ISearchPath& m_search_path; - InfoStringLoaderTracer m_info_string_loader; + tracer::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace tracer { - std::unique_ptr> CreateRawTracerLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace T6 +} // namespace tracer diff --git a/src/ObjLoading/Game/T6/Tracer/RawLoaderTracerT6.h b/src/ObjLoading/Game/T6/Tracer/RawLoaderTracerT6.h index a41e74c2..3667c410 100644 --- a/src/ObjLoading/Game/T6/Tracer/RawLoaderTracerT6.h +++ b/src/ObjLoading/Game/T6/Tracer/RawLoaderTracerT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace tracer { - std::unique_ptr> CreateRawTracerLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace tracer diff --git a/src/ObjLoading/Game/T6/Vehicle/GdtLoaderVehicleT6.cpp b/src/ObjLoading/Game/T6/Vehicle/GdtLoaderVehicleT6.cpp index 7b224773..e860a9d3 100644 --- a/src/ObjLoading/Game/T6/Vehicle/GdtLoaderVehicleT6.cpp +++ b/src/ObjLoading/Game/T6/Vehicle/GdtLoaderVehicleT6.cpp @@ -4,6 +4,7 @@ #include "Game/T6/T6.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderVehicleT6.h" +#include "Utils/Logging/Log.h" #include #include @@ -31,7 +32,7 @@ namespace InfoString infoString; if (!infoString.FromGdtProperties(*gdtEntry)) { - std::cerr << std::format("Failed to read vehicle gdt entry: \"{}\"\n", assetName); + con::error("Failed to read vehicle gdt entry: \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -40,14 +41,14 @@ namespace private: IGdtQueryable& m_gdt; - InfoStringLoaderVehicle m_info_string_loader; + vehicle::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace vehicle { - std::unique_ptr> CreateGdtVehicleLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) { return std::make_unique(memory, searchPath, gdt, zone); } -} // namespace T6 +} // namespace vehicle diff --git a/src/ObjLoading/Game/T6/Vehicle/GdtLoaderVehicleT6.h b/src/ObjLoading/Game/T6/Vehicle/GdtLoaderVehicleT6.h index fee2a56d..4bc647fe 100644 --- a/src/ObjLoading/Game/T6/Vehicle/GdtLoaderVehicleT6.h +++ b/src/ObjLoading/Game/T6/Vehicle/GdtLoaderVehicleT6.h @@ -8,7 +8,7 @@ #include -namespace T6 +namespace vehicle { - std::unique_ptr> CreateGdtVehicleLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); +} // namespace vehicle diff --git a/src/ObjLoading/Game/T6/Vehicle/InfoStringLoaderVehicleT6.cpp b/src/ObjLoading/Game/T6/Vehicle/InfoStringLoaderVehicleT6.cpp index 4999f0bb..1dbcba3b 100644 --- a/src/ObjLoading/Game/T6/Vehicle/InfoStringLoaderVehicleT6.cpp +++ b/src/ObjLoading/Game/T6/Vehicle/InfoStringLoaderVehicleT6.cpp @@ -3,6 +3,7 @@ #include "Game/T6/InfoString/InfoStringToStructConverter.h" #include "Game/T6/T6.h" #include "Game/T6/Vehicle/VehicleFields.h" +#include "Utils/Logging/Log.h" #include #include @@ -37,7 +38,7 @@ namespace if (endPtr != &value[value.size()]) { - std::cerr << std::format("Failed to parse value \"{}\" as mph\n", value); + con::error("Failed to parse value \"{}\" as mph", value); return false; } @@ -51,7 +52,7 @@ namespace if (endPtr != &value[value.size()]) { - std::cerr << std::format("Failed to parse value \"{}\" as pounds\n", value); + con::error("Failed to parse value \"{}\" as pounds", value); return false; } @@ -79,7 +80,7 @@ namespace } *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = TEAM_BAD; - std::cerr << std::format("Failed to parse value \"{}\" as team\n", value); + con::error("Failed to parse value \"{}\" as team", value); return false; } @@ -107,27 +108,30 @@ namespace }; } // namespace -InfoStringLoaderVehicle::InfoStringLoaderVehicle(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) +namespace vehicle { -} - -AssetCreationResult InfoStringLoaderVehicle::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) -{ - auto* vehicleDef = m_memory.Alloc(); - vehicleDef->name = m_memory.Dup(assetName.c_str()); - - AssetRegistration registration(assetName, vehicleDef); - - InfoStringToVehicleConverter converter( - infoString, *vehicleDef, m_zone.m_script_strings, m_memory, context, registration, vehicle_fields, std::extent_v); - if (!converter.Convert()) + InfoStringLoaderT6::InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone) { - std::cerr << std::format("Failed to parse vehicle: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + AssetCreationResult InfoStringLoaderT6::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + auto* vehicleDef = m_memory.Alloc(); + vehicleDef->name = m_memory.Dup(assetName.c_str()); + + AssetRegistration registration(assetName, vehicleDef); + + InfoStringToVehicleConverter converter( + infoString, *vehicleDef, m_zone.m_script_strings, m_memory, context, registration, vehicle_fields, std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse vehicle: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace vehicle diff --git a/src/ObjLoading/Game/T6/Vehicle/InfoStringLoaderVehicleT6.h b/src/ObjLoading/Game/T6/Vehicle/InfoStringLoaderVehicleT6.h index 9aed730e..15995e94 100644 --- a/src/ObjLoading/Game/T6/Vehicle/InfoStringLoaderVehicleT6.h +++ b/src/ObjLoading/Game/T6/Vehicle/InfoStringLoaderVehicleT6.h @@ -4,12 +4,12 @@ #include "Asset/AssetCreationResult.h" #include "InfoString/InfoString.h" -namespace T6 +namespace vehicle { - class InfoStringLoaderVehicle + class InfoStringLoaderT6 { public: - InfoStringLoaderVehicle(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); @@ -18,4 +18,4 @@ namespace T6 ISearchPath& m_search_path; Zone& m_zone; }; -} // namespace T6 +} // namespace vehicle diff --git a/src/ObjLoading/Game/T6/Vehicle/RawLoaderVehicleT6.cpp b/src/ObjLoading/Game/T6/Vehicle/RawLoaderVehicleT6.cpp index 68a8299e..9bbd462c 100644 --- a/src/ObjLoading/Game/T6/Vehicle/RawLoaderVehicleT6.cpp +++ b/src/ObjLoading/Game/T6/Vehicle/RawLoaderVehicleT6.cpp @@ -4,6 +4,8 @@ #include "Game/T6/T6.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderVehicleT6.h" +#include "Utils/Logging/Log.h" +#include "Vehicle/VehicleCommon.h" #include #include @@ -24,7 +26,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("vehicles/{}", assetName); + const auto fileName = vehicle::GetFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -32,7 +34,7 @@ namespace InfoString infoString; if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_VEHICLE, *file.m_stream)) { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); + con::error("Could not parse as info string file: \"{}\"", fileName); return AssetCreationResult::Failure(); } @@ -41,14 +43,14 @@ namespace private: ISearchPath& m_search_path; - InfoStringLoaderVehicle m_info_string_loader; + vehicle::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace vehicle { - std::unique_ptr> CreateRawVehicleLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace T6 +} // namespace vehicle diff --git a/src/ObjLoading/Game/T6/Vehicle/RawLoaderVehicleT6.h b/src/ObjLoading/Game/T6/Vehicle/RawLoaderVehicleT6.h index e88d6909..59759b0a 100644 --- a/src/ObjLoading/Game/T6/Vehicle/RawLoaderVehicleT6.h +++ b/src/ObjLoading/Game/T6/Vehicle/RawLoaderVehicleT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace vehicle { - std::unique_ptr> CreateRawVehicleLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace vehicle diff --git a/src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentT6.cpp b/src/ObjLoading/Game/T6/Weapon/AttachmentGdtLoaderT6.cpp similarity index 73% rename from src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentT6.cpp rename to src/ObjLoading/Game/T6/Weapon/AttachmentGdtLoaderT6.cpp index ceecfd7f..ac7df117 100644 --- a/src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentGdtLoaderT6.cpp @@ -1,9 +1,10 @@ -#include "GdtLoaderAttachmentT6.h" +#include "AttachmentGdtLoaderT6.h" +#include "AttachmentInfoStringLoaderT6.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/T6.h" #include "InfoString/InfoString.h" -#include "InfoStringLoaderAttachmentT6.h" +#include "Utils/Logging/Log.h" #include #include @@ -31,7 +32,7 @@ namespace InfoString infoString; if (!infoString.FromGdtProperties(*gdtEntry)) { - std::cerr << std::format("Failed to read attachment gdt entry: \"{}\"\n", assetName); + con::error("Failed to read attachment gdt entry: \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -40,14 +41,14 @@ namespace private: IGdtQueryable& m_gdt; - InfoStringLoaderAttachment m_info_string_loader; + attachment::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace attachment { - std::unique_ptr> CreateGdtAttachmentLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) { return std::make_unique(memory, searchPath, gdt, zone); } -} // namespace T6 +} // namespace attachment diff --git a/src/ObjLoading/Game/T6/Weapon/AttachmentGdtLoaderT6.h b/src/ObjLoading/Game/T6/Weapon/AttachmentGdtLoaderT6.h new file mode 100644 index 00000000..f2ca06ac --- /dev/null +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentGdtLoaderT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T6/T6.h" +#include "Gdt/IGdtQueryable.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace attachment +{ + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); +} // namespace attachment diff --git a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentT6.cpp b/src/ObjLoading/Game/T6/Weapon/AttachmentInfoStringLoaderT6.cpp similarity index 76% rename from src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentT6.cpp rename to src/ObjLoading/Game/T6/Weapon/AttachmentInfoStringLoaderT6.cpp index 83f71735..b81a2dd3 100644 --- a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentInfoStringLoaderT6.cpp @@ -1,9 +1,10 @@ -#include "InfoStringLoaderAttachmentT6.h" +#include "AttachmentInfoStringLoaderT6.h" #include "Game/T6/InfoString/InfoStringToStructConverter.h" #include "Game/T6/T6.h" #include "Game/T6/Weapon/AttachmentFields.h" #include "Game/T6/Weapon/WeaponStrings.h" +#include "Utils/Logging/Log.h" #include #include @@ -93,29 +94,32 @@ namespace } } // namespace -InfoStringLoaderAttachment::InfoStringLoaderAttachment(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) +namespace attachment { -} - -AssetCreationResult InfoStringLoaderAttachment::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) -{ - auto* attachment = m_memory.Alloc(); - attachment->szInternalName = m_memory.Dup(assetName.c_str()); - - AssetRegistration registration(assetName, attachment); - - InfoStringToAttachmentConverter converter( - infoString, *attachment, m_zone.m_script_strings, m_memory, context, registration, attachment_fields, std::extent_v); - if (!converter.Convert()) + InfoStringLoaderT6::InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone) { - std::cerr << std::format("Failed to parse attachment: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - CalculateAttachmentFields(*attachment); + AssetCreationResult InfoStringLoaderT6::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + auto* attachment = m_memory.Alloc(); + attachment->szInternalName = m_memory.Dup(assetName.c_str()); - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + AssetRegistration registration(assetName, attachment); + + InfoStringToAttachmentConverter converter( + infoString, *attachment, m_zone.m_script_strings, m_memory, context, registration, attachment_fields, std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse attachment: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + CalculateAttachmentFields(*attachment); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace attachment diff --git a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentT6.h b/src/ObjLoading/Game/T6/Weapon/AttachmentInfoStringLoaderT6.h similarity index 70% rename from src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentT6.h rename to src/ObjLoading/Game/T6/Weapon/AttachmentInfoStringLoaderT6.h index fc6d020d..b4782772 100644 --- a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentT6.h +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentInfoStringLoaderT6.h @@ -4,12 +4,12 @@ #include "Asset/AssetCreationResult.h" #include "InfoString/InfoString.h" -namespace T6 +namespace attachment { - class InfoStringLoaderAttachment + class InfoStringLoaderT6 { public: - InfoStringLoaderAttachment(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); @@ -18,4 +18,4 @@ namespace T6 ISearchPath& m_search_path; Zone& m_zone; }; -} // namespace T6 +} // namespace attachment diff --git a/src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentT6.cpp b/src/ObjLoading/Game/T6/Weapon/AttachmentRawLoaderT6.cpp similarity index 68% rename from src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentT6.cpp rename to src/ObjLoading/Game/T6/Weapon/AttachmentRawLoaderT6.cpp index bf318b2d..dd3da8d2 100644 --- a/src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentRawLoaderT6.cpp @@ -1,15 +1,18 @@ -#include "RawLoaderAttachmentT6.h" +#include "AttachmentRawLoaderT6.h" +#include "AttachmentInfoStringLoaderT6.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/T6.h" #include "InfoString/InfoString.h" -#include "InfoStringLoaderAttachmentT6.h" +#include "Utils/Logging/Log.h" +#include "Weapon/AttachmentCommon.h" #include #include #include using namespace T6; +using namespace ::attachment; namespace { @@ -24,7 +27,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("attachment/{}", assetName); + const auto fileName = GetInfoStringFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -32,7 +35,7 @@ namespace InfoString infoString; if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT, *file.m_stream)) { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); + con::error("Could not parse as info string file: \"{}\"", fileName); return AssetCreationResult::Failure(); } @@ -41,14 +44,14 @@ namespace private: ISearchPath& m_search_path; - InfoStringLoaderAttachment m_info_string_loader; + attachment::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace attachment { - std::unique_ptr> CreateRawAttachmentLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace T6 +} // namespace attachment diff --git a/src/ObjLoading/Game/T6/Weapon/AttachmentRawLoaderT6.h b/src/ObjLoading/Game/T6/Weapon/AttachmentRawLoaderT6.h new file mode 100644 index 00000000..43748226 --- /dev/null +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentRawLoaderT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T6/T6.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace attachment +{ + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace attachment diff --git a/src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentUniqueT6.cpp b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueGdtLoaderT6.cpp similarity index 72% rename from src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentUniqueT6.cpp rename to src/ObjLoading/Game/T6/Weapon/AttachmentUniqueGdtLoaderT6.cpp index ab53d947..4f505ca1 100644 --- a/src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentUniqueT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueGdtLoaderT6.cpp @@ -1,9 +1,10 @@ -#include "GdtLoaderAttachmentUniqueT6.h" +#include "AttachmentUniqueGdtLoaderT6.h" +#include "AttachmentUniqueInfoStringLoaderT6.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/T6.h" #include "InfoString/InfoString.h" -#include "InfoStringLoaderAttachmentUniqueT6.h" +#include "Utils/Logging/Log.h" #include #include @@ -31,7 +32,7 @@ namespace InfoString infoString; if (!infoString.FromGdtProperties(*gdtEntry)) { - std::cerr << std::format("Failed to read attachment unique gdt entry: \"{}\"\n", assetName); + con::error("Failed to read attachment unique gdt entry: \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -40,15 +41,14 @@ namespace private: IGdtQueryable& m_gdt; - InfoStringLoaderAttachmentUnique m_info_string_loader; + attachment_unique::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace attachment_unique { - std::unique_ptr> - CreateGdtAttachmentUniqueLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) { return std::make_unique(memory, searchPath, gdt, zone); } -} // namespace T6 +} // namespace attachment_unique diff --git a/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueGdtLoaderT6.h b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueGdtLoaderT6.h new file mode 100644 index 00000000..1b1ad408 --- /dev/null +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueGdtLoaderT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T6/T6.h" +#include "Gdt/IGdtQueryable.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace attachment_unique +{ + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); +} // namespace attachment_unique diff --git a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentUniqueT6.cpp b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueInfoStringLoaderT6.cpp similarity index 60% rename from src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentUniqueT6.cpp rename to src/ObjLoading/Game/T6/Weapon/AttachmentUniqueInfoStringLoaderT6.cpp index 98a17081..de1070b0 100644 --- a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentUniqueT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueInfoStringLoaderT6.cpp @@ -1,9 +1,10 @@ -#include "InfoStringLoaderAttachmentUniqueT6.h" +#include "AttachmentUniqueInfoStringLoaderT6.h" #include "Game/T6/InfoString/InfoStringToStructConverter.h" #include "Game/T6/T6.h" #include "Game/T6/Weapon/AttachmentUniqueFields.h" #include "Game/T6/Weapon/WeaponStrings.h" +#include "Utils/Logging/Log.h" #include #include @@ -48,13 +49,13 @@ namespace std::vector valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse hide tags as array\n"; + con::error("Failed to parse hide tags as array"); return false; } if (valueArray.size() > std::extent_v) { - std::cerr << std::format("Cannot have more than {} hide tags!\n", std::extent_v); + con::error("Cannot have more than {} hide tags!", std::extent_v); return false; } @@ -93,7 +94,7 @@ namespace if (camo == nullptr) { - std::cerr << std::format("Failed to load camo asset \"{}\"\n", value); + con::error("Failed to load camo asset \"{}\"", value); return false; } @@ -141,9 +142,9 @@ namespace { // combinedAttachmentTypeMask std::vector attachmentsFromName; - if (!InfoStringLoaderAttachmentUnique::ExtractAttachmentsFromAssetName(assetName, attachmentsFromName)) + if (!attachment_unique::ExtractAttachmentsFromAssetNameT6(assetName, attachmentsFromName)) { - std::cerr << std::format("Failed to determine attachments from attachment unique name \"{}\"\n", assetName); + con::error("Failed to determine attachments from attachment unique name \"{}\"", assetName); return false; } @@ -159,86 +160,89 @@ namespace } } // namespace -bool InfoStringLoaderAttachmentUnique::ExtractAttachmentsFromAssetName(const std::string& assetName, std::vector& attachmentList) +namespace attachment_unique { - std::vector parts; - - auto attachCount = 1u; - auto partStart = 0u; - for (auto ci = 0u; ci < assetName.size(); ci++) + bool ExtractAttachmentsFromAssetNameT6(const std::string& assetName, std::vector& attachmentList) { - if (assetName[ci] == '_') + std::vector parts; + + auto attachCount = 1u; + auto partStart = 0u; + for (auto ci = 0u; ci < assetName.size(); ci++) { - parts.emplace_back(assetName, partStart, ci - partStart); - partStart = ci + 1; - } - else if (assetName[ci] == '+') - { - attachCount++; - parts.emplace_back(assetName, partStart, ci - partStart); - partStart = ci + 1; - } - } - - if (partStart < assetName.size()) - parts.emplace_back(assetName, partStart, assetName.size() - partStart); - - for (auto attachPartOffset = parts.size() - attachCount; attachPartOffset < parts.size(); attachPartOffset++) - { - auto& specifiedAttachName = parts[attachPartOffset]; - - for (auto& c : specifiedAttachName) - c = static_cast(tolower(c)); - - auto foundAttachment = false; - for (auto attachIndex = 0u; attachIndex < std::extent_v; attachIndex++) - { - if (specifiedAttachName == szAttachmentTypeNames[attachIndex]) + if (assetName[ci] == '_') { - attachmentList.push_back(static_cast(attachIndex)); - foundAttachment = true; - break; + parts.emplace_back(assetName, partStart, ci - partStart); + partStart = ci + 1; + } + else if (assetName[ci] == '+') + { + attachCount++; + parts.emplace_back(assetName, partStart, ci - partStart); + partStart = ci + 1; } } - if (!foundAttachment) - return false; + if (partStart < assetName.size()) + parts.emplace_back(assetName, partStart, assetName.size() - partStart); + + for (auto attachPartOffset = parts.size() - attachCount; attachPartOffset < parts.size(); attachPartOffset++) + { + auto& specifiedAttachName = parts[attachPartOffset]; + + for (auto& c : specifiedAttachName) + c = static_cast(tolower(c)); + + auto foundAttachment = false; + for (auto attachIndex = 0u; attachIndex < std::extent_v; attachIndex++) + { + if (specifiedAttachName == szAttachmentTypeNames[attachIndex]) + { + attachmentList.push_back(static_cast(attachIndex)); + foundAttachment = true; + break; + } + } + + if (!foundAttachment) + return false; + } + + return true; } - return true; -} - -InfoStringLoaderAttachmentUnique::InfoStringLoaderAttachmentUnique(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) -{ -} - -AssetCreationResult InfoStringLoaderAttachmentUnique::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) -{ - auto* attachmentUniqueFull = m_memory.Alloc(); - attachmentUniqueFull->attachment.szInternalName = m_memory.Dup(assetName.c_str()); - - LinkAttachmentUniqueFullSubStructs(*attachmentUniqueFull); - - AssetRegistration registration(assetName, &attachmentUniqueFull->attachment); - - InfoStringToAttachmentUniqueConverter converter(infoString, - *attachmentUniqueFull, - m_zone.m_script_strings, - m_memory, - context, - registration, - attachment_unique_fields, - std::extent_v); - if (!converter.Convert()) + InfoStringLoaderT6::InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone) { - std::cerr << std::format("Failed to parse attachment unique: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - CalculateAttachmentUniqueFields(assetName, *attachmentUniqueFull); + AssetCreationResult InfoStringLoaderT6::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + auto* attachmentUniqueFull = m_memory.Alloc(); + attachmentUniqueFull->attachment.szInternalName = m_memory.Dup(assetName.c_str()); - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + LinkAttachmentUniqueFullSubStructs(*attachmentUniqueFull); + + AssetRegistration registration(assetName, &attachmentUniqueFull->attachment); + + InfoStringToAttachmentUniqueConverter converter(infoString, + *attachmentUniqueFull, + m_zone.m_script_strings, + m_memory, + context, + registration, + attachment_unique_fields, + std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse attachment unique: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + CalculateAttachmentUniqueFields(assetName, *attachmentUniqueFull); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace attachment_unique diff --git a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentUniqueT6.h b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueInfoStringLoaderT6.h similarity index 59% rename from src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentUniqueT6.h rename to src/ObjLoading/Game/T6/Weapon/AttachmentUniqueInfoStringLoaderT6.h index 3ff89621..265b294f 100644 --- a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderAttachmentUniqueT6.h +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueInfoStringLoaderT6.h @@ -7,20 +7,20 @@ #include -namespace T6 +namespace attachment_unique { - class InfoStringLoaderAttachmentUnique + bool ExtractAttachmentsFromAssetNameT6(const std::string& assetName, std::vector& attachmentList); + + class InfoStringLoaderT6 { public: - InfoStringLoaderAttachmentUnique(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); - static bool ExtractAttachmentsFromAssetName(const std::string& assetName, std::vector& attachmentList); - private: MemoryManager& m_memory; ISearchPath& m_search_path; Zone& m_zone; }; -} // namespace T6 +} // namespace attachment_unique diff --git a/src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentUniqueT6.cpp b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueRawLoaderT6.cpp similarity index 58% rename from src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentUniqueT6.cpp rename to src/ObjLoading/Game/T6/Weapon/AttachmentUniqueRawLoaderT6.cpp index f3af0c30..95fc13e6 100644 --- a/src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentUniqueT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueRawLoaderT6.cpp @@ -1,9 +1,11 @@ -#include "RawLoaderAttachmentUniqueT6.h" +#include "AttachmentUniqueRawLoaderT6.h" +#include "AttachmentUniqueInfoStringLoaderT6.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/T6.h" #include "InfoString/InfoString.h" -#include "InfoStringLoaderAttachmentUniqueT6.h" +#include "Utils/Logging/Log.h" +#include "Weapon/AttachmentUniqueCommon.h" #include #include @@ -13,10 +15,10 @@ using namespace T6; namespace { - class RawLoaderAttachmentUnique final : public AssetCreator + class RawLoader final : public AssetCreator { public: - RawLoaderAttachmentUnique(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + RawLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) : m_search_path(searchPath), m_info_string_loader(memory, searchPath, zone) { @@ -24,7 +26,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("attachmentunique/{}", assetName); + const auto fileName = attachment_unique::GetFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -32,7 +34,7 @@ namespace InfoString infoString; if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT_UNIQUE, *file.m_stream)) { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); + con::error("Could not parse as info string file: \"{}\"", fileName); return AssetCreationResult::Failure(); } @@ -41,14 +43,14 @@ namespace private: ISearchPath& m_search_path; - InfoStringLoaderAttachmentUnique m_info_string_loader; + attachment_unique::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace attachment_unique { - std::unique_ptr> CreateRawAttachmentUniqueLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { - return std::make_unique(memory, searchPath, zone); + return std::make_unique(memory, searchPath, zone); } -} // namespace T6 +} // namespace attachment_unique diff --git a/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueRawLoaderT6.h b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueRawLoaderT6.h new file mode 100644 index 00000000..b6c83a64 --- /dev/null +++ b/src/ObjLoading/Game/T6/Weapon/AttachmentUniqueRawLoaderT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T6/T6.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace attachment_unique +{ + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace attachment_unique diff --git a/src/ObjLoading/Game/T6/Weapon/JsonWeaponCamoLoaderT6.cpp b/src/ObjLoading/Game/T6/Weapon/CamoJsonLoaderT6.cpp similarity index 79% rename from src/ObjLoading/Game/T6/Weapon/JsonWeaponCamoLoaderT6.cpp rename to src/ObjLoading/Game/T6/Weapon/CamoJsonLoaderT6.cpp index fbe08536..a649bbab 100644 --- a/src/ObjLoading/Game/T6/Weapon/JsonWeaponCamoLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/CamoJsonLoaderT6.cpp @@ -1,8 +1,12 @@ -#include "JsonWeaponCamoLoaderT6.h" +#include "CamoJsonLoaderT6.h" -#include "Game/T6/CommonT6.h" #include "Game/T6/Json/JsonWeaponCamo.h" +#include "Game/T6/T6.h" +#include "Pool/GlobalAssetPool.h" +#include "Utils/Logging/Log.h" +#include "Weapon/CamoCommon.h" +#include #include #include #include @@ -26,27 +30,27 @@ namespace bool Load(WeaponCamo& weaponCamo) const { - const auto jRoot = json::parse(m_stream); - std::string type; - unsigned version; - - jRoot.at("_type").get_to(type); - jRoot.at("_version").get_to(version); - - if (type != "weaponCamo" || version != 1u) - { - std::cerr << std::format("Tried to load weapon camo \"{}\" but did not find expected type weaponCamo of version {}\n", weaponCamo.name, 1u); - return false; - } - try { + const auto jRoot = json::parse(m_stream); + std::string type; + unsigned version; + + jRoot.at("_type").get_to(type); + jRoot.at("_version").get_to(version); + + if (type != "weaponCamo" || version != 1u) + { + con::error("Tried to load weapon camo \"{}\" but did not find expected type weaponCamo of version {}", weaponCamo.name, 1u); + return false; + } + const auto jWeaponCamo = jRoot.get(); return CreateWeaponCamoFromJson(jWeaponCamo, weaponCamo); } catch (const json::exception& e) { - std::cerr << std::format("Failed to parse json of weapon camo: {}\n", e.what()); + con::error("Failed to parse json of weapon camo: {}", e.what()); } return false; @@ -55,7 +59,7 @@ namespace private: static void PrintError(const WeaponCamo& weaponCamo, const std::string& message) { - std::cerr << "Cannot load weapon camo \"" << weaponCamo.name << "\": " << message << "\n"; + con::error("Cannot load weapon camo \"{}\": {}", weaponCamo.name, message); } bool CreateWeaponCamoSetFromJson(const JsonWeaponCamoSet& jWeaponCamoSet, WeaponCamoSet& weaponCamoSet, const WeaponCamo& weaponCamo) const @@ -237,15 +241,46 @@ namespace AssetCreationContext& m_context; AssetRegistration& m_registration; }; + + class WeaponCamoLoader final : public AssetCreator + { + public: + WeaponCamoLoader(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto file = m_search_path.Open(camo::GetJsonFileNameForAssetName(assetName)); + if (!file.IsOpen()) + return AssetCreationResult::NoAction(); + + auto* weaponCamo = m_memory.Alloc(); + weaponCamo->name = m_memory.Dup(assetName.c_str()); + + AssetRegistration registration(assetName, weaponCamo); + const JsonLoader loader(*file.m_stream, m_memory, context, registration); + if (!loader.Load(*weaponCamo)) + { + con::error("Failed to load weapon camo \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; } // namespace -namespace T6 +namespace camo { - bool LoadWeaponCamoAsJson( - std::istream& stream, WeaponCamo& weaponCamo, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) + std::unique_ptr> CreateJsonLoaderT6(MemoryManager& memory, ISearchPath& searchPath) { - const JsonLoader loader(stream, memory, context, registration); - - return loader.Load(weaponCamo); + return std::make_unique(memory, searchPath); } -} // namespace T6 +} // namespace camo diff --git a/src/ObjLoading/Game/T6/Weapon/LoaderWeaponCamoT6.h b/src/ObjLoading/Game/T6/Weapon/CamoJsonLoaderT6.h similarity index 50% rename from src/ObjLoading/Game/T6/Weapon/LoaderWeaponCamoT6.h rename to src/ObjLoading/Game/T6/Weapon/CamoJsonLoaderT6.h index b5270fba..cd6da51d 100644 --- a/src/ObjLoading/Game/T6/Weapon/LoaderWeaponCamoT6.h +++ b/src/ObjLoading/Game/T6/Weapon/CamoJsonLoaderT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace camo { - std::unique_ptr> CreateWeaponCamoLoader(MemoryManager& memory, ISearchPath& searchPath); -} // namespace T6 + std::unique_ptr> CreateJsonLoaderT6(MemoryManager& memory, ISearchPath& searchPath); +} // namespace camo diff --git a/src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentT6.h b/src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentT6.h deleted file mode 100644 index 6455b204..00000000 --- a/src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentT6.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Asset/IAssetCreator.h" -#include "Game/T6/T6.h" -#include "Gdt/IGdtQueryable.h" -#include "SearchPath/ISearchPath.h" -#include "Utils/MemoryManager.h" - -#include - -namespace T6 -{ - std::unique_ptr> CreateGdtAttachmentLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentUniqueT6.h b/src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentUniqueT6.h deleted file mode 100644 index 3e39221d..00000000 --- a/src/ObjLoading/Game/T6/Weapon/GdtLoaderAttachmentUniqueT6.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "Asset/IAssetCreator.h" -#include "Game/T6/T6.h" -#include "Gdt/IGdtQueryable.h" -#include "SearchPath/ISearchPath.h" -#include "Utils/MemoryManager.h" - -#include - -namespace T6 -{ - std::unique_ptr> - CreateGdtAttachmentUniqueLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Weapon/JsonWeaponCamoLoaderT6.h b/src/ObjLoading/Game/T6/Weapon/JsonWeaponCamoLoaderT6.h deleted file mode 100644 index 6f5aea17..00000000 --- a/src/ObjLoading/Game/T6/Weapon/JsonWeaponCamoLoaderT6.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Asset/AssetCreationContext.h" -#include "Asset/AssetRegistration.h" -#include "Game/T6/T6.h" -#include "Utils/MemoryManager.h" - -#include - -namespace T6 -{ - bool LoadWeaponCamoAsJson( - std::istream& stream, WeaponCamo& weaponCamo, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration); -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Weapon/LoaderWeaponCamoT6.cpp b/src/ObjLoading/Game/T6/Weapon/LoaderWeaponCamoT6.cpp deleted file mode 100644 index c3409ac8..00000000 --- a/src/ObjLoading/Game/T6/Weapon/LoaderWeaponCamoT6.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "LoaderWeaponCamoT6.h" - -#include "Game/T6/T6.h" -#include "JsonWeaponCamoLoaderT6.h" -#include "Pool/GlobalAssetPool.h" - -#include -#include -#include - -using namespace T6; - -namespace -{ - class WeaponCamoLoader final : public AssetCreator - { - public: - WeaponCamoLoader(MemoryManager& memory, ISearchPath& searchPath) - : m_memory(memory), - m_search_path(searchPath) - { - } - - AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override - { - const auto file = m_search_path.Open(std::format("camo/{}.json", assetName)); - if (!file.IsOpen()) - return AssetCreationResult::NoAction(); - - auto* weaponCamo = m_memory.Alloc(); - weaponCamo->name = m_memory.Dup(assetName.c_str()); - - AssetRegistration registration(assetName, weaponCamo); - if (!LoadWeaponCamoAsJson(*file.m_stream, *weaponCamo, m_memory, context, registration)) - { - std::cerr << std::format("Failed to load weapon camo \"{}\"\n", assetName); - return AssetCreationResult::Failure(); - } - - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); - } - - private: - MemoryManager& m_memory; - ISearchPath& m_search_path; - }; -} // namespace - -namespace T6 -{ - std::unique_ptr> CreateWeaponCamoLoader(MemoryManager& memory, ISearchPath& searchPath) - { - return std::make_unique(memory, searchPath); - } -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentT6.h b/src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentT6.h deleted file mode 100644 index d83631d1..00000000 --- a/src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentT6.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Asset/IAssetCreator.h" -#include "Game/T6/T6.h" -#include "SearchPath/ISearchPath.h" -#include "Utils/MemoryManager.h" - -#include - -namespace T6 -{ - std::unique_ptr> CreateRawAttachmentLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentUniqueT6.h b/src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentUniqueT6.h deleted file mode 100644 index 47b66237..00000000 --- a/src/ObjLoading/Game/T6/Weapon/RawLoaderAttachmentUniqueT6.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Asset/IAssetCreator.h" -#include "Game/T6/T6.h" -#include "SearchPath/ISearchPath.h" -#include "Utils/MemoryManager.h" - -#include - -namespace T6 -{ - std::unique_ptr> CreateRawAttachmentUniqueLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Weapon/RawLoaderWeaponT6.h b/src/ObjLoading/Game/T6/Weapon/RawLoaderWeaponT6.h deleted file mode 100644 index 8eedbb71..00000000 --- a/src/ObjLoading/Game/T6/Weapon/RawLoaderWeaponT6.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Asset/IAssetCreator.h" -#include "Game/T6/T6.h" -#include "SearchPath/ISearchPath.h" -#include "Utils/MemoryManager.h" - -#include - -namespace T6 -{ - std::unique_ptr> CreateRawWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Weapon/GdtLoaderWeaponT6.cpp b/src/ObjLoading/Game/T6/Weapon/WeaponGdtLoaderT6.cpp similarity index 74% rename from src/ObjLoading/Game/T6/Weapon/GdtLoaderWeaponT6.cpp rename to src/ObjLoading/Game/T6/Weapon/WeaponGdtLoaderT6.cpp index 702213dd..5b9acf66 100644 --- a/src/ObjLoading/Game/T6/Weapon/GdtLoaderWeaponT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/WeaponGdtLoaderT6.cpp @@ -1,9 +1,10 @@ -#include "GdtLoaderWeaponT6.h" +#include "WeaponGdtLoaderT6.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/T6.h" #include "InfoString/InfoString.h" -#include "InfoStringLoaderWeaponT6.h" +#include "Utils/Logging/Log.h" +#include "WeaponInfoStringLoaderT6.h" #include #include @@ -31,7 +32,7 @@ namespace InfoString infoString; if (!infoString.FromGdtProperties(*gdtEntry)) { - std::cerr << std::format("Failed to read weapon gdt entry: \"{}\"\n", assetName); + con::error("Failed to read weapon gdt entry: \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -40,14 +41,14 @@ namespace private: IGdtQueryable& m_gdt; - InfoStringLoaderWeapon m_info_string_loader; + weapon::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace weapon { - std::unique_ptr> CreateGdtWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) { return std::make_unique(memory, searchPath, gdt, zone); } -} // namespace T6 +} // namespace weapon diff --git a/src/ObjLoading/Game/T6/Weapon/GdtLoaderWeaponT6.h b/src/ObjLoading/Game/T6/Weapon/WeaponGdtLoaderT6.h similarity index 56% rename from src/ObjLoading/Game/T6/Weapon/GdtLoaderWeaponT6.h rename to src/ObjLoading/Game/T6/Weapon/WeaponGdtLoaderT6.h index fdb1882b..ef2b6914 100644 --- a/src/ObjLoading/Game/T6/Weapon/GdtLoaderWeaponT6.h +++ b/src/ObjLoading/Game/T6/Weapon/WeaponGdtLoaderT6.h @@ -8,7 +8,7 @@ #include -namespace T6 +namespace weapon { - std::unique_ptr> CreateGdtWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); +} // namespace weapon diff --git a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderWeaponT6.cpp b/src/ObjLoading/Game/T6/Weapon/WeaponInfoStringLoaderT6.cpp similarity index 87% rename from src/ObjLoading/Game/T6/Weapon/InfoStringLoaderWeaponT6.cpp rename to src/ObjLoading/Game/T6/Weapon/WeaponInfoStringLoaderT6.cpp index 6d97ceab..8bc473e9 100644 --- a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderWeaponT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/WeaponInfoStringLoaderT6.cpp @@ -1,10 +1,11 @@ -#include "InfoStringLoaderWeaponT6.h" +#include "WeaponInfoStringLoaderT6.h" +#include "AttachmentUniqueInfoStringLoaderT6.h" #include "Game/T6/InfoString/InfoStringToStructConverter.h" #include "Game/T6/T6.h" #include "Game/T6/Weapon/WeaponFields.h" #include "Game/T6/Weapon/WeaponStrings.h" -#include "InfoStringLoaderAttachmentUniqueT6.h" +#include "Utils/Logging/Log.h" #include "Weapon/AccuracyGraphLoader.h" #include @@ -126,13 +127,13 @@ namespace std::vector valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse hide tags as array\n"; + con::error("Failed to parse hide tags as array"); return false; } if (valueArray.size() > std::extent_v) { - std::cerr << std::format("Cannot have more than {} hide tags!\n", std::extent_v); + con::error("Cannot have more than {} hide tags!", std::extent_v); return false; } @@ -183,13 +184,13 @@ namespace std::vector> pairs; if (!ParseAsArray(value, pairs)) { - std::cerr << "Failed to parse notetracksoundmap as pairs\n"; + con::error("Failed to parse notetracksoundmap as pairs"); return false; } if (pairs.size() > std::extent_v) { - std::cerr << "Cannot have more than " << std::extent_v << " notetracksoundmap entries!\n"; + con::error("Cannot have more than {} notetracksoundmap entries!", std::extent_v); return false; } @@ -239,7 +240,7 @@ namespace if (camo == nullptr) { - std::cerr << std::format("Failed to load camo asset \"{}\"\n", value); + con::error("Failed to load camo asset \"{}\"", value); return false; } @@ -254,7 +255,7 @@ namespace std::vector valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse attachments as array\n"; + con::error("Failed to parse attachments as array"); return false; } @@ -265,7 +266,7 @@ namespace auto* attachmentAssetInfo = m_context.ForceLoadDependency(attachmentName); if (attachmentAssetInfo == nullptr) { - std::cerr << std::format("Failed to load attachment asset \"{}\"\n", attachmentName); + con::error("Failed to load attachment asset \"{}\"", attachmentName); return false; } @@ -273,17 +274,17 @@ namespace if (static_cast(attachmentAsset->attachmentType) >= ATTACHMENT_TYPE_COUNT) { - std::cerr << std::format( - "Invalid attachment type {} for attachment asset \"{}\"\n", static_cast(attachmentAsset->attachmentType), attachmentName); + con::error( + "Invalid attachment type {} for attachment asset \"{}\"", static_cast(attachmentAsset->attachmentType), attachmentName); return false; } if (attachments[attachmentAsset->attachmentType] != nullptr) { - std::cerr << std::format("Already loaded attachment with same type {}: \"{}\", \"{}\"\n", - static_cast(attachmentAsset->attachmentType), - attachments[attachmentAsset->attachmentType]->szInternalName, - attachmentName); + con::error("Already loaded attachment with same type {}: \"{}\", \"{}\"", + static_cast(attachmentAsset->attachmentType), + attachments[attachmentAsset->attachmentType]->szInternalName, + attachmentName); return false; } @@ -305,7 +306,7 @@ namespace std::vector valueArray; if (!ParseAsArray(value, valueArray)) { - std::cerr << "Failed to parse attachment uniques as array\n"; + con::error("Failed to parse attachment uniques as array"); return false; } @@ -317,7 +318,7 @@ namespace auto* attachmentUniqueAssetInfo = m_context.ForceLoadDependency(attachmentUniqueName); if (attachmentUniqueAssetInfo == nullptr) { - std::cerr << std::format("Failed to load attachment unique asset \"{}\"\n", attachmentUniqueName); + con::error("Failed to load attachment unique asset \"{}\"", attachmentUniqueName); return false; } @@ -327,9 +328,8 @@ namespace { if (attachmentCombinationIndex >= std::extent_v) { - std::cerr << std::format( - "Cannot have more than {} combined attachment attachment unique entries!\n", - (std::extent_v - std::extent_v)); + con::error("Cannot have more than {} combined attachment attachment unique entries!", + (std::extent_v - std::extent_v)); return false; } @@ -340,18 +340,18 @@ namespace { if (static_cast(attachmentUniqueAsset->attachmentType) >= ATTACHMENT_TYPE_COUNT) { - std::cerr << std::format("Invalid attachment type {} for attachment unique asset \"{}\"\n", - static_cast(attachmentUniqueAsset->attachmentType), - attachmentUniqueName); + con::error("Invalid attachment type {} for attachment unique asset \"{}\"", + static_cast(attachmentUniqueAsset->attachmentType), + attachmentUniqueName); return false; } if (attachmentUniques[attachmentUniqueAsset->attachmentType] != nullptr) { - std::cerr << std::format("Already loaded attachment unique with same type {}: \"{}\", \"{}\"\n", - static_cast(attachmentUniqueAsset->attachmentType), - attachmentUniques[attachmentUniqueAsset->attachmentType]->szInternalName, - attachmentUniqueName); + con::error("Already loaded attachment unique with same type {}: \"{}\", \"{}\"", + static_cast(attachmentUniqueAsset->attachmentType), + attachmentUniques[attachmentUniqueAsset->attachmentType]->szInternalName, + attachmentUniqueName); return false; } @@ -568,8 +568,7 @@ namespace && weapon.attachmentUniques[attachmentUniqueIndex]->attachmentType != attachmentUnique.attachmentType) { std::vector attachments; - if (InfoStringLoaderAttachmentUnique::ExtractAttachmentsFromAssetName(weapon.attachmentUniques[attachmentUniqueIndex]->szInternalName, - attachments) + if (attachment_unique::ExtractAttachmentsFromAssetNameT6(weapon.attachmentUniques[attachmentUniqueIndex]->szInternalName, attachments) && attachments.front() == attachmentUnique.attachmentType) { if (lastSibling == nullptr) @@ -600,34 +599,37 @@ namespace } } // namespace -InfoStringLoaderWeapon::InfoStringLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) +namespace weapon { -} - -AssetCreationResult InfoStringLoaderWeapon::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) -{ - auto* weaponFullDef = m_memory.Alloc(); - weaponFullDef->weapVariantDef.szInternalName = m_memory.Dup(assetName.c_str()); - - LinkWeaponFullDefSubStructs(*weaponFullDef); - - AssetRegistration registration(assetName, &weaponFullDef->weapVariantDef); - - InfoStringToWeaponConverter converter( - infoString, *weaponFullDef, m_zone.m_script_strings, m_memory, context, registration, weapon_fields, std::extent_v); - if (!converter.Convert()) + InfoStringLoaderT6::InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone) { - std::cerr << std::format("Failed to parse weapon: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - CalculateWeaponFields(*weaponFullDef); - CalculateAttachmentFields(*weaponFullDef); + AssetCreationResult InfoStringLoaderT6::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + auto* weaponFullDef = m_memory.Alloc(); + weaponFullDef->weapVariantDef.szInternalName = m_memory.Dup(assetName.c_str()); - LoadAccuracyGraphs(*weaponFullDef, m_memory, m_search_path, context); + LinkWeaponFullDefSubStructs(*weaponFullDef); - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + AssetRegistration registration(assetName, &weaponFullDef->weapVariantDef); + + InfoStringToWeaponConverter converter( + infoString, *weaponFullDef, m_zone.m_script_strings, m_memory, context, registration, weapon_fields, std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse weapon: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + CalculateWeaponFields(*weaponFullDef); + CalculateAttachmentFields(*weaponFullDef); + + LoadAccuracyGraphs(*weaponFullDef, m_memory, m_search_path, context); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace weapon diff --git a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderWeaponT6.h b/src/ObjLoading/Game/T6/Weapon/WeaponInfoStringLoaderT6.h similarity index 71% rename from src/ObjLoading/Game/T6/Weapon/InfoStringLoaderWeaponT6.h rename to src/ObjLoading/Game/T6/Weapon/WeaponInfoStringLoaderT6.h index f745963a..a4d146cb 100644 --- a/src/ObjLoading/Game/T6/Weapon/InfoStringLoaderWeaponT6.h +++ b/src/ObjLoading/Game/T6/Weapon/WeaponInfoStringLoaderT6.h @@ -4,12 +4,12 @@ #include "Asset/AssetCreationResult.h" #include "InfoString/InfoString.h" -namespace T6 +namespace weapon { - class InfoStringLoaderWeapon + class InfoStringLoaderT6 { public: - InfoStringLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); @@ -18,4 +18,4 @@ namespace T6 ISearchPath& m_search_path; Zone& m_zone; }; -} // namespace T6 +} // namespace weapon diff --git a/src/ObjLoading/Game/T6/Weapon/RawLoaderWeaponT6.cpp b/src/ObjLoading/Game/T6/Weapon/WeaponRawLoaderT6.cpp similarity index 57% rename from src/ObjLoading/Game/T6/Weapon/RawLoaderWeaponT6.cpp rename to src/ObjLoading/Game/T6/Weapon/WeaponRawLoaderT6.cpp index 36cbcd48..2ed85259 100644 --- a/src/ObjLoading/Game/T6/Weapon/RawLoaderWeaponT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/WeaponRawLoaderT6.cpp @@ -1,9 +1,11 @@ -#include "RawLoaderWeaponT6.h" +#include "WeaponRawLoaderT6.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/T6.h" #include "InfoString/InfoString.h" -#include "InfoStringLoaderWeaponT6.h" +#include "Utils/Logging/Log.h" +#include "Weapon/WeaponCommon.h" +#include "WeaponInfoStringLoaderT6.h" #include #include @@ -13,10 +15,10 @@ using namespace T6; namespace { - class RawLoaderWeapon final : public AssetCreator + class RawLoader final : public AssetCreator { public: - RawLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + RawLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) : m_search_path(searchPath), m_info_string_loader(memory, searchPath, zone) { @@ -24,7 +26,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("weapons/{}", assetName); + const auto fileName = weapon::GetFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -32,7 +34,7 @@ namespace InfoString infoString; if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_WEAPON, *file.m_stream)) { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); + con::error("Could not parse as info string file: \"{}\"", fileName); return AssetCreationResult::Failure(); } @@ -41,14 +43,14 @@ namespace private: ISearchPath& m_search_path; - InfoStringLoaderWeapon m_info_string_loader; + weapon::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace weapon { - std::unique_ptr> CreateRawWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { - return std::make_unique(memory, searchPath, zone); + return std::make_unique(memory, searchPath, zone); } -} // namespace T6 +} // namespace weapon diff --git a/src/ObjLoading/Game/T6/Weapon/WeaponRawLoaderT6.h b/src/ObjLoading/Game/T6/Weapon/WeaponRawLoaderT6.h new file mode 100644 index 00000000..f026b99b --- /dev/null +++ b/src/ObjLoading/Game/T6/Weapon/WeaponRawLoaderT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T6/T6.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace weapon +{ + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace weapon diff --git a/src/ObjLoading/Game/T6/ZBarrier/GdtLoaderZBarrierT6.cpp b/src/ObjLoading/Game/T6/ZBarrier/GdtLoaderZBarrierT6.cpp index cba149e6..e69244aa 100644 --- a/src/ObjLoading/Game/T6/ZBarrier/GdtLoaderZBarrierT6.cpp +++ b/src/ObjLoading/Game/T6/ZBarrier/GdtLoaderZBarrierT6.cpp @@ -4,6 +4,7 @@ #include "Game/T6/T6.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderZBarrierT6.h" +#include "Utils/Logging/Log.h" #include #include @@ -31,7 +32,7 @@ namespace InfoString infoString; if (!infoString.FromGdtProperties(*gdtEntry)) { - std::cerr << std::format("Failed to read zbarrier gdt entry: \"{}\"\n", assetName); + con::error("Failed to read zbarrier gdt entry: \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -40,14 +41,14 @@ namespace private: IGdtQueryable& m_gdt; - InfoStringLoaderZBarrier m_info_string_loader; + z_barrier::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace z_barrier { - std::unique_ptr> CreateGdtZBarrierLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone) { return std::make_unique(memory, searchPath, gdt, zone); } -} // namespace T6 +} // namespace z_barrier diff --git a/src/ObjLoading/Game/T6/ZBarrier/GdtLoaderZBarrierT6.h b/src/ObjLoading/Game/T6/ZBarrier/GdtLoaderZBarrierT6.h index af3796b9..abf79682 100644 --- a/src/ObjLoading/Game/T6/ZBarrier/GdtLoaderZBarrierT6.h +++ b/src/ObjLoading/Game/T6/ZBarrier/GdtLoaderZBarrierT6.h @@ -8,7 +8,7 @@ #include -namespace T6 +namespace z_barrier { - std::unique_ptr> CreateGdtZBarrierLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateGdtLoaderT6(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone); +} // namespace z_barrier diff --git a/src/ObjLoading/Game/T6/ZBarrier/InfoStringLoaderZBarrierT6.cpp b/src/ObjLoading/Game/T6/ZBarrier/InfoStringLoaderZBarrierT6.cpp index 23c460b9..416a8a2a 100644 --- a/src/ObjLoading/Game/T6/ZBarrier/InfoStringLoaderZBarrierT6.cpp +++ b/src/ObjLoading/Game/T6/ZBarrier/InfoStringLoaderZBarrierT6.cpp @@ -3,6 +3,7 @@ #include "Game/T6/InfoString/InfoStringToStructConverter.h" #include "Game/T6/T6.h" #include "Game/T6/ZBarrier/ZBarrierFields.h" +#include "Utils/Logging/Log.h" #include #include @@ -55,29 +56,32 @@ namespace } } // namespace -InfoStringLoaderZBarrier::InfoStringLoaderZBarrier(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone) +namespace z_barrier { -} - -AssetCreationResult InfoStringLoaderZBarrier::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) -{ - auto* zbarrier = m_memory.Alloc(); - zbarrier->name = m_memory.Dup(assetName.c_str()); - - AssetRegistration registration(assetName, zbarrier); - - InfoStringToZBarrierConverter converter( - infoString, *zbarrier, m_zone.m_script_strings, m_memory, context, registration, zbarrier_fields, std::extent_v); - if (!converter.Convert()) + InfoStringLoaderT6::InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone) { - std::cerr << std::format("Failed to parse zbarrier: \"{}\"\n", assetName); - return AssetCreationResult::Failure(); } - CalculateZBarrierFields(*zbarrier); + AssetCreationResult InfoStringLoaderT6::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + auto* zbarrier = m_memory.Alloc(); + zbarrier->name = m_memory.Dup(assetName.c_str()); - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -} + AssetRegistration registration(assetName, zbarrier); + + InfoStringToZBarrierConverter converter( + infoString, *zbarrier, m_zone.m_script_strings, m_memory, context, registration, zbarrier_fields, std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse zbarrier: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + CalculateZBarrierFields(*zbarrier); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace z_barrier diff --git a/src/ObjLoading/Game/T6/ZBarrier/InfoStringLoaderZBarrierT6.h b/src/ObjLoading/Game/T6/ZBarrier/InfoStringLoaderZBarrierT6.h index 5a70ec28..403fec2e 100644 --- a/src/ObjLoading/Game/T6/ZBarrier/InfoStringLoaderZBarrierT6.h +++ b/src/ObjLoading/Game/T6/ZBarrier/InfoStringLoaderZBarrierT6.h @@ -4,12 +4,12 @@ #include "Asset/AssetCreationResult.h" #include "InfoString/InfoString.h" -namespace T6 +namespace z_barrier { - class InfoStringLoaderZBarrier + class InfoStringLoaderT6 { public: - InfoStringLoaderZBarrier(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); @@ -18,4 +18,4 @@ namespace T6 ISearchPath& m_search_path; Zone& m_zone; }; -} // namespace T6 +} // namespace z_barrier diff --git a/src/ObjLoading/Game/T6/ZBarrier/RawLoaderZBarrierT6.cpp b/src/ObjLoading/Game/T6/ZBarrier/RawLoaderZBarrierT6.cpp index 0a7d56d0..a1e2cd0f 100644 --- a/src/ObjLoading/Game/T6/ZBarrier/RawLoaderZBarrierT6.cpp +++ b/src/ObjLoading/Game/T6/ZBarrier/RawLoaderZBarrierT6.cpp @@ -4,6 +4,8 @@ #include "Game/T6/T6.h" #include "InfoString/InfoString.h" #include "InfoStringLoaderZBarrierT6.h" +#include "Utils/Logging/Log.h" +#include "ZBarrier/ZBarrierCommon.h" #include #include @@ -24,7 +26,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto fileName = std::format("zbarrier/{}", assetName); + const auto fileName = z_barrier::GetFileNameForAssetName(assetName); const auto file = m_search_path.Open(fileName); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -32,7 +34,7 @@ namespace InfoString infoString; if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_ZBARRIER, *file.m_stream)) { - std::cerr << std::format("Could not parse as info string file: \"{}\"\n", fileName); + con::error("Could not parse as info string file: \"{}\"", fileName); return AssetCreationResult::Failure(); } @@ -41,14 +43,14 @@ namespace private: ISearchPath& m_search_path; - InfoStringLoaderZBarrier m_info_string_loader; + z_barrier::InfoStringLoaderT6 m_info_string_loader; }; } // namespace -namespace T6 +namespace z_barrier { - std::unique_ptr> CreateRawZBarrierLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } -} // namespace T6 +} // namespace z_barrier diff --git a/src/ObjLoading/Game/T6/ZBarrier/RawLoaderZBarrierT6.h b/src/ObjLoading/Game/T6/ZBarrier/RawLoaderZBarrierT6.h index 007b2306..87ed18db 100644 --- a/src/ObjLoading/Game/T6/ZBarrier/RawLoaderZBarrierT6.h +++ b/src/ObjLoading/Game/T6/ZBarrier/RawLoaderZBarrierT6.h @@ -7,7 +7,7 @@ #include -namespace T6 +namespace z_barrier { - std::unique_ptr> CreateRawZBarrierLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); -} // namespace T6 + std::unique_ptr> CreateRawLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace z_barrier diff --git a/src/ObjLoading/InfoString/InfoStringToStructConverterBase.cpp b/src/ObjLoading/InfoString/InfoStringToStructConverterBase.cpp index 33e6dd60..0445b2e0 100644 --- a/src/ObjLoading/InfoString/InfoStringToStructConverterBase.cpp +++ b/src/ObjLoading/InfoString/InfoStringToStructConverterBase.cpp @@ -1,5 +1,7 @@ #include "InfoStringToStructConverterBase.h" +#include "Utils/Logging/Log.h" + #include #include #include @@ -66,7 +68,7 @@ bool InfoStringToStructConverterBase::ConvertInt(const std::string& value, const if (endPtr != &value[value.size()]) { - std::cerr << std::format("Failed to parse value \"{}\" as int\n", value); + con::error("Failed to parse value \"{}\" as int", value); return false; } @@ -80,7 +82,7 @@ bool InfoStringToStructConverterBase::ConvertUint(const std::string& value, cons if (endPtr != &value[value.size()]) { - std::cerr << std::format("Failed to parse value \"{}\" as uint\n", value); + con::error("Failed to parse value \"{}\" as uint", value); return false; } @@ -95,7 +97,7 @@ bool InfoStringToStructConverterBase::ConvertBool(const std::string& value, cons *reinterpret_cast(reinterpret_cast(m_structure) + offset) = intValue != 0; if (endPtr != &value[value.size()]) { - std::cerr << std::format("Failed to parse value \"{}\" as bool\n", value); + con::error("Failed to parse value \"{}\" as bool", value); return false; } @@ -110,7 +112,7 @@ bool InfoStringToStructConverterBase::ConvertQBoolean(const std::string& value, *reinterpret_cast(reinterpret_cast(m_structure) + offset) = intValue != 0 ? 1 : 0; if (endPtr != &value[value.size()]) { - std::cerr << std::format("Failed to parse value \"{}\" as qboolean\n", value); + con::error("Failed to parse value \"{}\" as qboolean", value); return false; } @@ -124,7 +126,7 @@ bool InfoStringToStructConverterBase::ConvertFloat(const std::string& value, con if (endPtr != &value[value.size()]) { - std::cerr << std::format("Failed to parse value \"{}\" as float\n", value); + con::error("Failed to parse value \"{}\" as float", value); return false; } @@ -138,7 +140,7 @@ bool InfoStringToStructConverterBase::ConvertMilliseconds(const std::string& val if (endPtr != &value[value.size()]) { - std::cerr << std::format("Failed to parse value \"{}\" as milliseconds\n", value); + con::error("Failed to parse value \"{}\" as milliseconds", value); return false; } @@ -176,8 +178,7 @@ bool InfoStringToStructConverterBase::ConvertEnumInt( ss << '"' << enumValues[i] << '"'; } - ss << '\n'; - std::cerr << ss.str(); + con::error(ss.str()); return false; } diff --git a/src/ObjLoading/InfoString/InfoStringToStructConverterBase.h b/src/ObjLoading/InfoString/InfoStringToStructConverterBase.h index af065f0f..e2f936d2 100644 --- a/src/ObjLoading/InfoString/InfoStringToStructConverterBase.h +++ b/src/ObjLoading/InfoString/InfoStringToStructConverterBase.h @@ -5,6 +5,7 @@ #include "InfoString/InfoString.h" #include "Pool/XAssetInfo.h" #include "Utils/ClassUtils.h" +#include "Utils/Logging/Log.h" #include "Utils/MemoryManager.h" #include "Zone/ZoneScriptStrings.h" @@ -51,7 +52,7 @@ protected: if (c == '\n' && currentEntryOffset != ARRAY_SIZE) { - std::cerr << "Expected value but got new line\n"; + con::error("Expected value but got new line"); return false; } @@ -89,7 +90,7 @@ protected: const auto isLastEntry = currentEntryOffset >= (ARRAY_SIZE - 1); if (isNextEntrySeparator != isLastEntry) { - std::cerr << std::format("Expected {} values but got new line\n", ARRAY_SIZE); + con::error("Expected {} values but got new line", ARRAY_SIZE); return false; } @@ -104,7 +105,7 @@ protected: if (currentEntryOffset > 0) { - std::cerr << std::format("Expected {} values but got new line\n", ARRAY_SIZE); + con::error("Expected {} values but got new line", ARRAY_SIZE); return false; } diff --git a/src/ObjLoading/Localize/Parsing/LocalizeFileReader.cpp b/src/ObjLoading/Localize/Parsing/LocalizeFileReader.cpp index 76a231b6..26aad4d1 100644 --- a/src/ObjLoading/Localize/Parsing/LocalizeFileReader.cpp +++ b/src/ObjLoading/Localize/Parsing/LocalizeFileReader.cpp @@ -3,6 +3,7 @@ #include "LocalizeFileParser.h" #include "Parsing/Impl/CommentRemovingStreamProxy.h" #include "Parsing/Impl/ParserSingleInputStream.h" +#include "Utils/Logging/Log.h" LocalizeFileReader::LocalizeFileReader(std::istream& stream, std::string fileName, GameLanguage language, ILocalizeFileDuplicationChecker& duplicationChecker) : m_file_name(std::move(fileName)), @@ -46,6 +47,6 @@ bool LocalizeFileReader::ReadLocalizeFile(std::vector& entr return true; } - std::cerr << "Parsing localization file failed!\n"; + con::error("Parsing localization file failed!"); return false; } diff --git a/src/ObjLoading/Localize/Parsing/Sequence/SequenceLocalizeFileLanguageValue.cpp b/src/ObjLoading/Localize/Parsing/Sequence/SequenceLocalizeFileLanguageValue.cpp index 60102949..39d7804e 100644 --- a/src/ObjLoading/Localize/Parsing/Sequence/SequenceLocalizeFileLanguageValue.cpp +++ b/src/ObjLoading/Localize/Parsing/Sequence/SequenceLocalizeFileLanguageValue.cpp @@ -1,6 +1,7 @@ #include "SequenceLocalizeFileLanguageValue.h" #include "Parsing/Simple/Matcher/SimpleMatcherFactory.h" +#include "Utils/Logging/Log.h" #include @@ -24,9 +25,9 @@ void SequenceLocalizeFileLanguageValue::ProcessMatch(LocalizeFileParserState* st const auto alreadyDefinedLanguage = state->m_current_reference_languages.find(langName); if (alreadyDefinedLanguage != state->m_current_reference_languages.end()) { - std::ostringstream str; - str << "Value for reference \"" << state->m_current_reference << "\" already defined for language \"" << langToken.IdentifierValue() << "\""; - throw ParsingException(langToken.GetPos(), str.str()); + throw ParsingException( + langToken.GetPos(), + std::format("Value for reference \"{}\" already defined for language \"{}\"", state->m_current_reference, langToken.IdentifierValue())); } state->m_current_reference_languages.emplace(langName); @@ -35,7 +36,7 @@ void SequenceLocalizeFileLanguageValue::ProcessMatch(LocalizeFileParserState* st const auto& currentReference = state->m_current_reference; if (!state->m_duplication_checker.CheckLocalizeEntryForDuplicates(currentReference)) { - std::cout << "Localize: a value for reference \"" << currentReference << "\" was already defined\n"; + con::warn("Localize: a value for reference \"{}\" was already defined", currentReference); } state->m_entries.emplace_back(currentReference, valueToken.StringValue()); diff --git a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template index 9ff75e42..248d12cc 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template @@ -1,8 +1,12 @@ -#options GAME (IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialLoader" + GAME + ".cpp" -#if GAME == "IW4" +#if GAME == "IW3" +#define FEATURE_IW3 +#define HAS_WATER +#define GAME_LOWER "iw3" +#elif GAME == "IW4" #define FEATURE_IW4 #define HAS_WATER #define GAME_LOWER "iw4" @@ -10,6 +14,10 @@ #define FEATURE_IW5 #define HAS_WATER #define GAME_LOWER "iw5" +#elif GAME == "T5" +#define FEATURE_T5 +#define HAS_WATER +#define GAME_LOWER "t5" #elif GAME == "T6" #define FEATURE_T6 #define GAME_LOWER "t6" @@ -25,14 +33,18 @@ #ifdef HAS_WATER #include "Base64.h" #endif + #set COMMON_HEADER "\"Game/" + GAME + "/Common" + GAME + ".h\"" #include COMMON_HEADER #set JSON_HEADER "\"Game/" + GAME + "/Material/JsonMaterial" + GAME + ".h\"" #include JSON_HEADER +#include "Utils/Logging/Log.h" #include #include +#include +using namespace nlohmann; using namespace GAME; namespace @@ -40,45 +52,47 @@ namespace class JsonLoader { public: - JsonLoader(MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) - : m_memory(memory), + JsonLoader(std::istream& stream, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) + : m_stream(stream), + m_memory(memory), m_context(context), m_registration(registration) { } - bool Load(json& jRoot, Material& material) const - { - std::string type; - unsigned version; - - jRoot.at("_type").get_to(type); - jRoot.at("_version").get_to(version); - - if (type != "material" || version != 1u) - { - std::cerr << std::format("Tried to load material \"{}\" but did not find expected type material of version 1\n", material.info.name); - return false; - } - -#ifndef FEATURE_T6 // T6 did not have this check in version 1, so to stay backwards compatible, let it stay that way - std::string game; - jRoot.at("_game").get_to(game); - if (game != GAME_LOWER) - { - std::cerr << std::format("Tried to load material \"{}\" but \"_game\" did not find expected type value {}\n", material.info.name, GAME_LOWER); - return false; - } -#endif - + bool Load(Material& material) const + { try { + const auto jRoot = json::parse(m_stream); + std::string type; + unsigned version; + + jRoot.at("_type").get_to(type); + jRoot.at("_version").get_to(version); + + if (type != "material" || version != 1u) + { + con::error("Tried to load material \"{}\" but did not find expected type material of version 1", material.info.name); + return false; + } + +#ifndef FEATURE_T6 // T6 did not have this check in version 1, so to stay backwards compatible, let it stay that way + std::string game; + jRoot.at("_game").get_to(game); + if (game != GAME_LOWER) + { + con::error("Tried to load material \"{}\" but \"_game\" did not find expected type value {}", material.info.name, GAME_LOWER); + return false; + } +#endif + const auto jMaterial = jRoot.get(); return CreateMaterialFromJson(jMaterial, material); } catch (const json::exception& e) { - std::cerr << std::format("Failed to parse json of material: {}\n", e.what()); + con::error("Failed to parse json of material: {}", e.what()); } return false; @@ -87,12 +101,12 @@ namespace private: static void PrintError(const Material& material, const std::string& message) { - std::cerr << std::format("Cannot load material \"{}\": {}\n", material.info.name, message); + con::error("Cannot load material \"{}\": {}", material.info.name, message); } -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) static bool CreateGameFlagsFromJson(const JsonMaterial& jMaterial, unsigned char& gameFlags) -#elif defined(FEATURE_T6) +#elif defined(FEATURE_T5) || defined(FEATURE_T6) static bool CreateGameFlagsFromJson(const JsonMaterial& jMaterial, unsigned& gameFlags) #endif { @@ -193,7 +207,7 @@ namespace CreateSamplerStateFromJson(jTexture.samplerState, textureDef.samplerState); textureDef.semantic = jTexture.semantic; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) textureDef.isMatureContent = jTexture.isMatureContent; #endif @@ -300,12 +314,18 @@ namespace structured.alphaTestDisabled = 0; structured.alphaTest = GFXS_ALPHA_TEST_GT_0; } -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::LT128) { structured.alphaTestDisabled = 0; structured.alphaTest = GFXS_ALPHA_TEST_LT_128; } +#elif defined(FEATURE_T5) + else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::GE255) + { + structured.alphaTestDisabled = 0; + structured.alphaTest = GFXS_ALPHA_TEST_GE_255; + } #endif else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::GE128) { @@ -399,9 +419,10 @@ namespace } material.info.surfaceTypeBits = jMaterial.surfaceTypeBits; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) material.info.layeredSurfaceTypes = jMaterial.layeredSurfaceTypes; - material.info.hashIndex = static_cast(jMaterial.hashIndex); +#endif +#if defined(FEATURE_T6) material.info.surfaceFlags = jMaterial.surfaceFlags; material.info.contents = jMaterial.contents; #endif @@ -416,9 +437,6 @@ namespace material.stateFlags = static_cast(jMaterial.stateFlags); material.cameraRegion = jMaterial.cameraRegion; -#ifdef FEATURE_T6 - material.probeMipBits = jMaterial.probeMipBits; -#endif auto* techniqueSet = m_context.LoadDependency(jMaterial.techniqueSet); if (!techniqueSet) @@ -501,6 +519,7 @@ namespace return true; } + std::istream& m_stream; MemoryManager& m_memory; AssetCreationContext& m_context; AssetRegistration& m_registration; @@ -510,20 +529,10 @@ namespace namespace GAME { bool LoadMaterialAsJson( - std::istream& stream, Material& material, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) - { - const JsonLoader loader(memory, context, registration); - - auto jRoot = json::parse(stream); - - return loader.Load(jRoot, material); - } - - bool LoadMaterialAsJson( - json& json, Material& material, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) - { - const JsonLoader loader(memory, context, registration); - - return loader.Load(json, material); - } + std::istream& stream, Material& material, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) + { + const JsonLoader loader(stream, memory, context, registration); + + return loader.Load(material); + } } // namespace GAME diff --git a/src/ObjLoading/Material/JsonMaterialLoader.h.template b/src/ObjLoading/Material/JsonMaterialLoader.h.template index 8a9dd63e..3e3887e7 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.h.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.h.template @@ -1,4 +1,4 @@ -#options GAME (IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialLoader" + GAME + ".h" diff --git a/src/ObjLoading/Menu/AbstractMenuConverter.cpp b/src/ObjLoading/Menu/AbstractMenuConverter.cpp index 86c4c6b6..3b95229e 100644 --- a/src/ObjLoading/Menu/AbstractMenuConverter.cpp +++ b/src/ObjLoading/Menu/AbstractMenuConverter.cpp @@ -1,5 +1,7 @@ #include "AbstractMenuConverter.h" +#include "Utils/Logging/Log.h" + #include #include @@ -15,15 +17,15 @@ AbstractMenuConverter::AbstractMenuConverter(const bool disableOptimizations, IS void AbstractMenuConverter::PrintConversionExceptionDetails(const MenuConversionException& e) { - std::cerr << "ERROR while converting menu:\n"; - std::cerr << std::format(" Menu: {}\n", e.m_menu->m_name); + con::error("ERROR while converting menu:"); + con::error(" Menu: {}", e.m_menu->m_name); if (e.m_item) { - std::cerr << std::format("Item: {}\n", e.m_item->m_name); + con::error("Item: {}", e.m_item->m_name); } - std::cerr << std::format(" Message: {}\n", e.m_message); + con::error(" Message: {}", e.m_message); } const char* AbstractMenuConverter::ConvertString(const std::string& str) const diff --git a/src/ObjLoading/ObjContainer/IPak/IPak.cpp b/src/ObjLoading/ObjContainer/IPak/IPak.cpp index 9308499d..f61fb95a 100644 --- a/src/ObjLoading/ObjContainer/IPak/IPak.cpp +++ b/src/ObjLoading/ObjContainer/IPak/IPak.cpp @@ -2,13 +2,14 @@ #include "IPakStreamManager.h" #include "ObjContainer/IPak/IPakTypes.h" -#include "zlib.h" +#include "Utils/Logging/Log.h" #include #include #include #include #include +#include namespace fs = std::filesystem; @@ -92,7 +93,7 @@ namespace m_stream->read(reinterpret_cast(&indexEntry), sizeof(indexEntry)); if (m_stream->gcount() != sizeof(indexEntry)) { - std::cerr << std::format("Unexpected eof when trying to load index entry {}.\n", itemIndex); + con::error("Unexpected eof when trying to load index entry {}.", itemIndex); return false; } @@ -115,7 +116,7 @@ namespace m_stream->read(reinterpret_cast(§ion), sizeof(section)); if (m_stream->gcount() != sizeof(section)) { - std::cerr << "Unexpected eof when trying to load section.\n"; + con::error("Unexpected eof when trying to load section."); return false; } @@ -143,19 +144,19 @@ namespace m_stream->read(reinterpret_cast(&header), sizeof(header)); if (m_stream->gcount() != sizeof(header)) { - std::cerr << "Unexpected eof when trying to load header.\n"; + con::error("Unexpected eof when trying to load header."); return false; } if (header.magic != ipak_consts::IPAK_MAGIC) { - std::cerr << std::format("Invalid ipak magic '{:#x}'.\n", header.magic); + con::error("Invalid ipak magic '{:#x}'.", header.magic); return false; } if (header.version != ipak_consts::IPAK_VERSION) { - std::cerr << std::format("Unsupported ipak version '{}'.\n", header.version); + con::error("Unsupported ipak version '{}'.", header.version); return false; } @@ -167,13 +168,13 @@ namespace if (m_index_section == nullptr) { - std::cerr << "IPak does not contain an index section.\n"; + con::error("IPak does not contain an index section."); return false; } if (m_data_section == nullptr) { - std::cerr << "IPak does not contain a data section.\n"; + con::error("IPak does not contain a data section."); return false; } diff --git a/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.cpp b/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.cpp index 710b41fb..3174a504 100644 --- a/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.cpp +++ b/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.cpp @@ -1,6 +1,7 @@ #include "IPakEntryReadStream.h" #include "ObjContainer/IPak/IPakTypes.h" +#include "Utils/Logging/Log.h" #include #include @@ -128,7 +129,7 @@ bool IPakEntryReadStream::ValidateBlockHeader(const IPakDataBlockHeader* blockHe { if (blockHeader->countAndOffset.count > 31) { - std::cerr << "IPak block has more than 31 commands: " << blockHeader->countAndOffset.count << " -> Invalid\n"; + con::error("IPak block has more than 31 commands: {} -> Invalid", blockHeader->countAndOffset.count); return false; } @@ -142,7 +143,7 @@ bool IPakEntryReadStream::ValidateBlockHeader(const IPakDataBlockHeader* blockHe // The game uses IPAK_COMMAND_SKIP as value for compressed when it intends to skip the specified amount of data if (blockHeader->commands[currentCommand].compressed == 0 || blockHeader->commands[currentCommand].compressed == 1) { - std::cerr << "IPak block offset (" << blockHeader->countAndOffset.offset << ") is not the file head (" << m_file_head << ") -> Invalid\n"; + con::error("IPak block offset ({}) is not the file head ({}) -> Invalid", blockHeader->countAndOffset.offset, m_file_head); return false; } } @@ -167,7 +168,7 @@ bool IPakEntryReadStream::AdjustChunkBufferWindowForBlockHeader(const IPakDataBl { if (requiredChunkCount > IPAK_CHUNK_COUNT_PER_READ) { - std::cerr << "IPak block spans over more than " << IPAK_CHUNK_COUNT_PER_READ << " chunks (" << requiredChunkCount << "), which is not supported.\n"; + con::error("IPak block spans over more than {} chunks ({}), which is not supported.", IPAK_CHUNK_COUNT_PER_READ, requiredChunkCount); return false; } @@ -221,7 +222,7 @@ bool IPakEntryReadStream::ProcessCommand(const size_t commandSize, const int com if (result != LZO_E_OK) { - std::cerr << "Decompressing block with lzo failed: " << result << "!\n"; + con::error("Decompressing block with lzo failed: {}!", result); return false; } diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBank.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBank.cpp index 06ab0d72..2d8c9847 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBank.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBank.cpp @@ -1,12 +1,13 @@ #include "SoundBank.h" -#include "zlib.h" +#include "Utils/Logging/Log.h" #include #include #include #include #include +#include ObjContainerRepository SoundBank::Repository; @@ -130,50 +131,50 @@ bool SoundBank::ReadHeader() m_stream->read(reinterpret_cast(&m_header), sizeof(m_header)); if (m_stream->gcount() != sizeof(m_header)) { - std::cerr << "Unexpected eof when trying to load sndbank header.\n"; + con::error("Unexpected eof when trying to load sndbank header."); return false; } if (m_header.magic != MAGIC) { - std::cerr << std::format("Invalid sndbank magic 0x{:x}\n", m_header.magic); + con::error("Invalid sndbank magic 0x{:x}", m_header.magic); return false; } if (m_header.version != VERSION) { - std::cerr << std::format("Unsupported sndbank version {} (should be {})\n", m_header.version, VERSION); + con::error("Unsupported sndbank version {} (should be {})", m_header.version, VERSION); return false; } if (m_header.entrySize != sizeof(SoundAssetBankEntry)) { - std::cerr << std::format("Invalid sndbank entry size 0x{:x} (should be 0x{:x})\n", m_header.entrySize, sizeof(SoundAssetBankEntry)); + con::error("Invalid sndbank entry size 0x{:x} (should be 0x{:x})", m_header.entrySize, sizeof(SoundAssetBankEntry)); return false; } if (m_header.fileSize != m_file_size) { - std::cerr << std::format("Invalid sndbank {} (header expects {})\n", m_file_size, m_header.fileSize); + con::error("Invalid sndbank {} (header expects {})", m_file_size, m_header.fileSize); return false; } if (m_header.entryCount && (m_header.entryOffset <= 0 || m_header.entryOffset + sizeof(SoundAssetBankEntry) * m_header.entryCount > static_cast(m_file_size))) { - std::cerr << std::format("Invalid sndbank entry offset {} (filesize is {})\n", m_header.entryOffset, m_file_size); + con::error("Invalid sndbank entry offset {} (filesize is {})", m_header.entryOffset, m_file_size); return false; } if (m_header.checksumOffset <= 0 || m_header.checksumOffset + sizeof(SoundAssetBankChecksum) * m_header.entryCount > static_cast(m_file_size)) { - std::cerr << std::format("Invalid sndbank checksum offset {} (filesize is {})\n", m_header.checksumOffset, m_file_size); + con::error("Invalid sndbank checksum offset {} (filesize is {})", m_header.checksumOffset, m_file_size); return false; } if (m_header.dependencyCount * m_header.dependencySize > sizeof(SoundAssetBankHeader::dependencies)) { - std::cerr << std::format("Invalid sndbank dependency sizes (count is {}; size is {})\n", m_header.dependencyCount, m_header.dependencySize); + con::error("Invalid sndbank dependency sizes (count is {}; size is {})", m_header.dependencyCount, m_header.dependencySize); return false; } @@ -202,13 +203,13 @@ bool SoundBank::ReadEntries() if (m_stream->gcount() != sizeof(entry)) { - std::cerr << std::format("Failed to read sound bank entry at index {}\n", i); + con::error("Failed to read sound bank entry at index {}", i); return false; } if (entry.offset == 0 || entry.offset + entry.size >= m_file_size) { - std::cerr << std::format("Invalid sound bank entry data offset {} (filesize is {})\n", entry.offset, m_header.fileSize); + con::error("Invalid sound bank entry data offset {} (filesize is {})", entry.offset, m_header.fileSize); return false; } @@ -230,7 +231,7 @@ bool SoundBank::ReadChecksums() if (m_stream->gcount() != sizeof(checksum)) { - std::cerr << std::format("Failed to read sound bank checksum at index {}\n", i); + con::error("Failed to read sound bank checksum at index {}", i); return false; } diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index fa31a407..67f8547e 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -5,6 +5,7 @@ #include "Sound/FlacDecoder.h" #include "Sound/WavTypes.h" #include "Utils/FileUtils.h" +#include "Utils/Logging/Log.h" #include "Utils/StringUtils.h" #include @@ -201,7 +202,7 @@ public: return true; } - std::cerr << std::format("Unable to decode .flac file for sound {}\n", filePath); + con::error("Unable to decode .flac file for sound {}", filePath); return false; } @@ -238,15 +239,14 @@ public: if (!LoadFileByExtension(soundFilePath, sound, soundData, soundSize)) { - std::cerr << std::format("Unable to find a compatible file for sound {}\n", soundFilePath); + con::error("Unable to find a compatible file for sound {}", soundFilePath); return false; } const auto lastEntry = m_entries.rbegin(); if (!sound.m_streamed && lastEntry->frameRateIndex != 6) { - std::cout << std::format("WARNING: Loaded sound \"{}\" should have a framerate of 48000 but doesn't. This sound may not work on all games!\n", - soundFilePath); + con::warn("Loaded sound \"{}\" should have a framerate of 48000 but doesn't. This sound may not work on all games!", soundFilePath); } SoundAssetBankChecksum checksum{}; @@ -297,7 +297,7 @@ public: { if (!WriteEntries()) { - std::cerr << "An error occurred writing the sound bank entries. Please check output.\n"; + con::error("An error occurred writing the sound bank entries. Please check output."); return false; } @@ -311,7 +311,7 @@ public: if (m_current_offset > UINT32_MAX) { - std::cerr << "Sound bank files must be under 4GB. Please reduce the number of sounds being written!\n"; + con::error("Sound bank files must be under 4GB. Please reduce the number of sounds being written!"); return false; } diff --git a/src/ObjLoading/ObjLoading.h b/src/ObjLoading/ObjLoading.h index 08d9a319..1ae2883a 100644 --- a/src/ObjLoading/ObjLoading.h +++ b/src/ObjLoading/ObjLoading.h @@ -6,7 +6,6 @@ public: static class Configuration_t { public: - bool Verbose = false; bool MenuPermissiveParsing = false; bool MenuNoOptimization = false; } Configuration; diff --git a/src/ObjLoading/Parsing/Graph2D/Graph2DReader.cpp b/src/ObjLoading/Parsing/Graph2D/Graph2DReader.cpp index f6803f80..487c7e52 100644 --- a/src/ObjLoading/Parsing/Graph2D/Graph2DReader.cpp +++ b/src/ObjLoading/Parsing/Graph2D/Graph2DReader.cpp @@ -5,6 +5,7 @@ #include "Parsing/Simple/Matcher/SimpleMatcherFactory.h" #include "Parsing/Simple/SimpleLexer.h" #include "Parsing/Simple/SimpleParserValue.h" +#include "Utils/Logging/Log.h" #include @@ -210,17 +211,17 @@ namespace graph2d if (!parser.Parse()) { - std::cerr << std::format("Failed to parse {} \"{}\"\n", graphTypeName, graphName); + con::error("Failed to parse {} \"{}\"", graphTypeName, graphName); return nullptr; } if (!parser.HasExpectedKnotCount()) { - std::cerr << std::format("Failed to load {} \"{}\": Actual knot count ({}) differs from expected ({})\n", - graphTypeName, - graphName, - parser.GetActualKnotCount(), - parser.GetExpectedKnotCount()); + con::error("Failed to load {} \"{}\": Actual knot count ({}) differs from expected ({})", + graphTypeName, + graphName, + parser.GetActualKnotCount(), + parser.GetExpectedKnotCount()); return nullptr; } diff --git a/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp b/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp index d475a806..6790807d 100644 --- a/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp +++ b/src/ObjLoading/Parsing/Menu/MenuFileReader.cpp @@ -8,6 +8,7 @@ #include "Parsing/Impl/ParserMultiInputStream.h" #include "Parsing/Impl/ParserSingleInputStream.h" #include "Parsing/Simple/SimpleLexer.h" +#include "Utils/Logging/Log.h" using namespace menu; @@ -65,25 +66,25 @@ bool MenuFileReader::IsValidEndState(const MenuFileParserState* state) const { if (state->m_current_item) { - std::cerr << "In \"" << m_file_name << "\": Unclosed item at end of file!\n"; + con::error("In \"{}\": Unclosed item at end of file!", m_file_name); return false; } if (state->m_current_menu) { - std::cerr << "In \"" << m_file_name << "\": Unclosed menu at end of file!\n"; + con::error("In \"{}\": Unclosed menu at end of file!", m_file_name); return false; } if (state->m_current_function) { - std::cerr << "In \"" << m_file_name << "\": Unclosed function at end of file!\n"; + con::error("In \"{}\": Unclosed function at end of file!", m_file_name); return false; } if (state->m_in_global_scope) { - std::cerr << "In \"" << m_file_name << "\": Did not close global scope!\n"; + con::error("In \"{}\": Did not close global scope!", m_file_name); return false; } @@ -125,11 +126,11 @@ std::unique_ptr MenuFileReader::ReadMenuFile() if (!parser->Parse()) { - std::cerr << "Parsing menu file failed!\n"; + con::error("Parsing menu file failed!"); const auto* parserEndState = parser->GetState(); if (parserEndState->m_current_event_handler_set && !parserEndState->m_permissive_mode) - std::cerr << "You can use the --menu-permissive option to try to compile the event handler script anyway.\n"; + con::error("You can use the --menu-permissive option to try to compile the event handler script anyway."); return nullptr; } diff --git a/src/ObjLoading/SearchPath/IWD.cpp b/src/ObjLoading/SearchPath/IWD.cpp index d84a8246..99e77807 100644 --- a/src/ObjLoading/SearchPath/IWD.cpp +++ b/src/ObjLoading/SearchPath/IWD.cpp @@ -2,6 +2,7 @@ #include "ObjLoading.h" #include "Utils/FileToZlibWrapper.h" +#include "Utils/Logging/Log.h" #include #include @@ -215,7 +216,7 @@ namespace if (m_unz_file == nullptr) { - std::cerr << std::format("Could not open IWD \"{}\"\n", m_path); + con::error("Could not open IWD \"{}\"", m_path); return false; } @@ -239,7 +240,7 @@ namespace ret = unzGoToNextFile(m_unz_file); } - std::cout << std::format("Loaded IWD \"{}\" with {} entries\n", m_path, m_entry_map.size()); + con::info("Loaded IWD \"{}\" with {} entries", m_path, m_entry_map.size()); return true; } @@ -261,7 +262,7 @@ namespace assert(!m_has_open_file); if (m_has_open_file) { - std::cerr << "Trying to open new IWD file while last one was not yet closed.\n"; + con::error("Trying to open new IWD file while last one was not yet closed."); return SearchPathOpenFile(); } diff --git a/src/ObjLoading/StateMap/StateMapHandler.cpp b/src/ObjLoading/StateMap/StateMapHandler.cpp index 07f2a364..1025fee4 100644 --- a/src/ObjLoading/StateMap/StateMapHandler.cpp +++ b/src/ObjLoading/StateMap/StateMapHandler.cpp @@ -1,5 +1,7 @@ #include "StateMapHandler.h" +#include "Utils/Logging/Log.h" + #include #include #include @@ -76,7 +78,7 @@ StateMapVars StateMapHandler::BuildVars(const uint32_t* baseStateBits) const if (matchingValue != var.m_values.end()) result.AddValue(var.m_name, matchingValue->m_name); else - std::cerr << "Could not find base value for state map var \"" << var.m_name << "\"\n"; + con::error("Could not find base value for state map var \"{}\"", var.m_name); } return result; diff --git a/src/ObjLoading/StateMap/StateMapReader.cpp b/src/ObjLoading/StateMap/StateMapReader.cpp index f025cff1..a55b98fa 100644 --- a/src/ObjLoading/StateMap/StateMapReader.cpp +++ b/src/ObjLoading/StateMap/StateMapReader.cpp @@ -4,6 +4,7 @@ #include "Parsing/Impl/ParserSingleInputStream.h" #include "Parsing/Matcher/StateMapExpressionMatchers.h" #include "Parsing/StateMapParser.h" +#include "Utils/Logging/Log.h" #include @@ -22,13 +23,13 @@ bool StateMapReader::IsValidEndState(const StateMapParserState* state) const { if (state->m_current_rule) { - std::cerr << "In \"" << m_file_name << "\": Unclosed rule at end of file!\n"; + con::error("In \"{}\": Unclosed rule at end of file!", m_file_name); return false; } if (state->m_in_entry) { - std::cerr << "In \"" << m_file_name << "\": Unclosed entry at end of file!\n"; + con::error("In \"{}\": Unclosed entry at end of file!", m_file_name); return false; } @@ -36,7 +37,7 @@ bool StateMapReader::IsValidEndState(const StateMapParserState* state) const { if (state->m_definition->m_state_map_entries[i].m_rules.empty()) { - std::cerr << "In \"" << m_file_name << "\": State map must define a rule for \"" << state->m_layout.m_entry_layout.m_entries[i].m_name << "\"!\n"; + con::error("In \"{}\": State map must define a rule for \"{}\"!", m_file_name, state->m_layout.m_entry_layout.m_entries[i].m_name); return false; } } @@ -59,7 +60,7 @@ std::unique_ptr StateMapReader::ReadStateMapDefinition() con const auto success = parser->Parse(); if (!success) { - std::cout << "Parsing state map file \"" << m_file_name << "\" failed!\n"; + con::error("Parsing state map file \"{}\" failed!", m_file_name); return {}; } diff --git a/src/ObjLoading/StructuredDataDef/StructuredDataDefReader.cpp b/src/ObjLoading/StructuredDataDef/StructuredDataDefReader.cpp index c5c9f62c..9ff1d5f4 100644 --- a/src/ObjLoading/StructuredDataDef/StructuredDataDefReader.cpp +++ b/src/ObjLoading/StructuredDataDef/StructuredDataDefReader.cpp @@ -6,6 +6,7 @@ #include "Parsing/Impl/ParserMultiInputStream.h" #include "Parsing/Impl/ParserSingleInputStream.h" #include "StructuredDataDef/Parsing/StructuredDataDefParser.h" +#include "Utils/Logging/Log.h" #include #include @@ -54,7 +55,7 @@ std::vector> StructuredDataDefReader::R if (success) return parser->GetDefs(); - std::cerr << std::format("Parsing structured data def file \"{}\" failed!\n", m_file_name); + con::error("Parsing structured data def file \"{}\" failed!", m_file_name); return {}; } diff --git a/src/ObjLoading/Techset/TechniqueFileReader.cpp b/src/ObjLoading/Techset/TechniqueFileReader.cpp index 60f2c5b8..71a40fbf 100644 --- a/src/ObjLoading/Techset/TechniqueFileReader.cpp +++ b/src/ObjLoading/Techset/TechniqueFileReader.cpp @@ -4,6 +4,7 @@ #include "Parsing/Impl/ParserSingleInputStream.h" #include "Parsing/Simple/SimpleLexer.h" #include "Parsing/TechniqueFileParser.h" +#include "Utils/Logging/Log.h" #include @@ -33,6 +34,6 @@ bool TechniqueFileReader::ReadTechniqueDefinition() const if (success) return true; - std::cout << "Parsing technique file \"" << m_file_name << "\" failed!\n"; + con::error("Parsing technique file \"{}\" failed!", m_file_name); return false; } diff --git a/src/ObjLoading/Techset/TechniqueFileReader.h b/src/ObjLoading/Techset/TechniqueFileReader.h index fe85f9a8..2090304b 100644 --- a/src/ObjLoading/Techset/TechniqueFileReader.h +++ b/src/ObjLoading/Techset/TechniqueFileReader.h @@ -11,14 +11,15 @@ namespace techset { class TechniqueFileReader { + public: + TechniqueFileReader(std::istream& stream, std::string fileName, ITechniqueDefinitionAcceptor* acceptor); + + [[nodiscard]] bool ReadTechniqueDefinition() const; + + private: std::string m_file_name; ITechniqueDefinitionAcceptor* m_acceptor; std::unique_ptr m_base_stream; std::unique_ptr m_comment_proxy; - - public: - TechniqueFileReader(std::istream& stream, std::string fileName, ITechniqueDefinitionAcceptor* acceptor); - - _NODISCARD bool ReadTechniqueDefinition() const; }; } // namespace techset diff --git a/src/ObjLoading/Techset/TechsetFileReader.cpp b/src/ObjLoading/Techset/TechsetFileReader.cpp index 6a704b0a..8b7c61e5 100644 --- a/src/ObjLoading/Techset/TechsetFileReader.cpp +++ b/src/ObjLoading/Techset/TechsetFileReader.cpp @@ -3,6 +3,7 @@ #include "Parsing/Impl/CommentRemovingStreamProxy.h" #include "Parsing/Impl/ParserSingleInputStream.h" #include "Parsing/TechsetFileParser.h" +#include "Utils/Logging/Log.h" #include #include @@ -34,6 +35,6 @@ std::unique_ptr TechsetFileReader::ReadTechsetDefini if (success) return parser->GetTechsetDefinition(); - std::cerr << std::format("Parsing techset file \"{}\" failed!\n", m_file_name); + con::error("Parsing techset file \"{}\" failed!", m_file_name); return nullptr; } diff --git a/src/ObjLoading/Weapon/AccuracyGraphLoader.cpp b/src/ObjLoading/Weapon/AccuracyGraphLoader.cpp index 75c22889..9c05316f 100644 --- a/src/ObjLoading/Weapon/AccuracyGraphLoader.cpp +++ b/src/ObjLoading/Weapon/AccuracyGraphLoader.cpp @@ -1,6 +1,7 @@ #include "AccuracyGraphLoader.h" #include "Parsing/Graph2D/Graph2DReader.h" +#include "Utils/Logging/Log.h" #include #include @@ -13,7 +14,7 @@ namespace const auto file = searchPath.Open(fileName); if (!file.IsOpen()) { - std::cerr << std::format("Failed to open file for accuracy graph: {}/{}\n", subFolder, graphName); + con::error("Failed to open file for accuracy graph: {}/{}", subFolder, graphName); return nullptr; } diff --git a/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp b/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp index b954adca..2433d75d 100644 --- a/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp @@ -1,5 +1,6 @@ #include "GltfBinInput.h" +#include "Utils/Logging/Log.h" #include "XModel/Gltf/GltfConstants.h" #include @@ -41,7 +42,7 @@ bool BinInput::ReadGltfData(std::istream& stream) if (magic != GLTF_MAGIC) { - std::cerr << "Invalid magic when trying to read GLB\n"; + con::error("Invalid magic when trying to read GLB"); return false; } @@ -51,7 +52,7 @@ bool BinInput::ReadGltfData(std::istream& stream) if (version != GLTF_VERSION) { - std::cerr << std::format("Unsupported version {} when trying to read GLB: Expected version {}\n", version, GLTF_VERSION); + con::error("Unsupported version {} when trying to read GLB: Expected version {}", version, GLTF_VERSION); return false; } @@ -82,7 +83,7 @@ bool BinInput::ReadGltfData(std::istream& stream) } catch (const nlohmann::json::exception& e) { - std::cerr << std::format("Failed trying to parse JSON of GLB: {}\n", e.what()); + con::error("Failed trying to parse JSON of GLB: {}", e.what()); return false; } } @@ -103,7 +104,7 @@ bool BinInput::ReadGltfData(std::istream& stream) if (!m_json) { - std::cerr << "Failed to load GLB due to missing JSON\n"; + con::error("Failed to load GLB due to missing JSON"); return false; } @@ -116,7 +117,7 @@ bool BinInput::Read(std::istream& stream, void* dest, const size_t dataSize, con if (stream.gcount() != dataSize) { if (errorWhenFailed) - std::cerr << std::format("Unexpected EOF while reading GLB {}\n", readTypeName); + con::error("Unexpected EOF while reading GLB {}", readTypeName); return false; } diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index df5255e2..0adba623 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -3,6 +3,7 @@ #include "Internal/GltfAccessor.h" #include "Internal/GltfBuffer.h" #include "Internal/GltfBufferView.h" +#include "Utils/Logging/Log.h" #pragma warning(push, 0) #include @@ -14,6 +15,7 @@ #include #include #include +#include #include using namespace gltf; @@ -22,24 +24,65 @@ namespace { struct AccessorsForVertex { - unsigned positionAccessor; - std::optional normalAccessor; - std::optional colorAccessor; - std::optional uvAccessor; - std::optional jointsAccessor; - std::optional weightsAccessor; - friend bool operator==(const AccessorsForVertex& lhs, const AccessorsForVertex& rhs) { - return lhs.positionAccessor == rhs.positionAccessor && lhs.normalAccessor == rhs.normalAccessor && lhs.colorAccessor == rhs.colorAccessor - && lhs.uvAccessor == rhs.uvAccessor && lhs.jointsAccessor == rhs.jointsAccessor && lhs.weightsAccessor == rhs.weightsAccessor; + return lhs.m_position_accessor == rhs.m_position_accessor && lhs.m_normal_accessor == rhs.m_normal_accessor + && lhs.m_color_accessor == rhs.m_color_accessor && lhs.m_uv_accessor == rhs.m_uv_accessor && lhs.m_joints_accessor == rhs.m_joints_accessor + && lhs.m_weights_accessor == rhs.m_weights_accessor; } friend bool operator!=(const AccessorsForVertex& lhs, const AccessorsForVertex& rhs) { return !(lhs == rhs); } + + unsigned m_position_accessor; + std::optional m_normal_accessor; + std::optional m_color_accessor; + std::optional m_uv_accessor; + std::optional m_joints_accessor; + std::optional m_weights_accessor; }; + + void RhcToLhcCoordinates(float (&coords)[3]) + { + const float two[3]{coords[0], coords[1], coords[2]}; + + coords[0] = two[0]; + coords[1] = -two[2]; + coords[2] = two[1]; + } + + void RhcToLhcScale(float (&coords)[3]) + { + const float two[3]{coords[0], coords[1], coords[2]}; + + coords[0] = two[0]; + coords[1] = two[2]; + coords[2] = two[1]; + } + + void RhcToLhcQuaternion(XModelQuaternion& quat) + { + Eigen::Quaternionf eigenQuat(quat.w, quat.x, quat.y, quat.z); + const Eigen::Quaternionf eigenRotationQuat(Eigen::AngleAxisf(std::numbers::pi_v / 2.f, Eigen::Vector3f::UnitX())); + + eigenQuat = eigenRotationQuat * eigenQuat; + + quat.x = eigenQuat.x(); + quat.y = eigenQuat.y(); + quat.z = eigenQuat.z(); + quat.w = eigenQuat.w(); + } + + void RhcToLhcIndices(unsigned (&indices)[3]) + { + const unsigned two[3]{indices[0], indices[1], indices[2]}; + + indices[0] = two[2]; + indices[1] = two[1]; + indices[2] = two[0]; + } } // namespace template<> struct std::hash @@ -47,12 +90,12 @@ template<> struct std::hash std::size_t operator()(const AccessorsForVertex& v) const noexcept { std::size_t seed = 0x7E42C0E6; - seed ^= (seed << 6) + (seed >> 2) + 0x47B15429 + static_cast(v.positionAccessor); - seed ^= (seed << 6) + (seed >> 2) + 0x66847B5C + std::hash>()(v.normalAccessor); - seed ^= (seed << 6) + (seed >> 2) + 0x77399D60 + std::hash>()(v.colorAccessor); - seed ^= (seed << 6) + (seed >> 2) + 0x477AF9AB + std::hash>()(v.uvAccessor); - seed ^= (seed << 6) + (seed >> 2) + 0x4421B4D9 + std::hash>()(v.jointsAccessor); - seed ^= (seed << 6) + (seed >> 2) + 0x13C2EBA1 + std::hash>()(v.weightsAccessor); + seed ^= (seed << 6) + (seed >> 2) + 0x47B15429 + static_cast(v.m_position_accessor); + seed ^= (seed << 6) + (seed >> 2) + 0x66847B5C + std::hash>()(v.m_normal_accessor); + seed ^= (seed << 6) + (seed >> 2) + 0x77399D60 + std::hash>()(v.m_color_accessor); + seed ^= (seed << 6) + (seed >> 2) + 0x477AF9AB + std::hash>()(v.m_uv_accessor); + seed ^= (seed << 6) + (seed >> 2) + 0x4421B4D9 + std::hash>()(v.m_joints_accessor); + seed ^= (seed << 6) + (seed >> 2) + 0x13C2EBA1 + std::hash>()(v.m_weights_accessor); return seed; } }; @@ -83,21 +126,22 @@ namespace struct ObjectToLoad { - unsigned meshIndex; - std::optional skinIndex; - ObjectToLoad(const unsigned meshIndex, const std::optional skinIndex) - : meshIndex(meshIndex), - skinIndex(skinIndex) + : m_mesh_index(meshIndex), + m_skin_index(skinIndex) { } + + unsigned m_mesh_index; + std::optional m_skin_index; }; class GltfLoaderImpl final : public Loader { public: - explicit GltfLoaderImpl(const Input* input) - : m_input(input) + GltfLoaderImpl(const Input& input, const bool useBadRotationFormulas) + : m_input(input), + m_bad_rotation_formulas(useBadRotationFormulas) { } @@ -143,7 +187,7 @@ namespace return; std::deque nodeQueue; - std::vector rootNodes = GetRootNodes(jRoot); + const std::vector rootNodes = GetRootNodes(jRoot); for (const auto rootNode : rootNodes) nodeQueue.emplace_back(rootNode); @@ -204,9 +248,9 @@ namespace unsigned CreateVertices(XModelCommon& common, const AccessorsForVertex& accessorsForVertex) { // clang-format off - auto* positionAccessor = GetAccessorForIndex( + const auto* positionAccessor = GetAccessorForIndex( "POSITION", - accessorsForVertex.positionAccessor, + accessorsForVertex.m_position_accessor, {JsonAccessorType::VEC3}, {JsonAccessorComponentType::FLOAT} ).value_or(nullptr); @@ -217,41 +261,41 @@ namespace OnesAccessor onesAccessor(vertexCount); // clang-format off - auto* normalAccessor = GetAccessorForIndex( + const auto* normalAccessor = GetAccessorForIndex( "NORMAL", - accessorsForVertex.normalAccessor, + accessorsForVertex.m_normal_accessor, {JsonAccessorType::VEC3}, {JsonAccessorComponentType::FLOAT} ).value_or(&nullAccessor); VerifyAccessorVertexCount("NORMAL", normalAccessor, vertexCount); - auto* uvAccessor = GetAccessorForIndex( + const auto* uvAccessor = GetAccessorForIndex( "TEXCOORD_0", - accessorsForVertex.uvAccessor, + accessorsForVertex.m_uv_accessor, {JsonAccessorType::VEC2}, {JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT} ).value_or(&nullAccessor); VerifyAccessorVertexCount("TEXCOORD_0", uvAccessor, vertexCount); - auto* colorAccessor = GetAccessorForIndex( + const auto* colorAccessor = GetAccessorForIndex( "COLOR_0", - accessorsForVertex.colorAccessor, + accessorsForVertex.m_color_accessor, {JsonAccessorType::VEC3, JsonAccessorType::VEC4}, {JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT} ).value_or(&onesAccessor); VerifyAccessorVertexCount("COLOR_0", colorAccessor, vertexCount); - auto* jointsAccessor = GetAccessorForIndex( + const auto* jointsAccessor = GetAccessorForIndex( "JOINTS_0", - accessorsForVertex.jointsAccessor, + accessorsForVertex.m_joints_accessor, {JsonAccessorType::VEC4}, {JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT} ).value_or(&nullAccessor); VerifyAccessorVertexCount("JOINTS_0", jointsAccessor, vertexCount); - auto* weightsAccessor = GetAccessorForIndex( + const auto* weightsAccessor = GetAccessorForIndex( "WEIGHTS_0", - accessorsForVertex.weightsAccessor, + accessorsForVertex.m_weights_accessor, {JsonAccessorType::VEC4}, {JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT} ).value_or(&nullAccessor); @@ -268,21 +312,15 @@ namespace unsigned joints[4]; float weights[4]; - float coordinates[3]; - float normal[3]; - if (!positionAccessor->GetFloatVec3(vertexIndex, coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, normal) + if (!positionAccessor->GetFloatVec3(vertexIndex, vertex.coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, vertex.normal) || !colorAccessor->GetFloatVec4(vertexIndex, vertex.color) || !uvAccessor->GetFloatVec2(vertexIndex, vertex.uv) || !jointsAccessor->GetUnsignedVec4(vertexIndex, joints) || !weightsAccessor->GetFloatVec4(vertexIndex, weights)) { return false; } - vertex.coordinates[0] = coordinates[0]; - vertex.coordinates[1] = -coordinates[2]; - vertex.coordinates[2] = coordinates[1]; - vertex.normal[0] = normal[0]; - vertex.normal[1] = -normal[2]; - vertex.normal[2] = normal[1]; + RhcToLhcCoordinates(vertex.coordinates); + RhcToLhcCoordinates(vertex.normal); common.m_vertices.emplace_back(vertex); @@ -315,13 +353,13 @@ namespace if (!primitives.attributes.POSITION) throw GltfLoadException("Requires primitives attribute POSITION"); - AccessorsForVertex accessorsForVertex{ - .positionAccessor = *primitives.attributes.POSITION, - .normalAccessor = primitives.attributes.NORMAL, - .colorAccessor = primitives.attributes.COLOR_0, - .uvAccessor = primitives.attributes.TEXCOORD_0, - .jointsAccessor = primitives.attributes.JOINTS_0, - .weightsAccessor = primitives.attributes.WEIGHTS_0, + const AccessorsForVertex accessorsForVertex{ + .m_position_accessor = *primitives.attributes.POSITION, + .m_normal_accessor = primitives.attributes.NORMAL, + .m_color_accessor = primitives.attributes.COLOR_0, + .m_uv_accessor = primitives.attributes.TEXCOORD_0, + .m_joints_accessor = primitives.attributes.JOINTS_0, + .m_weights_accessor = primitives.attributes.WEIGHTS_0, }; const auto existingVertices = m_vertex_offset_for_accessors.find(accessorsForVertex); @@ -351,11 +389,12 @@ namespace { return false; } + RhcToLhcIndices(indices); object.m_faces.emplace_back(XModelFace{ - vertexOffset + indices[2], - vertexOffset + indices[1], vertexOffset + indices[0], + vertexOffset + indices[1], + vertexOffset + indices[2], }); } @@ -412,7 +451,7 @@ namespace return std::nullopt; } - static void ApplyNodeMatrixTRS(XModelBone& bone, const JsonNode& node) + void ApplyNodeMatrixTRS(const JsonNode& node, float (&localOffsetRhc)[3], float (&localRotationRhc)[4], float (&scaleRhc)[3]) { const auto matrix = Eigen::Matrix4f({ {(*node.matrix)[0], (*node.matrix)[4], (*node.matrix)[8], (*node.matrix)[12]}, @@ -423,75 +462,102 @@ namespace Eigen::Affine3f transform(matrix); const auto translation = transform.translation(); - bone.localOffset[0] = translation.x(); - bone.localOffset[1] = -translation.z(); - bone.localOffset[2] = translation.y(); + + localOffsetRhc[0] = translation.x(); + localOffsetRhc[1] = translation.y(); + localOffsetRhc[2] = translation.z(); + if (m_bad_rotation_formulas) + RhcToLhcCoordinates(localOffsetRhc); const auto rotation = transform.rotation(); const auto rotationQuat = Eigen::Quaternionf(rotation); - bone.localRotation.x = rotationQuat.x(); - bone.localRotation.y = -rotationQuat.z(); - bone.localRotation.z = rotationQuat.y(); - bone.localRotation.w = rotationQuat.w(); - - bone.scale[0] = matrix.block<3, 1>(0, 0).norm(); - bone.scale[1] = matrix.block<3, 1>(0, 1).norm(); - bone.scale[2] = matrix.block<3, 1>(0, 2).norm(); - } - - static void ApplyNodeSeparateTRS(XModelBone& bone, const JsonNode& node) - { - if (node.translation) + if (!m_bad_rotation_formulas) { - bone.localOffset[0] = (*node.translation)[0]; - bone.localOffset[1] = -(*node.translation)[2]; - bone.localOffset[2] = (*node.translation)[1]; + localRotationRhc[0] = rotationQuat.x(); + localRotationRhc[1] = rotationQuat.y(); + localRotationRhc[2] = rotationQuat.z(); + localRotationRhc[3] = rotationQuat.w(); } else { - bone.localOffset[0] = 0.0f; - bone.localOffset[1] = 0.0f; - bone.localOffset[2] = 0.0f; + // Backwards compatibility + localRotationRhc[0] = rotationQuat.x(); + localRotationRhc[1] = -rotationQuat.z(); + localRotationRhc[2] = rotationQuat.y(); + localRotationRhc[3] = rotationQuat.w(); + } + + scaleRhc[0] = matrix.block<3, 1>(0, 0).norm(); + scaleRhc[1] = matrix.block<3, 1>(0, 1).norm(); + scaleRhc[2] = matrix.block<3, 1>(0, 2).norm(); + } + + void ApplyNodeSeparateTRS(const JsonNode& node, float (&localOffsetRhc)[3], float (&localRotationRhc)[4], float (&scaleRhc)[3]) + { + if (node.translation) + { + localOffsetRhc[0] = (*node.translation)[0]; + localOffsetRhc[1] = (*node.translation)[1]; + localOffsetRhc[2] = (*node.translation)[2]; + if (m_bad_rotation_formulas) + RhcToLhcCoordinates(localOffsetRhc); + } + else + { + localOffsetRhc[0] = 0.0f; + localOffsetRhc[1] = 0.0f; + localOffsetRhc[2] = 0.0f; } if (node.rotation) { - bone.localRotation.x = (*node.rotation)[0]; - bone.localRotation.y = -(*node.rotation)[2]; - bone.localRotation.z = (*node.rotation)[1]; - bone.localRotation.w = (*node.rotation)[3]; + if (!m_bad_rotation_formulas) + { + localRotationRhc[0] = (*node.rotation)[0]; + localRotationRhc[1] = (*node.rotation)[1]; + localRotationRhc[2] = (*node.rotation)[2]; + localRotationRhc[3] = (*node.rotation)[3]; + } + else + { + // Backwards compatibility + localRotationRhc[0] = (*node.rotation)[0]; + localRotationRhc[1] = -(*node.rotation)[2]; + localRotationRhc[2] = (*node.rotation)[1]; + localRotationRhc[3] = (*node.rotation)[3]; + } } else { - bone.localRotation.x = 0.0f; - bone.localRotation.y = 0.0f; - bone.localRotation.z = 0.0f; - bone.localRotation.w = 1.0f; + localRotationRhc[0] = 0.0f; + localRotationRhc[1] = 0.0f; + localRotationRhc[2] = 0.0f; + localRotationRhc[3] = 1.0f; } if (node.scale) { - bone.scale[0] = (*node.scale)[0]; - bone.scale[1] = (*node.scale)[1]; - bone.scale[2] = (*node.scale)[2]; + scaleRhc[0] = (*node.scale)[0]; + scaleRhc[1] = (*node.scale)[1]; + scaleRhc[2] = (*node.scale)[2]; } else { - bone.scale[0] = 1.0f; - bone.scale[1] = 1.0f; - bone.scale[2] = 1.0f; + scaleRhc[0] = 1.0f; + scaleRhc[1] = 1.0f; + scaleRhc[2] = 1.0f; } } - static bool ConvertJoint(const JsonRoot& jRoot, - const JsonSkin& skin, - XModelCommon& common, - const unsigned skinBoneOffset, - const unsigned nodeIndex, - const std::optional parentIndex, - const float (&parentOffset)[3], - const XModelQuaternion& parentRotation, - const float (&parentScale)[3]) + bool ConvertJoint(const JsonRoot& jRoot, + const JsonSkin& skin, + XModelCommon& common, + const unsigned skinBoneOffset, + const unsigned nodeIndex, + const std::optional parentIndex, + const Eigen::Vector3f& parentTranslationEigenRhc, + const Eigen::Quaternionf& parentRotationEigenRhc, + const float (&parentScale)[3]) { if (!jRoot.nodes || nodeIndex >= jRoot.nodes->size()) return false; @@ -507,33 +573,45 @@ namespace bone.name = node.name.value_or(std::string()); bone.parentIndex = parentIndex; + float localOffsetRhc[3]; + float localRotationRhc[4]; + float localScaleRhc[3]; if (node.matrix) - ApplyNodeMatrixTRS(bone, node); + ApplyNodeMatrixTRS(node, localOffsetRhc, localRotationRhc, localScaleRhc); else - ApplyNodeSeparateTRS(bone, node); + ApplyNodeSeparateTRS(node, localOffsetRhc, localRotationRhc, localScaleRhc); - bone.scale[0] *= parentScale[0]; - bone.scale[1] *= parentScale[1]; - bone.scale[2] *= parentScale[2]; + bone.scale[0] = localScaleRhc[0] * parentScale[0]; + bone.scale[1] = localScaleRhc[1] * parentScale[1]; + bone.scale[2] = localScaleRhc[2] * parentScale[2]; + if (!m_bad_rotation_formulas) + RhcToLhcScale(bone.scale); - bone.globalOffset[0] = bone.localOffset[0] + parentOffset[0]; - bone.globalOffset[1] = bone.localOffset[1] + parentOffset[1]; - bone.globalOffset[2] = bone.localOffset[2] + parentOffset[2]; + const Eigen::Vector3f localTranslationEigen(localOffsetRhc[0], localOffsetRhc[1], localOffsetRhc[2]); + const Eigen::Quaternionf localRotationEigen(localRotationRhc[3], localRotationRhc[0], localRotationRhc[1], localRotationRhc[2]); - const auto localRotationEigen = Eigen::Quaternionf(bone.localRotation.w, bone.localRotation.x, bone.localRotation.y, bone.localRotation.z); - const auto parentRotationEigen = Eigen::Quaternionf(parentRotation.w, parentRotation.x, parentRotation.y, parentRotation.z); - const auto globalRotationEigen = localRotationEigen * parentRotationEigen; + const Eigen::Quaternionf globalRotationEigenRhc((parentRotationEigenRhc * localRotationEigen).normalized()); + const Eigen::Vector3f globalTranslationEigenRhc((parentRotationEigenRhc * localTranslationEigen) + parentTranslationEigenRhc); - bone.globalRotation.x = globalRotationEigen.x(); - bone.globalRotation.y = globalRotationEigen.y(); - bone.globalRotation.z = globalRotationEigen.z(); - bone.globalRotation.w = globalRotationEigen.w(); + bone.globalOffset[0] = globalTranslationEigenRhc.x(); + bone.globalOffset[1] = globalTranslationEigenRhc.y(); + bone.globalOffset[2] = globalTranslationEigenRhc.z(); + if (!m_bad_rotation_formulas) + RhcToLhcCoordinates(bone.globalOffset); + + bone.globalRotation.x = globalRotationEigenRhc.x(); + bone.globalRotation.y = globalRotationEigenRhc.y(); + bone.globalRotation.z = globalRotationEigenRhc.z(); + bone.globalRotation.w = globalRotationEigenRhc.w(); + if (!m_bad_rotation_formulas) + RhcToLhcQuaternion(bone.globalRotation); if (node.children) { for (const auto childIndex : *node.children) { - if (!ConvertJoint(jRoot, skin, common, skinBoneOffset, childIndex, commonBoneOffset, bone.globalOffset, bone.globalRotation, bone.scale)) + if (!ConvertJoint( + jRoot, skin, common, skinBoneOffset, childIndex, commonBoneOffset, globalTranslationEigenRhc, globalRotationEigenRhc, bone.scale)) return false; } } @@ -541,7 +619,7 @@ namespace return true; } - static bool ConvertSkin(const JsonRoot& jRoot, const JsonSkin& skin, XModelCommon& common) + bool ConvertSkin(const JsonRoot& jRoot, const JsonSkin& skin, XModelCommon& common) { if (skin.joints.empty()) return true; @@ -552,11 +630,15 @@ namespace const auto skinBoneOffset = static_cast(common.m_bones.size()); common.m_bones.resize(skinBoneOffset + skin.joints.size()); - constexpr float defaultTranslation[3]{0.0f, 0.0f, 0.0f}; - constexpr XModelQuaternion defaultRotation{.x = 0.0f, .y = 0.0f, .z = 0.0f, .w = 1.0f}; + const Eigen::Vector3f defaultTranslation(0.0f, 0.0f, 0.0f); + const Eigen::Quaternionf defaultRotation(1.0f, 0.0f, 0.0f, 0.0f); constexpr float defaultScale[3]{1.0f, 1.0f, 1.0f}; - return ConvertJoint(jRoot, skin, common, skinBoneOffset, rootNode, std::nullopt, defaultTranslation, defaultRotation, defaultScale); + if (!ConvertJoint(jRoot, skin, common, skinBoneOffset, rootNode, std::nullopt, defaultTranslation, defaultRotation, defaultScale)) + return false; + + common.CalculateBoneLocalsFromGlobals(); + return true; } void ConvertObjects(const JsonRoot& jRoot, XModelCommon& common) @@ -568,26 +650,26 @@ namespace for (const auto& loadObject : m_load_objects) { - if (loadObject.skinIndex && jRoot.skins) + if (loadObject.m_skin_index && jRoot.skins) { if (alreadyLoadedSkinIndex) { - if (*alreadyLoadedSkinIndex != *loadObject.skinIndex) + if (*alreadyLoadedSkinIndex != *loadObject.m_skin_index) throw GltfLoadException("Only scenes with at most one skin are supported"); // Do not load already loaded skin } else { - const auto& skin = jRoot.skins.value()[*loadObject.skinIndex]; + const auto& skin = jRoot.skins.value()[*loadObject.m_skin_index]; if (!ConvertSkin(jRoot, skin, common)) return; - alreadyLoadedSkinIndex = *loadObject.skinIndex; + alreadyLoadedSkinIndex = *loadObject.m_skin_index; } } - const auto& mesh = jRoot.meshes.value()[loadObject.meshIndex]; + const auto& mesh = jRoot.meshes.value()[loadObject.m_mesh_index]; common.m_objects.reserve(common.m_objects.size() + mesh.primitives.size()); for (const auto& primitives : mesh.primitives) @@ -633,7 +715,7 @@ namespace { const void* embeddedBufferPtr = nullptr; size_t embeddedBufferSize = 0u; - if (!m_input->GetEmbeddedBuffer(embeddedBufferPtr, embeddedBufferSize) || embeddedBufferSize == 0u) + if (!m_input.GetEmbeddedBuffer(embeddedBufferPtr, embeddedBufferSize) || embeddedBufferSize == 0u) throw GltfLoadException("Buffer tried to access embedded data when there is none"); m_buffers.emplace_back(std::make_unique(embeddedBufferPtr, embeddedBufferSize)); @@ -713,11 +795,11 @@ namespace JsonRoot jRoot; try { - jRoot = m_input->GetJson().get(); + jRoot = m_input.GetJson().get(); } catch (const nlohmann::json::exception& e) { - std::cerr << std::format("Failed to parse GLTF JSON: {}\n", e.what()); + con::error("Failed to parse GLTF JSON: {}", e.what()); return nullptr; } @@ -737,22 +819,24 @@ namespace } catch (const GltfLoadException& e) { - std::cerr << std::format("Failed to load GLTF: {}\n", e.Str()); + con::error("Failed to load GLTF: {}", e.Str()); return nullptr; } } private: - const Input* m_input; + const Input& m_input; std::vector m_load_objects; std::unordered_map m_vertex_offset_for_accessors; std::vector> m_accessors; std::vector> m_buffer_views; std::vector> m_buffers; + + bool m_bad_rotation_formulas; }; } // namespace -std::unique_ptr Loader::CreateLoader(const Input* input) +std::unique_ptr Loader::CreateLoader(const Input& input, bool useBadRotationFormulas) { - return std::make_unique(input); + return std::make_unique(input, useBadRotationFormulas); } diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.h b/src/ObjLoading/XModel/Gltf/GltfLoader.h index 872394e4..46ae42ad 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.h +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.h @@ -19,6 +19,13 @@ namespace gltf Loader& operator=(const Loader& other) = default; Loader& operator=(Loader&& other) noexcept = default; - static std::unique_ptr CreateLoader(const Input* input); + /** + * \brief Creates a loader capable of loading gltf-like files + * \param input The gltf input + * \param useBadRotationFormulas Old versions used bad formulas for converting into gltf space. Set to \c true to use them for loading to preserve + * backwards compatibility. + * \return + */ + static std::unique_ptr CreateLoader(const Input& input, bool useBadRotationFormulas); }; } // namespace gltf diff --git a/src/ObjLoading/XModel/Gltf/GltfTextInput.cpp b/src/ObjLoading/XModel/Gltf/GltfTextInput.cpp index ff885002..7250c0a1 100644 --- a/src/ObjLoading/XModel/Gltf/GltfTextInput.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfTextInput.cpp @@ -1,5 +1,7 @@ #include "GltfTextInput.h" +#include "Utils/Logging/Log.h" + #include #include #include @@ -41,7 +43,7 @@ bool TextInput::ReadGltfData(std::istream& stream) } catch (nlohmann::json::exception& e) { - std::cerr << std::format("Failed to parse json of GLTF: {}", e.what()); + con::error("Failed to parse json of GLTF: {}", e.what()); } return false; diff --git a/src/ObjLoading/XModel/LoaderXModel.cpp.template b/src/ObjLoading/XModel/LoaderXModel.cpp.template index d0396fcf..0c6b4067 100644 --- a/src/ObjLoading/XModel/LoaderXModel.cpp.template +++ b/src/ObjLoading/XModel/LoaderXModel.cpp.template @@ -1,4 +1,4 @@ -#options GAME(IW5, T5, T6) +#options GAME(IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/LoaderXModel" + GAME + ".cpp" @@ -7,7 +7,11 @@ #set CONSTANTS_HEADER "\"Game/" + GAME + "/XModel/XModelConstants" + GAME + ".h\"" #set JSON_HEADER "\"Game/" + GAME + "/XModel/JsonXModel" + GAME + ".h\"" -#if GAME == "IW5" +#if GAME == "IW3" +#define FEATURE_IW3 +#elif GAME == "IW4" +#define FEATURE_IW4 +#elif GAME == "IW5" #define FEATURE_IW5 #elif GAME == "T5" #define FEATURE_T5 @@ -22,6 +26,7 @@ #include JSON_HEADER #include "Asset/AssetRegistration.h" +#include "Utils/Logging/Log.h" #include "Utils/QuatInt16.h" #include "Utils/StringUtils.h" #include "XModel/Gltf/GltfBinInput.h" @@ -53,7 +58,8 @@ namespace { public: XModelLoader(MemoryManager& memory, ISearchPath& searchPath, ZoneScriptStrings& scriptStrings) - : m_memory(memory), + : m_gltf_bad_rotation_formulas(false), + m_memory(memory), m_search_path(searchPath), m_script_strings(scriptStrings) { @@ -68,10 +74,13 @@ namespace auto* xmodel = m_memory.Alloc(); xmodel->name = m_memory.Dup(assetName.c_str()); + m_materials.clear(); + m_surfaces.clear(); + AssetRegistration registration(assetName, xmodel); if (!LoadFromFile(*file.m_stream, *xmodel, context, registration)) { - std::cerr << std::format("Failed to load xmodel \"{}\"\n", assetName); + con::error("Failed to load xmodel \"{}\"", assetName); return AssetCreationResult::Failure(); } @@ -81,27 +90,37 @@ namespace private: bool LoadFromFile(std::istream& jsonStream, XModel& xmodel, AssetCreationContext& context, AssetRegistration& registration) { - const auto jRoot = nlohmann::json::parse(jsonStream); - std::string type; - unsigned version; - - jRoot.at("_type").get_to(type); - jRoot.at("_version").get_to(version); - - if (type != "xmodel" || version != 1u) - { - std::cerr << std::format("Tried to load xmodel \"{}\" but did not find expected type material of version 1\n", xmodel.name); - return false; - } - try { + const auto jRoot = nlohmann::json::parse(jsonStream); + std::string type; + unsigned version; + + jRoot.at("_type").get_to(type); + jRoot.at("_version").get_to(version); + + if (type != "xmodel" || version < 1u || version > 2u) + { + con::error("Tried to load xmodel \"{}\" but did not find expected type material of version 1 or 2", xmodel.name); + return false; + } + + if (version == 1u) + { + m_gltf_bad_rotation_formulas = true; + con::warn("DEPRECATED: XModel {} is version 1 that made use of bad GLTF bone rotations.", xmodel.name); + } + else + { + m_gltf_bad_rotation_formulas = false; + } + const auto jXModel = jRoot.get(); return CreateXModelFromJson(jXModel, xmodel, context, registration); } catch (const nlohmann::json::exception& e) { - std::cerr << std::format("Failed to parse json of xmodel: {}\n", e.what()); + con::error("Failed to parse json of xmodel: {}", e.what()); } return false; @@ -109,10 +128,10 @@ namespace static void PrintError(const XModel& xmodel, const std::string& message) { - std::cerr << std::format("Cannot load xmodel \"{}\": {}\n", xmodel.name, message); + con::error("Cannot load xmodel \"{}\": {}", xmodel.name, message); } - static std::unique_ptr LoadModelByExtension(std::istream& stream, const std::string& extension) + std::unique_ptr LoadModelByExtension(std::istream& stream, const std::string& extension) const { if (extension == ".glb") { @@ -120,7 +139,7 @@ namespace if (!input.ReadGltfData(stream)) return nullptr; - const auto loader = gltf::Loader::CreateLoader(&input); + const auto loader = gltf::Loader::CreateLoader(input, m_gltf_bad_rotation_formulas); return loader->Load(); } @@ -130,7 +149,7 @@ namespace if (!input.ReadGltfData(stream)) return nullptr; - const auto loader = gltf::Loader::CreateLoader(&input); + const auto loader = gltf::Loader::CreateLoader(input, m_gltf_bad_rotation_formulas); return loader->Load(); } @@ -191,7 +210,7 @@ namespace if (common.m_bone_weight_data.weights.empty()) return; -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) vec3_t minCoordinate, maxCoordinate; auto& offset = info.bounds.midPoint; #else @@ -236,7 +255,7 @@ namespace const Eigen::Vector3f maxEigen(maxCoordinate.x, maxCoordinate.y, maxCoordinate.z); const Eigen::Vector3f boundsCenter = (minEigen + maxEigen) * 0.5f; const Eigen::Vector3f halfSizeEigen = maxEigen - boundsCenter; -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) info.bounds.halfSize.x = halfSizeEigen.x(); info.bounds.halfSize.y = halfSizeEigen.y(); @@ -251,7 +270,7 @@ namespace } bool ApplyCommonBonesToXModel( - const JsonXModelLod& jLod, XModel& xmodel, unsigned lodNumber, const XModelCommon& common, AssetRegistration& registration) + const JsonXModel& jXModel, const JsonXModelLod& jLod, XModel& xmodel, unsigned lodNumber, const XModelCommon& common, AssetRegistration& registration) { if (common.m_bones.empty()) return true; @@ -336,6 +355,12 @@ namespace } } + // Viewhands seem to have nulled trans for some reason? + if (jXModel.type.value_or(JsonXModelType::RIGID) == JsonXModelType::VIEWHANDS) + { + memset(xmodel.trans, 0, sizeof(float) * 4 * (xmodel.numBones - xmodel.numRootBones)); + } + return true; } @@ -425,8 +450,7 @@ namespace const auto rigidBoneIndexForTri = GetRigidBoneIndicesForTris(vertexIndices, surface, common); std::vector triSortList(surface.triCount); - std::iota(triSortList.begin(), triSortList.end(), 0); - + std::ranges::iota(triSortList, 0); std::ranges::sort(triSortList, [&rigidBoneIndexForTri](const size_t triIndex0, const size_t triIndex1) { @@ -505,9 +529,110 @@ namespace } } - void CreateVertsBlendData(XSurface& surface, const std::vector& vertexIndices, const XModelCommon& common) + static uint16_t BoneWeight16(const float value) { - // TODO + return static_cast(value * static_cast(std::numeric_limits::max())); + } + + void CreateVertsBlendData(XSurface& surface, const std::vector& vertexIndices, const XModelCommon& common) const + { + std::vector vertsBlendData; + const auto vertexCount = vertexIndices.size(); + auto vertexIndex = 0uz; + + // Reserve the minimum amount of data we know will follow + vertsBlendData.reserve(vertexCount); + while (vertexIndex < vertexCount) + { + const auto& boneWeights = common.m_vertex_bone_weights[vertexIndices[vertexIndex]]; + if (boneWeights.weightCount > 1) + break; + + const auto& weight0 = common.m_bone_weight_data.weights[boneWeights.weightOffset]; + + vertsBlendData.emplace_back(static_cast(weight0.boneIndex * sizeof(DObjSkelMat))); + + vertexIndex++; + surface.vertInfo.vertCount[0]++; + + AddBoneToXSurfacePartBits(surface, weight0.boneIndex); + } + + vertsBlendData.reserve(vertsBlendData.size() + (vertexCount - vertexIndex) * 3u); + while (vertexIndex < vertexCount) + { + const auto& boneWeights = common.m_vertex_bone_weights[vertexIndices[vertexIndex]]; + if (boneWeights.weightCount > 2) + break; + + const auto& weight0 = common.m_bone_weight_data.weights[boneWeights.weightOffset + 0]; + const auto& weight1 = common.m_bone_weight_data.weights[boneWeights.weightOffset + 1]; + + vertsBlendData.emplace_back(static_cast(weight0.boneIndex * sizeof(DObjSkelMat))); + vertsBlendData.emplace_back(static_cast(weight1.boneIndex * sizeof(DObjSkelMat))); + vertsBlendData.emplace_back(BoneWeight16(weight1.weight)); + + vertexIndex++; + surface.vertInfo.vertCount[1]++; + + AddBoneToXSurfacePartBits(surface, weight0.boneIndex); + AddBoneToXSurfacePartBits(surface, weight1.boneIndex); + } + + vertsBlendData.reserve(vertsBlendData.size() + (vertexCount - vertexIndex) * 5u); + while (vertexIndex < vertexCount) + { + const auto& boneWeights = common.m_vertex_bone_weights[vertexIndices[vertexIndex]]; + if (boneWeights.weightCount > 3) + break; + + const auto& weight0 = common.m_bone_weight_data.weights[boneWeights.weightOffset + 0]; + const auto& weight1 = common.m_bone_weight_data.weights[boneWeights.weightOffset + 1]; + const auto& weight2 = common.m_bone_weight_data.weights[boneWeights.weightOffset + 2]; + + vertsBlendData.emplace_back(static_cast(weight0.boneIndex * sizeof(DObjSkelMat))); + vertsBlendData.emplace_back(static_cast(weight1.boneIndex * sizeof(DObjSkelMat))); + vertsBlendData.emplace_back(BoneWeight16(weight1.weight)); + vertsBlendData.emplace_back(static_cast(weight2.boneIndex * sizeof(DObjSkelMat))); + vertsBlendData.emplace_back(BoneWeight16(weight2.weight)); + + vertexIndex++; + surface.vertInfo.vertCount[2]++; + + AddBoneToXSurfacePartBits(surface, weight0.boneIndex); + AddBoneToXSurfacePartBits(surface, weight1.boneIndex); + AddBoneToXSurfacePartBits(surface, weight2.boneIndex); + } + + vertsBlendData.reserve(vertsBlendData.size() + (vertexCount - vertexIndex) * 7u); + while (vertexIndex < vertexCount) + { + const auto& boneWeights = common.m_vertex_bone_weights[vertexIndices[vertexIndex]]; + + const auto& weight0 = common.m_bone_weight_data.weights[boneWeights.weightOffset + 0]; + const auto& weight1 = common.m_bone_weight_data.weights[boneWeights.weightOffset + 1]; + const auto& weight2 = common.m_bone_weight_data.weights[boneWeights.weightOffset + 2]; + const auto& weight3 = common.m_bone_weight_data.weights[boneWeights.weightOffset + 3]; + + vertsBlendData.emplace_back(static_cast(weight0.boneIndex * sizeof(DObjSkelMat))); + vertsBlendData.emplace_back(static_cast(weight1.boneIndex * sizeof(DObjSkelMat))); + vertsBlendData.emplace_back(BoneWeight16(weight1.weight)); + vertsBlendData.emplace_back(static_cast(weight2.boneIndex * sizeof(DObjSkelMat))); + vertsBlendData.emplace_back(BoneWeight16(weight2.weight)); + vertsBlendData.emplace_back(static_cast(weight3.boneIndex * sizeof(DObjSkelMat))); + vertsBlendData.emplace_back(BoneWeight16(weight3.weight)); + + vertexIndex++; + surface.vertInfo.vertCount[3]++; + + AddBoneToXSurfacePartBits(surface, weight0.boneIndex); + AddBoneToXSurfacePartBits(surface, weight1.boneIndex); + AddBoneToXSurfacePartBits(surface, weight2.boneIndex); + AddBoneToXSurfacePartBits(surface, weight3.boneIndex); + } + + surface.vertInfo.vertsBlend = m_memory.Alloc(vertsBlendData.size()); + std::memcpy(surface.vertInfo.vertsBlend, vertsBlendData.data(), sizeof(uint16_t) * vertsBlendData.size()); } static void ReorderVerticesByWeightCount(std::vector& vertexIndices, const XSurface& surface, const XModelCommon& common) @@ -517,8 +642,7 @@ namespace const auto vertexCount = vertexIndices.size(); std::vector reorderLookup(vertexCount); - std::iota(reorderLookup.begin(), reorderLookup.end(), 0); - + std::ranges::iota(reorderLookup, 0); std::ranges::sort(reorderLookup, [&common, &vertexIndices](const size_t& i0, const size_t& i1) { @@ -571,7 +695,7 @@ namespace constexpr auto maxTriCount = std::numeric_limits::max(); if (commonObject.m_faces.size() > maxTriCount) { - std::cerr << std::format("Surface cannot have more than {} faces\n", maxTriCount); + con::error("Surface cannot have more than {} faces", maxTriCount); return false; } @@ -604,7 +728,7 @@ namespace constexpr auto maxVertices = std::numeric_limits::max(); if (vertexOffset + xmodelToCommonVertexIndexLookup.size() > maxVertices) { - std::cerr << std::format("Lod exceeds limit of {} vertices\n", maxVertices); + con::error("Lod exceeds limit of {} vertices", maxVertices); return false; } @@ -625,15 +749,24 @@ namespace if (!common.m_bone_weight_data.weights.empty()) { // Since bone weights are sorted by weight count, the last must have the highest weight count - const auto hasVertsBlend = - common.m_vertex_bone_weights[xmodelToCommonVertexIndexLookup[xmodelToCommonVertexIndexLookup.size() - 1]].weightCount > 1; - if (!hasVertsBlend) + const auto maxWeightCount = + common.m_vertex_bone_weights[xmodelToCommonVertexIndexLookup[xmodelToCommonVertexIndexLookup.size() - 1]].weightCount; + + if (maxWeightCount == 0) // XModel is rigid + { CreateVertListData(surface, xmodelToCommonVertexIndexLookup, common); - else + } + else if (maxWeightCount < std::extent_v + 1) { CreateVertsBlendData(surface, xmodelToCommonVertexIndexLookup, common); - - std::cerr << "Only rigid models are supported at the moment\n"; +#if defined(FEATURE_T5) || defined(FEATURE_T6) + surface.flags |= XSURFACE_FLAG_SKINNED | XSURFACE_FLAG_DEFORMED; +#endif + } + else + { + con::error("Models must not have vertices that are influenced by more than {} bones", + std::extent_v + 1); return false; } } @@ -641,7 +774,12 @@ namespace return true; } - bool LoadLod(const JsonXModelLod& jLod, XModel& xmodel, unsigned lodNumber, AssetCreationContext& context, AssetRegistration& registration) + bool LoadLod(const JsonXModel& jXModel, + const JsonXModelLod& jLod, + XModel& xmodel, + unsigned lodNumber, + AssetCreationContext& context, + AssetRegistration& registration) { const auto file = m_search_path.Open(jLod.file); if (!file.IsOpen()) @@ -665,7 +803,7 @@ namespace if (lodNumber == 0u) { - if (!ApplyCommonBonesToXModel(jLod, xmodel, lodNumber, *common, registration)) + if (!ApplyCommonBonesToXModel(jXModel, jLod, xmodel, lodNumber, *common, registration)) return false; } else @@ -727,7 +865,7 @@ namespace lodInfo.partBits[i] |= surface.partBits[i]; } -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) auto* modelSurfs = m_memory.Alloc(); const auto modelSurfsName = std::format("{}_lod{}", xmodel.name, lodNumber); modelSurfs->name = m_memory.Dup(modelSurfsName.c_str()); @@ -752,7 +890,7 @@ namespace static void CalculateModelBounds(XModel& xmodel) { -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) if (!xmodel.lodInfo[0].modelSurfs || !xmodel.lodInfo[0].modelSurfs->surfs) return; @@ -768,7 +906,7 @@ namespace for (auto surfaceIndex = 0u; surfaceIndex < xmodel.lodInfo[0].numsurfs; surfaceIndex++) { -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) const auto& surface = surfs[surfaceIndex]; #else const auto& surface = xmodel.surfs[surfaceIndex + xmodel.lodInfo[0].surfIndex]; @@ -790,7 +928,7 @@ namespace } } -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) const Eigen::Vector3f minEigen(minCoordinate.x, minCoordinate.y, minCoordinate.z); const Eigen::Vector3f maxEigen(maxCoordinate.x, maxCoordinate.y, maxCoordinate.z); const Eigen::Vector3f boundsCenter = (minEigen + maxEigen) * 0.5f; @@ -812,6 +950,24 @@ namespace #endif } +#if defined(FEATURE_T5) || defined(FEATURE_T6) + static bool HasAnySkinnedSurfs(const XModel& xmodel) + { + for (auto lodIndex = 0u; lodIndex < xmodel.numLods; lodIndex++) + { + const auto& lod = xmodel.lodInfo[lodIndex]; + for (auto surfIndex = lod.surfIndex; surfIndex < lod.numsurfs; surfIndex++) + { + const auto& surf = xmodel.surfs[lod.surfIndex + surfIndex]; + if (surf.flags & XSURFACE_FLAG_DEFORMED) + return true; + } + } + + return false; + } +#endif + bool CreateXModelFromJson(const JsonXModel& jXModel, XModel& xmodel, AssetCreationContext& context, AssetRegistration& registration) { constexpr auto maxLods = std::extent_v; @@ -825,7 +981,7 @@ namespace xmodel.numLods = static_cast(jXModel.lods.size()); for (const auto& jLod : jXModel.lods) { - if (!LoadLod(jLod, xmodel, lodNumber++, context, registration)) + if (!LoadLod(jXModel, jLod, xmodel, lodNumber++, context, registration)) return false; } @@ -860,6 +1016,10 @@ namespace else xmodel.collLod = -1; +#if defined(FEATURE_T5) || defined(FEATURE_T6) + xmodel.lodRampType = HasAnySkinnedSurfs(xmodel) ? XMODEL_LOD_RAMP_SKINNED : XMODEL_LOD_RAMP_RIGID; +#endif + if (jXModel.physPreset) { auto* physPreset = context.LoadDependency(jXModel.physPreset.value()); @@ -876,7 +1036,7 @@ namespace xmodel.physPreset = nullptr; } -#if defined(FEATURE_IW5) +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) if (jXModel.physCollmap) { auto* physCollmap = context.LoadDependency(jXModel.physCollmap.value()); @@ -924,6 +1084,8 @@ namespace return true; } + bool m_gltf_bad_rotation_formulas; + std::vector m_surfaces; std::vector m_materials; @@ -932,11 +1094,13 @@ namespace ZoneScriptStrings& m_script_strings; PartClassificationState m_part_classification_state; }; -} // namespace GAME +} // namespace -namespace GAME +#set CREATE_LOADER_METHOD "CreateLoader" + GAME + +namespace xmodel { - std::unique_ptr> CreateXModelLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CREATE_LOADER_METHOD (MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone.m_script_strings); } diff --git a/src/ObjLoading/XModel/LoaderXModel.h.template b/src/ObjLoading/XModel/LoaderXModel.h.template index 16075cb2..f6668ca2 100644 --- a/src/ObjLoading/XModel/LoaderXModel.h.template +++ b/src/ObjLoading/XModel/LoaderXModel.h.template @@ -1,4 +1,4 @@ -#options GAME (IW5, T5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/LoaderXModel" + GAME + ".h" @@ -13,7 +13,9 @@ #include -namespace GAME +#set CREATE_LOADER_METHOD "CreateLoader" + GAME + +namespace xmodel { - std::unique_ptr> CreateXModelLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); + std::unique_ptr> CREATE_LOADER_METHOD (MemoryManager& memory, ISearchPath& searchPath, Zone& zone); } // namespace GAME diff --git a/src/ObjLoading/XModel/PartClassificationState.cpp b/src/ObjLoading/XModel/PartClassificationState.cpp index be898f50..688d3f29 100644 --- a/src/ObjLoading/XModel/PartClassificationState.cpp +++ b/src/ObjLoading/XModel/PartClassificationState.cpp @@ -2,6 +2,7 @@ #include "Csv/CsvStream.h" #include "ObjLoading.h" +#include "Utils/Logging/Log.h" #include "Utils/StringUtils.h" #include @@ -17,13 +18,12 @@ bool PartClassificationState::Load(const char** hitLocNames, const size_t hitLoc if (m_loaded) return true; - if (ObjLoading::Configuration.Verbose) - std::cout << "Loading part classification...\n"; + con::debug("Loading part classification..."); const auto file = searchPath.Open(PART_CLASSIFICATION_FILE); if (!file.IsOpen()) { - std::cerr << std::format("Could not load part classification: Failed to open {}\n", PART_CLASSIFICATION_FILE); + con::error("Could not load part classification: Failed to open {}", PART_CLASSIFICATION_FILE); return false; } @@ -58,7 +58,7 @@ bool PartClassificationState::LoadRow(const char** hitLocStart, const char** hit if (row.size() != 2) { - std::cerr << "Could not load part classification: Invalid row\n"; + con::error("Could not load part classification: Invalid row"); return false; } @@ -68,7 +68,7 @@ bool PartClassificationState::LoadRow(const char** hitLocStart, const char** hit const auto foundHitLoc = std::find(hitLocStart, hitLocEnd, row[1]); if (foundHitLoc == hitLocEnd) { - std::cerr << std::format("Invalid hitloc name in row {}: {}\n", rowIndex + 1, row[1]); + con::error("Invalid hitloc name in row {}: {}", rowIndex + 1, row[1]); return false; } diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.h deleted file mode 100644 index 009a7e57..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW3/IW3.h" -#include "Image/IImageWriter.h" - -#include - -namespace IW3 -{ - class AssetDumperGfxImage final : public AbstractAssetDumper - { - std::unique_ptr m_writer; - - [[nodiscard]] std::string GetAssetFileName(const XAssetInfo& asset) const; - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - - public: - AssetDumperGfxImage(); - }; -} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.cpp deleted file mode 100644 index de40981f..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "AssetDumperLoadedSound.h" - -#include "Sound/WavTypes.h" -#include "Sound/WavWriter.h" - -#include - -using namespace IW3; - -bool AssetDumperLoadedSound::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperLoadedSound::DumpWavPcm(const LoadedSound* asset, std::ostream& stream) -{ - const WavWriter writer(stream); - - const WavMetaData metaData{.channelCount = static_cast(asset->sound.info.channels), - .samplesPerSec = static_cast(asset->sound.info.rate), - .bitsPerSample = static_cast(asset->sound.info.bits)}; - - writer.WritePcmHeader(metaData, asset->sound.info.data_len); - writer.WritePcmData(asset->sound.data, asset->sound.info.data_len); -} - -void AssetDumperLoadedSound::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* loadedSound = asset->Asset(); - const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset->m_name)); - - if (!assetFile) - return; - - auto& stream = *assetFile; - switch (static_cast(loadedSound->sound.info.format)) - { - case WavFormat::PCM: - DumpWavPcm(loadedSound, stream); - break; - - default: - std::cerr << std::format("Unknown format {} for loaded sound: {}\n", loadedSound->sound.info.format, loadedSound->name); - break; - } -} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.h deleted file mode 100644 index 239e3b5b..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLoadedSound.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW3/IW3.h" - -namespace IW3 -{ - class AssetDumperLoadedSound final : public AbstractAssetDumper - { - static void DumpWavPcm(const LoadedSound* asset, std::ostream& stream); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.cpp deleted file mode 100644 index d95a03b7..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "AssetDumperLocalizeEntry.h" - -#include "Dumping/Localize/StringFileDumper.h" -#include "Localize/LocalizeCommon.h" - -#include -#include - -using namespace IW3; - -void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - if (pool->m_asset_lookup.empty()) - return; - - const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); - const auto assetFile = context.OpenAssetFile(std::format("{}/localizedstrings/{}.str", language, context.m_zone.m_name)); - - if (assetFile) - { - StringFileDumper stringFileDumper(context.m_zone, *assetFile); - - stringFileDumper.SetLanguageName(language); - - // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. - stringFileDumper.SetConfigFile(R"(C:/trees/cod3/cod3/bin/StringEd.cfg)"); - - stringFileDumper.SetNotes(""); - - for (auto* localizeEntry : *pool) - { - stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); - } - - stringFileDumper.Finalize(); - } - else - { - std::cerr << std::format("Could not create string file for dumping localized strings of zone '{}'\n", context.m_zone.m_name); - } -} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.h deleted file mode 100644 index 02d18955..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperLocalizeEntry.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW3/IW3.h" - -namespace IW3 -{ - class AssetDumperLocalizeEntry final : public IAssetDumper - { - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - }; -} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMapEnts.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMapEnts.cpp deleted file mode 100644 index 9e9cd441..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMapEnts.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "AssetDumperMapEnts.h" - -using namespace IW3; - -bool AssetDumperMapEnts::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMapEnts::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* mapEnts = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name + ".ents"); - - if (!assetFile) - return; - - auto& stream = *assetFile; - stream.write(mapEnts->entityString, mapEnts->numEntityChars); -} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMapEnts.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMapEnts.h deleted file mode 100644 index c769bec0..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMapEnts.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW3/IW3.h" - -namespace IW3 -{ - class AssetDumperMapEnts final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.cpp deleted file mode 100644 index 0f90a158..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.cpp +++ /dev/null @@ -1,467 +0,0 @@ -#include "AssetDumperMaterial.h" - -#include "Game/IW3/MaterialConstantsIW3.h" -#include "Game/IW3/TechsetConstantsIW3.h" - -#include -#include -#include - -// #define FLAGS_DEBUG 1 - -using namespace IW3; -using json = nlohmann::json; - -namespace IW3 -{ - const char* AssetName(const char* name) - { - if (name && name[0] == ',') - return &name[1]; - return name; - } - - template json ArrayEntry(const char* (&a)[S], const size_t index) - { - assert(index < S); - if (index < S) - return a[index]; - - return json{}; - } - - json BuildComplexTableJson(const complex_s* complexTable, const size_t count) - { - auto jArray = json::array(); - - if (complexTable) - { - for (auto index = 0u; index < count; index++) - { - const auto& entry = complexTable[index]; - jArray.emplace_back(json{ - {"real", entry.real}, - {"imag", entry.imag}, - }); - } - } - - return jArray; - } - - json BuildWaterJson(water_t* water) - { - if (!water) - return json{}; - - return json{ - {"floatTime", water->writable.floatTime}, - {"H0", BuildComplexTableJson(water->H0, water->M * water->N)}, - {"wTerm", water->wTerm ? json{std::vector(water->wTerm, water->wTerm + (water->M * water->N))} : json::array()}, - {"M", water->M}, - {"N", water->N}, - {"Lx", water->Lx}, - {"Lz", water->Lz}, - {"windvel", water->windvel}, - {"winddir", std::vector(std::begin(water->winddir), std::end(water->winddir))}, - {"amplitude", water->amplitude}, - {"codeConstant", std::vector(std::begin(water->codeConstant), std::end(water->codeConstant))}, - {"image", water->image && water->image->name ? AssetName(water->image->name) : nullptr}, - }; - } - - json BuildSamplerStateJson(unsigned char samplerState) - { - static const char* samplerFilterNames[]{ - "none", - "nearest", - "linear", - "aniso2x", - "aniso4x", - }; - static const char* samplerMipmapNames[]{ - "disabled", - "nearest", - "linear", - }; - - return json{ - {"filter", ArrayEntry(samplerFilterNames, (samplerState & SAMPLER_FILTER_MASK) >> SAMPLER_FILTER_SHIFT)}, - {"mipmap", ArrayEntry(samplerMipmapNames, (samplerState & SAMPLER_MIPMAP_MASK) >> SAMPLER_MIPMAP_SHIFT)}, - {"clampU", (samplerState & SAMPLER_CLAMP_U) ? true : false}, - {"clampV", (samplerState & SAMPLER_CLAMP_V) ? true : false}, - {"clampW", (samplerState & SAMPLER_CLAMP_W) ? true : false}, - }; - } - - json BuildTextureTableJson(const MaterialTextureDef* textureTable, const size_t count) - { - static const char* semanticNames[]{ - "2d", - "function", - "colorMap", - "unused1", - "unused2", - "normalMap", - "unused3", - "unused4", - "specularMap", - "unused5", - "unused6", - "waterMap", - }; - - auto jArray = json::array(); - - if (textureTable) - { - for (auto index = 0u; index < count; index++) - { - const auto& entry = textureTable[index]; - - json jEntry = { - {"samplerState", BuildSamplerStateJson(entry.samplerState)}, - {"semantic", ArrayEntry(semanticNames, entry.semantic)}, - }; - - const auto knownMaterialSourceName = knownMaterialSourceNames.find(entry.nameHash); - if (knownMaterialSourceName != knownMaterialSourceNames.end()) - { - jEntry["name"] = knownMaterialSourceName->second; - } - else - { - jEntry.merge_patch({ - {"nameHash", entry.nameHash }, - {"nameStart", entry.nameStart}, - {"nameEnd", entry.nameEnd }, - }); - } - - if (entry.semantic == TS_WATER_MAP) - { - jEntry["water"] = BuildWaterJson(entry.u.water); - } - else - { - jEntry["image"] = entry.u.image && entry.u.image->name ? AssetName(entry.u.image->name) : nullptr; - } - - jArray.emplace_back(std::move(jEntry)); - } - } - - return jArray; - } - - json BuildConstantTableJson(const MaterialConstantDef* constantTable, const size_t count) - { - auto jArray = json::array(); - - if (constantTable) - { - for (auto index = 0u; index < count; index++) - { - const auto& entry = constantTable[index]; - json jEntry = { - {"literal", std::vector(std::begin(entry.literal), std::end(entry.literal))}, - }; - - const auto nameLen = strnlen(entry.name, std::extent_v); - if (nameLen == std::extent_v) - { - std::string fullLengthName(entry.name, std::extent_v); - const auto fullLengthHash = Common::R_HashString(fullLengthName.c_str(), 0); - - if (fullLengthHash == entry.nameHash) - { - jEntry["name"] = fullLengthName; - } - else - { - const auto knownMaterialSourceName = knownMaterialSourceNames.find(entry.nameHash); - if (knownMaterialSourceName != knownMaterialSourceNames.end()) - { - jEntry["name"] = knownMaterialSourceName->second; - } - else - { - jEntry.merge_patch({ - {"nameHash", entry.nameHash}, - {"namePart", fullLengthName}, - }); - } - } - } - else - { - jEntry["name"] = std::string(entry.name, nameLen); - } - - jArray.emplace_back(std::move(jEntry)); - } - } - - return jArray; - } - - json BuildStateBitsTableJson(const GfxStateBits* stateBitsTable, const size_t count) - { - static const char* blendNames[]{ - "disabled", - "zero", - "one", - "srcColor", - "invSrcColor", - "srcAlpha", - "invSrcAlpha", - "destAlpha", - "invDestAlpha", - "destColor", - "invDestColor", - }; - static const char* blendOpNames[]{ - "disabled", - "add", - "subtract", - "revSubtract", - "min", - "max", - }; - static const char* depthTestNames[]{ - "always", - "less", - "equal", - "lessEqual", - }; - static const char* polygonOffsetNames[]{ - "0", - "1", - "2", - "shadowMap", - }; - static const char* stencilOpNames[]{ - "keep", - "zero", - "replace", - "incrSat", - "decrSat", - "invert", - "incr", - "decr", - }; - - auto jArray = json::array(); - - if (stateBitsTable) - { - for (auto index = 0u; index < count; index++) - { - const auto& entry = stateBitsTable[index]; - - const auto srcBlendRgb = (entry.loadBits[0] & GFXS0_SRCBLEND_RGB_MASK) >> GFXS0_SRCBLEND_RGB_SHIFT; - const auto dstBlendRgb = (entry.loadBits[0] & GFXS0_DSTBLEND_RGB_MASK) >> GFXS0_DSTBLEND_RGB_SHIFT; - const auto blendOpRgb = (entry.loadBits[0] & GFXS0_BLENDOP_RGB_MASK) >> GFXS0_BLENDOP_RGB_SHIFT; - const auto srcBlendAlpha = (entry.loadBits[0] & GFXS0_SRCBLEND_ALPHA_MASK) >> GFXS0_SRCBLEND_ALPHA_SHIFT; - const auto dstBlendAlpha = (entry.loadBits[0] & GFXS0_DSTBLEND_ALPHA_MASK) >> GFXS0_DSTBLEND_ALPHA_SHIFT; - const auto blendOpAlpha = (entry.loadBits[0] & GFXS0_BLENDOP_ALPHA_MASK) >> GFXS0_BLENDOP_ALPHA_SHIFT; - const auto depthTest = (entry.loadBits[1] & GFXS1_DEPTHTEST_MASK) >> GFXS1_DEPTHTEST_SHIFT; - const auto polygonOffset = (entry.loadBits[1] & GFXS1_POLYGON_OFFSET_MASK) >> GFXS1_POLYGON_OFFSET_SHIFT; - - const auto* alphaTest = "disable"; - if (entry.loadBits[0] & GFXS0_ATEST_GT_0) - alphaTest = "gt0"; - else if (entry.loadBits[0] & GFXS0_ATEST_LT_128) - alphaTest = "lt128"; - else if (entry.loadBits[0] & GFXS0_ATEST_GE_128) - alphaTest = "ge128"; - else - assert(entry.loadBits[0] & GFXS0_ATEST_DISABLE); - - const auto* cullFace = "none"; - if ((entry.loadBits[0] & GFXS0_CULL_MASK) == GFXS0_CULL_BACK) - cullFace = "back"; - else if ((entry.loadBits[0] & GFXS0_CULL_MASK) == GFXS0_CULL_FRONT) - cullFace = "front"; - else - assert((entry.loadBits[0] & GFXS0_CULL_MASK) == GFXS0_CULL_NONE); - - jArray.emplace_back(json{ - {"srcBlendRgb", ArrayEntry(blendNames, srcBlendRgb)}, - {"dstBlendRgb", ArrayEntry(blendNames, dstBlendRgb)}, - {"blendOpRgb", ArrayEntry(blendOpNames, blendOpRgb)}, - {"alphaTest", alphaTest}, - {"cullFace", cullFace}, - {"srcBlendAlpha", ArrayEntry(blendNames, srcBlendAlpha)}, - {"dstBlendAlpha", ArrayEntry(blendNames, dstBlendAlpha)}, - {"blendOpAlpha", ArrayEntry(blendOpNames, blendOpAlpha)}, - {"colorWriteRgb", (entry.loadBits[0] & GFXS0_COLORWRITE_RGB) ? true : false}, - {"colorWriteAlpha", (entry.loadBits[0] & GFXS0_COLORWRITE_ALPHA) ? true : false}, - {"polymodeLine", (entry.loadBits[0] & GFXS0_POLYMODE_LINE) ? true : false}, - - {"depthWrite", (entry.loadBits[1] & GFXS1_DEPTHWRITE) ? true : false}, - {"depthTest", (entry.loadBits[1] & GFXS1_DEPTHTEST_DISABLE) ? json("disable") : ArrayEntry(depthTestNames, depthTest)}, - {"polygonOffset", ArrayEntry(polygonOffsetNames, polygonOffset)}, - {"stencilFrontEnabled", (entry.loadBits[1] & GFXS1_STENCIL_FRONT_ENABLE) ? true : false}, - {"stencilBackEnabled", (entry.loadBits[1] & GFXS1_STENCIL_BACK_ENABLE) ? true : false}, - {"stencilFrontPass", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_PASS_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilFrontFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_FAIL_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilFrontZFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_ZFAIL_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilFrontFunc", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_FUNC_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilBackPass", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_PASS_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilBackFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_FAIL_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilBackZFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_ZFAIL_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilBackFunc", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_FUNC_SHIFT) & GFXS_STENCILOP_MASK)}, - }); - } - } - - return jArray; - } - - json BuildSurfaceTypeBitsJson(const unsigned surfaceTypeBits) - { - if (!surfaceTypeBits) - return json(surfaceTypeNames[SURF_TYPE_DEFAULT]); - - static constexpr auto NON_SURFACE_TYPE_BITS = - ~(std::numeric_limits::max() >> ((sizeof(unsigned) * 8) - (static_cast(SURF_TYPE_NUM) - 1))); - assert((surfaceTypeBits & NON_SURFACE_TYPE_BITS) == 0); - - std::ostringstream ss; - auto firstSurfaceType = true; - for (auto surfaceTypeIndex = static_cast(SURF_TYPE_BARK); surfaceTypeIndex < SURF_TYPE_NUM; surfaceTypeIndex++) - { - if ((surfaceTypeBits & (1 << (surfaceTypeIndex - 1))) == 0) - continue; - - if (firstSurfaceType) - firstSurfaceType = false; - else - ss << ","; - ss << surfaceTypeNames[surfaceTypeIndex]; - } - - if (firstSurfaceType) - return json(surfaceTypeNames[SURF_TYPE_DEFAULT]); - - return json(ss.str()); - } - - json BuildCharFlagsJson(const std::string& prefix, const unsigned char gameFlags) - { - std::vector values; - - for (auto i = 0u; i < (sizeof(gameFlags) * 8u); i++) - { - if (gameFlags & (1 << i)) - { - std::ostringstream ss; - ss << prefix << " 0x" << std::hex << (1 << i); - values.emplace_back(ss.str()); - } - } - - return json(values); - } -} // namespace IW3 - -bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - auto* material = asset->Asset(); - - std::ostringstream ss; - ss << "materials/" << asset->m_name << ".json"; - const auto assetFile = context.OpenAssetFile(ss.str()); - - if (!assetFile) - return; - - auto& stream = *assetFile; - - static const char* cameraRegionNames[]{"lit", "decal", "emissive", "none"}; - static std::unordered_map sortKeyNames{ - {0, "distortion" }, - {1, "opaque water" }, - {2, "boat hull" }, - {3, "opaque ambient" }, - {4, "opaque" }, - {5, "sky" }, - {6, "skybox - sun / moon" }, - {7, "skybox - clouds" }, - {8, "skybox - horizon" }, - {9, "decal - bottom 1" }, - {10, "decal - bottom 2" }, - {11, "decal - bottom 3" }, - {12, "decal - static decal" }, - {13, "decal - middle 1" }, - {14, "decal - middle 2" }, - {15, "decal - middle 3" }, - {24, "decal - weapon impact" }, - {29, "decal - top 1" }, - {30, "decal - top 2" }, - {31, "decal - top 3" }, - {32, "multiplicative" }, - {33, "banner / curtain" }, - {34, "hair" }, - {35, "underwater" }, - {36, "transparent water" }, - {37, "corona" }, - {38, "window inside" }, - {39, "window outside" }, - {40, "before effects - bottom"}, - {41, "before effects - middle"}, - {42, "before effects - top" }, - {43, "blend / additive" }, - {48, "effect - auto sort" }, - {56, "after effects - bottom" }, - {57, "after effects - middle" }, - {58, "after effects - top" }, - {59, "viewmodel effect" }, - }; - - const auto foundSortKeyName = sortKeyNames.find(material->info.sortKey); - assert(foundSortKeyName != sortKeyNames.end()); - - const json j = { - {"info", - { -#if defined(FLAGS_DEBUG) && FLAGS_DEBUG == 1 - {"gameFlags", BuildCharFlagsJson("gameFlag", material->info.gameFlags)}, // TODO: Find out what gameflags mean -#else - {"gameFlags", material->info.gameFlags}, // TODO: Find out what gameflags mean -#endif - {"sortKey", foundSortKeyName != sortKeyNames.end() ? foundSortKeyName->second : std::to_string(material->info.sortKey)}, - {"textureAtlasRowCount", material->info.textureAtlasRowCount}, - {"textureAtlasColumnCount", material->info.textureAtlasColumnCount}, - {"drawSurf", - {{"objectId", static_cast(material->info.drawSurf.fields.objectId)}, - {"reflectionProbeIndex", static_cast(material->info.drawSurf.fields.reflectionProbeIndex)}, - {"customIndex", static_cast(material->info.drawSurf.fields.customIndex)}, - {"materialSortedIndex", static_cast(material->info.drawSurf.fields.materialSortedIndex)}, - {"prepass", static_cast(material->info.drawSurf.fields.prepass)}, - {"useHeroLighting", static_cast(material->info.drawSurf.fields.primaryLightIndex)}, - {"surfType", static_cast(material->info.drawSurf.fields.surfType)}, - {"primarySortKey", static_cast(material->info.drawSurf.fields.primarySortKey)}}}, - {"surfaceTypeBits", BuildSurfaceTypeBitsJson(material->info.surfaceTypeBits)}, - {"hashIndex", material->info.hashIndex}}}, - {"stateBitsEntry", std::vector(std::begin(material->stateBitsEntry), std::end(material->stateBitsEntry))}, -#if defined(FLAGS_DEBUG) && FLAGS_DEBUG == 1 - {"stateFlags", BuildCharFlagsJson("stateFlag", material->stateFlags)}, -#else - {"stateFlags", material->stateFlags}, -#endif - {"cameraRegion", ArrayEntry(cameraRegionNames, material->cameraRegion)}, - {"techniqueSet", material->techniqueSet && material->techniqueSet->name ? AssetName(material->techniqueSet->name) : nullptr}, - {"textureTable", BuildTextureTableJson(material->textureTable, material->textureCount)}, - {"constantTable", BuildConstantTableJson(material->constantTable, material->constantCount)}, - {"stateBitsTable", BuildStateBitsTableJson(material->stateBitsTable, material->stateBitsCount)}, - }; - - stream << std::setw(4) << j; -} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.h deleted file mode 100644 index 09933cf4..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW3/IW3.h" - -namespace IW3 -{ - class AssetDumperMaterial final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.cpp deleted file mode 100644 index bd9b7ecd..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "AssetDumperRawFile.h" - -using namespace IW3; - -bool AssetDumperRawFile::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperRawFile::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* rawFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - stream.write(rawFile->buffer, rawFile->len); -} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.h deleted file mode 100644 index abeeda26..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperRawFile.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW3/IW3.h" - -namespace IW3 -{ - class AssetDumperRawFile final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.cpp deleted file mode 100644 index 26c8d514..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "AssetDumperStringTable.h" - -#include "Csv/CsvStream.h" - -using namespace IW3; - -bool AssetDumperStringTable::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperStringTable::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* stringTable = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - CsvOutputStream csv(*assetFile); - - for (auto row = 0; row < stringTable->rowCount; row++) - { - for (auto column = 0; column < stringTable->columnCount; column++) - { - csv.WriteColumn(stringTable->values[column + row * stringTable->columnCount]); - } - - csv.NextRow(); - } -} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.h deleted file mode 100644 index 915f4a40..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperStringTable.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW3/IW3.h" - -namespace IW3 -{ - class AssetDumperStringTable final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperWeapon.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperWeapon.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperWeapon.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperWeapon.h deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp deleted file mode 100644 index e95b1d47..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ /dev/null @@ -1,540 +0,0 @@ -#include "AssetDumperXModel.h" - -#include "Game/IW3/CommonIW3.h" -#include "ObjWriting.h" -#include "Utils/DistinctMapper.h" -#include "Utils/HalfFloat.h" -#include "Utils/QuatInt16.h" -#include "XModel/Export/XModelBinWriter.h" -#include "XModel/Export/XModelExportWriter.h" -#include "XModel/Gltf/GltfBinOutput.h" -#include "XModel/Gltf/GltfTextOutput.h" -#include "XModel/Gltf/GltfWriter.h" -#include "XModel/Obj/ObjWriter.h" -#include "XModel/XModelWriter.h" - -#include -#include - -using namespace IW3; - -namespace -{ - std::string GetFileNameForLod(const std::string& modelName, const unsigned lod, const std::string& extension) - { - return std::format("model_export/{}_lod{}{}", modelName, lod, extension); - } - - GfxImage* GetMaterialColorMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_COLOR_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 'c' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - GfxImage* GetMaterialNormalMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_NORMAL_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 'n' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - GfxImage* GetMaterialSpecularMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_SPECULAR_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 's' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) - { - for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) - { - XModelBone bone; - if (model->boneNames[boneNum] < context.m_zone.m_script_strings.Count()) - bone.name = context.m_zone.m_script_strings[model->boneNames[boneNum]]; - else - bone.name = "INVALID_BONE_NAME"; - - if (boneNum >= model->numRootBones) - bone.parentIndex = boneNum - static_cast(model->parentList[boneNum - model->numRootBones]); - else - bone.parentIndex = std::nullopt; - - bone.scale[0] = 1.0f; - bone.scale[1] = 1.0f; - bone.scale[2] = 1.0f; - - bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; - bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; - bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; - bone.globalRotation = { - .x = model->baseMat[boneNum].quat[0], - .y = model->baseMat[boneNum].quat[1], - .z = model->baseMat[boneNum].quat[2], - .w = model->baseMat[boneNum].quat[3], - }; - - if (boneNum < model->numRootBones) - { - bone.localOffset[0] = 0; - bone.localOffset[1] = 0; - bone.localOffset[2] = 0; - bone.localRotation = {.x = 0, .y = 0, .z = 0, .w = 1}; - } - else - { - bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; - bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; - bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; - bone.localRotation = { - .x = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - .y = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - .z = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - .w = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3]), - }; - } - - out.m_bones.emplace_back(std::move(bone)); - } - } - - const char* AssetName(const char* input) - { - if (input && input[0] == ',') - return &input[1]; - - return input; - } - - void AddXModelMaterials(XModelCommon& out, DistinctMapper& materialMapper, const XModel* model) - { - for (auto surfaceMaterialNum = 0u; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) - { - Material* material = model->materialHandles[surfaceMaterialNum]; - if (materialMapper.Add(material)) - { - XModelMaterial xMaterial; - xMaterial.ApplyDefaults(); - - xMaterial.name = AssetName(material->info.name); - const auto* colorMap = GetMaterialColorMap(material); - if (colorMap) - xMaterial.colorMapName = AssetName(colorMap->name); - - const auto* normalMap = GetMaterialNormalMap(material); - if (normalMap) - xMaterial.normalMapName = AssetName(normalMap->name); - - const auto* specularMap = GetMaterialSpecularMap(material); - if (specularMap) - xMaterial.specularMapName = AssetName(specularMap->name); - - out.m_materials.emplace_back(std::move(xMaterial)); - } - } - } - - void AddXModelObjects(XModelCommon& out, const XModel* model, const unsigned lod, const DistinctMapper& materialMapper) - { - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfaceIndex = model->lodInfo[lod].surfIndex; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - XModelObject object; - object.name = std::format("surf{}", surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - - out.m_objects.emplace_back(std::move(object)); - } - } - - void AddXModelVertices(XModelCommon& out, const XModel* model, const unsigned lod) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - - XModelVertex vertex{}; - vertex.coordinates[0] = v.xyz[0]; - vertex.coordinates[1] = v.xyz[1]; - vertex.coordinates[2] = v.xyz[2]; - Common::Vec3UnpackUnitVec(v.normal, vertex.normal); - Common::Vec4UnpackGfxColor(v.color, vertex.color); - Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv); - - out.m_vertices.emplace_back(vertex); - } - } - } - - void AllocateXModelBoneWeights(const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - auto totalWeightCount = 0u; - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - if (surface.vertList) - { - totalWeightCount += surface.vertListCount; - } - - if (surface.vertInfo.vertsBlend) - { - totalWeightCount += surface.vertInfo.vertCount[0] * 1; - totalWeightCount += surface.vertInfo.vertCount[1] * 2; - totalWeightCount += surface.vertInfo.vertCount[2] * 3; - totalWeightCount += surface.vertInfo.vertCount[3] * 4; - } - } - - weightCollection.weights.resize(totalWeightCount); - } - - float BoneWeight16(const uint16_t value) - { - return static_cast(value) / static_cast(std::numeric_limits::max()); - } - - void AddXModelVertexBoneWeights(XModelCommon& out, const XModel* model, const unsigned lod) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - auto& weightCollection = out.m_bone_weight_data; - - auto weightOffset = 0u; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - auto handledVertices = 0u; - - if (surface.vertList) - { - for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) - { - const auto& vertList = surface.vertList[vertListIndex]; - const auto boneWeightOffset = weightOffset; - - weightCollection.weights[weightOffset++] = - XModelBoneWeight{.boneIndex = static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), .weight = 1.0f}; - - for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) - { - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); - } - handledVertices += vertList.vertCount; - } - } - - auto vertsBlendOffset = 0u; - if (surface.vertInfo.vertsBlend) - { - // 1 bone weight - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = 1.0f}; - - vertsBlendOffset += 1; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); - } - - // 2 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneWeight0 = 1.0f - boneWeight1; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - - vertsBlendOffset += 3; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 2); - } - - // 3 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const unsigned boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); - const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex2, .weight = boneWeight2}; - - vertsBlendOffset += 5; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 3); - } - - // 4 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const unsigned boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); - const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const unsigned boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat); - const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex2, .weight = boneWeight2}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex3, .weight = boneWeight3}; - - vertsBlendOffset += 7; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 4); - } - - handledVertices += - surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; - } - - for (; handledVertices < surface.vertCount; handledVertices++) - { - out.m_vertex_bone_weights.emplace_back(0, 0); - } - } - } - - void AddXModelFaces(XModelCommon& out, const XModel* model, const unsigned lod) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - auto& object = out.m_objects[surfIndex]; - object.m_faces.reserve(surface.triCount); - - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - XModelFace face{}; - face.vertexIndex[0] = tri[0] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - object.m_faces.emplace_back(face); - } - } - } - - void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) - { - DistinctMapper materialMapper(model->numsurfs); - AllocateXModelBoneWeights(model, lod, out.m_bone_weight_data); - - out.m_name = std::format("{}_lod{}", model->name, lod); - AddXModelBones(out, context, model); - AddXModelMaterials(out, materialMapper, model); - AddXModelObjects(out, model, lod, materialMapper); - AddXModelVertices(out, model, lod); - AddXModelVertexBoneWeights(out, model, lod); - AddXModelFaces(out, model, lod); - } - - void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - const auto mtlFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); - - if (!mtlFile) - return; - - const auto writer = obj::CreateMtlWriter(*mtlFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - DistinctMapper materialMapper(model->numsurfs); - - writer->Write(common); - } - - void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".obj")); - - if (!assetFile) - return; - - const auto writer = obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - DistinctMapper materialMapper(model->numsurfs); - - writer->Write(common); - } - - void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".XMODEL_EXPORT")); - - if (!assetFile) - return; - - const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - writer->Write(common); - } - - void DumpXModelBinLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".xmodel_bin")); - - if (!assetFile) - return; - - const auto writer = xmodel_bin::CreateWriterForVersion7(*assetFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - writer->Write(common); - } - - template - void DumpGltfLod( - const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod, const std::string& extension) - { - const auto* model = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, extension)); - - if (!assetFile) - return; - - const auto output = std::make_unique(*assetFile); - const auto writer = gltf::Writer::CreateWriter(output.get(), context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - - writer->Write(common); - } - - void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - XModelCommon common; - PopulateXModelWriter(common, context, currentLod, asset->Asset()); - - switch (ObjWriting::Configuration.ModelOutputFormat) - { - case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObjLod(common, context, asset, currentLod); - if (currentLod == 0u) - DumpObjMtl(common, context, asset); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExportLod(common, context, asset, currentLod); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_BIN: - DumpXModelBinLod(common, context, asset, currentLod); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - DumpGltfLod(common, context, asset, currentLod, ".gltf"); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - DumpGltfLod(common, context, asset, currentLod, ".glb"); - break; - - default: - assert(false); - break; - } - } - } -} // namespace - -bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) -{ - return !asset->m_name.empty() && asset->m_name[0] != ','; -} - -void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - DumpXModelSurfs(context, asset); -} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h deleted file mode 100644 index e8aa5df8..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW3/IW3.h" - -namespace IW3 -{ - class AssetDumperXModel final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.cpp b/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.cpp similarity index 54% rename from src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.cpp rename to src/ObjWriting/Game/IW3/Image/ImageDumperIW3.cpp index 5f76f1c4..fbc44a99 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperGfxImage.cpp +++ b/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.cpp @@ -1,13 +1,15 @@ -#include "AssetDumperGfxImage.h" +#include "ImageDumperIW3.h" #include "Image/DdsWriter.h" #include "Image/Dx9TextureLoader.h" +#include "Image/ImageCommon.h" #include "Image/IwiLoader.h" #include "Image/IwiTypes.h" #include "Image/IwiWriter6.h" #include "Image/Texture.h" #include "ObjWriting.h" #include "SearchPath/ISearchPath.h" +#include "Utils/Logging/Log.h" #include #include @@ -38,11 +40,11 @@ namespace std::unique_ptr LoadImageFromIwi(const GfxImage& image, ISearchPath& searchPath) { - const auto imageFileName = std::format("images/{}.iwi", image.name); + const auto imageFileName = image::GetFileNameForAsset(image.name, ".iwi"); const auto filePathImage = searchPath.Open(imageFileName); if (!filePathImage.IsOpen()) { - std::cerr << std::format("Could not find data for image \"{}\"\n", image.name); + con::error("Could not find data for image \"{}\"", image.name); return nullptr; } @@ -58,48 +60,43 @@ namespace } } // namespace -AssetDumperGfxImage::AssetDumperGfxImage() +namespace image { - switch (ObjWriting::Configuration.ImageOutputFormat) + DumperIW3::DumperIW3() { - case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: - m_writer = std::make_unique(); - break; - case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: - m_writer = std::make_unique(); - break; - default: - assert(false); - m_writer = nullptr; - break; + switch (ObjWriting::Configuration.ImageOutputFormat) + { + case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: + m_writer = std::make_unique(); + break; + case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: + m_writer = std::make_unique(); + break; + default: + assert(false); + m_writer = nullptr; + break; + } } -} -bool AssetDumperGfxImage::ShouldDump(XAssetInfo* asset) -{ - return true; -} + bool DumperIW3::ShouldDump(XAssetInfo* asset) + { + return true; + } -std::string AssetDumperGfxImage::GetAssetFileName(const XAssetInfo& asset) const -{ - auto cleanAssetName = asset.m_name; - std::ranges::replace(cleanAssetName, '*', '_'); + void DumperIW3::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* image = asset->Asset(); + const auto texture = LoadImageData(context.m_obj_search_path, *image); + if (!texture) + return; - return std::format("images/{}{}", cleanAssetName, m_writer->GetFileExtension()); -} + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name, m_writer->GetFileExtension())); -void AssetDumperGfxImage::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* image = asset->Asset(); - const auto texture = LoadImageData(context.m_obj_search_path, *image); - if (!texture) - return; + if (!assetFile) + return; - const auto assetFile = context.OpenAssetFile(GetAssetFileName(*asset)); - - if (!assetFile) - return; - - auto& stream = *assetFile; - m_writer->DumpImage(stream, texture.get()); -} + auto& stream = *assetFile; + m_writer->DumpImage(stream, texture.get()); + } +} // namespace image diff --git a/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.h b/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.h new file mode 100644 index 00000000..7a1b181c --- /dev/null +++ b/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" +#include "Image/IImageWriter.h" + +#include + +namespace image +{ + class DumperIW3 final : public AbstractAssetDumper + { + public: + DumperIW3(); + + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + + private: + std::unique_ptr m_writer; + }; +} // namespace image diff --git a/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.cpp b/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.cpp new file mode 100644 index 00000000..777c419b --- /dev/null +++ b/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.cpp @@ -0,0 +1,45 @@ +#include "LocalizeDumperIW3.h" + +#include "Dumping/Localize/StringFileDumper.h" +#include "Localize/LocalizeCommon.h" +#include "Utils/Logging/Log.h" + +#include +#include + +using namespace IW3; + +namespace localize +{ + void DumperIW3::DumpPool(AssetDumpingContext& context, AssetPool* pool) + { + if (pool->m_asset_lookup.empty()) + return; + + const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); + const auto assetFile = context.OpenAssetFile(std::format("{}/localizedstrings/{}.str", language, context.m_zone.m_name)); + + if (assetFile) + { + StringFileDumper stringFileDumper(context.m_zone, *assetFile); + + stringFileDumper.SetLanguageName(language); + + // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. + stringFileDumper.SetConfigFile(R"(C:/trees/cod3/cod3/bin/StringEd.cfg)"); + + stringFileDumper.SetNotes(""); + + for (auto* localizeEntry : *pool) + { + stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); + } + + stringFileDumper.Finalize(); + } + else + { + con::error("Could not create string file for dumping localized strings of zone '{}'", context.m_zone.m_name); + } + } +} // namespace localize diff --git a/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.h b/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.h new file mode 100644 index 00000000..4cebd7c8 --- /dev/null +++ b/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" + +namespace localize +{ + class DumperIW3 final : public IAssetDumper + { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + }; +} // namespace localize diff --git a/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.cpp b/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.cpp new file mode 100644 index 00000000..759623e4 --- /dev/null +++ b/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.cpp @@ -0,0 +1,23 @@ +#include "MapEntsDumperIW3.h" + +using namespace IW3; + +namespace map_ents +{ + bool DumperIW3::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW3::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* mapEnts = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name + ".ents"); + + if (!assetFile) + return; + + auto& stream = *assetFile; + stream.write(mapEnts->entityString, mapEnts->numEntityChars); + } +} // namespace map_ents diff --git a/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.h b/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.h new file mode 100644 index 00000000..cd41ccc8 --- /dev/null +++ b/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" + +namespace map_ents +{ + class DumperIW3 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace map_ents diff --git a/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.cpp b/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.cpp new file mode 100644 index 00000000..ab66127c --- /dev/null +++ b/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.cpp @@ -0,0 +1,251 @@ +#include "MaterialConstantZoneStateIW3.h" + +#include "Game/IW3/CommonIW3.h" +#include "Game/IW3/GameAssetPoolIW3.h" +#include "Game/IW3/GameIW3.h" +#include "ObjWriting.h" +#include "Zone/ZoneRegistry.h" + +namespace IW3 +{ + const char* KNOWN_CONSTANT_NAMES[]{ + "worldViewProjectionMatrix", + "worldViewMatrix2", + "worldViewMatrix1", + "worldViewMatrix", + "worldOutdoorLookupMatrix", + "worldMatrix", + "waterColor", + "viewportDimensions", + "viewProjectionMatrix", + "uvScale", + "uvAnimParms", + "thermalColorOffset", + "sunShadowmapPixelAdjust", + "ssaoParms", + "spotShadowmapPixelAdjust", + "shadowmapSwitchPartition", + "shadowmapScale", + "shadowmapPolygonOffset", + "shadowLookupMatrix", + "renderTargetSize", + "renderSourceSize", + "projectionMatrix", + "playlistPopulationParams", + "pixelCostFracs", + "pixelCostDecode", + "particleCloudSparkColor2", + "particleCloudSparkColor1", + "particleCloudSparkColor0", + "particleCloudMatrix2", + "particleCloudMatrix1", + "particleCloudMatrix", + "particleCloudColor", + "outdoorFeatherParms", + "oceanUVAnimParmPaintedFoam", + "oceanUVAnimParmOctave2", + "oceanUVAnimParmOctave1", + "oceanUVAnimParmOctave0", + "oceanUVAnimParmFoam", + "oceanUVAnimParmDetail1", + "oceanUVAnimParmDetail0", + "oceanScrollParms", + "oceanMiscParms", + "oceanFoamParms", + "oceanAmplitude", + "materialColor", + "lightprobeAmbient", + "lightingLookupScale", + "lightSpotFactors", + "lightSpotDir", + "lightSpecular", + "lightPosition", + "lightFalloffPlacement", + "lightDiffuse", + "inverseWorldViewMatrix", + "inverseViewProjectionMatrix", + "inverseTransposeWorldViewMatrix", + "heatMapDetail", + "glowSetup", + "glowApply", + "gameTime", + "fullscreenDistortion", + "fogSunDir", + "fogSunConsts", + "fogSunColorLinear", + "fogSunColorGamma", + "fogConsts", + "fogColorLinear", + "fogColorGamma", + "flagParms", + "filterTap", + "featherParms", + "falloffParms", + "falloffEndColor", + "falloffBeginColor", + "fadeEffect", + "eyeOffsetParms", + "eyeOffset", + "envMapParms", + "dustTint", + "dustParms", + "dustEyeParms", + "dofRowDelta", + "dofLerpScale", + "dofLerpBias", + "dofEquationViewModelAndFarBlur", + "dofEquationScene", + "distortionScale", + "detailScale", + "depthFromClip", + "debugBumpmap", + "colorTintQuadraticDelta", + "colorTintDelta", + "colorTintBase", + "colorSaturationR", + "colorSaturationG", + "colorSaturationB", + "colorObjMin", + "colorObjMax", + "colorMatrixR", + "colorMatrixG", + "colorMatrixB", + "colorBias", + "codeMeshArg", + "clipSpaceLookupScale", + "clipSpaceLookupOffset", + "baseLightingCoords", + }; + + const char* KNOWN_TEXTURE_DEF_NAMES[]{ + "attenuation", + "attenuationSampler", + "cinematicA", + "cinematicASampler", + "cinematicCb", + "cinematicCbSampler", + "cinematicCr", + "cinematicCrSampler", + "cinematicY", + "cinematicYSampler", + "colorMap", + "colorMap1", + "colorMap2", + "colorMapPostSun", + "colorMapPostSunSampler", + "colorMapSampler", + "colorMapSampler1", + "colorMapSampler2", + "cucoloris", + "cucolorisSampler", + "detailMap", + "detailMapSampler", + "dust", + "dustSampler", + "fadeMap", + "fadeMapSampler", + "floatZ", + "floatZSampler", + "grainMap", + "grainMapSampler", + "halfParticleColor", + "halfParticleColorSampler", + "halfParticleDepth", + "halfParticleDepthSampler", + "heatmap", + "heatmapSampler", + "lightmapPrimary", + "lightmapSamplerPrimary", + "lightmapSamplerSecondary", + "lightmapSecondary", + "lookupMap", + "lookupMapSampler", + "modelLighting", + "modelLightingSampler", + "normalMap", + "normalMapSampler", + "oceanColorRamp", + "oceanColorRampSampler", + "oceanDetailNormal", + "oceanDetailNormalSampler", + "oceanDisplacement", + "oceanDisplacementSampler", + "oceanEnv", + "oceanEnvSampler", + "oceanFoam", + "oceanFoamSampler", + "oceanHeightNormal", + "oceanHeightNormalSampler", + "oceanPaintedFoam", + "oceanPaintedFoamSampler", + "outdoorMap", + "outdoorMapSampler", + "population", + "populationSampler", + "reflectionProbe", + "reflectionProbeSampler", + "shadowmapSamplerSpot", + "shadowmapSamplerSun", + "shadowmapSpot", + "shadowmapSun", + "skyMap", + "skyMapSampler", + "specularMap", + "specularMapSampler", + "ssao", + "ssaoSampler", + "worldMap", + "worldMapSampler", + }; + + void MaterialConstantZoneState::ExtractNamesFromZoneInternal() + { + for (const auto* zone : ZoneRegistry::GetRegistryForGame(GameId::IW3)->Zones()) + { + const auto* assetPools = dynamic_cast(zone->m_pools.get()); + if (!assetPools) + return; + + for (const auto* techniqueSetInfo : *assetPools->m_technique_set) + { + const auto* techniqueSet = techniqueSetInfo->Asset(); + + for (const auto* technique : techniqueSet->techniques) + { + if (technique) + ExtractNamesFromTechnique(technique); + } + } + } + } + + unsigned MaterialConstantZoneState::HashString(const std::string& str) + { + return Common::R_HashString(str.c_str()); + } + + void MaterialConstantZoneState::ExtractNamesFromTechnique(const MaterialTechnique* technique) + { + if (!ShouldDumpFromStruct(technique)) + return; + + for (auto passIndex = 0u; passIndex < technique->passCount; passIndex++) + { + const auto& pass = technique->passArray[passIndex]; + + if (pass.vertexShader && pass.vertexShader->prog.loadDef.program) + ExtractNamesFromShader(pass.vertexShader->prog.loadDef.program, pass.vertexShader->prog.loadDef.programSize); + + if (pass.pixelShader && pass.pixelShader->prog.loadDef.program) + ExtractNamesFromShader(pass.pixelShader->prog.loadDef.program, pass.pixelShader->prog.loadDef.programSize); + } + } + + void MaterialConstantZoneState::AddStaticKnownNames() + { + for (const auto* knownConstantName : KNOWN_CONSTANT_NAMES) + AddConstantName(knownConstantName); + for (const auto* knownTextureDefName : KNOWN_TEXTURE_DEF_NAMES) + AddTextureDefName(knownTextureDefName); + } +} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.h b/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.h new file mode 100644 index 00000000..f46d514b --- /dev/null +++ b/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Game/IW3/IW3.h" +#include "Material/AbstractMaterialConstantZoneState.h" + +#include + +namespace IW3 +{ + class MaterialConstantZoneState final : public AbstractMaterialConstantZoneStateDx9 + { + protected: + void ExtractNamesFromZoneInternal() override; + void ExtractNamesFromTechnique(const MaterialTechnique* technique); + void AddStaticKnownNames() override; + unsigned HashString(const std::string& str) override; + }; +} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp b/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp index 2dc30adf..418e3156 100644 --- a/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp +++ b/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp @@ -1,16 +1,15 @@ #include "ObjWriterIW3.h" -#include "AssetDumpers/AssetDumperGfxImage.h" -#include "AssetDumpers/AssetDumperLoadedSound.h" -#include "AssetDumpers/AssetDumperLocalizeEntry.h" -#include "AssetDumpers/AssetDumperMapEnts.h" -#include "AssetDumpers/AssetDumperMaterial.h" -#include "AssetDumpers/AssetDumperRawFile.h" -#include "AssetDumpers/AssetDumperStringTable.h" -#include "AssetDumpers/AssetDumperWeapon.h" -#include "AssetDumpers/AssetDumperXModel.h" #include "Game/IW3/GameAssetPoolIW3.h" +#include "Game/IW3/Material/MaterialJsonDumperIW3.h" +#include "Game/IW3/XModel/XModelDumperIW3.h" +#include "Image/ImageDumperIW3.h" +#include "Localize/LocalizeDumperIW3.h" +#include "Maps/MapEntsDumperIW3.h" #include "ObjWriting.h" +#include "RawFile/RawFileDumperIW3.h" +#include "Sound/LoadedSoundDumperIW3.h" +#include "StringTable/StringTableDumperIW3.h" using namespace IW3; @@ -27,30 +26,30 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const // DUMP_ASSET_POOL(AssetDumperPhysPreset, m_phys_preset, ASSET_TYPE_PHYSPRESET) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) - DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) - DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) + DUMP_ASSET_POOL(xmodel::DumperIW3, m_xmodel, ASSET_TYPE_XMODEL) + DUMP_ASSET_POOL(material::JsonDumperIW3, m_material, ASSET_TYPE_MATERIAL) // DUMP_ASSET_POOL(AssetDumperMaterialTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) - DUMP_ASSET_POOL(AssetDumperGfxImage, m_image, ASSET_TYPE_IMAGE) + DUMP_ASSET_POOL(image::DumperIW3, m_image, ASSET_TYPE_IMAGE) // DUMP_ASSET_POOL(AssetDumpersnd_alias_list_t, m_sound, ASSET_TYPE_SOUND) // DUMP_ASSET_POOL(AssetDumperSndCurve, m_sound_curve, ASSET_TYPE_SOUND_CURVE) - DUMP_ASSET_POOL(AssetDumperLoadedSound, m_loaded_sound, ASSET_TYPE_LOADED_SOUND) + DUMP_ASSET_POOL(sound::LoadedSoundDumperIW3, m_loaded_sound, ASSET_TYPE_LOADED_SOUND) // DUMP_ASSET_POOL(AssetDumperClipMap, m_clip_map, ASSET_TYPE_CLIPMAP_PVS) // DUMP_ASSET_POOL(AssetDumperComWorld, m_com_world, ASSET_TYPE_COMWORLD) // DUMP_ASSET_POOL(AssetDumperGameWorldSp, m_game_world_sp, ASSET_TYPE_GAMEWORLD_SP) // DUMP_ASSET_POOL(AssetDumperGameWorldMp, m_game_world_mp, ASSET_TYPE_GAMEWORLD_MP) - DUMP_ASSET_POOL(AssetDumperMapEnts, m_map_ents, ASSET_TYPE_MAP_ENTS) + DUMP_ASSET_POOL(map_ents::DumperIW3, m_map_ents, ASSET_TYPE_MAP_ENTS) // DUMP_ASSET_POOL(AssetDumperGfxWorld, m_gfx_world, ASSET_TYPE_GFXWORLD) // DUMP_ASSET_POOL(AssetDumperGfxLightDef, m_gfx_light_def, ASSET_TYPE_LIGHT_DEF) // DUMP_ASSET_POOL(AssetDumperFont_s, m_font, ASSET_TYPE_FONT) // DUMP_ASSET_POOL(AssetDumperMenuList, m_menu_list, ASSET_TYPE_MENULIST) // DUMP_ASSET_POOL(AssetDumpermenuDef_t, m_menu_def, ASSET_TYPE_MENU) - DUMP_ASSET_POOL(AssetDumperLocalizeEntry, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) + DUMP_ASSET_POOL(localize::DumperIW3, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) // DUMP_ASSET_POOL(AssetDumperWeapon, m_weapon, ASSET_TYPE_WEAPON) // DUMP_ASSET_POOL(AssetDumperSndDriverGlobals, m_snd_driver_globals, ASSET_TYPE_SNDDRIVER_GLOBALS) // DUMP_ASSET_POOL(AssetDumperFxEffectDef, m_fx, ASSET_TYPE_FX) // DUMP_ASSET_POOL(AssetDumperFxImpactTable, m_fx_impact_table, ASSET_TYPE_IMPACT_FX) - DUMP_ASSET_POOL(AssetDumperRawFile, m_raw_file, ASSET_TYPE_RAWFILE) - DUMP_ASSET_POOL(AssetDumperStringTable, m_string_table, ASSET_TYPE_STRINGTABLE) + DUMP_ASSET_POOL(raw_file::DumperIW3, m_raw_file, ASSET_TYPE_RAWFILE) + DUMP_ASSET_POOL(string_table::DumperIW3, m_string_table, ASSET_TYPE_STRINGTABLE) return true; diff --git a/src/ObjWriting/Game/IW3/ObjWriterIW3.h b/src/ObjWriting/Game/IW3/ObjWriterIW3.h index 6e6bdbe4..e28527d4 100644 --- a/src/ObjWriting/Game/IW3/ObjWriterIW3.h +++ b/src/ObjWriting/Game/IW3/ObjWriterIW3.h @@ -1,4 +1,5 @@ #pragma once + #include "IObjWriter.h" namespace IW3 diff --git a/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.cpp b/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.cpp new file mode 100644 index 00000000..4b2eb11b --- /dev/null +++ b/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.cpp @@ -0,0 +1,23 @@ +#include "RawFileDumperIW3.h" + +using namespace IW3; + +namespace raw_file +{ + bool DumperIW3::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW3::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* rawFile = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto& stream = *assetFile; + stream.write(rawFile->buffer, rawFile->len); + } +} // namespace raw_file diff --git a/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.h b/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.h new file mode 100644 index 00000000..250bea7c --- /dev/null +++ b/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" + +namespace raw_file +{ + class DumperIW3 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace raw_file diff --git a/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.cpp b/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.cpp new file mode 100644 index 00000000..923d3d57 --- /dev/null +++ b/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.cpp @@ -0,0 +1,53 @@ +#include "LoadedSoundDumperIW3.h" + +#include "Sound/WavTypes.h" +#include "Sound/WavWriter.h" +#include "Utils/Logging/Log.h" + +#include + +using namespace IW3; + +namespace +{ + void DumpWavPcm(const LoadedSound* asset, std::ostream& stream) + { + const WavWriter writer(stream); + + const WavMetaData metaData{.channelCount = static_cast(asset->sound.info.channels), + .samplesPerSec = static_cast(asset->sound.info.rate), + .bitsPerSample = static_cast(asset->sound.info.bits)}; + + writer.WritePcmHeader(metaData, asset->sound.info.data_len); + writer.WritePcmData(asset->sound.data, asset->sound.info.data_len); + } +} // namespace + +namespace sound +{ + bool LoadedSoundDumperIW3::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void LoadedSoundDumperIW3::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* loadedSound = asset->Asset(); + const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + switch (static_cast(loadedSound->sound.info.format)) + { + case WavFormat::PCM: + DumpWavPcm(loadedSound, stream); + break; + + default: + con::error("Unknown format {} for loaded sound: {}", loadedSound->sound.info.format, loadedSound->name); + break; + } + } +} // namespace sound diff --git a/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.h b/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.h new file mode 100644 index 00000000..458af961 --- /dev/null +++ b/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" + +namespace sound +{ + class LoadedSoundDumperIW3 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace sound diff --git a/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.cpp b/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.cpp new file mode 100644 index 00000000..b7188a34 --- /dev/null +++ b/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.cpp @@ -0,0 +1,34 @@ +#include "StringTableDumperIW3.h" + +#include "Csv/CsvStream.h" + +using namespace IW3; + +namespace string_table +{ + bool DumperIW3::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW3::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* stringTable = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + CsvOutputStream csv(*assetFile); + + for (auto row = 0; row < stringTable->rowCount; row++) + { + for (auto column = 0; column < stringTable->columnCount; column++) + { + csv.WriteColumn(stringTable->values[column + row * stringTable->columnCount]); + } + + csv.NextRow(); + } + } +} // namespace string_table diff --git a/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.h b/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.h new file mode 100644 index 00000000..21ef05d5 --- /dev/null +++ b/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" + +namespace string_table +{ + class DumperIW3 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace string_table diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperAddonMapEnts.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperAddonMapEnts.cpp deleted file mode 100644 index 58c745c5..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperAddonMapEnts.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#define NOMINMAX -#include "AssetDumperAddonMapEnts.h" - -#include - -using namespace IW4; - -bool AssetDumperAddonMapEnts::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperAddonMapEnts::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* addonMapEnts = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - - stream.write(addonMapEnts->entityString, std::max(addonMapEnts->numEntityChars - 1, 0)); -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperAddonMapEnts.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperAddonMapEnts.h deleted file mode 100644 index 8a5ad601..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperAddonMapEnts.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperAddonMapEnts final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxImage.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxImage.h deleted file mode 100644 index bcbee6ec..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxImage.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" -#include "Image/IImageWriter.h" - -#include - -namespace IW4 -{ - class AssetDumperGfxImage final : public AbstractAssetDumper - { - std::unique_ptr m_writer; - - [[nodiscard]] std::string GetAssetFileName(const XAssetInfo& asset) const; - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - - public: - AssetDumperGfxImage(); - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxLightDef.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxLightDef.cpp deleted file mode 100644 index af05c62d..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxLightDef.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "AssetDumperGfxLightDef.h" - -#include - -using namespace IW4; - -std::string AssetDumperGfxLightDef::GetAssetFilename(const std::string& assetName) -{ - std::ostringstream ss; - - ss << "lights/" << assetName; - - return ss.str(); -} - -bool AssetDumperGfxLightDef::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperGfxLightDef::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* lightDef = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetAssetFilename(asset->m_name)); - - if (!assetFile || lightDef->attenuation.image == nullptr || lightDef->attenuation.image->name == nullptr) - return; - - auto& stream = *assetFile; - - const auto* imageName = lightDef->attenuation.image->name; - if (imageName[0] == ',') - imageName = &imageName[1]; - - stream << lightDef->attenuation.samplerState << imageName << static_cast(lightDef->lmapLookupStart); -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxLightDef.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxLightDef.h deleted file mode 100644 index 275712ef..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxLightDef.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperGfxLightDef final : public AbstractAssetDumper - { - static std::string GetAssetFilename(const std::string& assetName); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLeaderboardDef.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLeaderboardDef.cpp deleted file mode 100644 index a761b471..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLeaderboardDef.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "AssetDumperLeaderboardDef.h" - -#include "Game/IW4/Leaderboard/JsonLeaderboardDefWriter.h" - -#include -#include - -using namespace IW4; - -bool AssetDumperLeaderboardDef::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperLeaderboardDef::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto assetName = asset->m_name; - const auto assetFile = context.OpenAssetFile(std::format("leaderboards/{}.json", assetName)); - - if (!assetFile) - return; - - DumpLeaderboardDefAsJson(*assetFile, asset->Asset()); -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLeaderboardDef.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLeaderboardDef.h deleted file mode 100644 index c8a344bf..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLeaderboardDef.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperLeaderboardDef final : public AbstractAssetDumper - { - protected: - _NODISCARD bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLoadedSound.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLoadedSound.cpp deleted file mode 100644 index 7348db5e..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLoadedSound.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "AssetDumperLoadedSound.h" - -#include "Sound/WavTypes.h" -#include "Sound/WavWriter.h" - -#include - -using namespace IW4; - -bool AssetDumperLoadedSound::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperLoadedSound::DumpWavPcm(const LoadedSound* asset, std::ostream& stream) -{ - const WavWriter writer(stream); - - const WavMetaData metaData{.channelCount = static_cast(asset->sound.info.channels), - .samplesPerSec = static_cast(asset->sound.info.rate), - .bitsPerSample = static_cast(asset->sound.info.bits)}; - - writer.WritePcmHeader(metaData, asset->sound.info.data_len); - writer.WritePcmData(asset->sound.data, asset->sound.info.data_len); -} - -void AssetDumperLoadedSound::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* loadedSound = asset->Asset(); - const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset->m_name)); - - if (!assetFile) - return; - - auto& stream = *assetFile; - switch (static_cast(loadedSound->sound.info.format)) - { - case WavFormat::PCM: - DumpWavPcm(loadedSound, stream); - break; - - default: - std::cerr << std::format("Unknown format {} for loaded sound: {}\n", loadedSound->sound.info.format, loadedSound->name); - break; - } -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLoadedSound.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLoadedSound.h deleted file mode 100644 index 8065af76..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLoadedSound.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperLoadedSound final : public AbstractAssetDumper - { - static void DumpWavPcm(const LoadedSound* asset, std::ostream& stream); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLocalizeEntry.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLocalizeEntry.cpp deleted file mode 100644 index ef1799ac..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLocalizeEntry.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "AssetDumperLocalizeEntry.h" - -#include "Dumping/Localize/StringFileDumper.h" -#include "Localize/LocalizeCommon.h" - -#include -#include - -using namespace IW4; - -void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - if (pool->m_asset_lookup.empty()) - return; - - const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); - const auto assetFile = context.OpenAssetFile(std::format("{}/localizedstrings/{}.str", language, context.m_zone.m_name)); - - if (assetFile) - { - StringFileDumper stringFileDumper(context.m_zone, *assetFile); - - stringFileDumper.SetLanguageName(language); - - // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. - stringFileDumper.SetConfigFile(R"(C:/trees/cod3/cod3/bin/StringEd.cfg)"); - - stringFileDumper.SetNotes(""); - - for (auto* localizeEntry : *pool) - { - stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); - } - - stringFileDumper.Finalize(); - } - else - { - std::cerr << std::format("Could not create string file for dumping localized strings of zone '{}'\n", context.m_zone.m_name); - } -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLocalizeEntry.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLocalizeEntry.h deleted file mode 100644 index cc1e07f2..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLocalizeEntry.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperLocalizeEntry final : public IAssetDumper - { - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp deleted file mode 100644 index 4a90b9ad..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "AssetDumperMenuDef.h" - -#include "AssetDumperMenuList.h" -#include "Game/IW4/GameAssetPoolIW4.h" -#include "Game/IW4/Menu/MenuDumperIW4.h" -#include "Menu/AbstractMenuDumper.h" -#include "ObjWriting.h" - -#include -#include - -using namespace IW4; - -std::string AssetDumperMenuDef::GetPathForMenu(menu::MenuDumpingZoneState* zoneState, XAssetInfo* asset) -{ - const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(asset->Asset()); - - if (menuDumpingState == zoneState->m_menu_dumping_state_map.end()) - return "ui_mp/" + std::string(asset->Asset()->window.name) + ".menu"; - - return menuDumpingState->second.m_path; -} - -bool AssetDumperMenuDef::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMenuDef::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* menu = asset->Asset(); - auto* zoneState = context.GetZoneAssetDumperState(); - - if (!ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST)) - { - // Make sure menu paths based on menu lists are created - const auto* gameAssetPool = dynamic_cast(asset->m_zone->m_pools.get()); - for (auto* menuListAsset : *gameAssetPool->m_menu_list) - AssetDumperMenuList::CreateDumpingStateForMenuList(zoneState, menuListAsset->Asset()); - } - - const auto menuFilePath = GetPathForMenu(zoneState, asset); - const auto assetFile = context.OpenAssetFile(menuFilePath); - - if (!assetFile) - return; - - MenuDumper menuDumper(*assetFile); - - menuDumper.Start(); - menuDumper.WriteMenu(menu); - menuDumper.End(); -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.h deleted file mode 100644 index fbebc2c6..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" -#include "Menu/MenuDumpingZoneState.h" - -namespace IW4 -{ - class AssetDumperMenuDef final : public AbstractAssetDumper - { - static std::string GetPathForMenu(menu::MenuDumpingZoneState* zoneState, XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.cpp deleted file mode 100644 index 2c4c690d..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include "AssetDumperMenuList.h" - -#include "Game/IW4/Menu/MenuDumperIW4.h" -#include "Menu/AbstractMenuDumper.h" -#include "ObjWriting.h" - -#include -#include -#include -#include - -namespace fs = std::filesystem; - -using namespace IW4; - -std::vector AssetDumperMenuList::GetAllUniqueExpressionSupportingData(const MenuList* menuList) -{ - std::vector result; - std::set alreadyAddedSupportingData; - - if (menuList->menus == nullptr) - return result; - - for (auto i = 0; i < menuList->menuCount; i++) - { - if (menuList->menus[i] == nullptr) - continue; - - const auto* menu = menuList->menus[i]; - - if (menu->expressionData == nullptr) - continue; - - if (alreadyAddedSupportingData.find(menu->expressionData) == alreadyAddedSupportingData.end()) - { - result.push_back(menu->expressionData); - alreadyAddedSupportingData.emplace(menu->expressionData); - } - } - - return result; -} - -void AssetDumperMenuList::DumpFunctions(MenuDumper& menuDumper, const MenuList* menuList) -{ - const auto allSupportingData = GetAllUniqueExpressionSupportingData(menuList); - auto functionIndex = 0u; - - assert(allSupportingData.size() <= 1); - - for (const auto* supportingData : allSupportingData) - { - if (supportingData->uifunctions.functions == nullptr) - continue; - - for (auto i = 0; i < supportingData->uifunctions.totalFunctions; i++) - { - const auto* function = supportingData->uifunctions.functions[i]; - if (function != nullptr) - { - std::stringstream ss; - ss << "FUNC_" << functionIndex; - - menuDumper.WriteFunctionDef(ss.str(), function); - } - - functionIndex++; - } - } -} - -void AssetDumperMenuList::DumpMenus(MenuDumper& menuDumper, menu::MenuDumpingZoneState* zoneState, const MenuList* menuList) -{ - for (auto menuNum = 0; menuNum < menuList->menuCount; menuNum++) - { - const auto* menu = menuList->menus[menuNum]; - - const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(menu); - if (menuDumpingState == zoneState->m_menu_dumping_state_map.end()) - continue; - - // If the menu was embedded directly as menu list write its data in the menu list file - if (menuDumpingState->second.m_alias_menu_list == menuList) - menuDumper.WriteMenu(menu); - else - menuDumper.IncludeMenu(menuDumpingState->second.m_path); - } -} - -bool AssetDumperMenuList::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMenuList::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* menuList = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto* zoneState = context.GetZoneAssetDumperState(); - - MenuDumper menuDumper(*assetFile); - - menuDumper.Start(); - - if (!ObjWriting::Configuration.MenuLegacyMode) - DumpFunctions(menuDumper, menuList); - - DumpMenus(menuDumper, zoneState, menuList); - - menuDumper.End(); -} - -std::string AssetDumperMenuList::PathForMenu(const std::string& menuListParentPath, const menuDef_t* menu) -{ - const auto* menuAssetName = menu->window.name; - - if (!menuAssetName) - return ""; - - if (menuAssetName[0] == ',') - menuAssetName = &menuAssetName[1]; - - std::ostringstream ss; - ss << menuListParentPath << menuAssetName << ".menu"; - - return ss.str(); -} - -void AssetDumperMenuList::CreateDumpingStateForMenuList(menu::MenuDumpingZoneState* zoneState, const MenuList* menuList) -{ - if (menuList->menuCount <= 0 || menuList->menus == nullptr || menuList->name == nullptr) - return; - - const std::string menuListName(menuList->name); - const fs::path p(menuListName); - std::string parentPath; - if (p.has_parent_path()) - parentPath = p.parent_path().string() + "/"; - - for (auto i = 0; i < menuList->menuCount; i++) - { - auto* menu = menuList->menus[i]; - - if (menu == nullptr) - continue; - - auto existingState = zoneState->m_menu_dumping_state_map.find(menu); - if (existingState == zoneState->m_menu_dumping_state_map.end()) - { - auto menuPath = PathForMenu(parentPath, menu); - const auto isTheSameAsMenuList = menuPath == menuListName; - zoneState->CreateMenuDumpingState(menu, std::move(menuPath), isTheSameAsMenuList ? menuList : nullptr); - } - else if (existingState->second.m_alias_menu_list == nullptr) - { - auto menuPath = PathForMenu(parentPath, menu); - const auto isTheSameAsMenuList = menuPath == menuListName; - if (isTheSameAsMenuList) - { - existingState->second.m_alias_menu_list = menuList; - existingState->second.m_path = std::move(menuPath); - } - } - } -} - -void AssetDumperMenuList::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - auto* zoneState = context.GetZoneAssetDumperState(); - - for (auto* asset : *pool) - CreateDumpingStateForMenuList(zoneState, asset->Asset()); - - AbstractAssetDumper::DumpPool(context, pool); -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.h deleted file mode 100644 index 2e368d7f..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" -#include "Game/IW4/Menu/MenuDumperIW4.h" -#include "Menu/MenuDumpingZoneState.h" - -namespace IW4 -{ - class AssetDumperMenuList final : public AbstractAssetDumper - { - static std::vector GetAllUniqueExpressionSupportingData(const MenuList* menuList); - - static void DumpFunctions(MenuDumper& menuDumper, const MenuList* menuList); - static void DumpMenus(MenuDumper& menuDumper, menu::MenuDumpingZoneState* zoneState, const MenuList* menuList); - - static std::string PathForMenu(const std::string& menuListParentPath, const menuDef_t* menu); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - - public: - static void CreateDumpingStateForMenuList(menu::MenuDumpingZoneState* zoneState, const MenuList* menuList); - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysCollmap.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysCollmap.cpp deleted file mode 100644 index f286a61e..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysCollmap.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "AssetDumperPhysCollmap.h" - -#include "Dumping/MapFile/MapFileDumper.h" - -#include -#include - -using namespace IW4; - -std::string AssetDumperPhysCollmap::GetAssetFilename(const std::string& assetName) -{ - std::ostringstream ss; - - ss << "phys_collmaps/" << assetName << ".map"; - - return ss.str(); -} - -bool AssetDumperPhysCollmap::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperPhysCollmap::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* physCollmap = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetAssetFilename(asset->m_name)); - - if (!assetFile) - return; - - MapFileDumper mapFileDumper(*assetFile); - mapFileDumper.Init(); - - if (physCollmap->count <= 0 || physCollmap->geoms == nullptr) - return; - - mapFileDumper.BeginEntity(); - - mapFileDumper.WriteKeyValue("classname", "worldspawn"); - - for (auto i = 0u; i < physCollmap->count; i++) - { - const auto& geom = physCollmap->geoms[i]; - mapFileDumper.BeginBrush(); - - switch (geom.type) - { - case PHYS_GEOM_NONE: - // TODO: Dump BrushWrapper (probably GJK related) - mapFileDumper.WriteComment("TODO: Brush data"); - break; - case PHYS_GEOM_BOX: - mapFileDumper.WritePhysicsBox({ - {geom.bounds.midPoint[0], geom.bounds.midPoint[1], geom.bounds.midPoint[2]}, - {geom.bounds.halfSize[0], geom.bounds.halfSize[1], geom.bounds.halfSize[2]}, - {geom.orientation[0][0], geom.orientation[0][1], geom.orientation[0][2] }, - {geom.orientation[1][0], geom.orientation[1][1], geom.orientation[1][2] }, - {geom.orientation[2][0], geom.orientation[2][1], geom.orientation[2][2] } - }); - break; - - case PHYS_GEOM_CYLINDER: - mapFileDumper.WritePhysicsCylinder({ - {geom.bounds.midPoint[0], geom.bounds.midPoint[1], geom.bounds.midPoint[2]}, - geom.bounds.halfSize[0], - geom.bounds.halfSize[2] * 2, - {geom.orientation[0][0], geom.orientation[0][1], geom.orientation[0][2] } - }); - break; - - case PHYS_GEOM_BRUSHMODEL: - case PHYS_GEOM_BRUSH: - case PHYS_GEOM_COLLMAP: - case PHYS_GEOM_CAPSULE: - case PHYS_GEOM_GLASS: - default: - // These do not seem to appear inside any collmap assets - assert(false); - break; - } - - mapFileDumper.EndBrush(); - } - - mapFileDumper.EndEntity(); -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysCollmap.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysCollmap.h deleted file mode 100644 index 213f1ce1..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysCollmap.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperPhysCollmap final : public AbstractAssetDumper - { - static std::string GetAssetFilename(const std::string& assetName); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysPreset.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysPreset.cpp deleted file mode 100644 index bfd6d990..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysPreset.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "AssetDumperPhysPreset.h" - -#include "Game/IW4/InfoString/InfoStringFromStructConverter.h" -#include "Game/IW4/ObjConstantsIW4.h" -#include "Game/IW4/PhysPreset/PhysPresetFields.h" - -#include -#include -#include -#include - -using namespace IW4; - -namespace IW4 -{ - class InfoStringFromPhysPresetConverter final : public InfoStringFromStructConverter - { - protected: - void FillFromExtensionField(const cspField_t& field) override - { - assert(false); - } - - public: - InfoStringFromPhysPresetConverter(const PhysPresetInfo* structure, - const cspField_t* fields, - const size_t fieldCount, - std::function scriptStringValueCallback) - : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) - { - } - }; -} // namespace IW4 - -void AssetDumperPhysPreset::CopyToPhysPresetInfo(const PhysPreset* physPreset, PhysPresetInfo* physPresetInfo) -{ - physPresetInfo->mass = std::clamp(physPreset->mass * 1000.0f, 1.0f, 2000.0f); - physPresetInfo->bounce = physPreset->bounce; - - if (std::isinf(physPreset->friction)) - { - physPresetInfo->isFrictionInfinity = 1; - physPresetInfo->friction = 0; - } - else - { - physPresetInfo->isFrictionInfinity = 0; - physPresetInfo->friction = physPreset->friction; - } - - physPresetInfo->bulletForceScale = physPreset->bulletForceScale; - physPresetInfo->explosiveForceScale = physPreset->explosiveForceScale; - physPresetInfo->sndAliasPrefix = physPreset->sndAliasPrefix; - physPresetInfo->piecesSpreadFraction = physPreset->piecesSpreadFraction; - physPresetInfo->piecesUpwardVelocity = physPreset->piecesUpwardVelocity; - physPresetInfo->tempDefaultToCylinder = physPreset->tempDefaultToCylinder ? 1 : 0; - physPresetInfo->perSurfaceSndAlias = physPreset->perSurfaceSndAlias ? 1 : 0; -} - -InfoString AssetDumperPhysPreset::CreateInfoString(XAssetInfo* asset) -{ - auto* physPresetInfo = new PhysPresetInfo; - CopyToPhysPresetInfo(asset->Asset(), physPresetInfo); - - InfoStringFromPhysPresetConverter converter(physPresetInfo, - phys_preset_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -bool AssetDumperPhysPreset::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperPhysPreset::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_PHYS_PRESET); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("physic/" + asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET); - stream.write(stringValue.c_str(), stringValue.size()); - } -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysPreset.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysPreset.h deleted file mode 100644 index 5de816cc..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysPreset.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" -#include "InfoString/InfoString.h" - -namespace IW4 -{ - class AssetDumperPhysPreset final : public AbstractAssetDumper - { - static void CopyToPhysPresetInfo(const PhysPreset* physPreset, PhysPresetInfo* physPresetInfo); - static InfoString CreateInfoString(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPixelShader.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPixelShader.cpp deleted file mode 100644 index 40bb4089..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPixelShader.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "AssetDumperPixelShader.h" - -#include - -using namespace IW4; - -bool AssetDumperPixelShader::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperPixelShader::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* pixelShader = asset->Asset(); - - std::ostringstream ss; - ss << "shader_bin/ps_" << pixelShader->name << ".cso"; - - const auto shaderFile = context.OpenAssetFile(ss.str()); - - if (!shaderFile) - return; - - shaderFile->write(reinterpret_cast(pixelShader->prog.loadDef.program), - static_cast(pixelShader->prog.loadDef.programSize) * 4u); -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPixelShader.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPixelShader.h deleted file mode 100644 index 925e80f9..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPixelShader.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperPixelShader final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperRawFile.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperRawFile.h deleted file mode 100644 index 5b2c2120..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperRawFile.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperRawFile final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperSndCurve.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperSndCurve.cpp deleted file mode 100644 index 71958fbb..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperSndCurve.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "AssetDumperSndCurve.h" - -#include "Dumping/SndCurve/SndCurveDumper.h" - -#include - -using namespace IW4; - -std::string AssetDumperSndCurve::GetAssetFilename(const std::string& assetName) -{ - std::ostringstream ss; - - ss << "soundaliases/" << assetName << ".vfcurve"; - - return ss.str(); -} - -bool AssetDumperSndCurve::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperSndCurve::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* sndCurve = asset->Asset(); - - const auto assetFile = context.OpenAssetFile(GetAssetFilename(sndCurve->filename)); - - if (!assetFile) - return; - - SndCurveDumper dumper(*assetFile); - - const auto knotCount = std::min(static_cast(sndCurve->knotCount), std::extent_v); - dumper.Init(knotCount); - - for (auto i = 0u; i < knotCount; i++) - dumper.WriteKnot(sndCurve->knots[i][0], sndCurve->knots[i][1]); -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperSndCurve.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperSndCurve.h deleted file mode 100644 index 0139fafe..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperSndCurve.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperSndCurve final : public AbstractAssetDumper - { - static std::string GetAssetFilename(const std::string& assetName); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStringTable.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStringTable.cpp deleted file mode 100644 index 8a7bd5e4..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStringTable.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "AssetDumperStringTable.h" - -#include "Csv/CsvStream.h" - -using namespace IW4; - -bool AssetDumperStringTable::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperStringTable::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* stringTable = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - CsvOutputStream csv(*assetFile); - - for (auto row = 0; row < stringTable->rowCount; row++) - { - for (auto column = 0; column < stringTable->columnCount; column++) - { - const auto* cell = &stringTable->values[column + row * stringTable->columnCount]; - if (cell->string != nullptr) - { - csv.WriteColumn(cell->string); - } - else - { - csv.WriteColumn(""); - } - } - - csv.NextRow(); - } -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStringTable.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStringTable.h deleted file mode 100644 index 9d50d431..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStringTable.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperStringTable final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp deleted file mode 100644 index f25c6bf4..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "AssetDumperStructuredDataDefSet.h" - -#include "StructuredDataDef/StructuredDataDefDumper.h" - -#include -#include -#include - -using namespace IW4; -using namespace std::string_literals; - -CommonStructuredDataType AssetDumperStructuredDataDefSet::ConvertType(const CommonStructuredDataDef* def, const StructuredDataType in) -{ - CommonStructuredDataType out; - - switch (in.type) - { - case DATA_INT: - out.m_category = CommonStructuredDataTypeCategory::INT; - break; - case DATA_BYTE: - out.m_category = CommonStructuredDataTypeCategory::BYTE; - break; - case DATA_BOOL: - out.m_category = CommonStructuredDataTypeCategory::BOOL; - break; - case DATA_FLOAT: - out.m_category = CommonStructuredDataTypeCategory::FLOAT; - break; - case DATA_SHORT: - out.m_category = CommonStructuredDataTypeCategory::SHORT; - break; - case DATA_STRING: - out.m_category = CommonStructuredDataTypeCategory::STRING; - out.m_info.string_length = in.u.stringDataLength; - break; - case DATA_ENUM: - assert(!def->m_enums.empty()); - out.m_category = CommonStructuredDataTypeCategory::ENUM; - out.m_info.type_index = std::max(std::min(static_cast(in.u.enumIndex), def->m_enums.size() - 1uz), 0uz); - break; - case DATA_STRUCT: - assert(!def->m_structs.empty()); - out.m_category = CommonStructuredDataTypeCategory::STRUCT; - out.m_info.type_index = std::max(std::min(static_cast(in.u.structIndex), def->m_structs.size() - 1uz), 0uz); - break; - case DATA_INDEXED_ARRAY: - assert(!def->m_indexed_arrays.empty()); - out.m_category = CommonStructuredDataTypeCategory::INDEXED_ARRAY; - out.m_info.type_index = std::max(std::min(static_cast(in.u.indexedArrayIndex), def->m_indexed_arrays.size() - 1uz), 0uz); - break; - case DATA_ENUM_ARRAY: - assert(!def->m_enumed_arrays.empty()); - out.m_category = CommonStructuredDataTypeCategory::ENUM_ARRAY; - out.m_info.type_index = std::max(std::min(static_cast(in.u.enumedArrayIndex), def->m_enumed_arrays.size() - 1uz), 0uz); - break; - case DATA_COUNT: - default: - assert(false); - break; - } - - return out; -} - -void AssetDumperStructuredDataDefSet::ConvertEnum(CommonStructuredDataEnum* out, const StructuredDataEnum* in, const size_t enumIndex) -{ - out->m_name = "ENUM_" + std::to_string(enumIndex); - - if (in->reservedEntryCount > 0 && in->reservedEntryCount != in->entryCount) - out->m_reserved_entry_count = std::max(in->reservedEntryCount, 0); - else - out->m_reserved_entry_count = -1; - - out->m_entries.resize(static_cast(std::max(in->entryCount, 0))); - for (auto i = 0u; i < out->m_entries.size(); i++) - { - auto& outEntry = out->m_entries[i]; - const auto& inEntry = in->entries[i]; - - outEntry.m_name = std::string(inEntry.string); - outEntry.m_value = inEntry.index; - } - - out->SortEntriesByOffset(); -} - -void AssetDumperStructuredDataDefSet::ConvertStruct(const CommonStructuredDataDef* def, - const StructuredDataDef* gameDef, - CommonStructuredDataStruct* out, - const StructuredDataStruct* in, - const size_t structIndex) -{ - if (gameDef->rootType.type == DATA_STRUCT && structIndex == static_cast(gameDef->rootType.u.structIndex)) - { - out->m_name = "root"; - out->m_size_in_byte = gameDef->size; - } - else - { - out->m_name = "STRUCT_" + std::to_string(structIndex); - out->m_size_in_byte = static_cast(std::max(in->size, 0)); - } - - out->m_bit_offset = in->bitOffset; - - out->m_properties.resize(static_cast(std::max(in->propertyCount, 0))); - for (auto i = 0u; i < out->m_properties.size(); i++) - { - auto& outProperty = out->m_properties[i]; - const auto& inProperty = in->properties[i]; - - outProperty.m_name = std::string(inProperty.name); - outProperty.m_type = ConvertType(def, inProperty.type); - - if (inProperty.type.type == DATA_BOOL) - outProperty.m_offset_in_bits = inProperty.offset; - else - outProperty.m_offset_in_bits = inProperty.offset * 8; - } - - out->SortPropertiesByOffset(); -} - -void AssetDumperStructuredDataDefSet::ConvertIndexedArray(const CommonStructuredDataDef* def, - CommonStructuredDataIndexedArray* out, - const StructuredDataIndexedArray* in) -{ - out->m_element_count = static_cast(std::max(in->arraySize, 0)); - out->m_element_size_in_bits = in->elementType.type == DATA_BOOL ? 1 : in->elementSize * 8; - out->m_array_type = ConvertType(def, in->elementType); -} - -void AssetDumperStructuredDataDefSet::ConvertEnumedArray(const CommonStructuredDataDef* def, - CommonStructuredDataEnumedArray* out, - const StructuredDataEnumedArray* in) -{ - assert(!def->m_enums.empty()); - out->m_element_size_in_bits = in->elementType.type == DATA_BOOL ? 1 : in->elementSize * 8; - out->m_array_type = ConvertType(def, in->elementType); - out->m_enum_index = std::max(std::min(static_cast(in->enumIndex), def->m_enums.size() - 1uz), 0uz); - - if (def->m_enums.empty()) - { - assert(false); - return; - } - - out->m_element_count = def->m_enums[out->m_enum_index]->ElementCount(); -} - -std::unique_ptr AssetDumperStructuredDataDefSet::ConvertDef(const StructuredDataDef* in) -{ - auto out = std::make_unique(); - - out->m_version = in->version; - out->m_checksum = in->formatChecksum; - out->m_size_in_byte = in->size; - - out->m_enums.resize(static_cast(std::max(in->enumCount, 0))); - out->m_structs.resize(static_cast(std::max(in->structCount, 0))); - out->m_indexed_arrays.resize(static_cast(std::max(in->indexedArrayCount, 0))); - out->m_enumed_arrays.resize(static_cast(std::max(in->enumedArrayCount, 0))); - - for (auto i = 0u; i < out->m_enums.size(); i++) - { - auto _enum = std::make_unique(); - ConvertEnum(_enum.get(), &in->enums[i], i); - out->m_enums[i] = std::move(_enum); - } - for (auto i = 0u; i < out->m_structs.size(); i++) - { - auto _struct = std::make_unique(); - ConvertStruct(out.get(), in, _struct.get(), &in->structs[i], i); - out->m_structs[i] = std::move(_struct); - } - for (auto i = 0u; i < out->m_indexed_arrays.size(); i++) - ConvertIndexedArray(out.get(), &out->m_indexed_arrays[i], &in->indexedArrays[i]); - for (auto i = 0u; i < out->m_enumed_arrays.size(); i++) - ConvertEnumedArray(out.get(), &out->m_enumed_arrays[i], &in->enumedArrays[i]); - - out->m_root_type = ConvertType(out.get(), in->rootType); - - return out; -} - -bool AssetDumperStructuredDataDefSet::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperStructuredDataDefSet::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* set = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile || set->defs == nullptr) - return; - - StructuredDataDefDumper dumper(*assetFile); - for (auto i = 0u; i < set->defCount; i++) - { - const auto def = ConvertDef(&set->defs[i]); - dumper.DumpDef(*def); - } -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h deleted file mode 100644 index 7859af57..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperStructuredDataDefSet.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" -#include "StructuredDataDef/CommonStructuredDataDef.h" - -#include - -namespace IW4 -{ - class AssetDumperStructuredDataDefSet final : public AbstractAssetDumper - { - static CommonStructuredDataType ConvertType(const CommonStructuredDataDef* def, StructuredDataType in); - static void ConvertEnum(CommonStructuredDataEnum* out, const StructuredDataEnum* in, size_t enumIndex); - static void ConvertStruct(const CommonStructuredDataDef* def, - const StructuredDataDef* gameDef, - CommonStructuredDataStruct* out, - const StructuredDataStruct* in, - size_t structIndex); - static void ConvertIndexedArray(const CommonStructuredDataDef* def, CommonStructuredDataIndexedArray* out, const StructuredDataIndexedArray* in); - static void ConvertEnumedArray(const CommonStructuredDataDef* def, CommonStructuredDataEnumedArray* out, const StructuredDataEnumedArray* in); - static std::unique_ptr ConvertDef(const StructuredDataDef* in); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTechniqueSet.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTechniqueSet.h deleted file mode 100644 index 67495b9b..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTechniqueSet.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperTechniqueSet final : public AbstractAssetDumper - { - static std::string GetTechniqueFileName(const MaterialTechnique* technique); - static std::string GetTechsetFileName(const MaterialTechniqueSet* techset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTracer.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTracer.cpp deleted file mode 100644 index 3a903ebc..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTracer.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "AssetDumperTracer.h" - -#include "Game/IW4/CommonIW4.h" -#include "Game/IW4/InfoString/InfoStringFromStructConverter.h" -#include "Game/IW4/ObjConstantsIW4.h" -#include "Game/IW4/Tracer/TracerFields.h" - -#include -#include -#include - -using namespace IW4; - -namespace IW4 -{ - class InfoStringFromTracerConverter final : public InfoStringFromStructConverter - { - protected: - void FillFromExtensionField(const cspField_t& field) override - { - assert(false); - } - - public: - InfoStringFromTracerConverter(const TracerDef* structure, - const cspField_t* fields, - const size_t fieldCount, - std::function scriptStringValueCallback) - : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) - { - } - }; -} // namespace IW4 - -InfoString AssetDumperTracer::CreateInfoString(XAssetInfo* asset) -{ - InfoStringFromTracerConverter converter(asset->Asset(), - tracer_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -bool AssetDumperTracer::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperTracer::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_TRACER); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_TRACER, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("tracer/" + asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_TRACER); - stream.write(stringValue.c_str(), stringValue.size()); - } -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTracer.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTracer.h deleted file mode 100644 index 6e32b860..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTracer.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" -#include "InfoString/InfoString.h" - -namespace IW4 -{ - class AssetDumperTracer final : public AbstractAssetDumper - { - static InfoString CreateInfoString(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVehicle.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVehicle.h deleted file mode 100644 index 66a1f074..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVehicle.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" -#include "InfoString/InfoString.h" - -namespace IW4 -{ - class AssetDumperVehicle final : public AbstractAssetDumper - { - static InfoString CreateInfoString(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVertexShader.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVertexShader.cpp deleted file mode 100644 index 6bc12d7a..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVertexShader.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "AssetDumperVertexShader.h" - -#include - -using namespace IW4; - -bool AssetDumperVertexShader::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperVertexShader::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* vertexShader = asset->Asset(); - - std::ostringstream ss; - ss << "shader_bin/vs_" << vertexShader->name << ".cso"; - - const auto shaderFile = context.OpenAssetFile(ss.str()); - - if (!shaderFile) - return; - - shaderFile->write(reinterpret_cast(vertexShader->prog.loadDef.program), - static_cast(vertexShader->prog.loadDef.programSize) * 4u); -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVertexShader.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVertexShader.h deleted file mode 100644 index a9d631ef..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVertexShader.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperVertexShader final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperWeapon.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperWeapon.h deleted file mode 100644 index 3abade7f..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperWeapon.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" -#include "InfoString/InfoString.h" - -namespace IW4 -{ - class AssetDumperWeapon final : public AbstractAssetDumper - { - static void CopyToFullDef(const WeaponCompleteDef* weapon, WeaponFullDef* fullDef); - static InfoString CreateInfoString(XAssetInfo* asset); - static void DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp deleted file mode 100644 index efb8dfd3..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ /dev/null @@ -1,530 +0,0 @@ -#include "AssetDumperXModel.h" - -#include "Game/IW4/CommonIW4.h" -#include "ObjWriting.h" -#include "Utils/DistinctMapper.h" -#include "Utils/HalfFloat.h" -#include "Utils/QuatInt16.h" -#include "XModel/Export/XModelBinWriter.h" -#include "XModel/Export/XModelExportWriter.h" -#include "XModel/Gltf/GltfBinOutput.h" -#include "XModel/Gltf/GltfTextOutput.h" -#include "XModel/Gltf/GltfWriter.h" -#include "XModel/Obj/ObjWriter.h" -#include "XModel/XModelWriter.h" - -#include -#include - -using namespace IW4; - -namespace -{ - GfxImage* GetMaterialColorMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_COLOR_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 'c' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - GfxImage* GetMaterialNormalMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_NORMAL_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 'n' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - GfxImage* GetMaterialSpecularMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_SPECULAR_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 's' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) - { - for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) - { - XModelBone bone; - if (model->boneNames[boneNum] < context.m_zone.m_script_strings.Count()) - bone.name = context.m_zone.m_script_strings[model->boneNames[boneNum]]; - else - bone.name = "INVALID_BONE_NAME"; - - if (boneNum >= model->numRootBones) - bone.parentIndex = boneNum - static_cast(model->parentList[boneNum - model->numRootBones]); - else - bone.parentIndex = std::nullopt; - - bone.scale[0] = 1.0f; - bone.scale[1] = 1.0f; - bone.scale[2] = 1.0f; - - bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; - bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; - bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; - bone.globalRotation = { - .x = model->baseMat[boneNum].quat[0], - .y = model->baseMat[boneNum].quat[1], - .z = model->baseMat[boneNum].quat[2], - .w = model->baseMat[boneNum].quat[3], - }; - - if (boneNum < model->numRootBones) - { - bone.localOffset[0] = 0; - bone.localOffset[1] = 0; - bone.localOffset[2] = 0; - bone.localRotation = {.x = 0, .y = 0, .z = 0, .w = 1}; - } - else - { - bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; - bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; - bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; - bone.localRotation = { - .x = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - .y = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - .z = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - .w = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3]), - }; - } - - out.m_bones.emplace_back(std::move(bone)); - } - } - - const char* AssetName(const char* input) - { - if (input && input[0] == ',') - return &input[1]; - - return input; - } - - void AddXModelMaterials(XModelCommon& out, DistinctMapper& materialMapper, const XModel* model) - { - for (auto surfaceMaterialNum = 0u; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) - { - Material* material = model->materialHandles[surfaceMaterialNum]; - if (materialMapper.Add(material)) - { - XModelMaterial xMaterial; - xMaterial.ApplyDefaults(); - - xMaterial.name = AssetName(material->info.name); - const auto* colorMap = GetMaterialColorMap(material); - if (colorMap) - xMaterial.colorMapName = AssetName(colorMap->name); - - const auto* normalMap = GetMaterialNormalMap(material); - if (normalMap) - xMaterial.normalMapName = AssetName(normalMap->name); - - const auto* specularMap = GetMaterialSpecularMap(material); - if (specularMap) - xMaterial.specularMapName = AssetName(specularMap->name); - - out.m_materials.emplace_back(std::move(xMaterial)); - } - } - } - - void AddXModelObjects(XModelCommon& out, const XModelSurfs* modelSurfs, const DistinctMapper& materialMapper, const int baseSurfaceIndex) - { - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - XModelObject object; - object.name = std::format("surf{}", surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - - out.m_objects.emplace_back(std::move(object)); - } - } - - void AddXModelVertices(XModelCommon& out, const XModelSurfs* modelSurfs) - { - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - - XModelVertex vertex{}; - vertex.coordinates[0] = v.xyz[0]; - vertex.coordinates[1] = v.xyz[1]; - vertex.coordinates[2] = v.xyz[2]; - Common::Vec3UnpackUnitVec(v.normal, vertex.normal); - Common::Vec4UnpackGfxColor(v.color, vertex.color); - Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv); - - out.m_vertices.emplace_back(vertex); - } - } - } - - void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) - { - auto totalWeightCount = 0u; - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - - if (surface.vertList) - { - totalWeightCount += surface.vertListCount; - } - - if (surface.vertInfo.vertsBlend) - { - totalWeightCount += surface.vertInfo.vertCount[0] * 1; - totalWeightCount += surface.vertInfo.vertCount[1] * 2; - totalWeightCount += surface.vertInfo.vertCount[2] * 3; - totalWeightCount += surface.vertInfo.vertCount[3] * 4; - } - } - - weightCollection.weights.resize(totalWeightCount); - } - - float BoneWeight16(const uint16_t value) - { - return static_cast(value) / static_cast(std::numeric_limits::max()); - } - - void AddXModelVertexBoneWeights(XModelCommon& out, const XModelSurfs* modelSurfs) - { - auto& weightCollection = out.m_bone_weight_data; - auto weightOffset = 0u; - - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - auto handledVertices = 0u; - - if (surface.vertList) - { - for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) - { - const auto& vertList = surface.vertList[vertListIndex]; - const auto boneWeightOffset = weightOffset; - - weightCollection.weights[weightOffset++] = - XModelBoneWeight{.boneIndex = static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), .weight = 1.0f}; - - for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) - { - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1u); - } - handledVertices += vertList.vertCount; - } - } - - auto vertsBlendOffset = 0u; - if (surface.vertInfo.vertsBlend) - { - // 1 bone weight - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = 1.0f}; - - vertsBlendOffset += 1; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); - } - - // 2 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneWeight0 = 1.0f - boneWeight1; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - - vertsBlendOffset += 3; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 2); - } - - // 3 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const unsigned boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); - const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex2, .weight = boneWeight2}; - - vertsBlendOffset += 5; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 3); - } - - // 4 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const unsigned boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); - const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const unsigned boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat); - const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex2, .weight = boneWeight2}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex3, .weight = boneWeight3}; - - vertsBlendOffset += 7; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 4); - } - - handledVertices += - surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; - } - - for (; handledVertices < surface.vertCount; handledVertices++) - { - out.m_vertex_bone_weights.emplace_back(0, 0); - } - } - } - - void AddXModelFaces(XModelCommon& out, const XModelSurfs* modelSurfs) - { - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - auto& object = out.m_objects[surfIndex]; - object.m_faces.reserve(surface.triCount); - - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - XModelFace face{}; - face.vertexIndex[0] = tri[0] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - object.m_faces.emplace_back(face); - } - } - } - - void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) - { - const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - - DistinctMapper materialMapper(model->numsurfs); - AllocateXModelBoneWeights(modelSurfs, out.m_bone_weight_data); - - out.m_name = modelSurfs->name; - AddXModelBones(out, context, model); - AddXModelMaterials(out, materialMapper, model); - AddXModelObjects(out, modelSurfs, materialMapper, model->lodInfo[lod].surfIndex); - AddXModelVertices(out, modelSurfs); - AddXModelVertexBoneWeights(out, modelSurfs); - AddXModelFaces(out, modelSurfs); - } - - void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - const auto mtlFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); - - if (!mtlFile) - return; - - const auto writer = obj::CreateMtlWriter(*mtlFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - DistinctMapper materialMapper(model->numsurfs); - - writer->Write(common); - } - - void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - - if (modelSurfs->name[0] == ',' || modelSurfs->surfs == nullptr) - return; - - const auto assetFile = context.OpenAssetFile(std::format("model_export/{}.obj", modelSurfs->name)); - - if (!assetFile) - return; - - const auto writer = obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - DistinctMapper materialMapper(model->numsurfs); - - writer->Write(common); - } - - void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - const auto assetFile = context.OpenAssetFile(std::format("model_export/{}.XMODEL_EXPORT", modelSurfs->name)); - - if (!assetFile) - return; - - const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - writer->Write(common); - } - - void DumpXModelBinLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - const auto assetFile = context.OpenAssetFile(std::format("model_export/{}.xmodel_bin", modelSurfs->name)); - - if (!assetFile) - return; - - const auto writer = xmodel_bin::CreateWriterForVersion7(*assetFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - writer->Write(common); - } - - template - void DumpGltfLod( - const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod, const std::string& extension) - { - const auto* model = asset->Asset(); - const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - const auto assetFile = context.OpenAssetFile(std::format("model_export/{}{}", modelSurfs->name, extension)); - - if (!assetFile) - return; - - const auto output = std::make_unique(*assetFile); - const auto writer = gltf::Writer::CreateWriter(output.get(), context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - - writer->Write(common); - } - - void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - XModelCommon common; - PopulateXModelWriter(common, context, currentLod, asset->Asset()); - - switch (ObjWriting::Configuration.ModelOutputFormat) - { - case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObjLod(common, context, asset, currentLod); - if (currentLod == 0u) - DumpObjMtl(common, context, asset); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExportLod(common, context, asset, currentLod); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_BIN: - DumpXModelBinLod(common, context, asset, currentLod); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - DumpGltfLod(common, context, asset, currentLod, ".gltf"); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - DumpGltfLod(common, context, asset, currentLod, ".glb"); - break; - - default: - assert(false); - break; - } - } - } -} // namespace - -bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) -{ - return !asset->m_name.empty() && asset->m_name[0] != ','; -} - -void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - DumpXModelSurfs(context, asset); -} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h deleted file mode 100644 index 84be5262..00000000 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperXModel final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxImage.cpp b/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.cpp similarity index 54% rename from src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxImage.cpp rename to src/ObjWriting/Game/IW4/Image/ImageDumperIW4.cpp index 4490e520..248c4098 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperGfxImage.cpp +++ b/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.cpp @@ -1,10 +1,12 @@ -#include "AssetDumperGfxImage.h" +#include "ImageDumperIW4.h" #include "Image/DdsWriter.h" #include "Image/Dx9TextureLoader.h" +#include "Image/ImageCommon.h" #include "Image/IwiLoader.h" #include "Image/IwiWriter8.h" #include "ObjWriting.h" +#include "Utils/Logging/Log.h" #include #include @@ -35,11 +37,11 @@ namespace std::unique_ptr LoadImageFromIwi(const GfxImage& image, ISearchPath& searchPath) { - const auto imageFileName = std::format("images/{}.iwi", image.name); + const auto imageFileName = image::GetFileNameForAsset(image.name, ".iwi"); const auto filePathImage = searchPath.Open(imageFileName); if (!filePathImage.IsOpen()) { - std::cerr << std::format("Could not find data for image \"{}\"\n", image.name); + con::error("Could not find data for image \"{}\"", image.name); return nullptr; } @@ -55,48 +57,43 @@ namespace } } // namespace -AssetDumperGfxImage::AssetDumperGfxImage() +namespace image { - switch (ObjWriting::Configuration.ImageOutputFormat) + DumperIW4::DumperIW4() { - case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: - m_writer = std::make_unique(); - break; - case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: - m_writer = std::make_unique(); - break; - default: - assert(false); - m_writer = nullptr; - break; + switch (ObjWriting::Configuration.ImageOutputFormat) + { + case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: + m_writer = std::make_unique(); + break; + case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: + m_writer = std::make_unique(); + break; + default: + assert(false); + m_writer = nullptr; + break; + } } -} -bool AssetDumperGfxImage::ShouldDump(XAssetInfo* asset) -{ - return true; -} + bool DumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } -std::string AssetDumperGfxImage::GetAssetFileName(const XAssetInfo& asset) const -{ - auto cleanAssetName = asset.m_name; - std::ranges::replace(cleanAssetName, '*', '_'); + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* image = asset->Asset(); + const auto texture = LoadImageData(context.m_obj_search_path, *image); + if (!texture) + return; - return std::format("images/{}{}", cleanAssetName, m_writer->GetFileExtension()); -} + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name, m_writer->GetFileExtension())); -void AssetDumperGfxImage::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* image = asset->Asset(); - const auto texture = LoadImageData(context.m_obj_search_path, *image); - if (!texture) - return; + if (!assetFile) + return; - const auto assetFile = context.OpenAssetFile(GetAssetFileName(*asset)); - - if (!assetFile) - return; - - auto& stream = *assetFile; - m_writer->DumpImage(stream, texture.get()); -} + auto& stream = *assetFile; + m_writer->DumpImage(stream, texture.get()); + } +} // namespace image diff --git a/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.h b/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.h new file mode 100644 index 00000000..5d268ccc --- /dev/null +++ b/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" +#include "Image/IImageWriter.h" + +#include + +namespace image +{ + class DumperIW4 final : public AbstractAssetDumper + { + public: + DumperIW4(); + + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + + private: + std::unique_ptr m_writer; + }; +} // namespace image diff --git a/src/ObjWriting/Game/IW4/Leaderboard/JsonLeaderboardDefWriter.h b/src/ObjWriting/Game/IW4/Leaderboard/JsonLeaderboardDefWriter.h deleted file mode 100644 index a1b4546d..00000000 --- a/src/ObjWriting/Game/IW4/Leaderboard/JsonLeaderboardDefWriter.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Dumping/AssetDumpingContext.h" -#include "Game/IW4/IW4.h" - -#include - -namespace IW4 -{ - void DumpLeaderboardDefAsJson(std::ostream& stream, const LeaderboardDef* leaderboardDef); -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/Leaderboard/JsonLeaderboardDefWriter.cpp b/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.cpp similarity index 73% rename from src/ObjWriting/Game/IW4/Leaderboard/JsonLeaderboardDefWriter.cpp rename to src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.cpp index fe6628bf..80ed3625 100644 --- a/src/ObjWriting/Game/IW4/Leaderboard/JsonLeaderboardDefWriter.cpp +++ b/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.cpp @@ -1,7 +1,8 @@ -#include "JsonLeaderboardDefWriter.h" +#include "LeaderboardJsonDumperIW4.h" #include "Game/IW4/CommonIW4.h" #include "Game/IW4/Leaderboard/JsonLeaderboardDef.h" +#include "Leaderboard/LeaderboardCommon.h" #include #include @@ -11,18 +12,18 @@ using namespace IW4; namespace { - class JsonDumper + class Dumper { public: - explicit JsonDumper(std::ostream& stream) + explicit Dumper(std::ostream& stream) : m_stream(stream) { } - void Dump(const LeaderboardDef* leaderboardDef) const + void Dump(const LeaderboardDef& leaderboardDef) const { JsonLeaderboardDef jsonLeaderboardDef; - CreateJsonLeaderboardDef(jsonLeaderboardDef, *leaderboardDef); + CreateJsonLeaderboardDef(jsonLeaderboardDef, leaderboardDef); json jRoot = jsonLeaderboardDef; @@ -74,11 +75,21 @@ namespace }; } // namespace -namespace IW4 +namespace leaderboard { - void DumpLeaderboardDefAsJson(std::ostream& stream, const LeaderboardDef* leaderboardDef) + bool JsonDumperIW4::ShouldDump(XAssetInfo* asset) { - JsonDumper dumper(stream); - dumper.Dump(leaderboardDef); + return true; } -} // namespace IW4 + + void JsonDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAsset(asset->m_name)); + + if (!assetFile) + return; + + Dumper dumper(*assetFile); + dumper.Dump(*asset->Asset()); + } +} // namespace leaderboard diff --git a/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.h b/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.h new file mode 100644 index 00000000..a56e5127 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace leaderboard +{ + class JsonDumperIW4 final : public AbstractAssetDumper + { + protected: + [[nodiscard]] bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace leaderboard diff --git a/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.cpp b/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.cpp new file mode 100644 index 00000000..205df266 --- /dev/null +++ b/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.cpp @@ -0,0 +1,32 @@ +#include "LightDefDumperIW4.h" + +#include "LightDef/LightDefCommon.h" + +#include + +using namespace IW4; + +namespace light_def +{ + bool DumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* lightDef = asset->Asset(); + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name)); + + if (!assetFile || lightDef->attenuation.image == nullptr || lightDef->attenuation.image->name == nullptr) + return; + + auto& stream = *assetFile; + + const auto* imageName = lightDef->attenuation.image->name; + if (imageName[0] == ',') + imageName = &imageName[1]; + + stream << lightDef->attenuation.samplerState << imageName << static_cast(lightDef->lmapLookupStart); + } +} // namespace light_def diff --git a/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.h b/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.h new file mode 100644 index 00000000..29531abb --- /dev/null +++ b/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace light_def +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace light_def diff --git a/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.cpp b/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.cpp new file mode 100644 index 00000000..bff7b2ee --- /dev/null +++ b/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.cpp @@ -0,0 +1,45 @@ +#include "LocalizeDumperIW4.h" + +#include "Dumping/Localize/StringFileDumper.h" +#include "Localize/LocalizeCommon.h" +#include "Utils/Logging/Log.h" + +#include +#include + +using namespace IW4; + +namespace localize +{ + void DumperIW4::DumpPool(AssetDumpingContext& context, AssetPool* pool) + { + if (pool->m_asset_lookup.empty()) + return; + + const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); + const auto assetFile = context.OpenAssetFile(std::format("{}/localizedstrings/{}.str", language, context.m_zone.m_name)); + + if (assetFile) + { + StringFileDumper stringFileDumper(context.m_zone, *assetFile); + + stringFileDumper.SetLanguageName(language); + + // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. + stringFileDumper.SetConfigFile(R"(C:/trees/cod3/cod3/bin/StringEd.cfg)"); + + stringFileDumper.SetNotes(""); + + for (auto* localizeEntry : *pool) + { + stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); + } + + stringFileDumper.Finalize(); + } + else + { + con::error("Could not create string file for dumping localized strings of zone '{}'", context.m_zone.m_name); + } + } +} // namespace localize diff --git a/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.h b/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.h new file mode 100644 index 00000000..fd88c200 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace localize +{ + class DumperIW4 final : public IAssetDumper + { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + }; +} // namespace localize diff --git a/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.cpp b/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.cpp new file mode 100644 index 00000000..37b45249 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.cpp @@ -0,0 +1,27 @@ +#define NOMINMAX +#include "AddonMapEntsDumperIW4.h" + +#include + +using namespace IW4; + +namespace addon_map_ents +{ + bool DumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* addonMapEnts = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto& stream = *assetFile; + + stream.write(addonMapEnts->entityString, std::max(addonMapEnts->numEntityChars - 1, 0)); + } +} // namespace addon_map_ents diff --git a/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.h b/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.h new file mode 100644 index 00000000..4f1ef6c8 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace addon_map_ents +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace addon_map_ents diff --git a/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.h b/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.h deleted file mode 100644 index c6792591..00000000 --- a/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "Dumping/AssetDumpingContext.h" -#include "Game/IW4/IW4.h" -#include "Obj/Gdt/GdtStream.h" - -namespace IW4 -{ - void DecompileMaterialToGdt(GdtOutputStream& out, const Material& material, AssetDumpingContext& context); -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.cpp b/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.cpp deleted file mode 100644 index 9d10c301..00000000 --- a/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "DumperMaterialIW4.h" - -#include "DecompilingMaterialDumperIW4.h" -#include "Game/IW4/Material/JsonMaterialWriterIW4.h" -#include "Game/IW4/Material/MaterialConstantZoneStateIW4.h" -#include "Material/MaterialCommon.h" - -using namespace IW4; - -void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - auto* materialConstantState = context.GetZoneAssetDumperState(); - materialConstantState->ExtractNamesFromZone(); - - AbstractAssetDumper::DumpPool(context, pool); -} - -bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ -#ifdef EXPERIMENTAL_MATERIAL_COMPILATION - if (context.m_gdt) - { - DecompileMaterialToGdt(*context.m_gdt, *asset->Asset(), context); - } -#else - const auto assetFile = context.OpenAssetFile(material::GetFileNameForAssetName(asset->m_name)); - - if (!assetFile) - return; - - DumpMaterialAsJson(*assetFile, *asset->Asset(), context); -#endif -} diff --git a/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.h b/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.h deleted file mode 100644 index 1d3447f3..00000000 --- a/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW4/IW4.h" - -namespace IW4 -{ - class AssetDumperMaterial final : public AbstractAssetDumper - { - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp index 70593610..5524e905 100644 --- a/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp +++ b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp @@ -4,6 +4,7 @@ #include "Game/IW4/GameAssetPoolIW4.h" #include "Game/IW4/GameIW4.h" #include "ObjWriting.h" +#include "Zone/ZoneRegistry.h" namespace IW4 { @@ -199,20 +200,20 @@ namespace IW4 void MaterialConstantZoneState::ExtractNamesFromZoneInternal() { - for (const auto* zone : IGame::GetGameById(GameId::IW5)->GetZones()) + for (const auto* zone : ZoneRegistry::GetRegistryForGame(GameId::IW4)->Zones()) { - const auto* iw5AssetPools = dynamic_cast(zone->m_pools.get()); - if (!iw5AssetPools) + const auto* assetPools = dynamic_cast(zone->m_pools.get()); + if (!assetPools) return; - for (const auto* vertexShaderAsset : *iw5AssetPools->m_material_vertex_shader) + for (const auto* vertexShaderAsset : *assetPools->m_material_vertex_shader) { const auto* vertexShader = vertexShaderAsset->Asset(); if (ShouldDumpFromStruct(vertexShader)) ExtractNamesFromShader(vertexShader->prog.loadDef.program, static_cast(vertexShader->prog.loadDef.programSize) * sizeof(uint32_t)); } - for (const auto* pixelShaderAsset : *iw5AssetPools->m_material_pixel_shader) + for (const auto* pixelShaderAsset : *assetPools->m_material_pixel_shader) { const auto* pixelShader = pixelShaderAsset->Asset(); if (ShouldDumpFromStruct(pixelShader)) diff --git a/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.cpp b/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.cpp similarity index 98% rename from src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.cpp rename to src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.cpp index e6a5443d..f706ed0e 100644 --- a/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.cpp @@ -1,9 +1,10 @@ -#include "DecompilingMaterialDumperIW4.h" +#include "MaterialDecompilingDumperIW4.h" #include "Game/IW4/MaterialConstantsIW4.h" #include "Game/IW4/ObjConstantsIW4.h" #include "Game/IW4/TechsetConstantsIW4.h" #include "Utils/ClassUtils.h" +#include "Utils/Logging/Log.h" #pragma warning(push, 0) #include @@ -550,7 +551,7 @@ namespace } else { - std::cout << "Could not determine material type for material \"" << m_material.info.name << "\"\n"; + con::error("Could not determine material type for material \"{}\"", m_material.info.name); } } @@ -937,7 +938,7 @@ namespace else { std::string constantNamePart(constant.name, strnlen(constant.name, std::extent_v)); - std::cout << "Unknown constant: " << constantNamePart << "\n"; + con::error("Unknown constant: {}", constantNamePart); } } } @@ -1031,8 +1032,7 @@ namespace if (knownMaterialSourceName == knownTextureMaps.end()) { assert(false); - std::cout << "Unknown material texture source name hash: 0x" << std::hex << entry.nameHash << " (" << entry.nameStart << "..." - << entry.nameEnd << ")\n"; + con::error("Unknown material texture source name hash: 0x{:x} ({}...{})", entry.nameHash, entry.nameStart, entry.nameEnd); continue; } @@ -1089,7 +1089,7 @@ namespace assert(filter != GdtFilter_e::UNKNOWN); if (filter == GdtFilter_e::UNKNOWN) { - std::cout << std::format("Unknown filter/mipmap combination: {} {}\n", entry.samplerState.filter, entry.samplerState.mipMap); + con::warn("Unknown filter/mipmap combination: {} {}", entry.samplerState.filter, entry.samplerState.mipMap); continue; } @@ -1108,11 +1108,19 @@ namespace }; } // namespace -namespace IW4 +namespace material { - void DecompileMaterialToGdt(GdtOutputStream& out, const Material& material, AssetDumpingContext& context) + bool DecompilingGdtDumperIW4::ShouldDump(XAssetInfo* asset) { - MaterialGdtDumper dumper(material); - out.WriteEntry(dumper.CreateGdtEntry()); + return true; } -} // namespace IW4 + + void DecompilingGdtDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + if (!context.m_gdt) + return; + + MaterialGdtDumper dumper(*asset->Asset()); + context.m_gdt->WriteEntry(dumper.CreateGdtEntry()); + } +} // namespace material diff --git a/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.h b/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.h new file mode 100644 index 00000000..e6b73e35 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace material +{ + class DecompilingGdtDumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace material diff --git a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp index 5d65da59..d3828080 100644 --- a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp @@ -1,923 +1,59 @@ #include "MenuDumperIW4.h" -#include "Game/IW4/MenuConstantsIW4.h" +#include "Game/IW4/GameAssetPoolIW4.h" +#include "Game/IW4/Menu/MenuDumperIW4.h" +#include "MenuListDumperIW4.h" +#include "MenuWriterIW4.h" #include "ObjWriting.h" -#include -#include -#include +#include +#include using namespace IW4; -// Uncomment this macro to skip interpretative expression dumping -// #define DUMP_NAIVE - -#ifdef DUMP_NAIVE -#define DUMP_FUNC WriteStatementNaive -#else -#define DUMP_FUNC WriteStatementSkipInitialUnnecessaryParenthesis -#endif - -size_t MenuDumper::FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition) +namespace { - assert(statement->numEntries >= 0); - assert(openingParenthesisPosition < static_cast(statement->numEntries)); - - const auto statementEnd = static_cast(statement->numEntries); - - // The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though. - // We will pretend it does since the game does sometimes leave out opening parenthesis from the entries. - auto currentParenthesisDepth = 1; - for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++) + std::string GetPathForMenu(menu::MenuDumpingZoneState* zoneState, XAssetInfo* asset) { - const auto& expEntry = statement->entries[currentSearchPosition]; - if (expEntry.type != EET_OPERATOR) - continue; + const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(asset->Asset()); - // Any function means a "left out" left paren - if (expEntry.data.op == OP_LEFTPAREN || expEntry.data.op >= OP_COUNT) - { - currentParenthesisDepth++; - } - else if (expEntry.data.op == OP_RIGHTPAREN) - { - if (currentParenthesisDepth > 0) - currentParenthesisDepth--; - if (currentParenthesisDepth == 0) - return currentSearchPosition; - } + if (menuDumpingState == zoneState->m_menu_dumping_state_map.end()) + return "ui_mp/" + std::string(asset->Asset()->window.name) + ".menu"; + + return menuDumpingState->second.m_path; + } +} // namespace + +namespace menu +{ + bool MenuDumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; } - return statementEnd; -} - -void MenuDumper::WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const -{ - const auto& expEntry = statement->entries[currentPos]; - - if (spaceNext && expEntry.data.op != OP_COMMA) - m_stream << " "; - - if (expEntry.data.op == OP_LEFTPAREN) + void MenuDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); - m_stream << "("; - WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); - m_stream << ")"; + const auto* menu = asset->Asset(); + auto* zoneState = context.GetZoneAssetDumperState(); - currentPos = closingParenPos + 1; - spaceNext = true; - } - else if (expEntry.data.op >= EXP_FUNC_STATIC_DVAR_INT && expEntry.data.op <= EXP_FUNC_STATIC_DVAR_STRING) - { - switch (expEntry.data.op) + if (!ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST)) { - case EXP_FUNC_STATIC_DVAR_INT: - m_stream << "dvarint"; - break; - - case EXP_FUNC_STATIC_DVAR_BOOL: - m_stream << "dvarbool"; - break; - - case EXP_FUNC_STATIC_DVAR_FLOAT: - m_stream << "dvarfloat"; - break; - - case EXP_FUNC_STATIC_DVAR_STRING: - m_stream << "dvarstring"; - break; - - default: - break; + // Make sure menu paths based on menu lists are created + const auto* gameAssetPool = dynamic_cast(asset->m_zone->m_pools.get()); + for (auto* menuListAsset : *gameAssetPool->m_menu_list) + CreateDumpingStateForMenuListIW4(zoneState, menuListAsset->Asset()); } - // Functions do not have opening parenthesis in the entries. We can just pretend they do though - const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); - m_stream << "("; + const auto menuFilePath = GetPathForMenu(zoneState, asset); + const auto assetFile = context.OpenAssetFile(menuFilePath); - if (closingParenPos - currentPos + 1 >= 1) - { - const auto& staticDvarEntry = statement->entries[currentPos + 1]; - if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT) - { - if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars && staticDvarEntry.data.operand.internals.intVal >= 0 - && staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars) - { - const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal]; - if (staticDvar && staticDvar->dvarName) - m_stream << staticDvar->dvarName; - } - else - { - m_stream << "#INVALID_DVAR_INDEX"; - } - } - else - { - m_stream << "#INVALID_DVAR_OPERAND"; - } - } + if (!assetFile) + return; - m_stream << ")"; - currentPos = closingParenPos + 1; - spaceNext = true; + auto menuWriter = CreateMenuWriterIW4(*assetFile); + + menuWriter->Start(); + menuWriter->WriteMenu(*menu); + menuWriter->End(); } - else - { - if (expEntry.data.op >= 0 && static_cast(expEntry.data.op) < std::extent_v) - m_stream << g_expFunctionNames[expEntry.data.op]; - - if (expEntry.data.op >= OP_COUNT) - { - // Functions do not have opening parenthesis in the entries. We can just pretend they do though - const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); - m_stream << "("; - WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); - m_stream << ")"; - currentPos = closingParenPos + 1; - } - else - currentPos++; - - spaceNext = expEntry.data.op != OP_NOT; - } -} - -void MenuDumper::WriteStatementOperandFunction(const Statement_s* statement, const size_t currentPos) const -{ - const auto& operand = statement->entries[currentPos].data.operand; - - if (operand.internals.function == nullptr) - return; - - if (!ObjWriting::Configuration.MenuLegacyMode) - { - int functionIndex = -1; - if (statement->supportingData && statement->supportingData->uifunctions.functions) - { - for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions; supportingFunctionIndex++) - { - if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function) - { - functionIndex = supportingFunctionIndex; - break; - } - } - } - - if (functionIndex >= 0) - m_stream << "FUNC_" << functionIndex; - else - m_stream << "INVALID_FUNC"; - m_stream << "()"; - } - else - { - m_stream << "("; - WriteStatementSkipInitialUnnecessaryParenthesis(operand.internals.function); - m_stream << ")"; - } -} - -void MenuDumper::WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const -{ - const auto& expEntry = statement->entries[currentPos]; - - if (spaceNext) - m_stream << " "; - - const auto& operand = expEntry.data.operand; - - switch (operand.dataType) - { - case VAL_FLOAT: - m_stream << operand.internals.floatVal; - break; - - case VAL_INT: - m_stream << operand.internals.intVal; - break; - - case VAL_STRING: - WriteEscapedString(operand.internals.stringVal.string); - break; - - case VAL_FUNCTION: - WriteStatementOperandFunction(statement, currentPos); - break; - - default: - break; - } - - currentPos++; - spaceNext = true; -} - -void MenuDumper::WriteStatementEntryRange(const Statement_s* statement, const size_t startOffset, const size_t endOffset) const -{ - assert(startOffset <= endOffset); - assert(endOffset <= static_cast(statement->numEntries)); - - auto currentPos = startOffset; - auto spaceNext = false; - while (currentPos < endOffset) - { - const auto& expEntry = statement->entries[currentPos]; - - if (expEntry.type == EET_OPERATOR) - { - WriteStatementOperator(statement, currentPos, spaceNext); - } - else - { - WriteStatementOperand(statement, currentPos, spaceNext); - } - } -} - -void MenuDumper::WriteStatement(const Statement_s* statement) const -{ - if (statement == nullptr || statement->numEntries < 0) - return; - - WriteStatementEntryRange(statement, 0, static_cast(statement->numEntries)); -} - -void MenuDumper::WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const -{ - if (statementValue == nullptr || statementValue->numEntries < 0) - return; - - const auto statementEnd = static_cast(statementValue->numEntries); - - if (statementValue->numEntries >= 1 && statementValue->entries[0].type == EET_OPERATOR && statementValue->entries[0].data.op == OP_LEFTPAREN) - { - const auto parenthesisEnd = FindStatementClosingParenthesis(statementValue, 0); - - if (parenthesisEnd >= statementEnd) - WriteStatementEntryRange(statementValue, 1, statementEnd); - else if (parenthesisEnd == statementEnd - 1) - WriteStatementEntryRange(statementValue, 1, statementEnd - 1); - else - WriteStatementEntryRange(statementValue, 0, statementEnd); - } - else - { - WriteStatementEntryRange(statementValue, 0, statementEnd); - } -} - -void MenuDumper::WriteStatementNaive(const Statement_s* statement) const -{ - const auto entryCount = static_cast(statement->numEntries); - - const auto missingClosingParenthesis = statement->numEntries > 0 && statement->entries[0].type == EET_OPERATOR - && statement->entries[0].data.op == OP_LEFTPAREN - && FindStatementClosingParenthesis(statement, 0) >= static_cast(statement->numEntries); - - for (auto i = 0uz; i < entryCount; i++) - { - const auto& entry = statement->entries[i]; - if (entry.type == EET_OPERAND) - { - size_t pos = i; - bool discard = false; - WriteStatementOperand(statement, pos, discard); - } - else if (entry.data.op >= EXP_FUNC_STATIC_DVAR_INT && entry.data.op <= EXP_FUNC_STATIC_DVAR_STRING) - { - switch (entry.data.op) - { - case EXP_FUNC_STATIC_DVAR_INT: - m_stream << "dvarint"; - break; - - case EXP_FUNC_STATIC_DVAR_BOOL: - m_stream << "dvarbool"; - break; - - case EXP_FUNC_STATIC_DVAR_FLOAT: - m_stream << "dvarfloat"; - break; - - case EXP_FUNC_STATIC_DVAR_STRING: - m_stream << "dvarstring"; - break; - - default: - break; - } - - // Functions do not have opening parenthesis in the entries. We can just pretend they do though - const auto closingParenPos = FindStatementClosingParenthesis(statement, i); - m_stream << "("; - - if (closingParenPos - i + 1u >= 1u) - { - const auto& staticDvarEntry = statement->entries[i + 1]; - if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT) - { - if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars && staticDvarEntry.data.operand.internals.intVal >= 0 - && staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars) - { - const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal]; - if (staticDvar && staticDvar->dvarName) - m_stream << staticDvar->dvarName; - } - else - { - m_stream << "#INVALID_DVAR_INDEX"; - } - } - else - { - m_stream << "#INVALID_DVAR_OPERAND"; - } - } - - m_stream << ")"; - i = closingParenPos; - } - else - { - assert(entry.data.op >= 0 && static_cast(entry.data.op) < std::extent_v); - if (entry.data.op >= 0 && static_cast(entry.data.op) < std::extent_v) - m_stream << g_expFunctionNames[entry.data.op]; - if (entry.data.op >= OP_COUNT) - m_stream << "("; - } - } - - if (missingClosingParenthesis) - m_stream << ")"; -} - -void MenuDumper::WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const -{ - if (statementValue == nullptr || statementValue->numEntries < 0) - return; - - Indent(); - WriteKey(propertyKey); - - if (isBooleanStatement) - { - m_stream << "when("; - DUMP_FUNC(statementValue); - m_stream << ");\n"; - } - else - { - DUMP_FUNC(statementValue); - m_stream << ";\n"; - } -} - -void MenuDumper::WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const -{ - if (setLocalVarData == nullptr) - return; - - Indent(); - m_stream << setFunction << " " << setLocalVarData->localVarName << " "; - WriteStatement(setLocalVarData->expression); - m_stream << ";\n"; -} - -// #define WRITE_ORIGINAL_SCRIPT - -void MenuDumper::WriteUnconditionalScript(const char* script) const -{ -#ifdef WRITE_ORIGINAL_SCRIPT - Indent(); - m_stream << script << "\n"; - return; -#endif - - const auto tokenList = CreateScriptTokenList(script); - - auto isNewStatement = true; - for (const auto& token : tokenList) - { - if (isNewStatement) - { - if (token == ";") - continue; - - Indent(); - } - - if (token == ";") - { - m_stream << ";\n"; - isNewStatement = true; - continue; - } - - if (!isNewStatement) - m_stream << " "; - else - isNewStatement = false; - - if (DoesTokenNeedQuotationMarks(token)) - m_stream << "\"" << token << "\""; - else - m_stream << token; - } - - if (!isNewStatement) - m_stream << ";\n"; -} - -void MenuDumper::WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet) -{ - Indent(); - m_stream << "{\n"; - IncIndent(); - - for (auto i = 0; i < eventHandlerSet->eventHandlerCount; i++) - { - const auto* eventHandler = eventHandlerSet->eventHandlers[i]; - if (eventHandler == nullptr) - continue; - - switch (eventHandler->eventType) - { - case EVENT_UNCONDITIONAL: - WriteUnconditionalScript(eventHandler->eventData.unconditionalScript); - break; - - case EVENT_IF: - if (eventHandler->eventData.conditionalScript == nullptr || eventHandler->eventData.conditionalScript->eventExpression == nullptr - || eventHandler->eventData.conditionalScript->eventHandlerSet == nullptr) - { - continue; - } - - Indent(); - m_stream << "if ("; - WriteStatementSkipInitialUnnecessaryParenthesis(eventHandler->eventData.conditionalScript->eventExpression); - m_stream << ")\n"; - WriteMenuEventHandlerSet(eventHandler->eventData.conditionalScript->eventHandlerSet); - break; - - case EVENT_ELSE: - if (eventHandler->eventData.elseScript == nullptr) - continue; - - Indent(); - m_stream << "else\n"; - WriteMenuEventHandlerSet(eventHandler->eventData.elseScript); - break; - - case EVENT_SET_LOCAL_VAR_BOOL: - WriteSetLocalVarData("setLocalVarBool", eventHandler->eventData.setLocalVarData); - break; - - case EVENT_SET_LOCAL_VAR_INT: - WriteSetLocalVarData("setLocalVarInt", eventHandler->eventData.setLocalVarData); - break; - - case EVENT_SET_LOCAL_VAR_FLOAT: - WriteSetLocalVarData("setLocalVarFloat", eventHandler->eventData.setLocalVarData); - break; - - case EVENT_SET_LOCAL_VAR_STRING: - WriteSetLocalVarData("setLocalVarString", eventHandler->eventData.setLocalVarData); - break; - - default: - break; - } - } - - DecIndent(); - Indent(); - m_stream << "}\n"; -} - -void MenuDumper::WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue) -{ - if (eventHandlerSetValue == nullptr) - return; - - Indent(); - m_stream << propertyKey << "\n"; - WriteMenuEventHandlerSet(eventHandlerSetValue); -} - -void MenuDumper::WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const -{ - Indent(); - WriteKey(propertyKey); - m_stream << rect.x << " " << rect.y << " " << rect.w << " " << rect.h << " " << static_cast(rect.horzAlign) << " " << static_cast(rect.vertAlign) - << "\n"; -} - -void MenuDumper::WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const -{ - if (materialValue == nullptr || materialValue->info.name == nullptr) - return; - - if (materialValue->info.name[0] == ',') - WriteStringProperty(propertyKey, &materialValue->info.name[1]); - else - WriteStringProperty(propertyKey, materialValue->info.name); -} - -void MenuDumper::WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const -{ - if (soundAliasValue == nullptr) - return; - - WriteStringProperty(propertyKey, soundAliasValue->aliasName); -} - -void MenuDumper::WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const -{ - if (!item->decayActive) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << item->fxLetterTime << " " << item->fxDecayStartTime << " " << item->fxDecayDuration << "\n"; -} - -void MenuDumper::WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue) -{ - for (const auto* currentHandler = itemKeyHandlerValue; currentHandler; currentHandler = currentHandler->next) - { - if (currentHandler->key >= '!' && currentHandler->key <= '~' && currentHandler->key != '"') - { - std::ostringstream ss; - ss << "execKey \"" << static_cast(currentHandler->key) << "\""; - WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); - } - else - { - std::ostringstream ss; - ss << "execKeyInt " << currentHandler->key; - WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); - } - } -} - -void MenuDumper::WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, int floatExpressionCount) const -{ - if (!floatExpressions) - return; - - for (int i = 0; i < floatExpressionCount; i++) - { - const auto& floatExpression = floatExpressions[i]; - - if (floatExpression.target < 0 || floatExpression.target >= ITEM_FLOATEXP_TGT_COUNT) - continue; - - std::string propertyName = std::string("exp ") + floatExpressionTargetBindings[floatExpression.target].name + std::string(" ") - + floatExpressionTargetBindings[floatExpression.target].componentName; - - WriteStatementProperty(propertyName, floatExpression.expression, false); - } -} - -void MenuDumper::WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const -{ - if (!value) - return; - - Indent(); - WriteKey(propertyKey); - - const auto tokenList = CreateScriptTokenList(value); - - auto firstToken = true; - m_stream << "{ "; - for (const auto& token : tokenList) - { - if (firstToken) - firstToken = false; - else - m_stream << ";"; - m_stream << "\"" << token << "\""; - } - if (!firstToken) - m_stream << " "; - m_stream << "}\n"; -} - -void MenuDumper::WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const -{ - if (listBox->numColumns <= 0) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << listBox->numColumns << "\n"; - - for (auto col = 0; col < listBox->numColumns; col++) - { - Indent(); - for (auto i = 0u; i < MENU_KEY_SPACING; i++) - m_stream << " "; - - m_stream << listBox->columnInfo[col].pos << " " << listBox->columnInfo[col].width << " " << listBox->columnInfo[col].maxChars << " " - << listBox->columnInfo[col].alignment << "\n"; - } -} - -void MenuDumper::WriteListBoxProperties(const itemDef_s* item) -{ - if (item->type != ITEM_TYPE_LISTBOX || item->typeData.listBox == nullptr) - return; - - const auto* listBox = item->typeData.listBox; - WriteKeywordProperty("notselectable", listBox->notselectable != 0); - WriteKeywordProperty("noscrollbars", listBox->noScrollBars != 0); - WriteKeywordProperty("usepaging", listBox->usePaging != 0); - WriteFloatProperty("elementwidth", listBox->elementWidth, 0.0f); - WriteFloatProperty("elementheight", listBox->elementHeight, 0.0f); - WriteFloatProperty("feeder", item->special, 0.0f); - WriteIntProperty("elementtype", listBox->elementStyle, 0); - WriteColumnProperty("columns", listBox); - WriteMenuEventHandlerSetProperty("doubleclick", listBox->onDoubleClick); - WriteColorProperty("selectBorder", listBox->selectBorder, COLOR_0000); - WriteMaterialProperty("selectIcon", listBox->selectIcon); -} - -void MenuDumper::WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const -{ - if (item->dvar == nullptr) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << "\"" << item->dvar << "\" " << editField->defVal << " " << editField->minVal << " " << editField->maxVal << "\n"; -} - -void MenuDumper::WriteEditFieldProperties(const itemDef_s* item) const -{ - switch (item->type) - { - case ITEM_TYPE_TEXT: - case ITEM_TYPE_EDITFIELD: - case ITEM_TYPE_NUMERICFIELD: - case ITEM_TYPE_SLIDER: - case ITEM_TYPE_YESNO: - case ITEM_TYPE_BIND: - case ITEM_TYPE_VALIDFILEFIELD: - case ITEM_TYPE_DECIMALFIELD: - case ITEM_TYPE_UPREDITFIELD: - case ITEM_TYPE_EMAILFIELD: - case ITEM_TYPE_PASSWORDFIELD: - break; - - default: - return; - } - - if (item->typeData.editField == nullptr) - return; - - const auto* editField = item->typeData.editField; - if (std::fabs(-1.0f - editField->defVal) >= std::numeric_limits::epsilon() - || std::fabs(-1.0f - editField->minVal) >= std::numeric_limits::epsilon() - || std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits::epsilon()) - { - WriteDvarFloatProperty("dvarFloat", item, editField); - } - else - { - WriteStringProperty("dvar", item->dvar); - } - WriteStringProperty("localvar", item->localVar); - WriteIntProperty("maxChars", editField->maxChars, 0); - WriteKeywordProperty("maxCharsGotoNext", editField->maxCharsGotoNext != 0); - WriteIntProperty("maxPaintChars", editField->maxPaintChars, 0); -} - -void MenuDumper::WriteMultiValueProperty(const multiDef_s* multiDef) const -{ - Indent(); - if (multiDef->strDef) - WriteKey("dvarStrList"); - else - WriteKey("dvarFloatList"); - - m_stream << "{"; - for (auto i = 0; i < multiDef->count; i++) - { - if (multiDef->dvarList[i] == nullptr || multiDef->strDef && multiDef->dvarStr[i] == nullptr) - continue; - - m_stream << " \"" << multiDef->dvarList[i] << "\""; - - if (multiDef->strDef) - m_stream << " \"" << multiDef->dvarStr[i] << "\""; - else - m_stream << " " << multiDef->dvarValue[i] << ""; - } - m_stream << " }\n"; -} - -void MenuDumper::WriteMultiProperties(const itemDef_s* item) const -{ - if (item->type != ITEM_TYPE_MULTI || item->typeData.multi == nullptr) - return; - - const auto* multiDef = item->typeData.multi; - - if (multiDef->count <= 0) - return; - - WriteStringProperty("dvar", item->dvar); - WriteStringProperty("localvar", item->localVar); - WriteMultiValueProperty(multiDef); -} - -void MenuDumper::WriteEnumDvarProperties(const itemDef_s* item) const -{ - if (item->type != ITEM_TYPE_DVARENUM) - return; - - WriteStringProperty("dvar", item->dvar); - WriteStringProperty("localvar", item->localVar); - WriteStringProperty("dvarEnumList", item->typeData.enumDvarName); -} - -void MenuDumper::WriteTickerProperties(const itemDef_s* item) const -{ - if (item->type != ITEM_TYPE_NEWS_TICKER || item->typeData.ticker == nullptr) - return; - - const auto* newsTickerDef = item->typeData.ticker; - WriteIntProperty("spacing", newsTickerDef->spacing, 0); - WriteIntProperty("speed", newsTickerDef->speed, 0); - WriteIntProperty("newsfeed", newsTickerDef->feedId, 0); -} - -void MenuDumper::WriteItemData(const itemDef_s* item) -{ - WriteStringProperty("name", item->window.name); - WriteStringProperty("text", item->text); - WriteKeywordProperty("textsavegame", item->itemFlags & ITEM_FLAG_SAVE_GAME_INFO); - WriteKeywordProperty("textcinematicsubtitle", item->itemFlags & ITEM_FLAG_CINEMATIC_SUBTITLE); - WriteStringProperty("group", item->window.group); - WriteRectProperty("rect", item->window.rectClient); - WriteIntProperty("style", item->window.style, 0); - WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION); - WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED); - WriteKeywordProperty("horizontalscroll", item->window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL); - WriteIntProperty("type", item->type, ITEM_TYPE_TEXT); - WriteIntProperty("border", item->window.border, 0); - WriteFloatProperty("borderSize", item->window.borderSize, 0.0f); - - if (item->visibleExp) - WriteStatementProperty("visible", item->visibleExp, true); - else if (item->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE) - WriteIntProperty("visible", 1, 0); - - WriteStatementProperty("disabled", item->disabledExp, true); - WriteIntProperty("ownerdraw", item->window.ownerDraw, 0); - WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags); - WriteIntProperty("align", item->alignment, 0); - WriteIntProperty("textalign", item->textAlignMode, 0); - WriteFloatProperty("textalignx", item->textalignx, 0.0f); - WriteFloatProperty("textaligny", item->textaligny, 0.0f); - WriteFloatProperty("textscale", item->textscale, 0.0f); - WriteIntProperty("textstyle", item->textStyle, 0); - WriteIntProperty("textfont", item->fontEnum, 0); - WriteColorProperty("backcolor", item->window.backColor, COLOR_0000); - WriteColorProperty("forecolor", item->window.foreColor, COLOR_1111); - WriteColorProperty("bordercolor", item->window.borderColor, COLOR_0000); - WriteColorProperty("outlinecolor", item->window.outlineColor, COLOR_0000); - WriteColorProperty("disablecolor", item->window.disableColor, COLOR_0000); - WriteColorProperty("glowcolor", item->glowColor, COLOR_0000); - WriteMaterialProperty("background", item->window.background); - WriteMenuEventHandlerSetProperty("onFocus", item->onFocus); - WriteMenuEventHandlerSetProperty("leaveFocus", item->leaveFocus); - WriteMenuEventHandlerSetProperty("mouseEnter", item->mouseEnter); - WriteMenuEventHandlerSetProperty("mouseExit", item->mouseExit); - WriteMenuEventHandlerSetProperty("mouseEnterText", item->mouseEnterText); - WriteMenuEventHandlerSetProperty("mouseExitText", item->mouseExitText); - WriteMenuEventHandlerSetProperty("action", item->action); - WriteMenuEventHandlerSetProperty("accept", item->accept); - // WriteFloatProperty("special", item->special, 0.0f); - WriteSoundAliasProperty("focusSound", item->focusSound); - WriteStringProperty("dvarTest", item->dvarTest); - - if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE) - WriteMultiTokenStringProperty("enableDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE) - WriteMultiTokenStringProperty("disableDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW) - WriteMultiTokenStringProperty("showDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE) - WriteMultiTokenStringProperty("hideDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS) - WriteMultiTokenStringProperty("focusDvar", item->enableDvar); - - WriteItemKeyHandlerProperty(item->onKey); - WriteStatementProperty("exp text", item->textExp, false); - WriteStatementProperty("exp material", item->materialExp, false); - WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount); - WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0); - WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0); - WriteDecodeEffectProperty("decodeEffect", item); - - WriteListBoxProperties(item); - WriteEditFieldProperties(item); - WriteMultiProperties(item); - WriteEnumDvarProperties(item); - WriteTickerProperties(item); -} - -void MenuDumper::WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount) -{ - for (auto i = 0u; i < itemCount; i++) - { - StartItemDefScope(); - - WriteItemData(itemDefs[i]); - - EndScope(); - } -} - -void MenuDumper::WriteMenuData(const menuDef_t* menu) -{ - WriteStringProperty("name", menu->window.name); - WriteBoolProperty("fullscreen", menu->fullScreen, false); - WriteKeywordProperty("screenSpace", menu->window.staticFlags & WINDOW_FLAG_SCREEN_SPACE); - WriteKeywordProperty("decoration", menu->window.staticFlags & WINDOW_FLAG_DECORATION); - WriteRectProperty("rect", menu->window.rect); - WriteIntProperty("style", menu->window.style, 0); - WriteIntProperty("border", menu->window.border, 0); - WriteFloatProperty("borderSize", menu->window.borderSize, 0.0f); - WriteColorProperty("backcolor", menu->window.backColor, COLOR_0000); - WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111); - WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000); - WriteColorProperty("focuscolor", menu->focusColor, COLOR_0000); - WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000); - WriteMaterialProperty("background", menu->window.background); - WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0); - WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags); - WriteKeywordProperty("outOfBoundsClick", menu->window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK); - WriteStringProperty("soundLoop", menu->soundName); - WriteKeywordProperty("popup", menu->window.staticFlags & WINDOW_FLAG_POPUP); - WriteFloatProperty("fadeClamp", menu->fadeClamp, 0.0f); - WriteIntProperty("fadeCycle", menu->fadeCycle, 0); - WriteFloatProperty("fadeAmount", menu->fadeAmount, 0.0f); - WriteFloatProperty("fadeInAmount", menu->fadeInAmount, 0.0f); - WriteFloatProperty("blurWorld", menu->blurRadius, 0.0f); - WriteKeywordProperty("legacySplitScreenScale", menu->window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE); - WriteKeywordProperty("hiddenDuringScope", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE); - WriteKeywordProperty("hiddenDuringFlashbang", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG); - WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI); - WriteStringProperty("allowedBinding", menu->allowedBinding); - WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS); - - if (menu->visibleExp) - WriteStatementProperty("visible", menu->visibleExp, true); - else if (menu->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE) - WriteIntProperty("visible", 1, 0); - - WriteStatementProperty("exp rect X", menu->rectXExp, false); - WriteStatementProperty("exp rect Y", menu->rectYExp, false); - WriteStatementProperty("exp rect W", menu->rectWExp, false); - WriteStatementProperty("exp rect H", menu->rectHExp, false); - WriteStatementProperty("exp openSound", menu->openSoundExp, false); - WriteStatementProperty("exp closeSound", menu->closeSoundExp, false); - WriteMenuEventHandlerSetProperty("onOpen", menu->onOpen); - WriteMenuEventHandlerSetProperty("onClose", menu->onClose); - WriteMenuEventHandlerSetProperty("onRequestClose", menu->onCloseRequest); - WriteMenuEventHandlerSetProperty("onESC", menu->onESC); - WriteItemKeyHandlerProperty(menu->onKey); - WriteItemDefs(menu->items, menu->itemCount); -} - -MenuDumper::MenuDumper(std::ostream& stream) - : AbstractMenuDumper(stream) -{ -} - -void MenuDumper::WriteFunctionDef(const std::string& functionName, const Statement_s* statement) -{ - StartFunctionDefScope(); - - WriteStringProperty("name", functionName); - WriteStatementProperty("value", statement, false); - - EndScope(); -} - -void MenuDumper::WriteMenu(const menuDef_t* menu) -{ - StartMenuDefScope(); - - WriteMenuData(menu); - - EndScope(); -} +} // namespace menu diff --git a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h index 9baff905..43317e26 100644 --- a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h @@ -1,57 +1,15 @@ #pragma once +#include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" -#include "Menu/AbstractMenuDumper.h" +#include "Menu/MenuDumpingZoneState.h" -#include - -namespace IW4 +namespace menu { - class MenuDumper : public AbstractMenuDumper + class MenuDumperIW4 final : public AbstractAssetDumper { - static size_t FindStatementClosingParenthesis(const Statement_s* statement, size_t openingParenthesisPosition); - - void WriteStatementNaive(const Statement_s* statement) const; - - void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const; - void WriteStatementOperandFunction(const Statement_s* statement, size_t currentPos) const; - void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const; - void WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const; - void WriteStatement(const Statement_s* statement) const; - void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const; - void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const; - - void WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const; - void WriteUnconditionalScript(const char* script) const; - void WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet); - void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue); - - void WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const; - void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const; - void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const; - void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const; - void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue); - void WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const; - void WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, int floatExpressionCount) const; - void WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const; - - void WriteListBoxProperties(const itemDef_s* item); - void WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const; - void WriteEditFieldProperties(const itemDef_s* item) const; - void WriteMultiValueProperty(const multiDef_s* multiDef) const; - void WriteMultiProperties(const itemDef_s* item) const; - void WriteEnumDvarProperties(const itemDef_s* item) const; - void WriteTickerProperties(const itemDef_s* item) const; - - void WriteItemData(const itemDef_s* item); - void WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount); - - void WriteMenuData(const menuDef_t* menu); - - public: - explicit MenuDumper(std::ostream& stream); - - void WriteFunctionDef(const std::string& functionName, const Statement_s* statement); - void WriteMenu(const menuDef_t* menu); + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; }; -} // namespace IW4 +} // namespace menu diff --git a/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.cpp b/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.cpp new file mode 100644 index 00000000..7946dfe9 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.cpp @@ -0,0 +1,186 @@ +#include "MenuListDumperIW4.h" + +#include "Game/IW4/Menu/MenuDumperIW4.h" +#include "Menu/AbstractMenuWriter.h" +#include "MenuWriterIW4.h" +#include "ObjWriting.h" + +#include +#include +#include +#include + +namespace fs = std::filesystem; + +using namespace IW4; + +namespace +{ + std::vector GetAllUniqueExpressionSupportingData(const MenuList* menuList) + { + std::vector result; + std::set alreadyAddedSupportingData; + + if (menuList->menus == nullptr) + return result; + + for (auto i = 0; i < menuList->menuCount; i++) + { + if (menuList->menus[i] == nullptr) + continue; + + const auto* menu = menuList->menus[i]; + + if (menu->expressionData == nullptr) + continue; + + if (alreadyAddedSupportingData.find(menu->expressionData) == alreadyAddedSupportingData.end()) + { + result.push_back(menu->expressionData); + alreadyAddedSupportingData.emplace(menu->expressionData); + } + } + + return result; + } + + void DumpFunctions(menu::IWriterIW4& menuDumper, const MenuList* menuList) + { + const auto allSupportingData = GetAllUniqueExpressionSupportingData(menuList); + auto functionIndex = 0u; + + assert(allSupportingData.size() <= 1); + + for (const auto* supportingData : allSupportingData) + { + if (supportingData->uifunctions.functions == nullptr) + continue; + + for (auto i = 0; i < supportingData->uifunctions.totalFunctions; i++) + { + const auto* function = supportingData->uifunctions.functions[i]; + if (function != nullptr) + { + std::stringstream ss; + ss << "FUNC_" << functionIndex; + + menuDumper.WriteFunctionDef(ss.str(), function); + } + + functionIndex++; + } + } + } + + void DumpMenus(menu::IWriterIW4& menuDumper, menu::MenuDumpingZoneState* zoneState, const MenuList* menuList) + { + for (auto menuNum = 0; menuNum < menuList->menuCount; menuNum++) + { + const auto* menu = menuList->menus[menuNum]; + + const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(menu); + if (menuDumpingState == zoneState->m_menu_dumping_state_map.end()) + continue; + + // If the menu was embedded directly as menu list write its data in the menu list file + if (menuDumpingState->second.m_alias_menu_list == menuList) + menuDumper.WriteMenu(*menu); + else + menuDumper.IncludeMenu(menuDumpingState->second.m_path); + } + } + + std::string PathForMenu(const std::string& menuListParentPath, const menuDef_t* menu) + { + const auto* menuAssetName = menu->window.name; + + if (!menuAssetName) + return ""; + + if (menuAssetName[0] == ',') + menuAssetName = &menuAssetName[1]; + + std::ostringstream ss; + ss << menuListParentPath << menuAssetName << ".menu"; + + return ss.str(); + } +} // namespace + +namespace menu +{ + void CreateDumpingStateForMenuListIW4(MenuDumpingZoneState* zoneState, const MenuList* menuList) + { + if (menuList->menuCount <= 0 || menuList->menus == nullptr || menuList->name == nullptr) + return; + + const std::string menuListName(menuList->name); + const fs::path p(menuListName); + std::string parentPath; + if (p.has_parent_path()) + parentPath = p.parent_path().string() + "/"; + + for (auto i = 0; i < menuList->menuCount; i++) + { + auto* menu = menuList->menus[i]; + + if (menu == nullptr) + continue; + + auto existingState = zoneState->m_menu_dumping_state_map.find(menu); + if (existingState == zoneState->m_menu_dumping_state_map.end()) + { + auto menuPath = PathForMenu(parentPath, menu); + const auto isTheSameAsMenuList = menuPath == menuListName; + zoneState->CreateMenuDumpingState(menu, std::move(menuPath), isTheSameAsMenuList ? menuList : nullptr); + } + else if (existingState->second.m_alias_menu_list == nullptr) + { + auto menuPath = PathForMenu(parentPath, menu); + const auto isTheSameAsMenuList = menuPath == menuListName; + if (isTheSameAsMenuList) + { + existingState->second.m_alias_menu_list = menuList; + existingState->second.m_path = std::move(menuPath); + } + } + } + } + + bool MenuListDumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void MenuListDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* menuList = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto* zoneState = context.GetZoneAssetDumperState(); + + auto menuWriter = CreateMenuWriterIW4(*assetFile); + + menuWriter->Start(); + + if (!ObjWriting::Configuration.MenuLegacyMode) + DumpFunctions(*menuWriter, menuList); + + DumpMenus(*menuWriter, zoneState, menuList); + + menuWriter->End(); + } + + void MenuListDumperIW4::DumpPool(AssetDumpingContext& context, AssetPool* pool) + { + auto* zoneState = context.GetZoneAssetDumperState(); + + for (auto* asset : *pool) + CreateDumpingStateForMenuListIW4(zoneState, asset->Asset()); + + AbstractAssetDumper::DumpPool(context, pool); + } +} // namespace menu diff --git a/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.h b/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.h new file mode 100644 index 00000000..b9329966 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" +#include "Game/IW4/Menu/MenuDumperIW4.h" +#include "Menu/MenuDumpingZoneState.h" + +namespace menu +{ + void CreateDumpingStateForMenuListIW4(MenuDumpingZoneState* zoneState, const IW4::MenuList* menuList); + + class MenuListDumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + }; +} // namespace menu diff --git a/src/ObjWriting/Game/IW4/Menu/MenuWriterIW4.cpp b/src/ObjWriting/Game/IW4/Menu/MenuWriterIW4.cpp new file mode 100644 index 00000000..b20cec15 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Menu/MenuWriterIW4.cpp @@ -0,0 +1,957 @@ +#include "MenuWriterIW4.h" + +#include "Game/IW4/MenuConstantsIW4.h" +#include "Menu/AbstractMenuWriter.h" +#include "ObjWriting.h" + +#include +#include +#include + +using namespace IW4; + +// Uncomment this macro to skip interpretative expression dumping +// #define DUMP_NAIVE + +#ifdef DUMP_NAIVE +#define DUMP_FUNC WriteStatementNaive +#else +#define DUMP_FUNC WriteStatementSkipInitialUnnecessaryParenthesis +#endif + +namespace +{ + size_t FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition) + { + assert(statement->numEntries >= 0); + assert(openingParenthesisPosition < static_cast(statement->numEntries)); + + const auto statementEnd = static_cast(statement->numEntries); + + // The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though. + // We will pretend it does since the game does sometimes leave out opening parenthesis from the entries. + auto currentParenthesisDepth = 1; + for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++) + { + const auto& expEntry = statement->entries[currentSearchPosition]; + if (expEntry.type != EET_OPERATOR) + continue; + + // Any function means a "left out" left paren + if (expEntry.data.op == OP_LEFTPAREN || expEntry.data.op >= OP_COUNT) + { + currentParenthesisDepth++; + } + else if (expEntry.data.op == OP_RIGHTPAREN) + { + if (currentParenthesisDepth > 0) + currentParenthesisDepth--; + if (currentParenthesisDepth == 0) + return currentSearchPosition; + } + } + + return statementEnd; + } + + class MenuWriter final : public ::menu::AbstractBaseWriter, public menu::IWriterIW4 + { + public: + explicit MenuWriter(std::ostream& stream) + : AbstractBaseWriter(stream) + { + } + + void WriteFunctionDef(const std::string& functionName, const Statement_s* statement) override + { + StartFunctionDefScope(); + + WriteStringProperty("name", functionName); + WriteStatementProperty("value", statement, false); + + EndScope(); + } + + void WriteMenu(const menuDef_t& menu) override + { + StartMenuDefScope(); + + WriteMenuData(&menu); + + EndScope(); + } + + void Start() override + { + AbstractBaseWriter::Start(); + } + + void End() override + { + AbstractBaseWriter::End(); + } + + void IncludeMenu(const std::string& menuPath) const override + { + AbstractBaseWriter::IncludeMenu(menuPath); + } + + private: + void WriteStatementNaive(const Statement_s* statement) const + { + const auto entryCount = static_cast(statement->numEntries); + + const auto missingClosingParenthesis = statement->numEntries > 0 && statement->entries[0].type == EET_OPERATOR + && statement->entries[0].data.op == OP_LEFTPAREN + && FindStatementClosingParenthesis(statement, 0) >= static_cast(statement->numEntries); + + for (auto i = 0uz; i < entryCount; i++) + { + const auto& entry = statement->entries[i]; + if (entry.type == EET_OPERAND) + { + size_t pos = i; + bool discard = false; + WriteStatementOperand(statement, pos, discard); + } + else if (entry.data.op >= EXP_FUNC_STATIC_DVAR_INT && entry.data.op <= EXP_FUNC_STATIC_DVAR_STRING) + { + switch (entry.data.op) + { + case EXP_FUNC_STATIC_DVAR_INT: + m_stream << "dvarint"; + break; + + case EXP_FUNC_STATIC_DVAR_BOOL: + m_stream << "dvarbool"; + break; + + case EXP_FUNC_STATIC_DVAR_FLOAT: + m_stream << "dvarfloat"; + break; + + case EXP_FUNC_STATIC_DVAR_STRING: + m_stream << "dvarstring"; + break; + + default: + break; + } + + // Functions do not have opening parenthesis in the entries. We can just pretend they do though + const auto closingParenPos = FindStatementClosingParenthesis(statement, i); + m_stream << "("; + + if (closingParenPos - i + 1u >= 1u) + { + const auto& staticDvarEntry = statement->entries[i + 1]; + if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT) + { + if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars + && staticDvarEntry.data.operand.internals.intVal >= 0 + && staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars) + { + const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal]; + if (staticDvar && staticDvar->dvarName) + m_stream << staticDvar->dvarName; + } + else + { + m_stream << "#INVALID_DVAR_INDEX"; + } + } + else + { + m_stream << "#INVALID_DVAR_OPERAND"; + } + } + + m_stream << ")"; + i = closingParenPos; + } + else + { + assert(entry.data.op >= 0 && static_cast(entry.data.op) < std::extent_v); + if (entry.data.op >= 0 && static_cast(entry.data.op) < std::extent_v) + m_stream << g_expFunctionNames[entry.data.op]; + if (entry.data.op >= OP_COUNT) + m_stream << "("; + } + } + + if (missingClosingParenthesis) + m_stream << ")"; + } + + void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const + { + const auto& expEntry = statement->entries[currentPos]; + + if (spaceNext && expEntry.data.op != OP_COMMA) + m_stream << " "; + + if (expEntry.data.op == OP_LEFTPAREN) + { + const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); + m_stream << "("; + WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); + m_stream << ")"; + + currentPos = closingParenPos + 1; + spaceNext = true; + } + else if (expEntry.data.op >= EXP_FUNC_STATIC_DVAR_INT && expEntry.data.op <= EXP_FUNC_STATIC_DVAR_STRING) + { + switch (expEntry.data.op) + { + case EXP_FUNC_STATIC_DVAR_INT: + m_stream << "dvarint"; + break; + + case EXP_FUNC_STATIC_DVAR_BOOL: + m_stream << "dvarbool"; + break; + + case EXP_FUNC_STATIC_DVAR_FLOAT: + m_stream << "dvarfloat"; + break; + + case EXP_FUNC_STATIC_DVAR_STRING: + m_stream << "dvarstring"; + break; + + default: + break; + } + + // Functions do not have opening parenthesis in the entries. We can just pretend they do though + const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); + m_stream << "("; + + if (closingParenPos - currentPos + 1 >= 1) + { + const auto& staticDvarEntry = statement->entries[currentPos + 1]; + if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT) + { + if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars + && staticDvarEntry.data.operand.internals.intVal >= 0 + && staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars) + { + const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal]; + if (staticDvar && staticDvar->dvarName) + m_stream << staticDvar->dvarName; + } + else + { + m_stream << "#INVALID_DVAR_INDEX"; + } + } + else + { + m_stream << "#INVALID_DVAR_OPERAND"; + } + } + + m_stream << ")"; + currentPos = closingParenPos + 1; + spaceNext = true; + } + else + { + if (expEntry.data.op >= 0 && static_cast(expEntry.data.op) < std::extent_v) + m_stream << g_expFunctionNames[expEntry.data.op]; + + if (expEntry.data.op >= OP_COUNT) + { + // Functions do not have opening parenthesis in the entries. We can just pretend they do though + const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); + m_stream << "("; + WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); + m_stream << ")"; + currentPos = closingParenPos + 1; + } + else + currentPos++; + + spaceNext = expEntry.data.op != OP_NOT; + } + } + + void WriteStatementOperandFunction(const Statement_s* statement, size_t currentPos) const + { + const auto& operand = statement->entries[currentPos].data.operand; + + if (operand.internals.function == nullptr) + return; + + if (!ObjWriting::Configuration.MenuLegacyMode) + { + int functionIndex = -1; + if (statement->supportingData && statement->supportingData->uifunctions.functions) + { + for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions; + supportingFunctionIndex++) + { + if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function) + { + functionIndex = supportingFunctionIndex; + break; + } + } + } + + if (functionIndex >= 0) + m_stream << "FUNC_" << functionIndex; + else + m_stream << "INVALID_FUNC"; + m_stream << "()"; + } + else + { + m_stream << "("; + WriteStatementSkipInitialUnnecessaryParenthesis(operand.internals.function); + m_stream << ")"; + } + } + + void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const + { + const auto& expEntry = statement->entries[currentPos]; + + if (spaceNext) + m_stream << " "; + + const auto& operand = expEntry.data.operand; + + switch (operand.dataType) + { + case VAL_FLOAT: + m_stream << operand.internals.floatVal; + break; + + case VAL_INT: + m_stream << operand.internals.intVal; + break; + + case VAL_STRING: + WriteEscapedString(operand.internals.stringVal.string); + break; + + case VAL_FUNCTION: + WriteStatementOperandFunction(statement, currentPos); + break; + + default: + break; + } + + currentPos++; + spaceNext = true; + } + + void WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const + { + assert(startOffset <= endOffset); + assert(endOffset <= static_cast(statement->numEntries)); + + auto currentPos = startOffset; + auto spaceNext = false; + while (currentPos < endOffset) + { + const auto& expEntry = statement->entries[currentPos]; + + if (expEntry.type == EET_OPERATOR) + { + WriteStatementOperator(statement, currentPos, spaceNext); + } + else + { + WriteStatementOperand(statement, currentPos, spaceNext); + } + } + } + + void WriteStatement(const Statement_s* statement) const + { + if (statement == nullptr || statement->numEntries < 0) + return; + + WriteStatementEntryRange(statement, 0, static_cast(statement->numEntries)); + } + + void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const + { + if (statementValue == nullptr || statementValue->numEntries < 0) + return; + + const auto statementEnd = static_cast(statementValue->numEntries); + + if (statementValue->numEntries >= 1 && statementValue->entries[0].type == EET_OPERATOR && statementValue->entries[0].data.op == OP_LEFTPAREN) + { + const auto parenthesisEnd = FindStatementClosingParenthesis(statementValue, 0); + + if (parenthesisEnd >= statementEnd) + WriteStatementEntryRange(statementValue, 1, statementEnd); + else if (parenthesisEnd == statementEnd - 1) + WriteStatementEntryRange(statementValue, 1, statementEnd - 1); + else + WriteStatementEntryRange(statementValue, 0, statementEnd); + } + else + { + WriteStatementEntryRange(statementValue, 0, statementEnd); + } + } + + void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const + { + if (statementValue == nullptr || statementValue->numEntries < 0) + return; + + Indent(); + WriteKey(propertyKey); + + if (isBooleanStatement) + { + m_stream << "when("; + DUMP_FUNC(statementValue); + m_stream << ");\n"; + } + else + { + DUMP_FUNC(statementValue); + m_stream << ";\n"; + } + } + + void WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const + { + if (setLocalVarData == nullptr) + return; + + Indent(); + m_stream << setFunction << " " << setLocalVarData->localVarName << " "; + WriteStatement(setLocalVarData->expression); + m_stream << ";\n"; + } + + // #define WRITE_ORIGINAL_SCRIPT + void WriteUnconditionalScript(const char* script) const + { +#ifdef WRITE_ORIGINAL_SCRIPT + Indent(); + m_stream << script << "\n"; + return; +#endif + + const auto tokenList = CreateScriptTokenList(script); + + auto isNewStatement = true; + for (const auto& token : tokenList) + { + if (isNewStatement) + { + if (token == ";") + continue; + + Indent(); + } + + if (token == ";") + { + m_stream << ";\n"; + isNewStatement = true; + continue; + } + + if (!isNewStatement) + m_stream << " "; + else + isNewStatement = false; + + if (DoesTokenNeedQuotationMarks(token)) + m_stream << "\"" << token << "\""; + else + m_stream << token; + } + + if (!isNewStatement) + m_stream << ";\n"; + } + + void WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet) + { + Indent(); + m_stream << "{\n"; + IncIndent(); + + for (auto i = 0; i < eventHandlerSet->eventHandlerCount; i++) + { + const auto* eventHandler = eventHandlerSet->eventHandlers[i]; + if (eventHandler == nullptr) + continue; + + switch (eventHandler->eventType) + { + case EVENT_UNCONDITIONAL: + WriteUnconditionalScript(eventHandler->eventData.unconditionalScript); + break; + + case EVENT_IF: + if (eventHandler->eventData.conditionalScript == nullptr || eventHandler->eventData.conditionalScript->eventExpression == nullptr + || eventHandler->eventData.conditionalScript->eventHandlerSet == nullptr) + { + continue; + } + + Indent(); + m_stream << "if ("; + WriteStatementSkipInitialUnnecessaryParenthesis(eventHandler->eventData.conditionalScript->eventExpression); + m_stream << ")\n"; + WriteMenuEventHandlerSet(eventHandler->eventData.conditionalScript->eventHandlerSet); + break; + + case EVENT_ELSE: + if (eventHandler->eventData.elseScript == nullptr) + continue; + + Indent(); + m_stream << "else\n"; + WriteMenuEventHandlerSet(eventHandler->eventData.elseScript); + break; + + case EVENT_SET_LOCAL_VAR_BOOL: + WriteSetLocalVarData("setLocalVarBool", eventHandler->eventData.setLocalVarData); + break; + + case EVENT_SET_LOCAL_VAR_INT: + WriteSetLocalVarData("setLocalVarInt", eventHandler->eventData.setLocalVarData); + break; + + case EVENT_SET_LOCAL_VAR_FLOAT: + WriteSetLocalVarData("setLocalVarFloat", eventHandler->eventData.setLocalVarData); + break; + + case EVENT_SET_LOCAL_VAR_STRING: + WriteSetLocalVarData("setLocalVarString", eventHandler->eventData.setLocalVarData); + break; + + default: + break; + } + } + + DecIndent(); + Indent(); + m_stream << "}\n"; + } + + void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue) + { + if (eventHandlerSetValue == nullptr) + return; + + Indent(); + m_stream << propertyKey << "\n"; + WriteMenuEventHandlerSet(eventHandlerSetValue); + } + + void WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const + { + Indent(); + WriteKey(propertyKey); + m_stream << rect.x << " " << rect.y << " " << rect.w << " " << rect.h << " " << static_cast(rect.horzAlign) << " " + << static_cast(rect.vertAlign) << "\n"; + } + + void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const + { + if (materialValue == nullptr || materialValue->info.name == nullptr) + return; + + if (materialValue->info.name[0] == ',') + WriteStringProperty(propertyKey, &materialValue->info.name[1]); + else + WriteStringProperty(propertyKey, materialValue->info.name); + } + + void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const + { + if (soundAliasValue == nullptr) + return; + + WriteStringProperty(propertyKey, soundAliasValue->aliasName); + } + + void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const + { + if (!item->decayActive) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << item->fxLetterTime << " " << item->fxDecayStartTime << " " << item->fxDecayDuration << "\n"; + } + + void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue) + { + for (const auto* currentHandler = itemKeyHandlerValue; currentHandler; currentHandler = currentHandler->next) + { + if (currentHandler->key >= '!' && currentHandler->key <= '~' && currentHandler->key != '"') + { + std::ostringstream ss; + ss << "execKey \"" << static_cast(currentHandler->key) << "\""; + WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); + } + else + { + std::ostringstream ss; + ss << "execKeyInt " << currentHandler->key; + WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); + } + } + } + + void WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const + { + if (!value) + return; + + Indent(); + WriteKey(propertyKey); + + const auto tokenList = CreateScriptTokenList(value); + + auto firstToken = true; + m_stream << "{ "; + for (const auto& token : tokenList) + { + if (firstToken) + firstToken = false; + else + m_stream << ";"; + m_stream << "\"" << token << "\""; + } + if (!firstToken) + m_stream << " "; + m_stream << "}\n"; + } + + void WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, int floatExpressionCount) const + { + if (!floatExpressions) + return; + + for (int i = 0; i < floatExpressionCount; i++) + { + const auto& floatExpression = floatExpressions[i]; + + if (floatExpression.target < 0 || floatExpression.target >= ITEM_FLOATEXP_TGT_COUNT) + continue; + + std::string propertyName = std::string("exp ") + floatExpressionTargetBindings[floatExpression.target].name + std::string(" ") + + floatExpressionTargetBindings[floatExpression.target].componentName; + + WriteStatementProperty(propertyName, floatExpression.expression, false); + } + } + + void WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const + { + if (listBox->numColumns <= 0) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << listBox->numColumns << "\n"; + + for (auto col = 0; col < listBox->numColumns; col++) + { + Indent(); + for (auto i = 0u; i < MENU_KEY_SPACING; i++) + m_stream << " "; + + m_stream << listBox->columnInfo[col].pos << " " << listBox->columnInfo[col].width << " " << listBox->columnInfo[col].maxChars << " " + << listBox->columnInfo[col].alignment << "\n"; + } + } + + void WriteListBoxProperties(const itemDef_s* item) + { + if (item->type != ITEM_TYPE_LISTBOX || item->typeData.listBox == nullptr) + return; + + const auto* listBox = item->typeData.listBox; + WriteKeywordProperty("notselectable", listBox->notselectable != 0); + WriteKeywordProperty("noscrollbars", listBox->noScrollBars != 0); + WriteKeywordProperty("usepaging", listBox->usePaging != 0); + WriteFloatProperty("elementwidth", listBox->elementWidth, 0.0f); + WriteFloatProperty("elementheight", listBox->elementHeight, 0.0f); + WriteFloatProperty("feeder", item->special, 0.0f); + WriteIntProperty("elementtype", listBox->elementStyle, 0); + WriteColumnProperty("columns", listBox); + WriteMenuEventHandlerSetProperty("doubleclick", listBox->onDoubleClick); + WriteColorProperty("selectBorder", listBox->selectBorder, COLOR_0000); + WriteMaterialProperty("selectIcon", listBox->selectIcon); + } + + void WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const + { + if (item->dvar == nullptr) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << "\"" << item->dvar << "\" " << editField->defVal << " " << editField->minVal << " " << editField->maxVal << "\n"; + } + + void WriteEditFieldProperties(const itemDef_s* item) const + { + switch (item->type) + { + case ITEM_TYPE_TEXT: + case ITEM_TYPE_EDITFIELD: + case ITEM_TYPE_NUMERICFIELD: + case ITEM_TYPE_SLIDER: + case ITEM_TYPE_YESNO: + case ITEM_TYPE_BIND: + case ITEM_TYPE_VALIDFILEFIELD: + case ITEM_TYPE_DECIMALFIELD: + case ITEM_TYPE_UPREDITFIELD: + case ITEM_TYPE_EMAILFIELD: + case ITEM_TYPE_PASSWORDFIELD: + break; + + default: + return; + } + + if (item->typeData.editField == nullptr) + return; + + const auto* editField = item->typeData.editField; + if (std::fabs(-1.0f - editField->defVal) >= std::numeric_limits::epsilon() + || std::fabs(-1.0f - editField->minVal) >= std::numeric_limits::epsilon() + || std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits::epsilon()) + { + WriteDvarFloatProperty("dvarFloat", item, editField); + } + else + { + WriteStringProperty("dvar", item->dvar); + } + WriteStringProperty("localvar", item->localVar); + WriteIntProperty("maxChars", editField->maxChars, 0); + WriteKeywordProperty("maxCharsGotoNext", editField->maxCharsGotoNext != 0); + WriteIntProperty("maxPaintChars", editField->maxPaintChars, 0); + } + + void WriteMultiValueProperty(const multiDef_s* multiDef) const + { + Indent(); + if (multiDef->strDef) + WriteKey("dvarStrList"); + else + WriteKey("dvarFloatList"); + + m_stream << "{"; + for (auto i = 0; i < multiDef->count; i++) + { + if (multiDef->dvarList[i] == nullptr || multiDef->strDef && multiDef->dvarStr[i] == nullptr) + continue; + + m_stream << " \"" << multiDef->dvarList[i] << "\""; + + if (multiDef->strDef) + m_stream << " \"" << multiDef->dvarStr[i] << "\""; + else + m_stream << " " << multiDef->dvarValue[i] << ""; + } + m_stream << " }\n"; + } + + void WriteMultiProperties(const itemDef_s* item) const + { + if (item->type != ITEM_TYPE_MULTI || item->typeData.multi == nullptr) + return; + + const auto* multiDef = item->typeData.multi; + + if (multiDef->count <= 0) + return; + + WriteStringProperty("dvar", item->dvar); + WriteStringProperty("localvar", item->localVar); + WriteMultiValueProperty(multiDef); + } + + void WriteEnumDvarProperties(const itemDef_s* item) const + { + if (item->type != ITEM_TYPE_DVARENUM) + return; + + WriteStringProperty("dvar", item->dvar); + WriteStringProperty("localvar", item->localVar); + WriteStringProperty("dvarEnumList", item->typeData.enumDvarName); + } + + void WriteTickerProperties(const itemDef_s* item) const + { + if (item->type != ITEM_TYPE_NEWS_TICKER || item->typeData.ticker == nullptr) + return; + + const auto* newsTickerDef = item->typeData.ticker; + WriteIntProperty("spacing", newsTickerDef->spacing, 0); + WriteIntProperty("speed", newsTickerDef->speed, 0); + WriteIntProperty("newsfeed", newsTickerDef->feedId, 0); + } + + void WriteItemData(const itemDef_s* item) + { + WriteStringProperty("name", item->window.name); + WriteStringProperty("text", item->text); + WriteKeywordProperty("textsavegame", item->itemFlags & ITEM_FLAG_SAVE_GAME_INFO); + WriteKeywordProperty("textcinematicsubtitle", item->itemFlags & ITEM_FLAG_CINEMATIC_SUBTITLE); + WriteStringProperty("group", item->window.group); + WriteRectProperty("rect", item->window.rectClient); + WriteIntProperty("style", item->window.style, 0); + WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION); + WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED); + WriteKeywordProperty("horizontalscroll", item->window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL); + WriteIntProperty("type", item->type, ITEM_TYPE_TEXT); + WriteIntProperty("border", item->window.border, 0); + WriteFloatProperty("borderSize", item->window.borderSize, 0.0f); + + if (item->visibleExp) + WriteStatementProperty("visible", item->visibleExp, true); + else if (item->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE) + WriteIntProperty("visible", 1, 0); + + WriteStatementProperty("disabled", item->disabledExp, true); + WriteIntProperty("ownerdraw", item->window.ownerDraw, 0); + WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags); + WriteIntProperty("align", item->alignment, 0); + WriteIntProperty("textalign", item->textAlignMode, 0); + WriteFloatProperty("textalignx", item->textalignx, 0.0f); + WriteFloatProperty("textaligny", item->textaligny, 0.0f); + WriteFloatProperty("textscale", item->textscale, 0.0f); + WriteIntProperty("textstyle", item->textStyle, 0); + WriteIntProperty("textfont", item->fontEnum, 0); + WriteColorProperty("backcolor", item->window.backColor, COLOR_0000); + WriteColorProperty("forecolor", item->window.foreColor, COLOR_1111); + WriteColorProperty("bordercolor", item->window.borderColor, COLOR_0000); + WriteColorProperty("outlinecolor", item->window.outlineColor, COLOR_0000); + WriteColorProperty("disablecolor", item->window.disableColor, COLOR_0000); + WriteColorProperty("glowcolor", item->glowColor, COLOR_0000); + WriteMaterialProperty("background", item->window.background); + WriteMenuEventHandlerSetProperty("onFocus", item->onFocus); + WriteMenuEventHandlerSetProperty("leaveFocus", item->leaveFocus); + WriteMenuEventHandlerSetProperty("mouseEnter", item->mouseEnter); + WriteMenuEventHandlerSetProperty("mouseExit", item->mouseExit); + WriteMenuEventHandlerSetProperty("mouseEnterText", item->mouseEnterText); + WriteMenuEventHandlerSetProperty("mouseExitText", item->mouseExitText); + WriteMenuEventHandlerSetProperty("action", item->action); + WriteMenuEventHandlerSetProperty("accept", item->accept); + // WriteFloatProperty("special", item->special, 0.0f); + WriteSoundAliasProperty("focusSound", item->focusSound); + WriteStringProperty("dvarTest", item->dvarTest); + + if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE) + WriteMultiTokenStringProperty("enableDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE) + WriteMultiTokenStringProperty("disableDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW) + WriteMultiTokenStringProperty("showDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE) + WriteMultiTokenStringProperty("hideDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS) + WriteMultiTokenStringProperty("focusDvar", item->enableDvar); + + WriteItemKeyHandlerProperty(item->onKey); + WriteStatementProperty("exp text", item->textExp, false); + WriteStatementProperty("exp material", item->materialExp, false); + WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount); + WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0); + WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0); + WriteDecodeEffectProperty("decodeEffect", item); + + WriteListBoxProperties(item); + WriteEditFieldProperties(item); + WriteMultiProperties(item); + WriteEnumDvarProperties(item); + WriteTickerProperties(item); + } + + void WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount) + { + for (auto i = 0u; i < itemCount; i++) + { + StartItemDefScope(); + + WriteItemData(itemDefs[i]); + + EndScope(); + } + } + + void WriteMenuData(const menuDef_t* menu) + { + WriteStringProperty("name", menu->window.name); + WriteBoolProperty("fullscreen", menu->fullScreen, false); + WriteKeywordProperty("screenSpace", menu->window.staticFlags & WINDOW_FLAG_SCREEN_SPACE); + WriteKeywordProperty("decoration", menu->window.staticFlags & WINDOW_FLAG_DECORATION); + WriteRectProperty("rect", menu->window.rect); + WriteIntProperty("style", menu->window.style, 0); + WriteIntProperty("border", menu->window.border, 0); + WriteFloatProperty("borderSize", menu->window.borderSize, 0.0f); + WriteColorProperty("backcolor", menu->window.backColor, COLOR_0000); + WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111); + WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000); + WriteColorProperty("focuscolor", menu->focusColor, COLOR_0000); + WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000); + WriteMaterialProperty("background", menu->window.background); + WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0); + WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags); + WriteKeywordProperty("outOfBoundsClick", menu->window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK); + WriteStringProperty("soundLoop", menu->soundName); + WriteKeywordProperty("popup", menu->window.staticFlags & WINDOW_FLAG_POPUP); + WriteFloatProperty("fadeClamp", menu->fadeClamp, 0.0f); + WriteIntProperty("fadeCycle", menu->fadeCycle, 0); + WriteFloatProperty("fadeAmount", menu->fadeAmount, 0.0f); + WriteFloatProperty("fadeInAmount", menu->fadeInAmount, 0.0f); + WriteFloatProperty("blurWorld", menu->blurRadius, 0.0f); + WriteKeywordProperty("legacySplitScreenScale", menu->window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE); + WriteKeywordProperty("hiddenDuringScope", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE); + WriteKeywordProperty("hiddenDuringFlashbang", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG); + WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI); + WriteStringProperty("allowedBinding", menu->allowedBinding); + WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS); + + if (menu->visibleExp) + WriteStatementProperty("visible", menu->visibleExp, true); + else if (menu->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE) + WriteIntProperty("visible", 1, 0); + + WriteStatementProperty("exp rect X", menu->rectXExp, false); + WriteStatementProperty("exp rect Y", menu->rectYExp, false); + WriteStatementProperty("exp rect W", menu->rectWExp, false); + WriteStatementProperty("exp rect H", menu->rectHExp, false); + WriteStatementProperty("exp openSound", menu->openSoundExp, false); + WriteStatementProperty("exp closeSound", menu->closeSoundExp, false); + WriteMenuEventHandlerSetProperty("onOpen", menu->onOpen); + WriteMenuEventHandlerSetProperty("onClose", menu->onClose); + WriteMenuEventHandlerSetProperty("onRequestClose", menu->onCloseRequest); + WriteMenuEventHandlerSetProperty("onESC", menu->onESC); + WriteItemKeyHandlerProperty(menu->onKey); + WriteItemDefs(menu->items, menu->itemCount); + } + }; +} // namespace + +namespace menu +{ + std::unique_ptr CreateMenuWriterIW4(std::ostream& stream) + { + return std::make_unique(stream); + } +} // namespace menu diff --git a/src/ObjWriting/Game/IW4/Menu/MenuWriterIW4.h b/src/ObjWriting/Game/IW4/Menu/MenuWriterIW4.h new file mode 100644 index 00000000..3988bbb3 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Menu/MenuWriterIW4.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Game/IW4/IW4.h" +#include "Menu/IMenuWriter.h" + +#include +#include + +namespace menu +{ + class IWriterIW4 : public IWriter + { + public: + virtual void WriteFunctionDef(const std::string& functionName, const IW4::Statement_s* statement) = 0; + virtual void WriteMenu(const IW4::menuDef_t& menu) = 0; + }; + + std::unique_ptr CreateMenuWriterIW4(std::ostream& stream); +} // namespace menu diff --git a/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp b/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp index 0516a4af..b749a985 100644 --- a/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp +++ b/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp @@ -1,29 +1,30 @@ #include "ObjWriterIW4.h" -#include "AssetDumpers/AssetDumperAddonMapEnts.h" -#include "AssetDumpers/AssetDumperGfxImage.h" -#include "AssetDumpers/AssetDumperGfxLightDef.h" -#include "AssetDumpers/AssetDumperLeaderboardDef.h" -#include "AssetDumpers/AssetDumperLoadedSound.h" -#include "AssetDumpers/AssetDumperLocalizeEntry.h" -#include "AssetDumpers/AssetDumperMenuDef.h" -#include "AssetDumpers/AssetDumperMenuList.h" -#include "AssetDumpers/AssetDumperPhysCollmap.h" -#include "AssetDumpers/AssetDumperPhysPreset.h" -#include "AssetDumpers/AssetDumperPixelShader.h" -#include "AssetDumpers/AssetDumperRawFile.h" -#include "AssetDumpers/AssetDumperSndCurve.h" -#include "AssetDumpers/AssetDumperStringTable.h" -#include "AssetDumpers/AssetDumperStructuredDataDefSet.h" -#include "AssetDumpers/AssetDumperTechniqueSet.h" -#include "AssetDumpers/AssetDumperTracer.h" -#include "AssetDumpers/AssetDumperVehicle.h" -#include "AssetDumpers/AssetDumperVertexShader.h" -#include "AssetDumpers/AssetDumperWeapon.h" -#include "AssetDumpers/AssetDumperXModel.h" #include "Game/IW4/GameAssetPoolIW4.h" -#include "Material/DumperMaterialIW4.h" +#include "Game/IW4/Material/MaterialJsonDumperIW4.h" +#include "Game/IW4/XModel/XModelDumperIW4.h" +#include "Image/ImageDumperIW4.h" +#include "Leaderboard/LeaderboardJsonDumperIW4.h" +#include "LightDef/LightDefDumperIW4.h" +#include "Localize/LocalizeDumperIW4.h" +#include "Maps/AddonMapEntsDumperIW4.h" +#include "Material/MaterialDecompilingDumperIW4.h" +#include "Menu/MenuDumperIW4.h" +#include "Menu/MenuListDumperIW4.h" #include "ObjWriting.h" +#include "PhysCollmap/PhysCollmapDumperIW4.h" +#include "PhysPreset/PhysPresetInfoStringDumperIW4.h" +#include "RawFile/RawFileDumperIW4.h" +#include "Shader/PixelShaderDumperIW4.h" +#include "Shader/VertexShaderDumperIW4.h" +#include "Sound/LoadedSoundDumperIW4.h" +#include "Sound/SndCurveDumperIW4.h" +#include "StringTable/StringTableDumperIW4.h" +#include "StructuredDataDef/StructuredDataDefDumperIW4.h" +#include "Techset/TechsetDumperIW4.h" +#include "Tracer/TracerDumperIW4.h" +#include "Vehicle/VehicleDumperIW4.h" +#include "Weapon/WeaponDumperIW4.h" using namespace IW4; @@ -38,18 +39,21 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const const auto* assetPools = dynamic_cast(context.m_zone.m_pools.get()); - DUMP_ASSET_POOL(AssetDumperPhysPreset, m_phys_preset, ASSET_TYPE_PHYSPRESET) - DUMP_ASSET_POOL(AssetDumperPhysCollmap, m_phys_collmap, ASSET_TYPE_PHYSCOLLMAP) + DUMP_ASSET_POOL(phys_preset::InfoStringDumperIW4, m_phys_preset, ASSET_TYPE_PHYSPRESET) + DUMP_ASSET_POOL(phys_collmap::DumperIW4, m_phys_collmap, ASSET_TYPE_PHYSCOLLMAP) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) - DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) - DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) - DUMP_ASSET_POOL(AssetDumperPixelShader, m_material_pixel_shader, ASSET_TYPE_PIXELSHADER) - DUMP_ASSET_POOL(AssetDumperVertexShader, m_material_vertex_shader, ASSET_TYPE_VERTEXSHADER) - DUMP_ASSET_POOL(AssetDumperTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) - DUMP_ASSET_POOL(AssetDumperGfxImage, m_image, ASSET_TYPE_IMAGE) + DUMP_ASSET_POOL(xmodel::DumperIW4, m_xmodel, ASSET_TYPE_XMODEL) + DUMP_ASSET_POOL(material::JsonDumperIW4, m_material, ASSET_TYPE_MATERIAL) +#ifdef EXPERIMENTAL_MATERIAL_COMPILATION + DUMP_ASSET_POOL(material::DecompilingGdtDumperIW4, m_material, ASSET_TYPE_MATERIAL) +#endif + DUMP_ASSET_POOL(shader::PixelShaderDumperIW4, m_material_pixel_shader, ASSET_TYPE_PIXELSHADER) + DUMP_ASSET_POOL(shader::VertexShaderDumperIW4, m_material_vertex_shader, ASSET_TYPE_VERTEXSHADER) + DUMP_ASSET_POOL(techset::DumperIW4, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) + DUMP_ASSET_POOL(image::DumperIW4, m_image, ASSET_TYPE_IMAGE) // DUMP_ASSET_POOL(AssetDumpersnd_alias_list_t, m_sound, ASSET_TYPE_SOUND) - DUMP_ASSET_POOL(AssetDumperSndCurve, m_sound_curve, ASSET_TYPE_SOUND_CURVE) - DUMP_ASSET_POOL(AssetDumperLoadedSound, m_loaded_sound, ASSET_TYPE_LOADED_SOUND) + DUMP_ASSET_POOL(sound_curve::DumperIW4, m_sound_curve, ASSET_TYPE_SOUND_CURVE) + DUMP_ASSET_POOL(sound::LoadedSoundDumperIW4, m_loaded_sound, ASSET_TYPE_LOADED_SOUND) // DUMP_ASSET_POOL(AssetDumperClipMap, m_clip_map, ASSET_TYPE_CLIPMAP_MP) // DUMP_ASSET_POOL(AssetDumperComWorld, m_com_world, ASSET_TYPE_COMWORLD) // DUMP_ASSET_POOL(AssetDumperGameWorldSp, m_game_world_sp, ASSET_TYPE_GAMEWORLD_SP) @@ -57,22 +61,22 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const // DUMP_ASSET_POOL(AssetDumperMapEnts, m_map_ents, ASSET_TYPE_MAP_ENTS) // DUMP_ASSET_POOL(AssetDumperFxWorld, m_fx_world, ASSET_TYPE_FXWORLD) // DUMP_ASSET_POOL(AssetDumperGfxWorld, m_gfx_world, ASSET_TYPE_GFXWORLD) - DUMP_ASSET_POOL(AssetDumperGfxLightDef, m_gfx_light_def, ASSET_TYPE_LIGHT_DEF) + DUMP_ASSET_POOL(light_def::DumperIW4, m_gfx_light_def, ASSET_TYPE_LIGHT_DEF) // DUMP_ASSET_POOL(AssetDumperFont_s, m_font, ASSET_TYPE_FONT) - DUMP_ASSET_POOL(AssetDumperMenuList, m_menu_list, ASSET_TYPE_MENULIST) - DUMP_ASSET_POOL(AssetDumperMenuDef, m_menu_def, ASSET_TYPE_MENU) - DUMP_ASSET_POOL(AssetDumperLocalizeEntry, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) - DUMP_ASSET_POOL(AssetDumperWeapon, m_weapon, ASSET_TYPE_WEAPON) + DUMP_ASSET_POOL(menu::MenuListDumperIW4, m_menu_list, ASSET_TYPE_MENULIST) + DUMP_ASSET_POOL(menu::MenuDumperIW4, m_menu_def, ASSET_TYPE_MENU) + DUMP_ASSET_POOL(localize::DumperIW4, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) + DUMP_ASSET_POOL(weapon::DumperIW4, m_weapon, ASSET_TYPE_WEAPON) // DUMP_ASSET_POOL(AssetDumperSndDriverGlobals, m_snd_driver_globals, ASSET_TYPE_SNDDRIVER_GLOBALS) // DUMP_ASSET_POOL(AssetDumperFxEffectDef, m_fx, ASSET_TYPE_FX) // DUMP_ASSET_POOL(AssetDumperFxImpactTable, m_fx_impact_table, ASSET_TYPE_IMPACT_FX) - DUMP_ASSET_POOL(AssetDumperRawFile, m_raw_file, ASSET_TYPE_RAWFILE) - DUMP_ASSET_POOL(AssetDumperStringTable, m_string_table, ASSET_TYPE_STRINGTABLE) - DUMP_ASSET_POOL(AssetDumperLeaderboardDef, m_leaderboard, ASSET_TYPE_LEADERBOARD) - DUMP_ASSET_POOL(AssetDumperStructuredDataDefSet, m_structed_data_def_set, ASSET_TYPE_STRUCTURED_DATA_DEF) - DUMP_ASSET_POOL(AssetDumperTracer, m_tracer, ASSET_TYPE_TRACER) - DUMP_ASSET_POOL(AssetDumperVehicle, m_vehicle, ASSET_TYPE_VEHICLE) - DUMP_ASSET_POOL(AssetDumperAddonMapEnts, m_addon_map_ents, ASSET_TYPE_ADDON_MAP_ENTS) + DUMP_ASSET_POOL(raw_file::DumperIW4, m_raw_file, ASSET_TYPE_RAWFILE) + DUMP_ASSET_POOL(string_table::DumperIW4, m_string_table, ASSET_TYPE_STRINGTABLE) + DUMP_ASSET_POOL(leaderboard::JsonDumperIW4, m_leaderboard, ASSET_TYPE_LEADERBOARD) + DUMP_ASSET_POOL(structured_data_def::DumperIW4, m_structed_data_def_set, ASSET_TYPE_STRUCTURED_DATA_DEF) + DUMP_ASSET_POOL(tracer::DumperIW4, m_tracer, ASSET_TYPE_TRACER) + DUMP_ASSET_POOL(vehicle::DumperIW4, m_vehicle, ASSET_TYPE_VEHICLE) + DUMP_ASSET_POOL(addon_map_ents::DumperIW4, m_addon_map_ents, ASSET_TYPE_ADDON_MAP_ENTS) return true; diff --git a/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.cpp b/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.cpp new file mode 100644 index 00000000..523c64c8 --- /dev/null +++ b/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.cpp @@ -0,0 +1,82 @@ +#include "PhysCollmapDumperIW4.h" + +#include "Dumping/MapFile/MapFileDumper.h" +#include "PhysCollmap/PhysCollmapCommon.h" + +#include +#include + +using namespace IW4; + +namespace phys_collmap +{ + bool DumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* physCollmap = asset->Asset(); + const auto assetFile = context.OpenAssetFile(phys_collmap::GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + MapFileDumper mapFileDumper(*assetFile); + mapFileDumper.Init(); + + if (physCollmap->count <= 0 || physCollmap->geoms == nullptr) + return; + + mapFileDumper.BeginEntity(); + + mapFileDumper.WriteKeyValue("classname", "worldspawn"); + + for (auto i = 0u; i < physCollmap->count; i++) + { + const auto& geom = physCollmap->geoms[i]; + mapFileDumper.BeginBrush(); + + switch (geom.type) + { + case PHYS_GEOM_NONE: + // TODO: Dump BrushWrapper (probably GJK related) + mapFileDumper.WriteComment("TODO: Brush data"); + break; + case PHYS_GEOM_BOX: + mapFileDumper.WritePhysicsBox({ + {geom.bounds.midPoint.v[0], geom.bounds.midPoint.v[1], geom.bounds.midPoint.v[2]}, + {geom.bounds.halfSize.v[0], geom.bounds.halfSize.v[1], geom.bounds.halfSize.v[2]}, + {geom.orientation[0][0], geom.orientation[0][1], geom.orientation[0][2] }, + {geom.orientation[1][0], geom.orientation[1][1], geom.orientation[1][2] }, + {geom.orientation[2][0], geom.orientation[2][1], geom.orientation[2][2] } + }); + break; + + case PHYS_GEOM_CYLINDER: + mapFileDumper.WritePhysicsCylinder({ + {geom.bounds.midPoint.v[0], geom.bounds.midPoint.v[1], geom.bounds.midPoint.v[2]}, + geom.bounds.halfSize.v[0], + geom.bounds.halfSize.v[2] * 2, + {geom.orientation[0][0], geom.orientation[0][1], geom.orientation[0][2] } + }); + break; + + case PHYS_GEOM_BRUSHMODEL: + case PHYS_GEOM_BRUSH: + case PHYS_GEOM_COLLMAP: + case PHYS_GEOM_CAPSULE: + case PHYS_GEOM_GLASS: + default: + // These do not seem to appear inside any collmap assets + assert(false); + break; + } + + mapFileDumper.EndBrush(); + } + + mapFileDumper.EndEntity(); + } +} // namespace phys_collmap diff --git a/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.h b/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.h new file mode 100644 index 00000000..4dbf68d6 --- /dev/null +++ b/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace phys_collmap +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace phys_collmap diff --git a/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.cpp b/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.cpp new file mode 100644 index 00000000..41ac1f64 --- /dev/null +++ b/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.cpp @@ -0,0 +1,111 @@ +#include "PhysPresetInfoStringDumperIW4.h" + +#include "Game/IW4/InfoString/InfoStringFromStructConverter.h" +#include "Game/IW4/ObjConstantsIW4.h" +#include "Game/IW4/PhysPreset/PhysPresetFields.h" +#include "PhysPreset/PhysPresetCommon.h" + +#include +#include +#include +#include + +using namespace IW4; + +namespace +{ + class InfoStringFromPhysPresetConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + assert(false); + } + + public: + InfoStringFromPhysPresetConverter(const PhysPresetInfo* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + void CopyToPhysPresetInfo(const PhysPreset* physPreset, PhysPresetInfo* physPresetInfo) + { + physPresetInfo->mass = std::clamp(physPreset->mass * 1000.0f, 1.0f, 2000.0f); + physPresetInfo->bounce = physPreset->bounce; + + if (std::isinf(physPreset->friction)) + { + physPresetInfo->isFrictionInfinity = 1; + physPresetInfo->friction = 0; + } + else + { + physPresetInfo->isFrictionInfinity = 0; + physPresetInfo->friction = physPreset->friction; + } + + physPresetInfo->bulletForceScale = physPreset->bulletForceScale; + physPresetInfo->explosiveForceScale = physPreset->explosiveForceScale; + physPresetInfo->sndAliasPrefix = physPreset->sndAliasPrefix; + physPresetInfo->piecesSpreadFraction = physPreset->piecesSpreadFraction; + physPresetInfo->piecesUpwardVelocity = physPreset->piecesUpwardVelocity; + physPresetInfo->tempDefaultToCylinder = physPreset->tempDefaultToCylinder ? 1 : 0; + physPresetInfo->perSurfaceSndAlias = physPreset->perSurfaceSndAlias ? 1 : 0; + } + + InfoString CreateInfoString(XAssetInfo* asset) + { + auto* physPresetInfo = new PhysPresetInfo; + CopyToPhysPresetInfo(asset->Asset(), physPresetInfo); + + InfoStringFromPhysPresetConverter converter(physPresetInfo, + phys_preset_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace phys_preset +{ + bool InfoStringDumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void InfoStringDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_PHYS_PRESET); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace phys_preset diff --git a/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.h b/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.h new file mode 100644 index 00000000..4651c1e0 --- /dev/null +++ b/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" +#include "InfoString/InfoString.h" + +namespace phys_preset +{ + class InfoStringDumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace phys_preset diff --git a/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.cpp b/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.cpp new file mode 100644 index 00000000..a1e9a045 --- /dev/null +++ b/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.cpp @@ -0,0 +1,72 @@ +#include "RawFileDumperIW4.h" + +#include "Utils/Logging/Log.h" + +#include +#include +#include + +using namespace IW4; + +namespace raw_file +{ + bool DumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* rawFile = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto& stream = *assetFile; + if (rawFile->compressedLen > 0) + { + 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->data.compressedBuffer); + zs.avail_in = rawFile->compressedLen; + + Bytef buffer[0x1000]; + + while (zs.avail_in > 0) + { + zs.next_out = buffer; + zs.avail_out = sizeof(buffer); + ret = inflate(&zs, Z_SYNC_FLUSH); + + if (ret < 0) + { + con::error("Inflate failed when attempting to dump rawfile '{}'", rawFile->name); + inflateEnd(&zs); + return; + } + + stream.write(reinterpret_cast(buffer), sizeof(buffer) - zs.avail_out); + } + + inflateEnd(&zs); + } + else if (rawFile->len > 0) + { + stream.write(rawFile->data.buffer, rawFile->len); + } + } +} // namespace raw_file diff --git a/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.h b/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.h new file mode 100644 index 00000000..9b8966ea --- /dev/null +++ b/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace raw_file +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace raw_file diff --git a/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.cpp b/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.cpp new file mode 100644 index 00000000..318c9a68 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.cpp @@ -0,0 +1,25 @@ +#include "PixelShaderDumperIW4.h" + +#include "Shader/ShaderCommon.h" + +using namespace IW4; + +namespace shader +{ + bool PixelShaderDumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void PixelShaderDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* pixelShader = asset->Asset(); + const auto shaderFile = context.OpenAssetFile(shader::GetFileNameForPixelShaderAssetName(asset->m_name)); + + if (!shaderFile) + return; + + shaderFile->write(reinterpret_cast(pixelShader->prog.loadDef.program), + static_cast(pixelShader->prog.loadDef.programSize) * 4u); + } +} // namespace shader diff --git a/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.h b/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.h new file mode 100644 index 00000000..01404d0a --- /dev/null +++ b/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace shader +{ + class PixelShaderDumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace shader diff --git a/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.cpp b/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.cpp new file mode 100644 index 00000000..c3954562 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.cpp @@ -0,0 +1,25 @@ +#include "VertexShaderDumperIW4.h" + +#include "Shader/ShaderCommon.h" + +using namespace IW4; + +namespace shader +{ + bool VertexShaderDumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void VertexShaderDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* vertexShader = asset->Asset(); + const auto shaderFile = context.OpenAssetFile(shader::GetFileNameForVertexShaderAssetName(asset->m_name)); + + if (!shaderFile) + return; + + shaderFile->write(reinterpret_cast(vertexShader->prog.loadDef.program), + static_cast(vertexShader->prog.loadDef.programSize) * 4u); + } +} // namespace shader diff --git a/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.h b/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.h new file mode 100644 index 00000000..1b722461 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace shader +{ + class VertexShaderDumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace shader diff --git a/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.cpp b/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.cpp new file mode 100644 index 00000000..20e34703 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.cpp @@ -0,0 +1,53 @@ +#include "LoadedSoundDumperIW4.h" + +#include "Sound/WavTypes.h" +#include "Sound/WavWriter.h" +#include "Utils/Logging/Log.h" + +#include + +using namespace IW4; + +namespace +{ + void DumpWavPcm(const LoadedSound* asset, std::ostream& stream) + { + const WavWriter writer(stream); + + const WavMetaData metaData{.channelCount = static_cast(asset->sound.info.channels), + .samplesPerSec = static_cast(asset->sound.info.rate), + .bitsPerSample = static_cast(asset->sound.info.bits)}; + + writer.WritePcmHeader(metaData, asset->sound.info.data_len); + writer.WritePcmData(asset->sound.data, asset->sound.info.data_len); + } +} // namespace + +namespace sound +{ + bool LoadedSoundDumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void LoadedSoundDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* loadedSound = asset->Asset(); + const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + switch (static_cast(loadedSound->sound.info.format)) + { + case WavFormat::PCM: + DumpWavPcm(loadedSound, stream); + break; + + default: + con::error("Unknown format {} for loaded sound: {}", loadedSound->sound.info.format, loadedSound->name); + break; + } + } +} // namespace sound diff --git a/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.h b/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.h new file mode 100644 index 00000000..7256bc88 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace sound +{ + class LoadedSoundDumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace sound diff --git a/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.cpp b/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.cpp new file mode 100644 index 00000000..b7e00629 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.cpp @@ -0,0 +1,34 @@ +#include "SndCurveDumperIW4.h" + +#include "Sound/SndCurveDumper.h" +#include "Sound/SoundCurveCommon.h" + +#include + +using namespace IW4; + +namespace sound_curve +{ + bool DumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* sndCurve = asset->Asset(); + + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(sndCurve->filename)); + + if (!assetFile) + return; + + SndCurveDumper dumper(*assetFile); + + const auto knotCount = std::min(static_cast(sndCurve->knotCount), std::extent_v); + dumper.Init(knotCount); + + for (auto i = 0u; i < knotCount; i++) + dumper.WriteKnot(sndCurve->knots[i][0], sndCurve->knots[i][1]); + } +} // namespace sound_curve diff --git a/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.h b/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.h new file mode 100644 index 00000000..241b7a79 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace sound_curve +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace sound_curve diff --git a/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.cpp b/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.cpp new file mode 100644 index 00000000..784164e2 --- /dev/null +++ b/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.cpp @@ -0,0 +1,42 @@ +#include "StringTableDumperIW4.h" + +#include "Csv/CsvStream.h" + +using namespace IW4; + +namespace string_table +{ + bool DumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* stringTable = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + CsvOutputStream csv(*assetFile); + + for (auto row = 0; row < stringTable->rowCount; row++) + { + for (auto column = 0; column < stringTable->columnCount; column++) + { + const auto* cell = &stringTable->values[column + row * stringTable->columnCount]; + if (cell->string != nullptr) + { + csv.WriteColumn(cell->string); + } + else + { + csv.WriteColumn(""); + } + } + + csv.NextRow(); + } + } +} // namespace string_table diff --git a/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.h b/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.h new file mode 100644 index 00000000..93f1af2b --- /dev/null +++ b/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace string_table +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace string_table diff --git a/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.cpp b/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.cpp new file mode 100644 index 00000000..2e5cf317 --- /dev/null +++ b/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.cpp @@ -0,0 +1,208 @@ +#include "StructuredDataDefDumperIW4.h" + +#include "StructuredDataDef/StructuredDataDefDumper.h" + +#include +#include +#include + +using namespace IW4; +using namespace std::string_literals; + +namespace +{ + CommonStructuredDataType ConvertType(const CommonStructuredDataDef* def, const StructuredDataType in) + { + CommonStructuredDataType out; + + switch (in.type) + { + case DATA_INT: + out.m_category = CommonStructuredDataTypeCategory::INT; + break; + case DATA_BYTE: + out.m_category = CommonStructuredDataTypeCategory::BYTE; + break; + case DATA_BOOL: + out.m_category = CommonStructuredDataTypeCategory::BOOL; + break; + case DATA_FLOAT: + out.m_category = CommonStructuredDataTypeCategory::FLOAT; + break; + case DATA_SHORT: + out.m_category = CommonStructuredDataTypeCategory::SHORT; + break; + case DATA_STRING: + out.m_category = CommonStructuredDataTypeCategory::STRING; + out.m_info.string_length = in.u.stringDataLength; + break; + case DATA_ENUM: + assert(!def->m_enums.empty()); + out.m_category = CommonStructuredDataTypeCategory::ENUM; + out.m_info.type_index = std::max(std::min(static_cast(in.u.enumIndex), def->m_enums.size() - 1uz), 0uz); + break; + case DATA_STRUCT: + assert(!def->m_structs.empty()); + out.m_category = CommonStructuredDataTypeCategory::STRUCT; + out.m_info.type_index = std::max(std::min(static_cast(in.u.structIndex), def->m_structs.size() - 1uz), 0uz); + break; + case DATA_INDEXED_ARRAY: + assert(!def->m_indexed_arrays.empty()); + out.m_category = CommonStructuredDataTypeCategory::INDEXED_ARRAY; + out.m_info.type_index = std::max(std::min(static_cast(in.u.indexedArrayIndex), def->m_indexed_arrays.size() - 1uz), 0uz); + break; + case DATA_ENUM_ARRAY: + assert(!def->m_enumed_arrays.empty()); + out.m_category = CommonStructuredDataTypeCategory::ENUM_ARRAY; + out.m_info.type_index = std::max(std::min(static_cast(in.u.enumedArrayIndex), def->m_enumed_arrays.size() - 1uz), 0uz); + break; + case DATA_COUNT: + default: + assert(false); + break; + } + + return out; + } + + void ConvertEnum(CommonStructuredDataEnum* out, const StructuredDataEnum* in, const size_t enumIndex) + { + out->m_name = "ENUM_" + std::to_string(enumIndex); + + if (in->reservedEntryCount > 0 && in->reservedEntryCount != in->entryCount) + out->m_reserved_entry_count = std::max(in->reservedEntryCount, 0); + else + out->m_reserved_entry_count = -1; + + out->m_entries.resize(static_cast(std::max(in->entryCount, 0))); + for (auto i = 0u; i < out->m_entries.size(); i++) + { + auto& outEntry = out->m_entries[i]; + const auto& inEntry = in->entries[i]; + + outEntry.m_name = std::string(inEntry.string); + outEntry.m_value = inEntry.index; + } + + out->SortEntriesByOffset(); + } + + void ConvertStruct(const CommonStructuredDataDef* def, + const StructuredDataDef* gameDef, + CommonStructuredDataStruct* out, + const StructuredDataStruct* in, + const size_t structIndex) + { + if (gameDef->rootType.type == DATA_STRUCT && structIndex == static_cast(gameDef->rootType.u.structIndex)) + { + out->m_name = "root"; + out->m_size_in_byte = gameDef->size; + } + else + { + out->m_name = "STRUCT_" + std::to_string(structIndex); + out->m_size_in_byte = static_cast(std::max(in->size, 0)); + } + + out->m_bit_offset = in->bitOffset; + + out->m_properties.resize(static_cast(std::max(in->propertyCount, 0))); + for (auto i = 0u; i < out->m_properties.size(); i++) + { + auto& outProperty = out->m_properties[i]; + const auto& inProperty = in->properties[i]; + + outProperty.m_name = std::string(inProperty.name); + outProperty.m_type = ConvertType(def, inProperty.type); + + if (inProperty.type.type == DATA_BOOL) + outProperty.m_offset_in_bits = inProperty.offset; + else + outProperty.m_offset_in_bits = inProperty.offset * 8; + } + + out->SortPropertiesByOffset(); + } + + void ConvertIndexedArray(const CommonStructuredDataDef* def, CommonStructuredDataIndexedArray* out, const StructuredDataIndexedArray* in) + { + out->m_element_count = static_cast(std::max(in->arraySize, 0)); + out->m_element_size_in_bits = in->elementType.type == DATA_BOOL ? 1 : in->elementSize * 8; + out->m_array_type = ConvertType(def, in->elementType); + } + + void ConvertEnumedArray(const CommonStructuredDataDef* def, CommonStructuredDataEnumedArray* out, const StructuredDataEnumedArray* in) + { + assert(!def->m_enums.empty()); + out->m_element_size_in_bits = in->elementType.type == DATA_BOOL ? 1 : in->elementSize * 8; + out->m_array_type = ConvertType(def, in->elementType); + out->m_enum_index = std::max(std::min(static_cast(in->enumIndex), def->m_enums.size() - 1uz), 0uz); + + if (def->m_enums.empty()) + { + assert(false); + return; + } + + out->m_element_count = def->m_enums[out->m_enum_index]->ElementCount(); + } + + std::unique_ptr ConvertDef(const StructuredDataDef* in) + { + auto out = std::make_unique(); + + out->m_version = in->version; + out->m_checksum = in->formatChecksum; + out->m_size_in_byte = in->size; + + out->m_enums.resize(static_cast(std::max(in->enumCount, 0))); + out->m_structs.resize(static_cast(std::max(in->structCount, 0))); + out->m_indexed_arrays.resize(static_cast(std::max(in->indexedArrayCount, 0))); + out->m_enumed_arrays.resize(static_cast(std::max(in->enumedArrayCount, 0))); + + for (auto i = 0u; i < out->m_enums.size(); i++) + { + auto _enum = std::make_unique(); + ConvertEnum(_enum.get(), &in->enums[i], i); + out->m_enums[i] = std::move(_enum); + } + for (auto i = 0u; i < out->m_structs.size(); i++) + { + auto _struct = std::make_unique(); + ConvertStruct(out.get(), in, _struct.get(), &in->structs[i], i); + out->m_structs[i] = std::move(_struct); + } + for (auto i = 0u; i < out->m_indexed_arrays.size(); i++) + ConvertIndexedArray(out.get(), &out->m_indexed_arrays[i], &in->indexedArrays[i]); + for (auto i = 0u; i < out->m_enumed_arrays.size(); i++) + ConvertEnumedArray(out.get(), &out->m_enumed_arrays[i], &in->enumedArrays[i]); + + out->m_root_type = ConvertType(out.get(), in->rootType); + + return out; + } +} // namespace + +namespace structured_data_def +{ + bool DumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* set = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile || set->defs == nullptr) + return; + + StructuredDataDefDumper dumper(*assetFile); + for (auto i = 0u; i < set->defCount; i++) + { + const auto def = ConvertDef(&set->defs[i]); + dumper.DumpDef(*def); + } + } +} // namespace structured_data_def diff --git a/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.h b/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.h new file mode 100644 index 00000000..e03d8c61 --- /dev/null +++ b/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" +#include "StructuredDataDef/CommonStructuredDataDef.h" + +#include + +namespace structured_data_def +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace structured_data_def diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTechniqueSet.cpp b/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.cpp similarity index 90% rename from src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTechniqueSet.cpp rename to src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.cpp index 101d323d..f8eb6e90 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTechniqueSet.cpp +++ b/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.cpp @@ -1,12 +1,15 @@ -#include "AssetDumperTechniqueSet.h" +#include "TechsetDumperIW4.h" #include "Dumping/AbstractTextDumper.h" #include "Game/IW4/TechsetConstantsIW4.h" #include "Pool/GlobalAssetPool.h" #include "Shader/D3D9ShaderAnalyser.h" +#include "Techset/TechsetCommon.h" +#include "Utils/Logging/Log.h" #include #include +#include #include #include #include @@ -265,8 +268,8 @@ namespace IW4 { // Cannot dump when shader is referenced due to unknown constant names and unknown version Indent(); - std::cerr << "Cannot dump vertex shader " << &vertexShader->name[1] << " due to being a referenced asset\n"; - m_stream << "// Cannot dump vertex shader " << &vertexShader->name[1] << " due to being a referenced asset\n"; + con::error("Cannot dump vertex shader {} due to being a referenced asset", &vertexShader->name[1]); + m_stream << std::format("// Cannot dump vertex shader {} due to being a referenced asset\n", &vertexShader->name[1]); return; } vertexShader = loadedVertexShaderFromOtherZone->Asset(); @@ -320,8 +323,8 @@ namespace IW4 { // Cannot dump when shader is referenced due to unknown constant names and unknown version Indent(); - std::cerr << "Cannot dump pixel shader " << &pixelShader->name[1] << " due to being a referenced asset\n"; - m_stream << "// Cannot dump pixel shader " << &pixelShader->name[1] << " due to being a referenced asset\n"; + con::error("Cannot dump pixel shader {} due to being a referenced asset", &pixelShader->name[1]); + m_stream << std::format("// Cannot dump pixel shader {} due to being a referenced asset\n", &pixelShader->name[1]); return; } pixelShader = loadedPixelShaderFromOtherZone->Asset(); @@ -392,8 +395,8 @@ namespace IW4 { // Cannot dump when shader is referenced due to unknown constant names and unknown version Indent(); - std::cerr << "Cannot dump vertex decl " << &vertexDecl->name[1] << " due to being a referenced asset\n"; - m_stream << "// Cannot dump vertex decl " << &vertexDecl->name[1] << " due to being a referenced asset\n"; + con::error("Cannot dump vertex decl {} due to being a referenced asset", &vertexDecl->name[1]); + m_stream << std::format("// Cannot dump vertex decl {} due to being a referenced asset\n", &vertexDecl->name[1]); return; } vertexDecl = loadedVertexDeclFromOtherZone->Asset(); @@ -531,48 +534,37 @@ namespace IW4 }; } // namespace IW4 -std::string AssetDumperTechniqueSet::GetTechniqueFileName(const MaterialTechnique* technique) +namespace techset { - std::ostringstream ss; - ss << "techniques/" << technique->name << ".tech"; - return ss.str(); -} - -std::string AssetDumperTechniqueSet::GetTechsetFileName(const MaterialTechniqueSet* techset) -{ - std::ostringstream ss; - ss << "techsets/" << techset->name << ".techset"; - return ss.str(); -} - -bool AssetDumperTechniqueSet::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperTechniqueSet::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* techset = asset->Asset(); - - const auto techsetFile = context.OpenAssetFile(GetTechsetFileName(techset)); - - if (techsetFile) + bool DumperIW4::ShouldDump(XAssetInfo* asset) { - TechsetFileWriter writer(*techsetFile); - writer.DumpTechset(techset); + return true; } - auto* techniqueState = context.GetZoneAssetDumperState(); - for (const auto* technique : techset->techniques) + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - if (technique && techniqueState->ShouldDumpTechnique(technique)) + const auto* techset = asset->Asset(); + + const auto techsetFile = context.OpenAssetFile(GetFileNameForTechsetName(techset->name)); + + if (techsetFile) { - const auto techniqueFile = context.OpenAssetFile(GetTechniqueFileName(technique)); - if (techniqueFile) + TechsetFileWriter writer(*techsetFile); + writer.DumpTechset(techset); + } + + auto* techniqueState = context.GetZoneAssetDumperState(); + for (const auto* technique : techset->techniques) + { + if (technique && techniqueState->ShouldDumpTechnique(technique)) { - TechniqueFileWriter writer(*techniqueFile); - writer.DumpTechnique(technique); + const auto techniqueFile = context.OpenAssetFile(GetFileNameForTechniqueName(technique->name)); + if (techniqueFile) + { + TechniqueFileWriter writer(*techniqueFile); + writer.DumpTechnique(technique); + } } } } -} +} // namespace techset diff --git a/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.h b/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.h new file mode 100644 index 00000000..8115f527 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace techset +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace techset diff --git a/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.cpp b/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.cpp new file mode 100644 index 00000000..2e3635f8 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.cpp @@ -0,0 +1,83 @@ +#include "TracerDumperIW4.h" + +#include "Game/IW4/CommonIW4.h" +#include "Game/IW4/InfoString/InfoStringFromStructConverter.h" +#include "Game/IW4/ObjConstantsIW4.h" +#include "Game/IW4/Tracer/TracerFields.h" +#include "Tracer/TracerCommon.h" + +#include +#include +#include + +using namespace IW4; + +namespace +{ + class InfoStringFromTracerConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + assert(false); + } + + public: + InfoStringFromTracerConverter(const TracerDef* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateInfoString(XAssetInfo* asset) + { + InfoStringFromTracerConverter converter(asset->Asset(), + tracer_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace tracer +{ + bool DumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_TRACER); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_TRACER, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_TRACER); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace tracer diff --git a/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.h b/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.h new file mode 100644 index 00000000..d13a9f60 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" +#include "InfoString/InfoString.h" + +namespace tracer +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace tracer diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVehicle.cpp b/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.cpp similarity index 54% rename from src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVehicle.cpp rename to src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.cpp index 56ab8de7..9110b559 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperVehicle.cpp +++ b/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.cpp @@ -1,10 +1,11 @@ -#include "AssetDumperVehicle.h" +#include "VehicleDumperIW4.h" #include "Game/IW4/CommonIW4.h" #include "Game/IW4/InfoString/EnumStrings.h" #include "Game/IW4/InfoString/InfoStringFromStructConverter.h" #include "Game/IW4/ObjConstantsIW4.h" #include "Game/IW4/Vehicle/VehicleFields.h" +#include "Vehicle/VehicleCommon.h" #include #include @@ -12,7 +13,7 @@ using namespace IW4; -namespace IW4 +namespace { class InfoStringFromVehicleConverter final : public InfoStringFromStructConverter { @@ -71,50 +72,53 @@ namespace IW4 { } }; -} // namespace IW4 -InfoString AssetDumperVehicle::CreateInfoString(XAssetInfo* asset) -{ - InfoStringFromVehicleConverter converter(asset->Asset(), - vehicle_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -bool AssetDumperVehicle::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperVehicle::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) + InfoString CreateInfoString(XAssetInfo* asset) { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_VEHICLE); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_VEHICLE, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); + InfoStringFromVehicleConverter converter(asset->Asset(), + vehicle_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); } - else +} // namespace + +namespace vehicle +{ + bool DumperIW4::ShouldDump(XAssetInfo* asset) { - const auto assetFile = context.OpenAssetFile("vehicles/" + asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_VEHICLE); - stream.write(stringValue.c_str(), stringValue.size()); + return true; } -} + + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_VEHICLE); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_VEHICLE, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_VEHICLE); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace vehicle diff --git a/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.h b/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.h new file mode 100644 index 00000000..5afae348 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" +#include "InfoString/InfoString.h" + +namespace vehicle +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace vehicle diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperWeapon.cpp b/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.cpp similarity index 50% rename from src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperWeapon.cpp rename to src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.cpp index 0d388e3c..1ebabfe7 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperWeapon.cpp +++ b/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.cpp @@ -1,4 +1,4 @@ -#include "AssetDumperWeapon.h" +#include "WeaponDumperIW4.h" #include "Game/IW4/CommonIW4.h" #include "Game/IW4/InfoString/EnumStrings.h" @@ -6,6 +6,7 @@ #include "Game/IW4/ObjConstantsIW4.h" #include "Game/IW4/Weapon/WeaponFields.h" #include "Weapon/AccuracyGraphWriter.h" +#include "Weapon/WeaponCommon.h" #include #include @@ -14,7 +15,7 @@ using namespace IW4; -namespace IW4 +namespace { class InfoStringFromWeaponConverter final : public InfoStringFromStructConverter { @@ -237,195 +238,201 @@ namespace IW4 return graph; } -} // namespace IW4 -void AssetDumperWeapon::CopyToFullDef(const WeaponCompleteDef* weapon, WeaponFullDef* fullDef) -{ - fullDef->weapCompleteDef = *weapon; - - if (weapon->weapDef) + void CopyToFullDef(const WeaponCompleteDef* weapon, WeaponFullDef* fullDef) { - fullDef->weapDef = *weapon->weapDef; - fullDef->weapCompleteDef.weapDef = &fullDef->weapDef; + fullDef->weapCompleteDef = *weapon; + + if (weapon->weapDef) + { + fullDef->weapDef = *weapon->weapDef; + fullDef->weapCompleteDef.weapDef = &fullDef->weapDef; + } + + if (weapon->hideTags) + { + assert(sizeof(WeaponFullDef::hideTags) >= sizeof(scr_string_t) * std::extent_v); + memcpy(fullDef->hideTags, weapon->hideTags, sizeof(scr_string_t) * std::extent_v); + fullDef->weapCompleteDef.hideTags = fullDef->hideTags; + } + + if (weapon->szXAnims) + { + assert(sizeof(WeaponFullDef::szXAnims) >= sizeof(void*) * NUM_WEAP_ANIMS); + memcpy(fullDef->szXAnims, weapon->szXAnims, sizeof(void*) * NUM_WEAP_ANIMS); + fullDef->weapCompleteDef.szXAnims = fullDef->szXAnims; + } + + if (fullDef->weapDef.gunXModel) + { + assert(sizeof(WeaponFullDef::gunXModel) >= sizeof(void*) * std::extent_v); + memcpy(fullDef->gunXModel, fullDef->weapDef.gunXModel, sizeof(void*) * std::extent_v); + fullDef->weapDef.gunXModel = fullDef->gunXModel; + } + + if (fullDef->weapDef.szXAnimsRightHanded) + { + assert(sizeof(WeaponFullDef::szXAnimsRightHanded) >= sizeof(void*) * NUM_WEAP_ANIMS); + memcpy(fullDef->szXAnimsRightHanded, fullDef->weapDef.szXAnimsRightHanded, sizeof(void*) * NUM_WEAP_ANIMS); + fullDef->weapDef.szXAnimsRightHanded = fullDef->szXAnimsRightHanded; + } + + if (fullDef->weapDef.szXAnimsLeftHanded) + { + assert(sizeof(WeaponFullDef::szXAnimsLeftHanded) >= sizeof(void*) * NUM_WEAP_ANIMS); + memcpy(fullDef->szXAnimsLeftHanded, fullDef->weapDef.szXAnimsLeftHanded, sizeof(void*) * NUM_WEAP_ANIMS); + fullDef->weapDef.szXAnimsLeftHanded = fullDef->szXAnimsLeftHanded; + } + + if (fullDef->weapDef.notetrackSoundMapKeys) + { + assert(sizeof(WeaponFullDef::notetrackSoundMapKeys) >= sizeof(scr_string_t) * std::extent_v); + memcpy(fullDef->notetrackSoundMapKeys, + fullDef->weapDef.notetrackSoundMapKeys, + sizeof(scr_string_t) * std::extent_v); + fullDef->weapDef.notetrackSoundMapKeys = fullDef->notetrackSoundMapKeys; + } + + if (fullDef->weapDef.notetrackSoundMapValues) + { + assert(sizeof(WeaponFullDef::notetrackSoundMapValues) >= sizeof(scr_string_t) * std::extent_v); + memcpy(fullDef->notetrackSoundMapValues, + fullDef->weapDef.notetrackSoundMapValues, + sizeof(scr_string_t) * std::extent_v); + fullDef->weapDef.notetrackSoundMapValues = fullDef->notetrackSoundMapValues; + } + + if (fullDef->weapDef.notetrackRumbleMapKeys) + { + assert(sizeof(WeaponFullDef::notetrackRumbleMapKeys) >= sizeof(scr_string_t) * std::extent_v); + memcpy(fullDef->notetrackRumbleMapKeys, + fullDef->weapDef.notetrackRumbleMapKeys, + sizeof(scr_string_t) * std::extent_v); + fullDef->weapDef.notetrackRumbleMapKeys = fullDef->notetrackRumbleMapKeys; + } + + if (fullDef->weapDef.notetrackRumbleMapValues) + { + assert(sizeof(WeaponFullDef::notetrackRumbleMapValues) >= sizeof(scr_string_t) * std::extent_v); + memcpy(fullDef->notetrackRumbleMapValues, + fullDef->weapDef.notetrackRumbleMapValues, + sizeof(scr_string_t) * std::extent_v); + fullDef->weapDef.notetrackRumbleMapValues = fullDef->notetrackRumbleMapValues; + } + + if (fullDef->weapDef.worldModel) + { + assert(sizeof(WeaponFullDef::worldModel) >= sizeof(void*) * std::extent_v); + memcpy(fullDef->worldModel, fullDef->weapDef.worldModel, sizeof(void*) * std::extent_v); + fullDef->weapDef.worldModel = fullDef->worldModel; + } + + if (fullDef->weapDef.parallelBounce) + { + assert(sizeof(WeaponFullDef::parallelBounce) >= sizeof(float) * std::extent_v); + memcpy(fullDef->parallelBounce, fullDef->weapDef.parallelBounce, sizeof(float) * std::extent_v); + fullDef->weapDef.parallelBounce = fullDef->parallelBounce; + } + + if (fullDef->weapDef.perpendicularBounce) + { + assert(sizeof(WeaponFullDef::perpendicularBounce) >= sizeof(float) * std::extent_v); + memcpy(fullDef->perpendicularBounce, + fullDef->weapDef.perpendicularBounce, + sizeof(float) * std::extent_v); + fullDef->weapDef.perpendicularBounce = fullDef->perpendicularBounce; + } + + if (fullDef->weapDef.locationDamageMultipliers) + { + assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * std::extent_v); + memcpy(fullDef->locationDamageMultipliers, + fullDef->weapDef.locationDamageMultipliers, + sizeof(float) * std::extent_v); + fullDef->weapDef.locationDamageMultipliers = fullDef->locationDamageMultipliers; + } } - if (weapon->hideTags) + InfoString CreateInfoString(XAssetInfo* asset) { - assert(sizeof(WeaponFullDef::hideTags) >= sizeof(scr_string_t) * std::extent_v); - memcpy(fullDef->hideTags, weapon->hideTags, sizeof(scr_string_t) * std::extent_v); - fullDef->weapCompleteDef.hideTags = fullDef->hideTags; + const auto fullDef = std::make_unique(); + memset(fullDef.get(), 0, sizeof(WeaponFullDef)); + CopyToFullDef(asset->Asset(), fullDef.get()); + + InfoStringFromWeaponConverter converter(fullDef.get(), + weapon_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); } - if (weapon->szXAnims) + void DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset) { - assert(sizeof(WeaponFullDef::szXAnims) >= sizeof(void*) * NUM_WEAP_ANIMS); - memcpy(fullDef->szXAnims, weapon->szXAnims, sizeof(void*) * NUM_WEAP_ANIMS); - fullDef->weapCompleteDef.szXAnims = fullDef->szXAnims; - } + auto* accuracyGraphWriter = context.GetZoneAssetDumperState(); + const auto weapon = asset->Asset(); + const auto* weapDef = weapon->weapDef; - if (fullDef->weapDef.gunXModel) - { - assert(sizeof(WeaponFullDef::gunXModel) >= sizeof(void*) * std::extent_v); - memcpy(fullDef->gunXModel, fullDef->weapDef.gunXModel, sizeof(void*) * std::extent_v); - fullDef->weapDef.gunXModel = fullDef->gunXModel; - } - - if (fullDef->weapDef.szXAnimsRightHanded) - { - assert(sizeof(WeaponFullDef::szXAnimsRightHanded) >= sizeof(void*) * NUM_WEAP_ANIMS); - memcpy(fullDef->szXAnimsRightHanded, fullDef->weapDef.szXAnimsRightHanded, sizeof(void*) * NUM_WEAP_ANIMS); - fullDef->weapDef.szXAnimsRightHanded = fullDef->szXAnimsRightHanded; - } - - if (fullDef->weapDef.szXAnimsLeftHanded) - { - assert(sizeof(WeaponFullDef::szXAnimsLeftHanded) >= sizeof(void*) * NUM_WEAP_ANIMS); - memcpy(fullDef->szXAnimsLeftHanded, fullDef->weapDef.szXAnimsLeftHanded, sizeof(void*) * NUM_WEAP_ANIMS); - fullDef->weapDef.szXAnimsLeftHanded = fullDef->szXAnimsLeftHanded; - } - - if (fullDef->weapDef.notetrackSoundMapKeys) - { - assert(sizeof(WeaponFullDef::notetrackSoundMapKeys) >= sizeof(scr_string_t) * std::extent_v); - memcpy(fullDef->notetrackSoundMapKeys, - fullDef->weapDef.notetrackSoundMapKeys, - sizeof(scr_string_t) * std::extent_v); - fullDef->weapDef.notetrackSoundMapKeys = fullDef->notetrackSoundMapKeys; - } - - if (fullDef->weapDef.notetrackSoundMapValues) - { - assert(sizeof(WeaponFullDef::notetrackSoundMapValues) >= sizeof(scr_string_t) * std::extent_v); - memcpy(fullDef->notetrackSoundMapValues, - fullDef->weapDef.notetrackSoundMapValues, - sizeof(scr_string_t) * std::extent_v); - fullDef->weapDef.notetrackSoundMapValues = fullDef->notetrackSoundMapValues; - } - - if (fullDef->weapDef.notetrackRumbleMapKeys) - { - assert(sizeof(WeaponFullDef::notetrackRumbleMapKeys) >= sizeof(scr_string_t) * std::extent_v); - memcpy(fullDef->notetrackRumbleMapKeys, - fullDef->weapDef.notetrackRumbleMapKeys, - sizeof(scr_string_t) * std::extent_v); - fullDef->weapDef.notetrackRumbleMapKeys = fullDef->notetrackRumbleMapKeys; - } - - if (fullDef->weapDef.notetrackRumbleMapValues) - { - assert(sizeof(WeaponFullDef::notetrackRumbleMapValues) >= sizeof(scr_string_t) * std::extent_v); - memcpy(fullDef->notetrackRumbleMapValues, - fullDef->weapDef.notetrackRumbleMapValues, - sizeof(scr_string_t) * std::extent_v); - fullDef->weapDef.notetrackRumbleMapValues = fullDef->notetrackRumbleMapValues; - } - - if (fullDef->weapDef.worldModel) - { - assert(sizeof(WeaponFullDef::worldModel) >= sizeof(void*) * std::extent_v); - memcpy(fullDef->worldModel, fullDef->weapDef.worldModel, sizeof(void*) * std::extent_v); - fullDef->weapDef.worldModel = fullDef->worldModel; - } - - if (fullDef->weapDef.parallelBounce) - { - assert(sizeof(WeaponFullDef::parallelBounce) >= sizeof(float) * std::extent_v); - memcpy(fullDef->parallelBounce, fullDef->weapDef.parallelBounce, sizeof(float) * std::extent_v); - fullDef->weapDef.parallelBounce = fullDef->parallelBounce; - } - - if (fullDef->weapDef.perpendicularBounce) - { - assert(sizeof(WeaponFullDef::perpendicularBounce) >= sizeof(float) * std::extent_v); - memcpy(fullDef->perpendicularBounce, fullDef->weapDef.perpendicularBounce, sizeof(float) * std::extent_v); - fullDef->weapDef.perpendicularBounce = fullDef->perpendicularBounce; - } - - if (fullDef->weapDef.locationDamageMultipliers) - { - assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * std::extent_v); - memcpy(fullDef->locationDamageMultipliers, - fullDef->weapDef.locationDamageMultipliers, - sizeof(float) * std::extent_v); - fullDef->weapDef.locationDamageMultipliers = fullDef->locationDamageMultipliers; - } -} - -InfoString AssetDumperWeapon::CreateInfoString(XAssetInfo* asset) -{ - const auto fullDef = std::make_unique(); - memset(fullDef.get(), 0, sizeof(WeaponFullDef)); - CopyToFullDef(asset->Asset(), fullDef.get()); - - InfoStringFromWeaponConverter converter(fullDef.get(), - weapon_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -void AssetDumperWeapon::DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset) -{ - auto* accuracyGraphWriter = context.GetZoneAssetDumperState(); - const auto weapon = asset->Asset(); - const auto* weapDef = weapon->weapDef; - - if (!weapDef) - return; - - if (weapDef->aiVsAiAccuracyGraphName && weapDef->originalAiVsAiAccuracyGraphKnots - && accuracyGraphWriter->ShouldDumpAiVsAiGraph(weapDef->aiVsAiAccuracyGraphName)) - { - AccuracyGraphWriter::DumpAiVsAiGraph( - context, - ConvertAccuracyGraph(weapDef->aiVsAiAccuracyGraphName, weapDef->originalAiVsAiAccuracyGraphKnots, weapDef->originalAiVsAiAccuracyGraphKnotCount)); - } - - if (weapDef->aiVsPlayerAccuracyGraphName && weapDef->originalAiVsPlayerAccuracyGraphKnots - && accuracyGraphWriter->ShouldDumpAiVsPlayerGraph(weapDef->aiVsPlayerAccuracyGraphName)) - { - AccuracyGraphWriter::DumpAiVsPlayerGraph(context, - ConvertAccuracyGraph(weapDef->aiVsPlayerAccuracyGraphName, - weapDef->originalAiVsPlayerAccuracyGraphKnots, - weapDef->originalAiVsPlayerAccuracyGraphKnotCount)); - } -} - -bool AssetDumperWeapon::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperWeapon::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_WEAPON, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("weapons/" + asset->m_name); - - if (!assetFile) + if (!weapDef) return; - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_WEAPON); - stream.write(stringValue.c_str(), stringValue.size()); + if (weapDef->aiVsAiAccuracyGraphName && weapDef->originalAiVsAiAccuracyGraphKnots + && accuracyGraphWriter->ShouldDumpAiVsAiGraph(weapDef->aiVsAiAccuracyGraphName)) + { + AccuracyGraphWriter::DumpAiVsAiGraph(context, + ConvertAccuracyGraph(weapDef->aiVsAiAccuracyGraphName, + weapDef->originalAiVsAiAccuracyGraphKnots, + weapDef->originalAiVsAiAccuracyGraphKnotCount)); + } + + if (weapDef->aiVsPlayerAccuracyGraphName && weapDef->originalAiVsPlayerAccuracyGraphKnots + && accuracyGraphWriter->ShouldDumpAiVsPlayerGraph(weapDef->aiVsPlayerAccuracyGraphName)) + { + AccuracyGraphWriter::DumpAiVsPlayerGraph(context, + ConvertAccuracyGraph(weapDef->aiVsPlayerAccuracyGraphName, + weapDef->originalAiVsPlayerAccuracyGraphKnots, + weapDef->originalAiVsPlayerAccuracyGraphKnotCount)); + } + } +} // namespace + +namespace weapon +{ + bool DumperIW4::ShouldDump(XAssetInfo* asset) + { + return true; } - DumpAccuracyGraphs(context, asset); -} + void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_WEAPON, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_WEAPON); + stream.write(stringValue.c_str(), stringValue.size()); + } + + DumpAccuracyGraphs(context, asset); + } +} // namespace weapon diff --git a/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.h b/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.h new file mode 100644 index 00000000..d6935e77 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" +#include "InfoString/InfoString.h" + +namespace weapon +{ + class DumperIW4 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace weapon diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperAddonMapEnts.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperAddonMapEnts.cpp deleted file mode 100644 index 547881cf..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperAddonMapEnts.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#define NOMINMAX -#include "AssetDumperAddonMapEnts.h" - -#include - -using namespace IW5; - -bool AssetDumperAddonMapEnts::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperAddonMapEnts::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* addonMapEnts = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - stream.write(addonMapEnts->entityString, std::max(addonMapEnts->numEntityChars - 1, 0)); -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperAddonMapEnts.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperAddonMapEnts.h deleted file mode 100644 index 9e5ec39d..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperAddonMapEnts.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperAddonMapEnts final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperGfxImage.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperGfxImage.h deleted file mode 100644 index fac3f66b..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperGfxImage.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" -#include "Image/IImageWriter.h" - -#include - -namespace IW5 -{ - class AssetDumperGfxImage final : public AbstractAssetDumper - { - std::unique_ptr m_writer; - - [[nodiscard]] std::string GetAssetFileName(const XAssetInfo& asset) const; - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - - public: - AssetDumperGfxImage(); - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLeaderboardDef.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLeaderboardDef.cpp deleted file mode 100644 index 9b1804f2..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLeaderboardDef.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "AssetDumperLeaderboardDef.h" - -#include "Game/IW5/Leaderboard/JsonLeaderboardDefWriter.h" - -#include -#include - -using namespace IW5; - -bool AssetDumperLeaderboardDef::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperLeaderboardDef::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto assetName = asset->m_name; - const auto assetFile = context.OpenAssetFile(std::format("leaderboards/{}.json", assetName)); - - if (!assetFile) - return; - - DumpLeaderboardDefAsJson(*assetFile, asset->Asset()); -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLeaderboardDef.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLeaderboardDef.h deleted file mode 100644 index 934ad940..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLeaderboardDef.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperLeaderboardDef final : public AbstractAssetDumper - { - protected: - _NODISCARD bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLoadedSound.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLoadedSound.cpp deleted file mode 100644 index 7cb254e2..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLoadedSound.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "AssetDumperLoadedSound.h" - -#include "Sound/WavTypes.h" -#include "Sound/WavWriter.h" - -#include - -using namespace IW5; - -bool AssetDumperLoadedSound::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperLoadedSound::DumpWavPcm(const LoadedSound* asset, std::ostream& stream) -{ - const WavWriter writer(stream); - - const WavMetaData metaData{.channelCount = static_cast(asset->sound.info.channels), - .samplesPerSec = static_cast(asset->sound.info.rate), - .bitsPerSample = static_cast(asset->sound.info.bits)}; - - writer.WritePcmHeader(metaData, asset->sound.info.data_len); - writer.WritePcmData(asset->sound.data, asset->sound.info.data_len); -} - -void AssetDumperLoadedSound::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* loadedSound = asset->Asset(); - const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset->m_name)); - - if (!assetFile) - return; - - auto& stream = *assetFile; - switch (static_cast(loadedSound->sound.info.format)) - { - case WavFormat::PCM: - DumpWavPcm(loadedSound, stream); - break; - - default: - std::cerr << std::format("Unknown format {} for loaded sound: {}\n", loadedSound->sound.info.format, loadedSound->name); - break; - } -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLoadedSound.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLoadedSound.h deleted file mode 100644 index 4f575072..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLoadedSound.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperLoadedSound final : public AbstractAssetDumper - { - static void DumpWavPcm(const LoadedSound* asset, std::ostream& stream); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLocalizeEntry.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLocalizeEntry.cpp deleted file mode 100644 index 600e41b8..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLocalizeEntry.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "AssetDumperLocalizeEntry.h" - -#include "Dumping/Localize/StringFileDumper.h" -#include "Localize/LocalizeCommon.h" - -#include -#include - -using namespace IW5; - -void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - if (pool->m_asset_lookup.empty()) - return; - - const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); - const auto assetFile = context.OpenAssetFile(std::format("{}/localizedstrings/{}.str", language, context.m_zone.m_name)); - - if (assetFile) - { - StringFileDumper stringFileDumper(context.m_zone, *assetFile); - - stringFileDumper.SetLanguageName(language); - - // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. - stringFileDumper.SetConfigFile(R"(C:/trees/cod3/cod3/bin/StringEd.cfg)"); - - stringFileDumper.SetNotes(""); - - for (auto* localizeEntry : *pool) - { - stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); - } - - stringFileDumper.Finalize(); - } - else - { - std::cerr << std::format("Could not create string file for dumping localized strings of zone '{}'\n", context.m_zone.m_name); - } -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLocalizeEntry.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLocalizeEntry.h deleted file mode 100644 index 151dcd73..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperLocalizeEntry.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperLocalizeEntry final : public IAssetDumper - { - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.cpp deleted file mode 100644 index b14b5dbe..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "AssetDumperMenuDef.h" - -#include "Game/IW5/GameAssetPoolIW5.h" -#include "Game/IW5/Menu/MenuDumperIW5.h" -#include "Menu/AbstractMenuDumper.h" -#include "ObjWriting.h" - -#include -#include - -namespace fs = std::filesystem; - -using namespace IW5; - -const MenuList* AssetDumperMenuDef::GetParentMenuList(XAssetInfo* asset) -{ - const auto* menu = asset->Asset(); - const auto* gameAssetPool = dynamic_cast(asset->m_zone->m_pools.get()); - for (const auto* menuList : *gameAssetPool->m_menu_list) - { - const auto* menuListAsset = menuList->Asset(); - - for (auto menuIndex = 0; menuIndex < menuListAsset->menuCount; menuIndex++) - { - if (menuListAsset->menus[menuIndex] == menu) - return menuListAsset; - } - } - - return nullptr; -} - -std::string AssetDumperMenuDef::GetPathForMenu(XAssetInfo* asset) -{ - const auto* list = GetParentMenuList(asset); - - if (!list) - return "ui_mp/" + std::string(asset->Asset()->window.name) + ".menu"; - - const fs::path p(list->name); - std::string parentPath; - if (p.has_parent_path()) - parentPath = p.parent_path().string() + "/"; - - return parentPath + std::string(asset->Asset()->window.name) + ".menu"; -} - -bool AssetDumperMenuDef::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMenuDef::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* menu = asset->Asset(); - const auto menuFilePath = GetPathForMenu(asset); - - if (ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST)) - { - // Don't dump menu file separately if the name matches the menu list - const auto* menuListParent = GetParentMenuList(asset); - if (menuListParent && menuFilePath == menuListParent->name) - return; - } - - const auto assetFile = context.OpenAssetFile(menuFilePath); - - if (!assetFile) - return; - - MenuDumper menuDumper(*assetFile); - - menuDumper.Start(); - menuDumper.WriteMenu(menu); - menuDumper.End(); -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.h deleted file mode 100644 index 696da199..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperMenuDef final : public AbstractAssetDumper - { - static const MenuList* GetParentMenuList(XAssetInfo* asset); - static std::string GetPathForMenu(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.cpp deleted file mode 100644 index c3b9bb44..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "AssetDumperMenuList.h" - -#include "Game/IW5/Menu/MenuDumperIW5.h" -#include "Menu/AbstractMenuDumper.h" -#include "ObjWriting.h" - -#include -#include -#include -#include - -namespace fs = std::filesystem; - -using namespace IW5; - -std::vector AssetDumperMenuList::GetAllUniqueExpressionSupportingData(const MenuList* menuList) -{ - std::vector result; - std::set alreadyAddedSupportingData; - - if (menuList->menus == nullptr) - return result; - - for (auto i = 0; i < menuList->menuCount; i++) - { - if (menuList->menus[i] == nullptr) - continue; - - const auto* menu = menuList->menus[i]; - - if (menu->data == nullptr || menu->data->expressionData == nullptr) - continue; - - if (alreadyAddedSupportingData.find(menu->data->expressionData) == alreadyAddedSupportingData.end()) - { - result.push_back(menu->data->expressionData); - alreadyAddedSupportingData.emplace(menu->data->expressionData); - } - } - - return result; -} - -void AssetDumperMenuList::DumpFunctions(MenuDumper& menuDumper, const MenuList* menuList) -{ - const auto allSupportingData = GetAllUniqueExpressionSupportingData(menuList); - auto functionIndex = 0u; - - assert(allSupportingData.size() <= 1); - - for (const auto* supportingData : allSupportingData) - { - if (supportingData->uifunctions.functions == nullptr) - continue; - - for (auto i = 0; i < supportingData->uifunctions.totalFunctions; i++) - { - const auto* function = supportingData->uifunctions.functions[i]; - if (function == nullptr) - continue; - - std::stringstream ss; - ss << "FUNC_" << functionIndex; - - menuDumper.WriteFunctionDef(ss.str(), function); - - functionIndex++; - } - } -} - -void AssetDumperMenuList::DumpMenus(MenuDumper& menuDumper, const MenuList* menuList) -{ - const fs::path p(menuList->name); - - std::string parentPath; - if (p.has_parent_path()) - parentPath = p.parent_path().string() + "/"; - - for (auto menuNum = 0; menuNum < menuList->menuCount; menuNum++) - { - const auto* menu = menuList->menus[menuNum]; - const auto* menuAssetName = menu->window.name; - - bool isReference = false; - if (menuAssetName && menuAssetName[0] == ',') - { - menuAssetName = &menuAssetName[1]; - isReference = true; - } - - std::ostringstream ss; - ss << parentPath << menuAssetName << ".menu"; - - const auto menuName = ss.str(); - - // If the menu was embedded directly as menu list write its data in the menu list file - if (!isReference && menuName == menuList->name) - menuDumper.WriteMenu(menu); - else - menuDumper.IncludeMenu(ss.str()); - } -} - -bool AssetDumperMenuList::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMenuList::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* menuList = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - MenuDumper menuDumper(*assetFile); - - menuDumper.Start(); - - if (!ObjWriting::Configuration.MenuLegacyMode) - DumpFunctions(menuDumper, menuList); - - DumpMenus(menuDumper, menuList); - - menuDumper.End(); -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.h deleted file mode 100644 index 58a83904..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" -#include "Game/IW5/Menu/MenuDumperIW5.h" - -namespace IW5 -{ - class AssetDumperMenuList final : public AbstractAssetDumper - { - static std::vector GetAllUniqueExpressionSupportingData(const MenuList* menuList); - - static void DumpFunctions(MenuDumper& menuDumper, const MenuList* menuList); - static void DumpMenus(MenuDumper& menuDumper, const MenuList* menuList); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperRawFile.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperRawFile.cpp deleted file mode 100644 index 283b8bda..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperRawFile.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "AssetDumperRawFile.h" - -#include -#include -#include - -using namespace IW5; - -bool AssetDumperRawFile::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperRawFile::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* rawFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - if (rawFile->compressedLen <= 0) - 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); - zs.avail_in = rawFile->compressedLen; - - Bytef buffer[0x1000]; - - while (zs.avail_in > 0) - { - zs.next_out = buffer; - zs.avail_out = sizeof(buffer); - ret = inflate(&zs, Z_SYNC_FLUSH); - - if (ret < 0) - { - std::cerr << std::format("Inflate failed when attempting to dump rawfile '{}'\n", rawFile->name); - inflateEnd(&zs); - return; - } - - stream.write(reinterpret_cast(buffer), sizeof(buffer) - zs.avail_out); - } - - inflateEnd(&zs); -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperRawFile.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperRawFile.h deleted file mode 100644 index 57e35274..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperRawFile.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperRawFile final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperScriptFile.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperScriptFile.cpp deleted file mode 100644 index c432a700..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperScriptFile.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "AssetDumperScriptFile.h" - -using namespace IW5; - -bool AssetDumperScriptFile::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -// See https://github.com/xensik/gsc-tool#file-format for an in-depth explanation about the .gscbin format -void AssetDumperScriptFile::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - auto* scriptFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name + ".gscbin"); - - if (!assetFile) - return; - - auto& stream = *assetFile; - - // Dump the name and the numeric fields - stream.write(asset->m_name.c_str(), asset->m_name.size() + 1); - stream.write(reinterpret_cast(&scriptFile->compressedLen), sizeof(scriptFile->compressedLen)); - stream.write(reinterpret_cast(&scriptFile->len), sizeof(scriptFile->len)); - stream.write(reinterpret_cast(&scriptFile->bytecodeLen), sizeof(scriptFile->bytecodeLen)); - - // Dump the buffers - stream.write(scriptFile->buffer, scriptFile->compressedLen); - stream.write(reinterpret_cast(scriptFile->bytecode), scriptFile->bytecodeLen); -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperScriptFile.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperScriptFile.h deleted file mode 100644 index 2397f387..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperScriptFile.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperScriptFile final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperStringTable.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperStringTable.cpp deleted file mode 100644 index 0612c075..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperStringTable.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "AssetDumperStringTable.h" - -#include "Csv/CsvStream.h" - -using namespace IW5; - -bool AssetDumperStringTable::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperStringTable::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* stringTable = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - CsvOutputStream csv(*assetFile); - - for (auto row = 0; row < stringTable->rowCount; row++) - { - for (auto column = 0; column < stringTable->columnCount; column++) - { - const auto* cell = &stringTable->values[column + row * stringTable->columnCount]; - if (cell->string != nullptr) - { - csv.WriteColumn(cell->string); - } - else - { - csv.WriteColumn(""); - } - } - - csv.NextRow(); - } -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperStringTable.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperStringTable.h deleted file mode 100644 index 9e834267..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperStringTable.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperStringTable final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeapon.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeapon.h deleted file mode 100644 index d13b00a5..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeapon.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" -#include "InfoString/InfoString.h" - -namespace IW5 -{ - class AssetDumperWeapon final : public AbstractAssetDumper - { - static void CopyToFullDef(const WeaponCompleteDef* weapon, WeaponFullDef* fullDef); - static InfoString CreateInfoString(XAssetInfo* asset); - static void DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeaponAttachment.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeaponAttachment.cpp deleted file mode 100644 index d63e1649..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeaponAttachment.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "AssetDumperWeaponAttachment.h" - -#include "Game/IW5/Weapon/JsonWeaponAttachmentWriter.h" - -#include - -using namespace IW5; - -bool AssetDumperWeaponAttachment::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperWeaponAttachment::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto assetFile = context.OpenAssetFile(std::format("attachment/{}.json", asset->m_name)); - - if (!assetFile) - return; - - DumpWeaponAttachmentAsJson(*assetFile, asset->Asset(), context); -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeaponAttachment.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeaponAttachment.h deleted file mode 100644 index 11f32004..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeaponAttachment.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperWeaponAttachment final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp deleted file mode 100644 index bd882e41..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "AssetDumperXModel.h" - -#include "Game/IW5/XModel/XModelDumperIW5.h" - -using namespace IW5; - -bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) -{ - return !asset->m_name.empty() && asset->m_name[0] != ','; -} - -void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - DumpXModel(context, asset); -} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h deleted file mode 100644 index 28880a59..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperXModel final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperGfxImage.cpp b/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.cpp similarity index 54% rename from src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperGfxImage.cpp rename to src/ObjWriting/Game/IW5/Image/ImageDumperIW5.cpp index 82d3cca1..038f94ea 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperGfxImage.cpp +++ b/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.cpp @@ -1,10 +1,12 @@ -#include "AssetDumperGfxImage.h" +#include "ImageDumperIW5.h" #include "Image/DdsWriter.h" #include "Image/Dx9TextureLoader.h" +#include "Image/ImageCommon.h" #include "Image/IwiLoader.h" #include "Image/IwiWriter8.h" #include "ObjWriting.h" +#include "Utils/Logging/Log.h" #include #include @@ -36,11 +38,11 @@ namespace std::unique_ptr LoadImageFromIwi(const GfxImage& image, ISearchPath& searchPath) { - const auto imageFileName = std::format("images/{}.iwi", image.name); + const auto imageFileName = image::GetFileNameForAsset(image.name, ".iwi"); const auto filePathImage = searchPath.Open(imageFileName); if (!filePathImage.IsOpen()) { - std::cerr << std::format("Could not find data for image \"{}\"\n", image.name); + con::error("Could not find data for image \"{}\"", image.name); return nullptr; } @@ -56,48 +58,43 @@ namespace } } // namespace -AssetDumperGfxImage::AssetDumperGfxImage() +namespace image { - switch (ObjWriting::Configuration.ImageOutputFormat) + DumperIW5::DumperIW5() { - case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: - m_writer = std::make_unique(); - break; - case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: - m_writer = std::make_unique(); - break; - default: - assert(false); - m_writer = nullptr; - break; + switch (ObjWriting::Configuration.ImageOutputFormat) + { + case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: + m_writer = std::make_unique(); + break; + case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: + m_writer = std::make_unique(); + break; + default: + assert(false); + m_writer = nullptr; + break; + } } -} -bool AssetDumperGfxImage::ShouldDump(XAssetInfo* asset) -{ - return true; -} + bool DumperIW5::ShouldDump(XAssetInfo* asset) + { + return true; + } -std::string AssetDumperGfxImage::GetAssetFileName(const XAssetInfo& asset) const -{ - auto cleanAssetName = asset.m_name; - std::ranges::replace(cleanAssetName, '*', '_'); + void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* image = asset->Asset(); + const auto texture = LoadImageData(context.m_obj_search_path, *image); + if (!texture) + return; - return std::format("images/{}{}", cleanAssetName, m_writer->GetFileExtension()); -} + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name, m_writer->GetFileExtension())); -void AssetDumperGfxImage::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* image = asset->Asset(); - const auto texture = LoadImageData(context.m_obj_search_path, *image); - if (!texture) - return; + if (!assetFile) + return; - const auto assetFile = context.OpenAssetFile(GetAssetFileName(*asset)); - - if (!assetFile) - return; - - auto& stream = *assetFile; - m_writer->DumpImage(stream, texture.get()); -} + auto& stream = *assetFile; + m_writer->DumpImage(stream, texture.get()); + } +} // namespace image diff --git a/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.h b/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.h new file mode 100644 index 00000000..414465aa --- /dev/null +++ b/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" +#include "Image/IImageWriter.h" + +#include + +namespace image +{ + class DumperIW5 final : public AbstractAssetDumper + { + public: + DumperIW5(); + + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + + private: + std::unique_ptr m_writer; + }; +} // namespace image diff --git a/src/ObjWriting/Game/IW5/Leaderboard/JsonLeaderboardDefWriter.h b/src/ObjWriting/Game/IW5/Leaderboard/JsonLeaderboardDefWriter.h deleted file mode 100644 index b992e6b2..00000000 --- a/src/ObjWriting/Game/IW5/Leaderboard/JsonLeaderboardDefWriter.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Dumping/AssetDumpingContext.h" -#include "Game/IW5/IW5.h" - -#include - -namespace IW5 -{ - void DumpLeaderboardDefAsJson(std::ostream& stream, const LeaderboardDef* leaderboardDef); -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/Leaderboard/JsonLeaderboardDefWriter.cpp b/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.cpp similarity index 77% rename from src/ObjWriting/Game/IW5/Leaderboard/JsonLeaderboardDefWriter.cpp rename to src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.cpp index ef365d79..f79ea849 100644 --- a/src/ObjWriting/Game/IW5/Leaderboard/JsonLeaderboardDefWriter.cpp +++ b/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.cpp @@ -1,28 +1,30 @@ -#include "JsonLeaderboardDefWriter.h" +#include "LeaderboardJsonDumperIW5.h" #include "Game/IW5/CommonIW5.h" #include "Game/IW5/Leaderboard/JsonLeaderboardDef.h" +#include "Leaderboard/LeaderboardCommon.h" #include #include +#include using namespace nlohmann; using namespace IW5; namespace { - class JsonDumper + class Dumper { public: - explicit JsonDumper(std::ostream& stream) + explicit Dumper(std::ostream& stream) : m_stream(stream) { } - void Dump(const LeaderboardDef* leaderboardDef) const + void Dump(const LeaderboardDef& leaderboardDef) const { JsonLeaderboardDef jsonLeaderboardDef; - CreateJsonLeaderboardDef(jsonLeaderboardDef, *leaderboardDef); + CreateJsonLeaderboardDef(jsonLeaderboardDef, leaderboardDef); json jRoot = jsonLeaderboardDef; @@ -90,11 +92,21 @@ namespace }; } // namespace -namespace IW5 +namespace leaderboard { - void DumpLeaderboardDefAsJson(std::ostream& stream, const LeaderboardDef* leaderboardDef) + bool JsonDumperIW5::ShouldDump(XAssetInfo* asset) { - JsonDumper dumper(stream); - dumper.Dump(leaderboardDef); + return true; } -} // namespace IW5 + + void JsonDumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAsset(asset->m_name)); + + if (!assetFile) + return; + + Dumper dumper(*assetFile); + dumper.Dump(*asset->Asset()); + } +} // namespace leaderboard diff --git a/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.h b/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.h new file mode 100644 index 00000000..3e6ff7f8 --- /dev/null +++ b/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace leaderboard +{ + class JsonDumperIW5 final : public AbstractAssetDumper + { + protected: + [[nodiscard]] bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace leaderboard diff --git a/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.cpp b/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.cpp new file mode 100644 index 00000000..a9f979ee --- /dev/null +++ b/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.cpp @@ -0,0 +1,45 @@ +#include "LocalizeDumperIW5.h" + +#include "Dumping/Localize/StringFileDumper.h" +#include "Localize/LocalizeCommon.h" +#include "Utils/Logging/Log.h" + +#include +#include + +using namespace IW5; + +namespace localize +{ + void DumperIW5::DumpPool(AssetDumpingContext& context, AssetPool* pool) + { + if (pool->m_asset_lookup.empty()) + return; + + const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); + const auto assetFile = context.OpenAssetFile(std::format("{}/localizedstrings/{}.str", language, context.m_zone.m_name)); + + if (assetFile) + { + StringFileDumper stringFileDumper(context.m_zone, *assetFile); + + stringFileDumper.SetLanguageName(language); + + // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. + stringFileDumper.SetConfigFile(R"(C:/trees/cod3/cod3/bin/StringEd.cfg)"); + + stringFileDumper.SetNotes(""); + + for (auto* localizeEntry : *pool) + { + stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); + } + + stringFileDumper.Finalize(); + } + else + { + con::error("Could not create string file for dumping localized strings of zone '{}'", context.m_zone.m_name); + } + } +} // namespace localize diff --git a/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.h b/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.h new file mode 100644 index 00000000..26373073 --- /dev/null +++ b/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace localize +{ + class DumperIW5 final : public IAssetDumper + { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + }; +} // namespace localize diff --git a/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.cpp b/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.cpp new file mode 100644 index 00000000..3facfa1c --- /dev/null +++ b/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.cpp @@ -0,0 +1,26 @@ +#define NOMINMAX +#include "AddonMapEntsDumperIW5.h" + +#include + +using namespace IW5; + +namespace addon_map_ents +{ + bool DumperIW5::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* addonMapEnts = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto& stream = *assetFile; + stream.write(addonMapEnts->entityString, std::max(addonMapEnts->numEntityChars - 1, 0)); + } +} // namespace addon_map_ents diff --git a/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.h b/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.h new file mode 100644 index 00000000..6c251086 --- /dev/null +++ b/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace addon_map_ents +{ + class DumperIW5 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace addon_map_ents diff --git a/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.cpp b/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.cpp deleted file mode 100644 index 6b9f6e2c..00000000 --- a/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "DumperMaterialIW5.h" - -#include "Game/IW5/Material/JsonMaterialWriterIW5.h" -#include "Game/IW5/Material/MaterialConstantZoneStateIW5.h" -#include "Material/MaterialCommon.h" - -using namespace IW5; - -void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - auto* materialConstantState = context.GetZoneAssetDumperState(); - materialConstantState->ExtractNamesFromZone(); - - AbstractAssetDumper::DumpPool(context, pool); -} - -bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto assetFile = context.OpenAssetFile(material::GetFileNameForAssetName(asset->m_name)); - - if (!assetFile) - return; - - DumpMaterialAsJson(*assetFile, *asset->Asset(), context); -} diff --git a/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.h b/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.h deleted file mode 100644 index 12e57790..00000000 --- a/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - class AssetDumperMaterial final : public AbstractAssetDumper - { - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneStateIW5.cpp b/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneStateIW5.cpp index 7039a12c..753470de 100644 --- a/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneStateIW5.cpp +++ b/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneStateIW5.cpp @@ -4,6 +4,7 @@ #include "Game/IW5/GameAssetPoolIW5.h" #include "Game/IW5/GameIW5.h" #include "ObjWriting.h" +#include "Zone/ZoneRegistry.h" namespace IW5 { @@ -199,7 +200,7 @@ namespace IW5 void MaterialConstantZoneState::ExtractNamesFromZoneInternal() { - for (const auto* zone : IGame::GetGameById(GameId::IW5)->GetZones()) + for (const auto* zone : ZoneRegistry::GetRegistryForGame(GameId::IW5)->Zones()) { const auto* iw5AssetPools = dynamic_cast(zone->m_pools.get()); if (!iw5AssetPools) diff --git a/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp index d96e3c0b..2abe44a0 100644 --- a/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp @@ -1,920 +1,83 @@ #include "MenuDumperIW5.h" -#include "Game/IW5/MenuConstantsIW5.h" +#include "Game/IW5/GameAssetPoolIW5.h" +#include "Game/IW5/Menu/MenuDumperIW5.h" +#include "MenuWriterIW5.h" #include "ObjWriting.h" -#include -#include -#include +#include +#include +#include + +namespace fs = std::filesystem; using namespace IW5; -// Uncomment this macro to skip interpretative expression dumping -// #define DUMP_NAIVE - -#ifdef DUMP_NAIVE -#define DUMP_FUNC WriteStatementNaive -#else -#define DUMP_FUNC WriteStatementSkipInitialUnnecessaryParenthesis -#endif - -size_t MenuDumper::FindStatementClosingParenthesis(const Statement_s* statement, size_t openingParenthesisPosition) +namespace { - assert(statement->numEntries >= 0); - assert(openingParenthesisPosition < static_cast(statement->numEntries)); - - const auto statementEnd = static_cast(statement->numEntries); - - // The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though. - // We will pretend it does since the game does sometimes leave out opening parenthesis from the entries. - auto currentParenthesisDepth = 1; - for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++) + const MenuList* GetParentMenuList(XAssetInfo* asset) { - const auto& expEntry = statement->entries[currentSearchPosition]; - if (expEntry.type != EET_OPERATOR) - continue; - - // Any function means a "left out" left paren - if (expEntry.data.op == OP_LEFTPAREN || expEntry.data.op >= OP_COUNT) + const auto* menu = asset->Asset(); + const auto* gameAssetPool = dynamic_cast(asset->m_zone->m_pools.get()); + for (const auto* menuList : *gameAssetPool->m_menu_list) { - currentParenthesisDepth++; - } - else if (expEntry.data.op == OP_RIGHTPAREN) - { - if (currentParenthesisDepth > 0) - currentParenthesisDepth--; - if (currentParenthesisDepth == 0) - return currentSearchPosition; - } - } + const auto* menuListAsset = menuList->Asset(); - return statementEnd; -} - -void MenuDumper::WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const -{ - const auto& expEntry = statement->entries[currentPos]; - - if (spaceNext && expEntry.data.op != OP_COMMA) - m_stream << " "; - - if (expEntry.data.op == OP_LEFTPAREN) - { - const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); - m_stream << "("; - WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); - m_stream << ")"; - - currentPos = closingParenPos + 1; - spaceNext = true; - } - else if (expEntry.data.op >= EXP_FUNC_STATIC_DVAR_INT && expEntry.data.op <= EXP_FUNC_STATIC_DVAR_STRING) - { - switch (expEntry.data.op) - { - case EXP_FUNC_STATIC_DVAR_INT: - m_stream << "dvarint"; - break; - - case EXP_FUNC_STATIC_DVAR_BOOL: - m_stream << "dvarbool"; - break; - - case EXP_FUNC_STATIC_DVAR_FLOAT: - m_stream << "dvarfloat"; - break; - - case EXP_FUNC_STATIC_DVAR_STRING: - m_stream << "dvarstring"; - break; - - default: - break; - } - - // Functions do not have opening parenthesis in the entries. We can just pretend they do though - const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); - m_stream << "("; - - if (closingParenPos - currentPos + 1 >= 1) - { - const auto& staticDvarEntry = statement->entries[currentPos + 1]; - if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT) + for (auto menuIndex = 0; menuIndex < menuListAsset->menuCount; menuIndex++) { - if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars && staticDvarEntry.data.operand.internals.intVal >= 0 - && staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars) - { - const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal]; - if (staticDvar && staticDvar->dvarName) - m_stream << staticDvar->dvarName; - } - else - { - m_stream << "#INVALID_DVAR_INDEX"; - } - } - else - { - m_stream << "#INVALID_DVAR_OPERAND"; + if (menuListAsset->menus[menuIndex] == menu) + return menuListAsset; } } - m_stream << ")"; - currentPos = closingParenPos + 1; - spaceNext = true; + return nullptr; } - else + + std::string GetPathForMenu(XAssetInfo* asset) { - if (expEntry.data.op >= 0 && static_cast(expEntry.data.op) < std::extent_v) - m_stream << g_expFunctionNames[expEntry.data.op]; + const auto* list = GetParentMenuList(asset); - if (expEntry.data.op >= OP_COUNT) - { - // Functions do not have opening parenthesis in the entries. We can just pretend they do though - const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); - m_stream << "("; - WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); - m_stream << ")"; - currentPos = closingParenPos + 1; - } - else - currentPos++; + if (!list) + return std::format("ui_mp/{}.menu", asset->Asset()->window.name); - spaceNext = expEntry.data.op != OP_NOT; + const fs::path p(list->name); + std::string parentPath; + if (p.has_parent_path()) + parentPath = p.parent_path().string() + "/"; + + return std::format("{}{}.menu", parentPath, asset->Asset()->window.name); } -} +} // namespace -void MenuDumper::WriteStatementOperandFunction(const Statement_s* statement, const size_t currentPos) const +namespace menu { - const auto& operand = statement->entries[currentPos].data.operand; - - if (operand.internals.function == nullptr) - return; - - if (!ObjWriting::Configuration.MenuLegacyMode) + bool MenuDumperIW5::ShouldDump(XAssetInfo* asset) { - int functionIndex = -1; - if (statement->supportingData && statement->supportingData->uifunctions.functions) + return true; + } + + void MenuDumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* menu = asset->Asset(); + const auto menuFilePath = GetPathForMenu(asset); + + if (ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST)) { - for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions; supportingFunctionIndex++) - { - if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function) - { - functionIndex = supportingFunctionIndex; - break; - } - } + // Don't dump menu file separately if the name matches the menu list + const auto* menuListParent = GetParentMenuList(asset); + if (menuListParent && menuFilePath == menuListParent->name) + return; } - if (functionIndex >= 0) - m_stream << "FUNC_" << functionIndex; - else - m_stream << "INVALID_FUNC"; - m_stream << "()"; + const auto assetFile = context.OpenAssetFile(menuFilePath); + + if (!assetFile) + return; + + auto menuWriter = CreateMenuWriterIW5(*assetFile); + + menuWriter->Start(); + menuWriter->WriteMenu(*menu); + menuWriter->End(); } - else - { - m_stream << "("; - WriteStatementSkipInitialUnnecessaryParenthesis(operand.internals.function); - m_stream << ")"; - } -} - -void MenuDumper::WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const -{ - const auto& expEntry = statement->entries[currentPos]; - - if (spaceNext) - m_stream << " "; - - const auto& operand = expEntry.data.operand; - - switch (operand.dataType) - { - case VAL_FLOAT: - m_stream << operand.internals.floatVal; - break; - - case VAL_INT: - m_stream << operand.internals.intVal; - break; - - case VAL_STRING: - WriteEscapedString(operand.internals.stringVal.string); - break; - - case VAL_FUNCTION: - WriteStatementOperandFunction(statement, currentPos); - break; - - default: - break; - } - - currentPos++; - spaceNext = true; -} - -void MenuDumper::WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const -{ - assert(startOffset <= endOffset); - assert(endOffset <= static_cast(statement->numEntries)); - - auto currentPos = startOffset; - auto spaceNext = false; - while (currentPos < endOffset) - { - const auto& expEntry = statement->entries[currentPos]; - - if (expEntry.type == EET_OPERATOR) - { - WriteStatementOperator(statement, currentPos, spaceNext); - } - else - { - WriteStatementOperand(statement, currentPos, spaceNext); - } - } -} - -void MenuDumper::WriteStatementNaive(const Statement_s* statement) const -{ - const auto entryCount = static_cast(statement->numEntries); - for (auto i = 0uz; i < entryCount; i++) - { - const auto& entry = statement->entries[i]; - if (entry.type == EET_OPERAND) - { - size_t pos = i; - bool discard = false; - WriteStatementOperand(statement, pos, discard); - } - else if (entry.data.op >= EXP_FUNC_STATIC_DVAR_INT && entry.data.op <= EXP_FUNC_STATIC_DVAR_STRING) - { - switch (entry.data.op) - { - case EXP_FUNC_STATIC_DVAR_INT: - m_stream << "dvarint"; - break; - - case EXP_FUNC_STATIC_DVAR_BOOL: - m_stream << "dvarbool"; - break; - - case EXP_FUNC_STATIC_DVAR_FLOAT: - m_stream << "dvarfloat"; - break; - - case EXP_FUNC_STATIC_DVAR_STRING: - m_stream << "dvarstring"; - break; - - default: - break; - } - - // Functions do not have opening parenthesis in the entries. We can just pretend they do though - const auto closingParenPos = FindStatementClosingParenthesis(statement, i); - m_stream << "("; - - if (closingParenPos - i + 1u >= 1u) - { - const auto& staticDvarEntry = statement->entries[i + 1u]; - if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT) - { - if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars && staticDvarEntry.data.operand.internals.intVal >= 0 - && staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars) - { - const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal]; - if (staticDvar && staticDvar->dvarName) - m_stream << staticDvar->dvarName; - } - else - { - m_stream << "#INVALID_DVAR_INDEX"; - } - } - else - { - m_stream << "#INVALID_DVAR_OPERAND"; - } - } - - m_stream << ")"; - i = closingParenPos; - } - else - { - assert(entry.data.op >= 0 && static_cast(entry.data.op) < std::extent_v); - if (entry.data.op >= 0 && static_cast(entry.data.op) < std::extent_v) - m_stream << g_expFunctionNames[entry.data.op]; - if (entry.data.op >= OP_COUNT) - m_stream << "("; - } - } -} - -void MenuDumper::WriteStatement(const Statement_s* statement) const -{ - if (statement == nullptr || statement->numEntries < 0) - return; - - WriteStatementEntryRange(statement, 0, static_cast(statement->numEntries)); -} - -void MenuDumper::WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const -{ - if (statementValue == nullptr || statementValue->numEntries < 0) - return; - - const auto statementEnd = static_cast(statementValue->numEntries); - - if (statementValue->numEntries >= 1 && statementValue->entries[0].type == EET_OPERATOR && statementValue->entries[0].data.op == OP_LEFTPAREN) - { - const auto parenthesisEnd = FindStatementClosingParenthesis(statementValue, 0); - - if (parenthesisEnd >= statementEnd) - WriteStatementEntryRange(statementValue, 1, statementEnd); - else if (parenthesisEnd == statementEnd - 1) - WriteStatementEntryRange(statementValue, 1, statementEnd - 1); - else - WriteStatementEntryRange(statementValue, 0, statementEnd); - } - else - { - WriteStatementEntryRange(statementValue, 0, statementEnd); - } -} - -void MenuDumper::WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const -{ - if (statementValue == nullptr || statementValue->numEntries < 0) - return; - - Indent(); - WriteKey(propertyKey); - - if (isBooleanStatement) - { - m_stream << "when("; - DUMP_FUNC(statementValue); - m_stream << ");\n"; - } - else - { - DUMP_FUNC(statementValue); - m_stream << ";\n"; - } -} - -void MenuDumper::WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const -{ - if (setLocalVarData == nullptr) - return; - - Indent(); - m_stream << setFunction << " " << setLocalVarData->localVarName << " "; - WriteStatement(setLocalVarData->expression); - m_stream << ";\n"; -} - -// #define WRITE_ORIGINAL_SCRIPT - -void MenuDumper::WriteUnconditionalScript(const char* script) const -{ -#ifdef WRITE_ORIGINAL_SCRIPT - Indent(); - m_stream << script << "\n"; - return; -#endif - - const auto tokenList = CreateScriptTokenList(script); - - auto isNewStatement = true; - for (const auto& token : tokenList) - { - if (isNewStatement) - { - if (token == ";") - continue; - - Indent(); - } - - if (token == ";") - { - m_stream << ";\n"; - isNewStatement = true; - continue; - } - - if (!isNewStatement) - m_stream << " "; - else - isNewStatement = false; - - if (DoesTokenNeedQuotationMarks(token)) - m_stream << "\"" << token << "\""; - else - m_stream << token; - } - - if (!isNewStatement) - m_stream << ";\n"; -} - -void MenuDumper::WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet) -{ - Indent(); - m_stream << "{\n"; - IncIndent(); - - for (auto i = 0; i < eventHandlerSet->eventHandlerCount; i++) - { - const auto* eventHandler = eventHandlerSet->eventHandlers[i]; - if (eventHandler == nullptr) - continue; - - switch (eventHandler->eventType) - { - case EVENT_UNCONDITIONAL: - WriteUnconditionalScript(eventHandler->eventData.unconditionalScript); - break; - - case EVENT_IF: - if (eventHandler->eventData.conditionalScript == nullptr || eventHandler->eventData.conditionalScript->eventExpression == nullptr - || eventHandler->eventData.conditionalScript->eventHandlerSet == nullptr) - { - continue; - } - - Indent(); - m_stream << "if ("; - WriteStatementSkipInitialUnnecessaryParenthesis(eventHandler->eventData.conditionalScript->eventExpression); - m_stream << ")\n"; - WriteMenuEventHandlerSet(eventHandler->eventData.conditionalScript->eventHandlerSet); - break; - - case EVENT_ELSE: - if (eventHandler->eventData.elseScript == nullptr) - continue; - - Indent(); - m_stream << "else\n"; - WriteMenuEventHandlerSet(eventHandler->eventData.elseScript); - break; - - case EVENT_SET_LOCAL_VAR_BOOL: - WriteSetLocalVarData("setLocalVarBool", eventHandler->eventData.setLocalVarData); - break; - - case EVENT_SET_LOCAL_VAR_INT: - WriteSetLocalVarData("setLocalVarInt", eventHandler->eventData.setLocalVarData); - break; - - case EVENT_SET_LOCAL_VAR_FLOAT: - WriteSetLocalVarData("setLocalVarFloat", eventHandler->eventData.setLocalVarData); - break; - - case EVENT_SET_LOCAL_VAR_STRING: - WriteSetLocalVarData("setLocalVarString", eventHandler->eventData.setLocalVarData); - break; - - default: - break; - } - } - - DecIndent(); - Indent(); - m_stream << "}\n"; -} - -void MenuDumper::WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue) -{ - if (eventHandlerSetValue == nullptr) - return; - - Indent(); - m_stream << propertyKey << "\n"; - WriteMenuEventHandlerSet(eventHandlerSetValue); -} - -void MenuDumper::WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const -{ - Indent(); - WriteKey(propertyKey); - m_stream << rect.x << " " << rect.y << " " << rect.w << " " << rect.h << " " << static_cast(rect.horzAlign) << " " << static_cast(rect.vertAlign) - << "\n"; -} - -void MenuDumper::WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const -{ - if (materialValue == nullptr || materialValue->info.name == nullptr) - return; - - if (materialValue->info.name[0] == ',') - WriteStringProperty(propertyKey, &materialValue->info.name[1]); - else - WriteStringProperty(propertyKey, materialValue->info.name); -} - -void MenuDumper::WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const -{ - if (soundAliasValue == nullptr) - return; - - WriteStringProperty(propertyKey, soundAliasValue->aliasName); -} - -void MenuDumper::WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const -{ - if (!item->decayActive) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << item->fxLetterTime << " " << item->fxDecayStartTime << " " << item->fxDecayDuration << "\n"; -} - -void MenuDumper::WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue) -{ - for (const auto* currentHandler = itemKeyHandlerValue; currentHandler; currentHandler = currentHandler->next) - { - if (currentHandler->key >= '!' && currentHandler->key <= '~' && currentHandler->key != '"') - { - std::ostringstream ss; - ss << "execKey \"" << static_cast(currentHandler->key) << "\""; - WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); - } - else - { - std::ostringstream ss; - ss << "execKeyInt " << currentHandler->key; - WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); - } - } -} - -void MenuDumper::WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, int floatExpressionCount) const -{ - if (!floatExpressions) - return; - - for (int i = 0; i < floatExpressionCount; i++) - { - const auto& floatExpression = floatExpressions[i]; - - if (floatExpression.target < 0 || floatExpression.target >= ITEM_FLOATEXP_TGT_COUNT) - continue; - - std::string propertyName = std::string("exp ") + floatExpressionTargetBindings[floatExpression.target].name + std::string(" ") - + floatExpressionTargetBindings[floatExpression.target].componentName; - - WriteStatementProperty(propertyName, floatExpression.expression, false); - } -} - -void MenuDumper::WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const -{ - if (!value) - return; - - Indent(); - WriteKey(propertyKey); - - const auto tokenList = CreateScriptTokenList(value); - - auto firstToken = true; - m_stream << "{ "; - for (const auto& token : tokenList) - { - if (firstToken) - firstToken = false; - else - m_stream << ";"; - m_stream << "\"" << token << "\""; - } - if (!firstToken) - m_stream << " "; - m_stream << "}\n"; -} - -void MenuDumper::WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const -{ - if (listBox->numColumns <= 0) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << listBox->numColumns << "\n"; - - for (auto col = 0; col < listBox->numColumns; col++) - { - Indent(); - for (auto i = 0u; i < MENU_KEY_SPACING; i++) - m_stream << " "; - - m_stream << listBox->columnInfo[col].xpos << " " << listBox->columnInfo[col].ypos << " " << listBox->columnInfo[col].width << " " - << listBox->columnInfo[col].height << " " << listBox->columnInfo[col].maxChars << " " << listBox->columnInfo[col].alignment << "\n"; - } -} - -void MenuDumper::WriteListBoxProperties(const itemDef_s* item) -{ - if (item->type != ITEM_TYPE_LISTBOX || item->typeData.listBox == nullptr) - return; - - const auto* listBox = item->typeData.listBox; - WriteKeywordProperty("notselectable", listBox->notselectable != 0); - WriteKeywordProperty("noscrollbars", listBox->noScrollBars != 0); - WriteKeywordProperty("usepaging", listBox->usePaging != 0); - WriteFloatProperty("elementwidth", listBox->elementWidth, 0.0f); - WriteFloatProperty("elementheight", listBox->elementHeight, 0.0f); - WriteFloatProperty("feeder", item->special, 0.0f); - WriteIntProperty("elementtype", listBox->elementStyle, 0); - WriteColumnProperty("columns", listBox); - WriteMenuEventHandlerSetProperty("doubleclick", listBox->onDoubleClick); - WriteColorProperty("selectBorder", listBox->selectBorder, COLOR_0000); - WriteMaterialProperty("selectIcon", listBox->selectIcon); - WriteStatementProperty("exp elementheight", listBox->elementHeightExp, false); -} - -void MenuDumper::WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const -{ - if (item->dvar == nullptr) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << "\"" << item->dvar << "\" " << editField->stepVal << " " << editField->minVal << " " << editField->maxVal << "\n"; -} - -void MenuDumper::WriteEditFieldProperties(const itemDef_s* item) const -{ - switch (item->type) - { - case ITEM_TYPE_TEXT: - case ITEM_TYPE_EDITFIELD: - case ITEM_TYPE_NUMERICFIELD: - case ITEM_TYPE_SLIDER: - case ITEM_TYPE_YESNO: - case ITEM_TYPE_BIND: - case ITEM_TYPE_VALIDFILEFIELD: - case ITEM_TYPE_DECIMALFIELD: - case ITEM_TYPE_UPREDITFIELD: - case ITEM_TYPE_EMAILFIELD: - case ITEM_TYPE_PASSWORDFIELD: - break; - - default: - return; - } - - if (item->typeData.editField == nullptr) - return; - - const auto* editField = item->typeData.editField; - if (std::fabs(-1.0f - editField->stepVal) >= std::numeric_limits::epsilon() - || std::fabs(-1.0f - editField->minVal) >= std::numeric_limits::epsilon() - || std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits::epsilon()) - { - WriteDvarFloatProperty("dvarFloat", item, editField); - } - else - { - WriteStringProperty("dvar", item->dvar); - } - WriteStringProperty("localvar", item->localVar); - WriteIntProperty("maxChars", editField->maxChars, 0); - WriteKeywordProperty("maxCharsGotoNext", editField->maxCharsGotoNext != 0); - WriteIntProperty("maxPaintChars", editField->maxPaintChars, 0); -} - -void MenuDumper::WriteMultiValueProperty(const multiDef_s* multiDef) const -{ - Indent(); - if (multiDef->strDef) - WriteKey("dvarStrList"); - else - WriteKey("dvarFloatList"); - - m_stream << "{"; - for (auto i = 0; i < multiDef->count; i++) - { - if (multiDef->dvarList[i] == nullptr || multiDef->strDef && multiDef->dvarStr[i] == nullptr) - continue; - - m_stream << " \"" << multiDef->dvarList[i] << "\""; - - if (multiDef->strDef) - m_stream << " \"" << multiDef->dvarStr[i] << "\""; - else - m_stream << " " << multiDef->dvarValue[i] << ""; - } - m_stream << " }\n"; -} - -void MenuDumper::WriteMultiProperties(const itemDef_s* item) const -{ - if (item->type != ITEM_TYPE_MULTI || item->typeData.multi == nullptr) - return; - - const auto* multiDef = item->typeData.multi; - - if (multiDef->count <= 0) - return; - - WriteStringProperty("dvar", item->dvar); - WriteStringProperty("localvar", item->localVar); - WriteMultiValueProperty(multiDef); -} - -void MenuDumper::WriteEnumDvarProperties(const itemDef_s* item) const -{ - if (item->type != ITEM_TYPE_DVARENUM) - return; - - WriteStringProperty("dvar", item->dvar); - WriteStringProperty("localvar", item->localVar); - WriteStringProperty("dvarEnumList", item->typeData.enumDvarName); -} - -void MenuDumper::WriteTickerProperties(const itemDef_s* item) const -{ - if (item->type != ITEM_TYPE_NEWS_TICKER || item->typeData.ticker == nullptr) - return; - - const auto* newsTickerDef = item->typeData.ticker; - WriteIntProperty("spacing", newsTickerDef->spacing, 0); - WriteIntProperty("speed", newsTickerDef->speed, 0); - WriteIntProperty("newsfeed", newsTickerDef->feedId, 0); -} - -void MenuDumper::WriteItemData(const itemDef_s* item) -{ - WriteStringProperty("name", item->window.name); - WriteStringProperty("text", item->text); - WriteKeywordProperty("textsavegame", item->itemFlags & ITEM_FLAG_SAVE_GAME_INFO); - WriteKeywordProperty("textcinematicsubtitle", item->itemFlags & ITEM_FLAG_CINEMATIC_SUBTITLE); - WriteStringProperty("group", item->window.group); - WriteRectProperty("rect", item->window.rectClient); - WriteIntProperty("style", item->window.style, 0); - WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION); - WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED); - WriteKeywordProperty("horizontalscroll", item->window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL); - WriteIntProperty("type", item->type, ITEM_TYPE_TEXT); - WriteIntProperty("border", item->window.border, 0); - WriteFloatProperty("borderSize", item->window.borderSize, 0.0f); - - if (item->visibleExp) - WriteStatementProperty("visible", item->visibleExp, true); - else if (item->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE) - WriteIntProperty("visible", 1, 0); - - WriteStatementProperty("disabled", item->disabledExp, true); - WriteIntProperty("ownerdraw", item->window.ownerDraw, 0); - WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags); - WriteIntProperty("align", item->alignment, 0); - WriteIntProperty("textalign", item->textAlignMode, 0); - WriteFloatProperty("textalignx", item->textalignx, 0.0f); - WriteFloatProperty("textaligny", item->textaligny, 0.0f); - WriteFloatProperty("textscale", item->textscale, 0.0f); - WriteIntProperty("textstyle", item->textStyle, 0); - WriteIntProperty("textfont", item->fontEnum, 0); - WriteColorProperty("backcolor", item->window.backColor, COLOR_0000); - WriteColorProperty("forecolor", item->window.foreColor, COLOR_1111); - WriteColorProperty("bordercolor", item->window.borderColor, COLOR_0000); - WriteColorProperty("outlinecolor", item->window.outlineColor, COLOR_0000); - WriteColorProperty("disablecolor", item->window.disableColor, COLOR_0000); - WriteColorProperty("glowcolor", item->glowColor, COLOR_0000); - WriteMaterialProperty("background", item->window.background); - WriteMenuEventHandlerSetProperty("onFocus", item->onFocus); - WriteMenuEventHandlerSetProperty("hasFocus", item->hasFocus); - WriteMenuEventHandlerSetProperty("leaveFocus", item->leaveFocus); - WriteMenuEventHandlerSetProperty("mouseEnter", item->mouseEnter); - WriteMenuEventHandlerSetProperty("mouseExit", item->mouseExit); - WriteMenuEventHandlerSetProperty("mouseEnterText", item->mouseEnterText); - WriteMenuEventHandlerSetProperty("mouseExitText", item->mouseExitText); - WriteMenuEventHandlerSetProperty("action", item->action); - WriteMenuEventHandlerSetProperty("accept", item->accept); - // WriteFloatProperty("special", item->special, 0.0f); - WriteSoundAliasProperty("focusSound", item->focusSound); - WriteStringProperty("dvarTest", item->dvarTest); - - if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE) - WriteMultiTokenStringProperty("enableDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE) - WriteMultiTokenStringProperty("disableDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW) - WriteMultiTokenStringProperty("showDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE) - WriteMultiTokenStringProperty("hideDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS) - WriteMultiTokenStringProperty("focusDvar", item->enableDvar); - - WriteItemKeyHandlerProperty(item->onKey); - WriteStatementProperty("exp text", item->textExp, false); - WriteStatementProperty("exp textaligny", item->textAlignYExp, false); - WriteStatementProperty("exp material", item->materialExp, false); - WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount); - WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0); - WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0); - WriteDecodeEffectProperty("decodeEffect", item); - - WriteListBoxProperties(item); - WriteEditFieldProperties(item); - WriteMultiProperties(item); - WriteEnumDvarProperties(item); - WriteTickerProperties(item); -} - -void MenuDumper::WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount) -{ - for (auto i = 0u; i < itemCount; i++) - { - StartItemDefScope(); - - WriteItemData(itemDefs[i]); - - EndScope(); - } -} - -void MenuDumper::WriteMenuData(const menuDef_t* menu) -{ - WriteStringProperty("name", menu->window.name); - WriteBoolProperty("fullscreen", menu->data->fullScreen, false); - WriteKeywordProperty("screenSpace", menu->window.staticFlags & WINDOW_FLAG_SCREEN_SPACE); - WriteKeywordProperty("decoration", menu->window.staticFlags & WINDOW_FLAG_DECORATION); - WriteRectProperty("rect", menu->window.rect); - WriteIntProperty("style", menu->window.style, 0); - WriteIntProperty("border", menu->window.border, 0); - WriteFloatProperty("borderSize", menu->window.borderSize, 0.0f); - WriteColorProperty("backcolor", menu->window.backColor, COLOR_0000); - WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111); - WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000); - WriteColorProperty("focuscolor", menu->data->focusColor, COLOR_0000); - WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000); - WriteMaterialProperty("background", menu->window.background); - WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0); - WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags); - WriteKeywordProperty("outOfBoundsClick", menu->window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK); - WriteStringProperty("soundLoop", menu->data->soundName); - WriteKeywordProperty("popup", menu->window.staticFlags & WINDOW_FLAG_POPUP); - WriteFloatProperty("fadeClamp", menu->data->fadeClamp, 0.0f); - WriteIntProperty("fadeCycle", menu->data->fadeCycle, 0); - WriteFloatProperty("fadeAmount", menu->data->fadeAmount, 0.0f); - WriteFloatProperty("fadeInAmount", menu->data->fadeInAmount, 0.0f); - WriteFloatProperty("blurWorld", menu->data->blurRadius, 0.0f); - WriteKeywordProperty("legacySplitScreenScale", menu->window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE); - WriteKeywordProperty("hiddenDuringScope", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE); - WriteKeywordProperty("hiddenDuringFlashbang", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG); - WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI); - WriteStringProperty("allowedBinding", menu->data->allowedBinding); - WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS); - - if (menu->data->visibleExp) - WriteStatementProperty("visible", menu->data->visibleExp, true); - else if (menu->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE) - WriteIntProperty("visible", 1, 0); - - WriteStatementProperty("exp rect X", menu->data->rectXExp, false); - WriteStatementProperty("exp rect Y", menu->data->rectYExp, false); - WriteStatementProperty("exp rect W", menu->data->rectWExp, false); - WriteStatementProperty("exp rect H", menu->data->rectHExp, false); - WriteStatementProperty("exp openSound", menu->data->openSoundExp, false); - WriteStatementProperty("exp closeSound", menu->data->closeSoundExp, false); - WriteStatementProperty("exp soundLoop", menu->data->soundLoopExp, false); - WriteMenuEventHandlerSetProperty("onOpen", menu->data->onOpen); - WriteMenuEventHandlerSetProperty("onClose", menu->data->onClose); - WriteMenuEventHandlerSetProperty("onRequestClose", menu->data->onCloseRequest); - WriteMenuEventHandlerSetProperty("onESC", menu->data->onESC); - WriteMenuEventHandlerSetProperty("onFocusDueToClose", menu->data->onFocusDueToClose); - WriteItemKeyHandlerProperty(menu->data->onKey); - WriteItemDefs(menu->items, menu->itemCount); -} - -MenuDumper::MenuDumper(std::ostream& stream) - : AbstractMenuDumper(stream) -{ -} - -void MenuDumper::WriteFunctionDef(const std::string& functionName, const Statement_s* statement) -{ - StartFunctionDefScope(); - - WriteStringProperty("name", functionName); - WriteStatementProperty("value", statement, false); - - EndScope(); -} - -void MenuDumper::WriteMenu(const menuDef_t* menu) -{ - StartMenuDefScope(); - - WriteMenuData(menu); - - EndScope(); -} +} // namespace menu diff --git a/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h index 9e3e3000..03577fcb 100644 --- a/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h @@ -1,57 +1,15 @@ #pragma once +#include "Dumping/AbstractAssetDumper.h" #include "Game/IW5/IW5.h" -#include "Menu/AbstractMenuDumper.h" +#include "Game/IW5/Menu/MenuDumperIW5.h" -#include - -namespace IW5 +namespace menu { - class MenuDumper : public AbstractMenuDumper + class MenuDumperIW5 final : public AbstractAssetDumper { - static size_t FindStatementClosingParenthesis(const Statement_s* statement, size_t openingParenthesisPosition); - - void WriteStatementNaive(const Statement_s* statement) const; - - void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const; - void WriteStatementOperandFunction(const Statement_s* statement, size_t currentPos) const; - void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const; - void WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const; - void WriteStatement(const Statement_s* statement) const; - void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const; - void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const; - - void WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const; - void WriteUnconditionalScript(const char* script) const; - void WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet); - void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue); - - void WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const; - void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const; - void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const; - void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const; - void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue); - void WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const; - void WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, int floatExpressionCount) const; - void WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const; - - void WriteListBoxProperties(const itemDef_s* item); - void WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const; - void WriteEditFieldProperties(const itemDef_s* item) const; - void WriteMultiValueProperty(const multiDef_s* multiDef) const; - void WriteMultiProperties(const itemDef_s* item) const; - void WriteEnumDvarProperties(const itemDef_s* item) const; - void WriteTickerProperties(const itemDef_s* item) const; - - void WriteItemData(const itemDef_s* item); - void WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount); - - void WriteMenuData(const menuDef_t* menu); - - public: - explicit MenuDumper(std::ostream& stream); - - void WriteFunctionDef(const std::string& functionName, const Statement_s* statement); - void WriteMenu(const menuDef_t* menu); + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; }; -} // namespace IW5 +} // namespace menu diff --git a/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.cpp b/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.cpp new file mode 100644 index 00000000..9a1d674c --- /dev/null +++ b/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.cpp @@ -0,0 +1,133 @@ +#include "MenuListDumperIW5.h" + +#include "Game/IW5/Menu/MenuWriterIW5.h" +#include "Menu/AbstractMenuWriter.h" +#include "MenuWriterIW5.h" +#include "ObjWriting.h" + +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +using namespace IW5; + +namespace +{ + std::vector GetAllUniqueExpressionSupportingData(const MenuList* menuList) + { + std::vector result; + std::set alreadyAddedSupportingData; + + if (menuList->menus == nullptr) + return result; + + for (auto i = 0; i < menuList->menuCount; i++) + { + if (menuList->menus[i] == nullptr) + continue; + + const auto* menu = menuList->menus[i]; + + if (menu->data == nullptr || menu->data->expressionData == nullptr) + continue; + + if (alreadyAddedSupportingData.find(menu->data->expressionData) == alreadyAddedSupportingData.end()) + { + result.push_back(menu->data->expressionData); + alreadyAddedSupportingData.emplace(menu->data->expressionData); + } + } + + return result; + } + + void DumpFunctions(menu::IWriterIW5& menuDumper, const MenuList* menuList) + { + const auto allSupportingData = GetAllUniqueExpressionSupportingData(menuList); + auto functionIndex = 0u; + + assert(allSupportingData.size() <= 1); + + for (const auto* supportingData : allSupportingData) + { + if (supportingData->uifunctions.functions == nullptr) + continue; + + for (auto i = 0; i < supportingData->uifunctions.totalFunctions; i++) + { + const auto* function = supportingData->uifunctions.functions[i]; + if (function == nullptr) + continue; + + menuDumper.WriteFunctionDef(std::format("FUNC_{}", functionIndex), function); + + functionIndex++; + } + } + } + + void DumpMenus(menu::IWriterIW5& menuDumper, const MenuList* menuList) + { + const fs::path p(menuList->name); + + std::string parentPath; + if (p.has_parent_path()) + parentPath = p.parent_path().string() + "/"; + + for (auto menuNum = 0; menuNum < menuList->menuCount; menuNum++) + { + const auto* menu = menuList->menus[menuNum]; + const auto* menuAssetName = menu->window.name; + + bool isReference = false; + if (menuAssetName && menuAssetName[0] == ',') + { + menuAssetName = &menuAssetName[1]; + isReference = true; + } + + std::ostringstream ss; + ss << parentPath << menuAssetName << ".menu"; + + const auto menuName = ss.str(); + + // If the menu was embedded directly as menu list write its data in the menu list file + if (!isReference && menuName == menuList->name) + menuDumper.WriteMenu(*menu); + else + menuDumper.IncludeMenu(ss.str()); + } + } +} // namespace + +namespace menu +{ + bool MenuListDumperIW5::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void MenuListDumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* menuList = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto menuWriter = CreateMenuWriterIW5(*assetFile); + + menuWriter->Start(); + + if (!ObjWriting::Configuration.MenuLegacyMode) + DumpFunctions(*menuWriter, menuList); + + DumpMenus(*menuWriter, menuList); + + menuWriter->End(); + } +} // namespace menu diff --git a/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.h b/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.h new file mode 100644 index 00000000..d4da415f --- /dev/null +++ b/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace menu +{ + class MenuListDumperIW5 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace menu diff --git a/src/ObjWriting/Game/IW5/Menu/MenuWriterIW5.cpp b/src/ObjWriting/Game/IW5/Menu/MenuWriterIW5.cpp new file mode 100644 index 00000000..72a9307c --- /dev/null +++ b/src/ObjWriting/Game/IW5/Menu/MenuWriterIW5.cpp @@ -0,0 +1,957 @@ +#include "MenuWriterIW5.h" + +#include "Game/IW5/MenuConstantsIW5.h" +#include "Menu/AbstractMenuWriter.h" +#include "ObjWriting.h" + +#include +#include +#include + +using namespace IW5; + +// Uncomment this macro to skip interpretative expression dumping +// #define DUMP_NAIVE + +#ifdef DUMP_NAIVE +#define DUMP_FUNC WriteStatementNaive +#else +#define DUMP_FUNC WriteStatementSkipInitialUnnecessaryParenthesis +#endif + +namespace +{ + size_t FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition) + { + assert(statement->numEntries >= 0); + assert(openingParenthesisPosition < static_cast(statement->numEntries)); + + const auto statementEnd = static_cast(statement->numEntries); + + // The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though. + // We will pretend it does since the game does sometimes leave out opening parenthesis from the entries. + auto currentParenthesisDepth = 1; + for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++) + { + const auto& expEntry = statement->entries[currentSearchPosition]; + if (expEntry.type != EET_OPERATOR) + continue; + + // Any function means a "left out" left paren + if (expEntry.data.op == OP_LEFTPAREN || expEntry.data.op >= OP_COUNT) + { + currentParenthesisDepth++; + } + else if (expEntry.data.op == OP_RIGHTPAREN) + { + if (currentParenthesisDepth > 0) + currentParenthesisDepth--; + if (currentParenthesisDepth == 0) + return currentSearchPosition; + } + } + + return statementEnd; + } +} // namespace + +namespace +{ + class MenuWriter final : public menu::AbstractBaseWriter, public menu::IWriterIW5 + { + public: + explicit MenuWriter(std::ostream& stream) + : AbstractBaseWriter(stream) + { + } + + void WriteFunctionDef(const std::string& functionName, const Statement_s* statement) override + { + StartFunctionDefScope(); + + WriteStringProperty("name", functionName); + WriteStatementProperty("value", statement, false); + + EndScope(); + } + + void WriteMenu(const menuDef_t& menu) override + { + StartMenuDefScope(); + + WriteMenuData(&menu); + + EndScope(); + } + + void Start() override + { + AbstractBaseWriter::Start(); + } + + void End() override + { + AbstractBaseWriter::End(); + } + + void IncludeMenu(const std::string& menuPath) const override + { + AbstractBaseWriter::IncludeMenu(menuPath); + } + + private: + void WriteStatementNaive(const Statement_s* statement) const + { + const auto entryCount = static_cast(statement->numEntries); + for (auto i = 0uz; i < entryCount; i++) + { + const auto& entry = statement->entries[i]; + if (entry.type == EET_OPERAND) + { + size_t pos = i; + bool discard = false; + WriteStatementOperand(statement, pos, discard); + } + else if (entry.data.op >= EXP_FUNC_STATIC_DVAR_INT && entry.data.op <= EXP_FUNC_STATIC_DVAR_STRING) + { + switch (entry.data.op) + { + case EXP_FUNC_STATIC_DVAR_INT: + m_stream << "dvarint"; + break; + + case EXP_FUNC_STATIC_DVAR_BOOL: + m_stream << "dvarbool"; + break; + + case EXP_FUNC_STATIC_DVAR_FLOAT: + m_stream << "dvarfloat"; + break; + + case EXP_FUNC_STATIC_DVAR_STRING: + m_stream << "dvarstring"; + break; + + default: + break; + } + + // Functions do not have opening parenthesis in the entries. We can just pretend they do though + const auto closingParenPos = FindStatementClosingParenthesis(statement, i); + m_stream << "("; + + if (closingParenPos - i + 1u >= 1u) + { + const auto& staticDvarEntry = statement->entries[i + 1u]; + if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT) + { + if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars + && staticDvarEntry.data.operand.internals.intVal >= 0 + && staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars) + { + const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal]; + if (staticDvar && staticDvar->dvarName) + m_stream << staticDvar->dvarName; + } + else + { + m_stream << "#INVALID_DVAR_INDEX"; + } + } + else + { + m_stream << "#INVALID_DVAR_OPERAND"; + } + } + + m_stream << ")"; + i = closingParenPos; + } + else + { + assert(entry.data.op >= 0 && static_cast(entry.data.op) < std::extent_v); + if (entry.data.op >= 0 && static_cast(entry.data.op) < std::extent_v) + m_stream << g_expFunctionNames[entry.data.op]; + if (entry.data.op >= OP_COUNT) + m_stream << "("; + } + } + } + + void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const + { + const auto& expEntry = statement->entries[currentPos]; + + if (spaceNext && expEntry.data.op != OP_COMMA) + m_stream << " "; + + if (expEntry.data.op == OP_LEFTPAREN) + { + const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); + m_stream << "("; + WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); + m_stream << ")"; + + currentPos = closingParenPos + 1; + spaceNext = true; + } + else if (expEntry.data.op >= EXP_FUNC_STATIC_DVAR_INT && expEntry.data.op <= EXP_FUNC_STATIC_DVAR_STRING) + { + switch (expEntry.data.op) + { + case EXP_FUNC_STATIC_DVAR_INT: + m_stream << "dvarint"; + break; + + case EXP_FUNC_STATIC_DVAR_BOOL: + m_stream << "dvarbool"; + break; + + case EXP_FUNC_STATIC_DVAR_FLOAT: + m_stream << "dvarfloat"; + break; + + case EXP_FUNC_STATIC_DVAR_STRING: + m_stream << "dvarstring"; + break; + + default: + break; + } + + // Functions do not have opening parenthesis in the entries. We can just pretend they do though + const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); + m_stream << "("; + + if (closingParenPos - currentPos + 1 >= 1) + { + const auto& staticDvarEntry = statement->entries[currentPos + 1]; + if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT) + { + if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars + && staticDvarEntry.data.operand.internals.intVal >= 0 + && staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars) + { + const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal]; + if (staticDvar && staticDvar->dvarName) + m_stream << staticDvar->dvarName; + } + else + { + m_stream << "#INVALID_DVAR_INDEX"; + } + } + else + { + m_stream << "#INVALID_DVAR_OPERAND"; + } + } + + m_stream << ")"; + currentPos = closingParenPos + 1; + spaceNext = true; + } + else + { + if (expEntry.data.op >= 0 && static_cast(expEntry.data.op) < std::extent_v) + m_stream << g_expFunctionNames[expEntry.data.op]; + + if (expEntry.data.op >= OP_COUNT) + { + // Functions do not have opening parenthesis in the entries. We can just pretend they do though + const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); + m_stream << "("; + WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); + m_stream << ")"; + currentPos = closingParenPos + 1; + } + else + currentPos++; + + spaceNext = expEntry.data.op != OP_NOT; + } + } + + void WriteStatementOperandFunction(const Statement_s* statement, size_t currentPos) const + { + const auto& operand = statement->entries[currentPos].data.operand; + + if (operand.internals.function == nullptr) + return; + + if (!ObjWriting::Configuration.MenuLegacyMode) + { + int functionIndex = -1; + if (statement->supportingData && statement->supportingData->uifunctions.functions) + { + for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions; + supportingFunctionIndex++) + { + if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function) + { + functionIndex = supportingFunctionIndex; + break; + } + } + } + + if (functionIndex >= 0) + m_stream << "FUNC_" << functionIndex; + else + m_stream << "INVALID_FUNC"; + m_stream << "()"; + } + else + { + m_stream << "("; + WriteStatementSkipInitialUnnecessaryParenthesis(operand.internals.function); + m_stream << ")"; + } + } + + void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const + { + const auto& expEntry = statement->entries[currentPos]; + + if (spaceNext) + m_stream << " "; + + const auto& operand = expEntry.data.operand; + + switch (operand.dataType) + { + case VAL_FLOAT: + m_stream << operand.internals.floatVal; + break; + + case VAL_INT: + m_stream << operand.internals.intVal; + break; + + case VAL_STRING: + WriteEscapedString(operand.internals.stringVal.string); + break; + + case VAL_FUNCTION: + WriteStatementOperandFunction(statement, currentPos); + break; + + default: + break; + } + + currentPos++; + spaceNext = true; + } + + void WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const + { + assert(startOffset <= endOffset); + assert(endOffset <= static_cast(statement->numEntries)); + + auto currentPos = startOffset; + auto spaceNext = false; + while (currentPos < endOffset) + { + const auto& expEntry = statement->entries[currentPos]; + + if (expEntry.type == EET_OPERATOR) + { + WriteStatementOperator(statement, currentPos, spaceNext); + } + else + { + WriteStatementOperand(statement, currentPos, spaceNext); + } + } + } + + void WriteStatement(const Statement_s* statement) const + { + if (statement == nullptr || statement->numEntries < 0) + return; + + WriteStatementEntryRange(statement, 0, static_cast(statement->numEntries)); + } + + void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const + { + if (statementValue == nullptr || statementValue->numEntries < 0) + return; + + const auto statementEnd = static_cast(statementValue->numEntries); + + if (statementValue->numEntries >= 1 && statementValue->entries[0].type == EET_OPERATOR && statementValue->entries[0].data.op == OP_LEFTPAREN) + { + const auto parenthesisEnd = FindStatementClosingParenthesis(statementValue, 0); + + if (parenthesisEnd >= statementEnd) + WriteStatementEntryRange(statementValue, 1, statementEnd); + else if (parenthesisEnd == statementEnd - 1) + WriteStatementEntryRange(statementValue, 1, statementEnd - 1); + else + WriteStatementEntryRange(statementValue, 0, statementEnd); + } + else + { + WriteStatementEntryRange(statementValue, 0, statementEnd); + } + } + + void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const + { + if (statementValue == nullptr || statementValue->numEntries < 0) + return; + + Indent(); + WriteKey(propertyKey); + + if (isBooleanStatement) + { + m_stream << "when("; + DUMP_FUNC(statementValue); + m_stream << ");\n"; + } + else + { + DUMP_FUNC(statementValue); + m_stream << ";\n"; + } + } + + void WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const + { + if (setLocalVarData == nullptr) + return; + + Indent(); + m_stream << setFunction << " " << setLocalVarData->localVarName << " "; + WriteStatement(setLocalVarData->expression); + m_stream << ";\n"; + } + + // #define WRITE_ORIGINAL_SCRIPT + void WriteUnconditionalScript(const char* script) const + { +#ifdef WRITE_ORIGINAL_SCRIPT + Indent(); + m_stream << script << "\n"; + return; +#endif + + const auto tokenList = CreateScriptTokenList(script); + + auto isNewStatement = true; + for (const auto& token : tokenList) + { + if (isNewStatement) + { + if (token == ";") + continue; + + Indent(); + } + + if (token == ";") + { + m_stream << ";\n"; + isNewStatement = true; + continue; + } + + if (!isNewStatement) + m_stream << " "; + else + isNewStatement = false; + + if (DoesTokenNeedQuotationMarks(token)) + m_stream << "\"" << token << "\""; + else + m_stream << token; + } + + if (!isNewStatement) + m_stream << ";\n"; + } + + void WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet) + { + Indent(); + m_stream << "{\n"; + IncIndent(); + + for (auto i = 0; i < eventHandlerSet->eventHandlerCount; i++) + { + const auto* eventHandler = eventHandlerSet->eventHandlers[i]; + if (eventHandler == nullptr) + continue; + + switch (eventHandler->eventType) + { + case EVENT_UNCONDITIONAL: + WriteUnconditionalScript(eventHandler->eventData.unconditionalScript); + break; + + case EVENT_IF: + if (eventHandler->eventData.conditionalScript == nullptr || eventHandler->eventData.conditionalScript->eventExpression == nullptr + || eventHandler->eventData.conditionalScript->eventHandlerSet == nullptr) + { + continue; + } + + Indent(); + m_stream << "if ("; + WriteStatementSkipInitialUnnecessaryParenthesis(eventHandler->eventData.conditionalScript->eventExpression); + m_stream << ")\n"; + WriteMenuEventHandlerSet(eventHandler->eventData.conditionalScript->eventHandlerSet); + break; + + case EVENT_ELSE: + if (eventHandler->eventData.elseScript == nullptr) + continue; + + Indent(); + m_stream << "else\n"; + WriteMenuEventHandlerSet(eventHandler->eventData.elseScript); + break; + + case EVENT_SET_LOCAL_VAR_BOOL: + WriteSetLocalVarData("setLocalVarBool", eventHandler->eventData.setLocalVarData); + break; + + case EVENT_SET_LOCAL_VAR_INT: + WriteSetLocalVarData("setLocalVarInt", eventHandler->eventData.setLocalVarData); + break; + + case EVENT_SET_LOCAL_VAR_FLOAT: + WriteSetLocalVarData("setLocalVarFloat", eventHandler->eventData.setLocalVarData); + break; + + case EVENT_SET_LOCAL_VAR_STRING: + WriteSetLocalVarData("setLocalVarString", eventHandler->eventData.setLocalVarData); + break; + + default: + break; + } + } + + DecIndent(); + Indent(); + m_stream << "}\n"; + } + + void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue) + { + if (eventHandlerSetValue == nullptr) + return; + + Indent(); + m_stream << propertyKey << "\n"; + WriteMenuEventHandlerSet(eventHandlerSetValue); + } + + void WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const + { + Indent(); + WriteKey(propertyKey); + m_stream << rect.x << " " << rect.y << " " << rect.w << " " << rect.h << " " << static_cast(rect.horzAlign) << " " + << static_cast(rect.vertAlign) << "\n"; + } + + void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const + { + if (materialValue == nullptr || materialValue->info.name == nullptr) + return; + + if (materialValue->info.name[0] == ',') + WriteStringProperty(propertyKey, &materialValue->info.name[1]); + else + WriteStringProperty(propertyKey, materialValue->info.name); + } + + void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const + { + if (soundAliasValue == nullptr) + return; + + WriteStringProperty(propertyKey, soundAliasValue->aliasName); + } + + void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const + { + if (!item->decayActive) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << item->fxLetterTime << " " << item->fxDecayStartTime << " " << item->fxDecayDuration << "\n"; + } + + void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue) + { + for (const auto* currentHandler = itemKeyHandlerValue; currentHandler; currentHandler = currentHandler->next) + { + if (currentHandler->key >= '!' && currentHandler->key <= '~' && currentHandler->key != '"') + { + std::ostringstream ss; + ss << "execKey \"" << static_cast(currentHandler->key) << "\""; + WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); + } + else + { + std::ostringstream ss; + ss << "execKeyInt " << currentHandler->key; + WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); + } + } + } + + void WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const + { + if (!value) + return; + + Indent(); + WriteKey(propertyKey); + + const auto tokenList = CreateScriptTokenList(value); + + auto firstToken = true; + m_stream << "{ "; + for (const auto& token : tokenList) + { + if (firstToken) + firstToken = false; + else + m_stream << ";"; + m_stream << "\"" << token << "\""; + } + if (!firstToken) + m_stream << " "; + m_stream << "}\n"; + } + + void WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, int floatExpressionCount) const + { + if (!floatExpressions) + return; + + for (int i = 0; i < floatExpressionCount; i++) + { + const auto& floatExpression = floatExpressions[i]; + + if (floatExpression.target < 0 || floatExpression.target >= ITEM_FLOATEXP_TGT_COUNT) + continue; + + std::string propertyName = std::string("exp ") + floatExpressionTargetBindings[floatExpression.target].name + std::string(" ") + + floatExpressionTargetBindings[floatExpression.target].componentName; + + WriteStatementProperty(propertyName, floatExpression.expression, false); + } + } + + void WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const + { + if (listBox->numColumns <= 0) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << listBox->numColumns << "\n"; + + for (auto col = 0; col < listBox->numColumns; col++) + { + Indent(); + for (auto i = 0u; i < MENU_KEY_SPACING; i++) + m_stream << " "; + + m_stream << listBox->columnInfo[col].xpos << " " << listBox->columnInfo[col].ypos << " " << listBox->columnInfo[col].width << " " + << listBox->columnInfo[col].height << " " << listBox->columnInfo[col].maxChars << " " << listBox->columnInfo[col].alignment << "\n"; + } + } + + void WriteListBoxProperties(const itemDef_s* item) + { + if (item->type != ITEM_TYPE_LISTBOX || item->typeData.listBox == nullptr) + return; + + const auto* listBox = item->typeData.listBox; + WriteKeywordProperty("notselectable", listBox->notselectable != 0); + WriteKeywordProperty("noscrollbars", listBox->noScrollBars != 0); + WriteKeywordProperty("usepaging", listBox->usePaging != 0); + WriteFloatProperty("elementwidth", listBox->elementWidth, 0.0f); + WriteFloatProperty("elementheight", listBox->elementHeight, 0.0f); + WriteFloatProperty("feeder", item->special, 0.0f); + WriteIntProperty("elementtype", listBox->elementStyle, 0); + WriteColumnProperty("columns", listBox); + WriteMenuEventHandlerSetProperty("doubleclick", listBox->onDoubleClick); + WriteColorProperty("selectBorder", listBox->selectBorder, COLOR_0000); + WriteMaterialProperty("selectIcon", listBox->selectIcon); + WriteStatementProperty("exp elementheight", listBox->elementHeightExp, false); + } + + void WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const + { + if (item->dvar == nullptr) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << "\"" << item->dvar << "\" " << editField->stepVal << " " << editField->minVal << " " << editField->maxVal << "\n"; + } + + void WriteEditFieldProperties(const itemDef_s* item) const + { + switch (item->type) + { + case ITEM_TYPE_TEXT: + case ITEM_TYPE_EDITFIELD: + case ITEM_TYPE_NUMERICFIELD: + case ITEM_TYPE_SLIDER: + case ITEM_TYPE_YESNO: + case ITEM_TYPE_BIND: + case ITEM_TYPE_VALIDFILEFIELD: + case ITEM_TYPE_DECIMALFIELD: + case ITEM_TYPE_UPREDITFIELD: + case ITEM_TYPE_EMAILFIELD: + case ITEM_TYPE_PASSWORDFIELD: + break; + + default: + return; + } + + if (item->typeData.editField == nullptr) + return; + + const auto* editField = item->typeData.editField; + if (std::fabs(-1.0f - editField->stepVal) >= std::numeric_limits::epsilon() + || std::fabs(-1.0f - editField->minVal) >= std::numeric_limits::epsilon() + || std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits::epsilon()) + { + WriteDvarFloatProperty("dvarFloat", item, editField); + } + else + { + WriteStringProperty("dvar", item->dvar); + } + WriteStringProperty("localvar", item->localVar); + WriteIntProperty("maxChars", editField->maxChars, 0); + WriteKeywordProperty("maxCharsGotoNext", editField->maxCharsGotoNext != 0); + WriteIntProperty("maxPaintChars", editField->maxPaintChars, 0); + } + + void WriteMultiValueProperty(const multiDef_s* multiDef) const + { + Indent(); + if (multiDef->strDef) + WriteKey("dvarStrList"); + else + WriteKey("dvarFloatList"); + + m_stream << "{"; + for (auto i = 0; i < multiDef->count; i++) + { + if (multiDef->dvarList[i] == nullptr || multiDef->strDef && multiDef->dvarStr[i] == nullptr) + continue; + + m_stream << " \"" << multiDef->dvarList[i] << "\""; + + if (multiDef->strDef) + m_stream << " \"" << multiDef->dvarStr[i] << "\""; + else + m_stream << " " << multiDef->dvarValue[i] << ""; + } + m_stream << " }\n"; + } + + void WriteMultiProperties(const itemDef_s* item) const + { + if (item->type != ITEM_TYPE_MULTI || item->typeData.multi == nullptr) + return; + + const auto* multiDef = item->typeData.multi; + + if (multiDef->count <= 0) + return; + + WriteStringProperty("dvar", item->dvar); + WriteStringProperty("localvar", item->localVar); + WriteMultiValueProperty(multiDef); + } + + void WriteEnumDvarProperties(const itemDef_s* item) const + { + if (item->type != ITEM_TYPE_DVARENUM) + return; + + WriteStringProperty("dvar", item->dvar); + WriteStringProperty("localvar", item->localVar); + WriteStringProperty("dvarEnumList", item->typeData.enumDvarName); + } + + void WriteTickerProperties(const itemDef_s* item) const + { + if (item->type != ITEM_TYPE_NEWS_TICKER || item->typeData.ticker == nullptr) + return; + + const auto* newsTickerDef = item->typeData.ticker; + WriteIntProperty("spacing", newsTickerDef->spacing, 0); + WriteIntProperty("speed", newsTickerDef->speed, 0); + WriteIntProperty("newsfeed", newsTickerDef->feedId, 0); + } + + void WriteItemData(const itemDef_s* item) + { + WriteStringProperty("name", item->window.name); + WriteStringProperty("text", item->text); + WriteKeywordProperty("textsavegame", item->itemFlags & ITEM_FLAG_SAVE_GAME_INFO); + WriteKeywordProperty("textcinematicsubtitle", item->itemFlags & ITEM_FLAG_CINEMATIC_SUBTITLE); + WriteStringProperty("group", item->window.group); + WriteRectProperty("rect", item->window.rectClient); + WriteIntProperty("style", item->window.style, 0); + WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION); + WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED); + WriteKeywordProperty("horizontalscroll", item->window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL); + WriteIntProperty("type", item->type, ITEM_TYPE_TEXT); + WriteIntProperty("border", item->window.border, 0); + WriteFloatProperty("borderSize", item->window.borderSize, 0.0f); + + if (item->visibleExp) + WriteStatementProperty("visible", item->visibleExp, true); + else if (item->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE) + WriteIntProperty("visible", 1, 0); + + WriteStatementProperty("disabled", item->disabledExp, true); + WriteIntProperty("ownerdraw", item->window.ownerDraw, 0); + WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags); + WriteIntProperty("align", item->alignment, 0); + WriteIntProperty("textalign", item->textAlignMode, 0); + WriteFloatProperty("textalignx", item->textalignx, 0.0f); + WriteFloatProperty("textaligny", item->textaligny, 0.0f); + WriteFloatProperty("textscale", item->textscale, 0.0f); + WriteIntProperty("textstyle", item->textStyle, 0); + WriteIntProperty("textfont", item->fontEnum, 0); + WriteColorProperty("backcolor", item->window.backColor, COLOR_0000); + WriteColorProperty("forecolor", item->window.foreColor, COLOR_1111); + WriteColorProperty("bordercolor", item->window.borderColor, COLOR_0000); + WriteColorProperty("outlinecolor", item->window.outlineColor, COLOR_0000); + WriteColorProperty("disablecolor", item->window.disableColor, COLOR_0000); + WriteColorProperty("glowcolor", item->glowColor, COLOR_0000); + WriteMaterialProperty("background", item->window.background); + WriteMenuEventHandlerSetProperty("onFocus", item->onFocus); + WriteMenuEventHandlerSetProperty("hasFocus", item->hasFocus); + WriteMenuEventHandlerSetProperty("leaveFocus", item->leaveFocus); + WriteMenuEventHandlerSetProperty("mouseEnter", item->mouseEnter); + WriteMenuEventHandlerSetProperty("mouseExit", item->mouseExit); + WriteMenuEventHandlerSetProperty("mouseEnterText", item->mouseEnterText); + WriteMenuEventHandlerSetProperty("mouseExitText", item->mouseExitText); + WriteMenuEventHandlerSetProperty("action", item->action); + WriteMenuEventHandlerSetProperty("accept", item->accept); + // WriteFloatProperty("special", item->special, 0.0f); + WriteSoundAliasProperty("focusSound", item->focusSound); + WriteStringProperty("dvarTest", item->dvarTest); + + if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE) + WriteMultiTokenStringProperty("enableDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE) + WriteMultiTokenStringProperty("disableDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW) + WriteMultiTokenStringProperty("showDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE) + WriteMultiTokenStringProperty("hideDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS) + WriteMultiTokenStringProperty("focusDvar", item->enableDvar); + + WriteItemKeyHandlerProperty(item->onKey); + WriteStatementProperty("exp text", item->textExp, false); + WriteStatementProperty("exp textaligny", item->textAlignYExp, false); + WriteStatementProperty("exp material", item->materialExp, false); + WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount); + WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0); + WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0); + WriteDecodeEffectProperty("decodeEffect", item); + + WriteListBoxProperties(item); + WriteEditFieldProperties(item); + WriteMultiProperties(item); + WriteEnumDvarProperties(item); + WriteTickerProperties(item); + } + + void WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount) + { + for (auto i = 0u; i < itemCount; i++) + { + StartItemDefScope(); + + WriteItemData(itemDefs[i]); + + EndScope(); + } + } + + void WriteMenuData(const menuDef_t* menu) + { + WriteStringProperty("name", menu->window.name); + WriteBoolProperty("fullscreen", menu->data->fullScreen, false); + WriteKeywordProperty("screenSpace", menu->window.staticFlags & WINDOW_FLAG_SCREEN_SPACE); + WriteKeywordProperty("decoration", menu->window.staticFlags & WINDOW_FLAG_DECORATION); + WriteRectProperty("rect", menu->window.rect); + WriteIntProperty("style", menu->window.style, 0); + WriteIntProperty("border", menu->window.border, 0); + WriteFloatProperty("borderSize", menu->window.borderSize, 0.0f); + WriteColorProperty("backcolor", menu->window.backColor, COLOR_0000); + WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111); + WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000); + WriteColorProperty("focuscolor", menu->data->focusColor, COLOR_0000); + WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000); + WriteMaterialProperty("background", menu->window.background); + WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0); + WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags); + WriteKeywordProperty("outOfBoundsClick", menu->window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK); + WriteStringProperty("soundLoop", menu->data->soundName); + WriteKeywordProperty("popup", menu->window.staticFlags & WINDOW_FLAG_POPUP); + WriteFloatProperty("fadeClamp", menu->data->fadeClamp, 0.0f); + WriteIntProperty("fadeCycle", menu->data->fadeCycle, 0); + WriteFloatProperty("fadeAmount", menu->data->fadeAmount, 0.0f); + WriteFloatProperty("fadeInAmount", menu->data->fadeInAmount, 0.0f); + WriteFloatProperty("blurWorld", menu->data->blurRadius, 0.0f); + WriteKeywordProperty("legacySplitScreenScale", menu->window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE); + WriteKeywordProperty("hiddenDuringScope", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE); + WriteKeywordProperty("hiddenDuringFlashbang", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG); + WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI); + WriteStringProperty("allowedBinding", menu->data->allowedBinding); + WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS); + + if (menu->data->visibleExp) + WriteStatementProperty("visible", menu->data->visibleExp, true); + else if (menu->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE) + WriteIntProperty("visible", 1, 0); + + WriteStatementProperty("exp rect X", menu->data->rectXExp, false); + WriteStatementProperty("exp rect Y", menu->data->rectYExp, false); + WriteStatementProperty("exp rect W", menu->data->rectWExp, false); + WriteStatementProperty("exp rect H", menu->data->rectHExp, false); + WriteStatementProperty("exp openSound", menu->data->openSoundExp, false); + WriteStatementProperty("exp closeSound", menu->data->closeSoundExp, false); + WriteStatementProperty("exp soundLoop", menu->data->soundLoopExp, false); + WriteMenuEventHandlerSetProperty("onOpen", menu->data->onOpen); + WriteMenuEventHandlerSetProperty("onClose", menu->data->onClose); + WriteMenuEventHandlerSetProperty("onRequestClose", menu->data->onCloseRequest); + WriteMenuEventHandlerSetProperty("onESC", menu->data->onESC); + WriteMenuEventHandlerSetProperty("onFocusDueToClose", menu->data->onFocusDueToClose); + WriteItemKeyHandlerProperty(menu->data->onKey); + WriteItemDefs(menu->items, menu->itemCount); + } + }; +} // namespace + +namespace menu +{ + std::unique_ptr CreateMenuWriterIW5(std::ostream& stream) + { + return std::make_unique(stream); + } +} // namespace menu diff --git a/src/ObjWriting/Game/IW5/Menu/MenuWriterIW5.h b/src/ObjWriting/Game/IW5/Menu/MenuWriterIW5.h new file mode 100644 index 00000000..dbf6428a --- /dev/null +++ b/src/ObjWriting/Game/IW5/Menu/MenuWriterIW5.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Game/IW5/IW5.h" +#include "Menu/IMenuWriter.h" + +#include +#include + +namespace menu +{ + class IWriterIW5 : public IWriter + { + public: + virtual void WriteFunctionDef(const std::string& functionName, const IW5::Statement_s* statement) = 0; + virtual void WriteMenu(const IW5::menuDef_t& menu) = 0; + }; + + std::unique_ptr CreateMenuWriterIW5(std::ostream& stream); +} // namespace menu diff --git a/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp b/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp index 09a16378..4cbd3f31 100644 --- a/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp +++ b/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp @@ -1,21 +1,21 @@ #include "ObjWriterIW5.h" -#include "AssetDumpers/AssetDumperAddonMapEnts.h" -#include "AssetDumpers/AssetDumperGfxImage.h" -#include "AssetDumpers/AssetDumperLeaderboardDef.h" -#include "AssetDumpers/AssetDumperLoadedSound.h" -#include "AssetDumpers/AssetDumperLocalizeEntry.h" -#include "AssetDumpers/AssetDumperMenuDef.h" -#include "AssetDumpers/AssetDumperMenuList.h" -#include "AssetDumpers/AssetDumperRawFile.h" -#include "AssetDumpers/AssetDumperScriptFile.h" -#include "AssetDumpers/AssetDumperStringTable.h" -#include "AssetDumpers/AssetDumperWeapon.h" -#include "AssetDumpers/AssetDumperWeaponAttachment.h" -#include "AssetDumpers/AssetDumperXModel.h" #include "Game/IW5/GameAssetPoolIW5.h" -#include "Material/DumperMaterialIW5.h" +#include "Game/IW5/Material/MaterialJsonDumperIW5.h" +#include "Game/IW5/XModel/XModelDumperIW5.h" +#include "Image/ImageDumperIW5.h" +#include "Leaderboard/LeaderboardJsonDumperIW5.h" +#include "Localize/LocalizeDumperIW5.h" +#include "Maps/AddonMapEntsDumperIW5.h" +#include "Menu/MenuDumperIW5.h" +#include "Menu/MenuListDumperIW5.h" #include "ObjWriting.h" +#include "RawFile/RawFileDumperIW5.h" +#include "Script/ScriptDumperIW5.h" +#include "Sound/LoadedSoundDumperIW5.h" +#include "StringTable/StringTableDumperIW5.h" +#include "Weapon/AttachmentJsonDumperIW5.h" +#include "Weapon/WeaponDumperIW5.h" using namespace IW5; @@ -33,16 +33,16 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const // DUMP_ASSET_POOL(AssetDumperPhysCollmap, m_phys_collmap, ASSET_TYPE_PHYSCOLLMAP) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) // DUMP_ASSET_POOL(AssetDumperXModelSurfs, m_xmodel_surfs, ASSET_TYPE_XMODEL_SURFS) - DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) - DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) + DUMP_ASSET_POOL(xmodel::DumperIW5, m_xmodel, ASSET_TYPE_XMODEL) + DUMP_ASSET_POOL(material::JsonDumperIW5, m_material, ASSET_TYPE_MATERIAL) // DUMP_ASSET_POOL(AssetDumperMaterialPixelShader, m_material_pixel_shader, ASSET_TYPE_PIXELSHADER) // DUMP_ASSET_POOL(AssetDumperMaterialVertexShader, m_material_vertex_shader, ASSET_TYPE_VERTEXSHADER) // DUMP_ASSET_POOL(AssetDumperMaterialVertexDeclaration, m_material_vertex_decl, ASSET_TYPE_VERTEXDECL) // DUMP_ASSET_POOL(AssetDumperMaterialTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) - DUMP_ASSET_POOL(AssetDumperGfxImage, m_image, ASSET_TYPE_IMAGE) + DUMP_ASSET_POOL(image::DumperIW5, m_image, ASSET_TYPE_IMAGE) // DUMP_ASSET_POOL(AssetDumpersnd_alias_list_t, m_sound, ASSET_TYPE_SOUND) // DUMP_ASSET_POOL(AssetDumperSndCurve, m_sound_curve, ASSET_TYPE_SOUND_CURVE) - DUMP_ASSET_POOL(AssetDumperLoadedSound, m_loaded_sound, ASSET_TYPE_LOADED_SOUND) + DUMP_ASSET_POOL(sound::LoadedSoundDumperIW5, m_loaded_sound, ASSET_TYPE_LOADED_SOUND) // DUMP_ASSET_POOL(AssetDumperclipMap_t, m_clip_map, ASSET_TYPE_CLIPMAP) // DUMP_ASSET_POOL(AssetDumperComWorld, m_com_world, ASSET_TYPE_COMWORLD) // DUMP_ASSET_POOL(AssetDumperGlassWorld, m_glass_world, ASSET_TYPE_GLASSWORLD) @@ -53,22 +53,22 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const // DUMP_ASSET_POOL(AssetDumperGfxWorld, m_gfx_world, ASSET_TYPE_GFXWORLD) // DUMP_ASSET_POOL(AssetDumperGfxLightDef, m_gfx_light_def, ASSET_TYPE_LIGHT_DEF) // DUMP_ASSET_POOL(AssetDumperFont_s, m_font, ASSET_TYPE_FONT) - DUMP_ASSET_POOL(AssetDumperMenuList, m_menu_list, ASSET_TYPE_MENULIST) - DUMP_ASSET_POOL(AssetDumperMenuDef, m_menu_def, ASSET_TYPE_MENU) - DUMP_ASSET_POOL(AssetDumperLocalizeEntry, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) - DUMP_ASSET_POOL(AssetDumperWeaponAttachment, m_attachment, ASSET_TYPE_ATTACHMENT) - DUMP_ASSET_POOL(AssetDumperWeapon, m_weapon, ASSET_TYPE_WEAPON) + DUMP_ASSET_POOL(menu::MenuListDumperIW5, m_menu_list, ASSET_TYPE_MENULIST) + DUMP_ASSET_POOL(menu::MenuDumperIW5, m_menu_def, ASSET_TYPE_MENU) + DUMP_ASSET_POOL(localize::DumperIW5, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) + DUMP_ASSET_POOL(attachment::JsonDumperIW5, m_attachment, ASSET_TYPE_ATTACHMENT) + DUMP_ASSET_POOL(weapon::DumperIW5, m_weapon, ASSET_TYPE_WEAPON) // DUMP_ASSET_POOL(AssetDumperFxEffectDef, m_fx, ASSET_TYPE_FX) // DUMP_ASSET_POOL(AssetDumperFxImpactTable, m_fx_impact_table, ASSET_TYPE_IMPACT_FX) // DUMP_ASSET_POOL(AssetDumperSurfaceFxTable, m_surface_fx_table, ASSET_TYPE_SURFACE_FX) - DUMP_ASSET_POOL(AssetDumperRawFile, m_raw_file, ASSET_TYPE_RAWFILE) - DUMP_ASSET_POOL(AssetDumperScriptFile, m_script_file, ASSET_TYPE_SCRIPTFILE) - DUMP_ASSET_POOL(AssetDumperStringTable, m_string_table, ASSET_TYPE_STRINGTABLE) - DUMP_ASSET_POOL(AssetDumperLeaderboardDef, m_leaderboard, ASSET_TYPE_LEADERBOARD) + DUMP_ASSET_POOL(raw_file::DumperIW5, m_raw_file, ASSET_TYPE_RAWFILE) + DUMP_ASSET_POOL(script::DumperIW5, m_script_file, ASSET_TYPE_SCRIPTFILE) + DUMP_ASSET_POOL(string_table::DumperIW5, m_string_table, ASSET_TYPE_STRINGTABLE) + DUMP_ASSET_POOL(leaderboard::JsonDumperIW5, m_leaderboard, ASSET_TYPE_LEADERBOARD) // DUMP_ASSET_POOL(AssetDumperStructuredDataDefSet, m_structed_data_def_set, ASSET_TYPE_STRUCTURED_DATA_DEF) // DUMP_ASSET_POOL(AssetDumperTracerDef, m_tracer, ASSET_TYPE_TRACER) // DUMP_ASSET_POOL(AssetDumperVehicleDef, m_vehicle, ASSET_TYPE_VEHICLE) - DUMP_ASSET_POOL(AssetDumperAddonMapEnts, m_addon_map_ents, ASSET_TYPE_ADDON_MAP_ENTS) + DUMP_ASSET_POOL(addon_map_ents::DumperIW5, m_addon_map_ents, ASSET_TYPE_ADDON_MAP_ENTS) return true; diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperRawFile.cpp b/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.cpp similarity index 54% rename from src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperRawFile.cpp rename to src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.cpp index a1669136..9021e3b3 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperRawFile.cpp +++ b/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.cpp @@ -1,29 +1,33 @@ -#include "AssetDumperRawFile.h" +#include "RawFileDumperIW5.h" + +#include "Utils/Logging/Log.h" #include #include #include -using namespace IW4; +using namespace IW5; -bool AssetDumperRawFile::ShouldDump(XAssetInfo* asset) +namespace raw_file { - return true; -} - -void AssetDumperRawFile::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* rawFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - if (rawFile->compressedLen > 0) + bool DumperIW5::ShouldDump(XAssetInfo* asset) { - z_stream_s zs{}; + return true; + } + void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* rawFile = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto& stream = *assetFile; + if (rawFile->compressedLen <= 0) + return; + + z_stream_s zs{}; zs.zalloc = Z_NULL; zs.zfree = Z_NULL; zs.opaque = Z_NULL; @@ -37,7 +41,7 @@ void AssetDumperRawFile::DumpAsset(AssetDumpingContext& context, XAssetInfo(rawFile->data.compressedBuffer); + zs.next_in = reinterpret_cast(rawFile->buffer); zs.avail_in = rawFile->compressedLen; Bytef buffer[0x1000]; @@ -50,7 +54,7 @@ void AssetDumperRawFile::DumpAsset(AssetDumpingContext& context, XAssetInfoname); + con::error("Inflate failed when attempting to dump rawfile '{}'", rawFile->name); inflateEnd(&zs); return; } @@ -60,8 +64,4 @@ void AssetDumperRawFile::DumpAsset(AssetDumpingContext& context, XAssetInfolen > 0) - { - stream.write(rawFile->data.buffer, rawFile->len); - } -} +} // namespace raw_file diff --git a/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.h b/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.h new file mode 100644 index 00000000..ed2ceb4f --- /dev/null +++ b/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace raw_file +{ + class DumperIW5 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace raw_file diff --git a/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.cpp b/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.cpp new file mode 100644 index 00000000..d334cb1c --- /dev/null +++ b/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.cpp @@ -0,0 +1,33 @@ +#include "ScriptDumperIW5.h" + +using namespace IW5; + +namespace script +{ + bool DumperIW5::ShouldDump(XAssetInfo* asset) + { + return true; + } + + // See https://github.com/xensik/gsc-tool#file-format for an in-depth explanation about the .gscbin format + void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + auto* scriptFile = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name + ".gscbin"); + + if (!assetFile) + return; + + auto& stream = *assetFile; + + // Dump the name and the numeric fields + stream.write(asset->m_name.c_str(), asset->m_name.size() + 1); + stream.write(reinterpret_cast(&scriptFile->compressedLen), sizeof(scriptFile->compressedLen)); + stream.write(reinterpret_cast(&scriptFile->len), sizeof(scriptFile->len)); + stream.write(reinterpret_cast(&scriptFile->bytecodeLen), sizeof(scriptFile->bytecodeLen)); + + // Dump the buffers + stream.write(scriptFile->buffer, scriptFile->compressedLen); + stream.write(reinterpret_cast(scriptFile->bytecode), scriptFile->bytecodeLen); + } +} // namespace script diff --git a/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.h b/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.h new file mode 100644 index 00000000..e1220f5f --- /dev/null +++ b/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace script +{ + class DumperIW5 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace script diff --git a/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.cpp b/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.cpp new file mode 100644 index 00000000..4a50e20b --- /dev/null +++ b/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.cpp @@ -0,0 +1,53 @@ +#include "LoadedSoundDumperIW5.h" + +#include "Sound/WavTypes.h" +#include "Sound/WavWriter.h" +#include "Utils/Logging/Log.h" + +#include + +using namespace IW5; + +namespace +{ + void DumpWavPcm(const LoadedSound* asset, std::ostream& stream) + { + const WavWriter writer(stream); + + const WavMetaData metaData{.channelCount = static_cast(asset->sound.info.channels), + .samplesPerSec = static_cast(asset->sound.info.rate), + .bitsPerSample = static_cast(asset->sound.info.bits)}; + + writer.WritePcmHeader(metaData, asset->sound.info.data_len); + writer.WritePcmData(asset->sound.data, asset->sound.info.data_len); + } +} // namespace + +namespace sound +{ + bool LoadedSoundDumperIW5::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void LoadedSoundDumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* loadedSound = asset->Asset(); + const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + switch (static_cast(loadedSound->sound.info.format)) + { + case WavFormat::PCM: + DumpWavPcm(loadedSound, stream); + break; + + default: + con::error("Unknown format {} for loaded sound: {}", loadedSound->sound.info.format, loadedSound->name); + break; + } + } +} // namespace sound diff --git a/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.h b/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.h new file mode 100644 index 00000000..8079f27d --- /dev/null +++ b/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace sound +{ + class LoadedSoundDumperIW5 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace sound diff --git a/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.cpp b/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.cpp new file mode 100644 index 00000000..2bd7fa00 --- /dev/null +++ b/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.cpp @@ -0,0 +1,42 @@ +#include "StringTableDumperIW5.h" + +#include "Csv/CsvStream.h" + +using namespace IW5; + +namespace string_table +{ + bool DumperIW5::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* stringTable = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + CsvOutputStream csv(*assetFile); + + for (auto row = 0; row < stringTable->rowCount; row++) + { + for (auto column = 0; column < stringTable->columnCount; column++) + { + const auto* cell = &stringTable->values[column + row * stringTable->columnCount]; + if (cell->string != nullptr) + { + csv.WriteColumn(cell->string); + } + else + { + csv.WriteColumn(""); + } + } + + csv.NextRow(); + } + } +} // namespace string_table diff --git a/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.h b/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.h new file mode 100644 index 00000000..e826dfd0 --- /dev/null +++ b/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace string_table +{ + class DumperIW5 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace string_table diff --git a/src/ObjWriting/Game/IW5/Weapon/JsonWeaponAttachmentWriter.cpp b/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.cpp similarity index 97% rename from src/ObjWriting/Game/IW5/Weapon/JsonWeaponAttachmentWriter.cpp rename to src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.cpp index 69f50071..c44f99a2 100644 --- a/src/ObjWriting/Game/IW5/Weapon/JsonWeaponAttachmentWriter.cpp +++ b/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.cpp @@ -1,7 +1,8 @@ -#include "JsonWeaponAttachmentWriter.h" +#include "AttachmentJsonDumperIW5.h" #include "Game/IW5/CommonIW5.h" #include "Game/IW5/Weapon/JsonWeaponAttachment.h" +#include "Weapon/AttachmentCommon.h" #include #include @@ -11,10 +12,10 @@ using namespace IW5; namespace { - class JsonDumper + class JsonDumperImpl { public: - JsonDumper(AssetDumpingContext& context, std::ostream& stream) + JsonDumperImpl(AssetDumpingContext& context, std::ostream& stream) : m_stream(stream) { } @@ -392,11 +393,21 @@ namespace }; } // namespace -namespace IW5 +namespace attachment { - void DumpWeaponAttachmentAsJson(std::ostream& stream, const WeaponAttachment* attachment, AssetDumpingContext& context) + bool JsonDumperIW5::ShouldDump(XAssetInfo* asset) { - const JsonDumper dumper(context, stream); - dumper.Dump(attachment); + return true; } -} // namespace IW5 + + void JsonDumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + const JsonDumperImpl dumper(context, *assetFile); + dumper.Dump(asset->Asset()); + } +} // namespace attachment diff --git a/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.h b/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.h new file mode 100644 index 00000000..81088a63 --- /dev/null +++ b/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace attachment +{ + class JsonDumperIW5 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace attachment diff --git a/src/ObjWriting/Game/IW5/Weapon/JsonWeaponAttachmentWriter.h b/src/ObjWriting/Game/IW5/Weapon/JsonWeaponAttachmentWriter.h deleted file mode 100644 index dc0a84c2..00000000 --- a/src/ObjWriting/Game/IW5/Weapon/JsonWeaponAttachmentWriter.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Dumping/AssetDumpingContext.h" -#include "Game/IW5/IW5.h" - -#include - -namespace IW5 -{ - void DumpWeaponAttachmentAsJson(std::ostream& stream, const WeaponAttachment* attachment, AssetDumpingContext& context); -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeapon.cpp b/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.cpp similarity index 68% rename from src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeapon.cpp rename to src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.cpp index 83e4cc93..67ab7628 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeapon.cpp +++ b/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.cpp @@ -1,10 +1,11 @@ -#include "AssetDumperWeapon.h" +#include "WeaponDumperIW5.h" #include "Game/IW5/CommonIW5.h" #include "Game/IW5/InfoString/InfoStringFromStructConverter.h" #include "Game/IW5/ObjConstantsIW5.h" #include "Game/IW5/Weapon/WeaponFields.h" #include "Weapon/AccuracyGraphWriter.h" +#include "Weapon/WeaponCommon.h" #include #include @@ -15,7 +16,7 @@ using namespace IW5; -namespace IW5 +namespace { class InfoStringFromWeaponConverter final : public InfoStringFromStructConverter { @@ -551,211 +552,217 @@ namespace IW5 return graph; } -} // namespace IW5 -void AssetDumperWeapon::CopyToFullDef(const WeaponCompleteDef* weapon, WeaponFullDef* fullDef) -{ - fullDef->weapCompleteDef = *weapon; - - if (weapon->weapDef) + void CopyToFullDef(const WeaponCompleteDef* weapon, WeaponFullDef* fullDef) { - fullDef->weapDef = *weapon->weapDef; - fullDef->weapCompleteDef.weapDef = &fullDef->weapDef; + fullDef->weapCompleteDef = *weapon; + + if (weapon->weapDef) + { + fullDef->weapDef = *weapon->weapDef; + fullDef->weapCompleteDef.weapDef = &fullDef->weapDef; + } + + if (weapon->hideTags) + { + memcpy(fullDef->hideTags, weapon->hideTags, sizeof(scr_string_t) * std::extent_v); + fullDef->weapCompleteDef.hideTags = fullDef->hideTags; + } + + if (weapon->szXAnims) + { + static_assert(std::extent_v == WEAP_ANIM_COUNT); + memcpy(fullDef->szXAnims, weapon->szXAnims, sizeof(void*) * WEAP_ANIM_COUNT); + fullDef->weapCompleteDef.szXAnims = fullDef->szXAnims; + } + + if (fullDef->weapDef.gunXModel) + { + memcpy(fullDef->gunXModel, fullDef->weapDef.gunXModel, sizeof(void*) * std::extent_v); + fullDef->weapDef.gunXModel = fullDef->gunXModel; + } + + if (fullDef->weapDef.szXAnimsRightHanded) + { + static_assert(std::extent_v == WEAP_ANIM_COUNT); + memcpy(fullDef->szXAnimsRightHanded, fullDef->weapDef.szXAnimsRightHanded, sizeof(void*) * WEAP_ANIM_COUNT); + fullDef->weapDef.szXAnimsRightHanded = fullDef->szXAnimsRightHanded; + } + + if (fullDef->weapDef.szXAnimsLeftHanded) + { + static_assert(std::extent_v == WEAP_ANIM_COUNT); + memcpy(fullDef->szXAnimsLeftHanded, fullDef->weapDef.szXAnimsLeftHanded, sizeof(void*) * WEAP_ANIM_COUNT); + fullDef->weapDef.szXAnimsLeftHanded = fullDef->szXAnimsLeftHanded; + } + + if (fullDef->weapDef.notetrackSoundMapKeys) + { + memcpy(fullDef->notetrackSoundMapKeys, + fullDef->weapDef.notetrackSoundMapKeys, + sizeof(scr_string_t) * std::extent_v); + fullDef->weapDef.notetrackSoundMapKeys = fullDef->notetrackSoundMapKeys; + } + + if (fullDef->weapDef.notetrackSoundMapValues) + { + memcpy(fullDef->notetrackSoundMapValues, + fullDef->weapDef.notetrackSoundMapValues, + sizeof(scr_string_t) * std::extent_v); + fullDef->weapDef.notetrackSoundMapValues = fullDef->notetrackSoundMapValues; + } + + if (fullDef->weapDef.notetrackRumbleMapKeys) + { + memcpy(fullDef->notetrackRumbleMapKeys, + fullDef->weapDef.notetrackRumbleMapKeys, + sizeof(scr_string_t) * std::extent_v); + fullDef->weapDef.notetrackRumbleMapKeys = fullDef->notetrackRumbleMapKeys; + } + + if (fullDef->weapDef.notetrackRumbleMapValues) + { + memcpy(fullDef->notetrackRumbleMapValues, + fullDef->weapDef.notetrackRumbleMapValues, + sizeof(scr_string_t) * std::extent_v); + fullDef->weapDef.notetrackRumbleMapValues = fullDef->notetrackRumbleMapValues; + } + + if (fullDef->weapDef.worldModel) + { + memcpy(fullDef->worldModel, fullDef->weapDef.worldModel, sizeof(void*) * std::extent_v); + fullDef->weapDef.worldModel = fullDef->worldModel; + } + + if (fullDef->weapDef.parallelBounce) + { + static_assert(std::extent_v == SURF_TYPE_COUNT); + assert(sizeof(WeaponFullDef::parallelBounce) >= sizeof(float) * std::extent_v); + memcpy(fullDef->parallelBounce, fullDef->weapDef.parallelBounce, sizeof(float) * std::extent_v); + fullDef->weapDef.parallelBounce = fullDef->parallelBounce; + } + + if (fullDef->weapDef.perpendicularBounce) + { + static_assert(std::extent_v == SURF_TYPE_COUNT); + assert(sizeof(WeaponFullDef::perpendicularBounce) >= sizeof(float) * std::extent_v); + memcpy(fullDef->perpendicularBounce, + fullDef->weapDef.perpendicularBounce, + sizeof(float) * std::extent_v); + fullDef->weapDef.perpendicularBounce = fullDef->perpendicularBounce; + } + + if (fullDef->weapDef.locationDamageMultipliers) + { + static_assert(std::extent_v == HITLOC_COUNT); + assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * std::extent_v); + memcpy(fullDef->locationDamageMultipliers, + fullDef->weapDef.locationDamageMultipliers, + sizeof(float) * std::extent_v); + fullDef->weapDef.locationDamageMultipliers = fullDef->locationDamageMultipliers; + } + + if (fullDef->weapCompleteDef.scopes) + { + memcpy(fullDef->scopes, fullDef->weapCompleteDef.scopes, sizeof(void*) * std::extent_v); + fullDef->weapCompleteDef.scopes = fullDef->scopes; + } + + if (fullDef->weapCompleteDef.underBarrels) + { + memcpy(fullDef->underBarrels, fullDef->weapCompleteDef.underBarrels, sizeof(void*) * std::extent_v); + fullDef->weapCompleteDef.underBarrels = fullDef->underBarrels; + } + + if (fullDef->weapCompleteDef.others) + { + memcpy(fullDef->others, fullDef->weapCompleteDef.others, sizeof(void*) * std::extent_v); + fullDef->weapCompleteDef.others = fullDef->others; + } } - if (weapon->hideTags) + InfoString CreateInfoString(XAssetInfo* asset) { - memcpy(fullDef->hideTags, weapon->hideTags, sizeof(scr_string_t) * std::extent_v); - fullDef->weapCompleteDef.hideTags = fullDef->hideTags; + const auto fullDef = std::make_unique(); + memset(fullDef.get(), 0, sizeof(WeaponFullDef)); + CopyToFullDef(asset->Asset(), fullDef.get()); + + InfoStringFromWeaponConverter converter(fullDef.get(), + weapon_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); } - if (weapon->szXAnims) + void DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset) { - static_assert(std::extent_v == WEAP_ANIM_COUNT); - memcpy(fullDef->szXAnims, weapon->szXAnims, sizeof(void*) * WEAP_ANIM_COUNT); - fullDef->weapCompleteDef.szXAnims = fullDef->szXAnims; - } + auto* accuracyGraphWriter = context.GetZoneAssetDumperState(); + const auto weapon = asset->Asset(); + const auto* weapDef = weapon->weapDef; - if (fullDef->weapDef.gunXModel) - { - memcpy(fullDef->gunXModel, fullDef->weapDef.gunXModel, sizeof(void*) * std::extent_v); - fullDef->weapDef.gunXModel = fullDef->gunXModel; - } - - if (fullDef->weapDef.szXAnimsRightHanded) - { - static_assert(std::extent_v == WEAP_ANIM_COUNT); - memcpy(fullDef->szXAnimsRightHanded, fullDef->weapDef.szXAnimsRightHanded, sizeof(void*) * WEAP_ANIM_COUNT); - fullDef->weapDef.szXAnimsRightHanded = fullDef->szXAnimsRightHanded; - } - - if (fullDef->weapDef.szXAnimsLeftHanded) - { - static_assert(std::extent_v == WEAP_ANIM_COUNT); - memcpy(fullDef->szXAnimsLeftHanded, fullDef->weapDef.szXAnimsLeftHanded, sizeof(void*) * WEAP_ANIM_COUNT); - fullDef->weapDef.szXAnimsLeftHanded = fullDef->szXAnimsLeftHanded; - } - - if (fullDef->weapDef.notetrackSoundMapKeys) - { - memcpy(fullDef->notetrackSoundMapKeys, - fullDef->weapDef.notetrackSoundMapKeys, - sizeof(scr_string_t) * std::extent_v); - fullDef->weapDef.notetrackSoundMapKeys = fullDef->notetrackSoundMapKeys; - } - - if (fullDef->weapDef.notetrackSoundMapValues) - { - memcpy(fullDef->notetrackSoundMapValues, - fullDef->weapDef.notetrackSoundMapValues, - sizeof(scr_string_t) * std::extent_v); - fullDef->weapDef.notetrackSoundMapValues = fullDef->notetrackSoundMapValues; - } - - if (fullDef->weapDef.notetrackRumbleMapKeys) - { - memcpy(fullDef->notetrackRumbleMapKeys, - fullDef->weapDef.notetrackRumbleMapKeys, - sizeof(scr_string_t) * std::extent_v); - fullDef->weapDef.notetrackRumbleMapKeys = fullDef->notetrackRumbleMapKeys; - } - - if (fullDef->weapDef.notetrackRumbleMapValues) - { - memcpy(fullDef->notetrackRumbleMapValues, - fullDef->weapDef.notetrackRumbleMapValues, - sizeof(scr_string_t) * std::extent_v); - fullDef->weapDef.notetrackRumbleMapValues = fullDef->notetrackRumbleMapValues; - } - - if (fullDef->weapDef.worldModel) - { - memcpy(fullDef->worldModel, fullDef->weapDef.worldModel, sizeof(void*) * std::extent_v); - fullDef->weapDef.worldModel = fullDef->worldModel; - } - - if (fullDef->weapDef.parallelBounce) - { - static_assert(std::extent_v == SURF_TYPE_COUNT); - assert(sizeof(WeaponFullDef::parallelBounce) >= sizeof(float) * std::extent_v); - memcpy(fullDef->parallelBounce, fullDef->weapDef.parallelBounce, sizeof(float) * std::extent_v); - fullDef->weapDef.parallelBounce = fullDef->parallelBounce; - } - - if (fullDef->weapDef.perpendicularBounce) - { - static_assert(std::extent_v == SURF_TYPE_COUNT); - assert(sizeof(WeaponFullDef::perpendicularBounce) >= sizeof(float) * std::extent_v); - memcpy(fullDef->perpendicularBounce, fullDef->weapDef.perpendicularBounce, sizeof(float) * std::extent_v); - fullDef->weapDef.perpendicularBounce = fullDef->perpendicularBounce; - } - - if (fullDef->weapDef.locationDamageMultipliers) - { - static_assert(std::extent_v == HITLOC_COUNT); - assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * std::extent_v); - memcpy(fullDef->locationDamageMultipliers, - fullDef->weapDef.locationDamageMultipliers, - sizeof(float) * std::extent_v); - fullDef->weapDef.locationDamageMultipliers = fullDef->locationDamageMultipliers; - } - - if (fullDef->weapCompleteDef.scopes) - { - memcpy(fullDef->scopes, fullDef->weapCompleteDef.scopes, sizeof(void*) * std::extent_v); - fullDef->weapCompleteDef.scopes = fullDef->scopes; - } - - if (fullDef->weapCompleteDef.underBarrels) - { - memcpy(fullDef->underBarrels, fullDef->weapCompleteDef.underBarrels, sizeof(void*) * std::extent_v); - fullDef->weapCompleteDef.underBarrels = fullDef->underBarrels; - } - - if (fullDef->weapCompleteDef.others) - { - memcpy(fullDef->others, fullDef->weapCompleteDef.others, sizeof(void*) * std::extent_v); - fullDef->weapCompleteDef.others = fullDef->others; - } -} - -InfoString AssetDumperWeapon::CreateInfoString(XAssetInfo* asset) -{ - const auto fullDef = std::make_unique(); - memset(fullDef.get(), 0, sizeof(WeaponFullDef)); - CopyToFullDef(asset->Asset(), fullDef.get()); - - InfoStringFromWeaponConverter converter(fullDef.get(), - weapon_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -void AssetDumperWeapon::DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset) -{ - auto* accuracyGraphWriter = context.GetZoneAssetDumperState(); - const auto weapon = asset->Asset(); - const auto* weapDef = weapon->weapDef; - - if (!weapDef) - return; - - if (weapDef->aiVsAiAccuracyGraphName && weapDef->originalAiVsAiAccuracyGraphKnots - && accuracyGraphWriter->ShouldDumpAiVsAiGraph(weapDef->aiVsAiAccuracyGraphName)) - { - AccuracyGraphWriter::DumpAiVsAiGraph( - context, - ConvertAccuracyGraph(weapDef->aiVsAiAccuracyGraphName, weapDef->originalAiVsAiAccuracyGraphKnots, weapDef->originalAiVsAiAccuracyGraphKnotCount)); - } - - if (weapDef->aiVsPlayerAccuracyGraphName && weapDef->originalAiVsPlayerAccuracyGraphKnots - && accuracyGraphWriter->ShouldDumpAiVsPlayerGraph(weapDef->aiVsPlayerAccuracyGraphName)) - { - AccuracyGraphWriter::DumpAiVsPlayerGraph(context, - ConvertAccuracyGraph(weapDef->aiVsPlayerAccuracyGraphName, - weapDef->originalAiVsPlayerAccuracyGraphKnots, - weapDef->originalAiVsPlayerAccuracyGraphKnotCount)); - } -} - -bool AssetDumperWeapon::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperWeapon::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // TODO: only dump infostring fields when non-default - - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_WEAPON, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("weapons/" + asset->m_name); - - if (!assetFile) + if (!weapDef) return; - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_WEAPON); - stream.write(stringValue.c_str(), stringValue.size()); + if (weapDef->aiVsAiAccuracyGraphName && weapDef->originalAiVsAiAccuracyGraphKnots + && accuracyGraphWriter->ShouldDumpAiVsAiGraph(weapDef->aiVsAiAccuracyGraphName)) + { + AccuracyGraphWriter::DumpAiVsAiGraph(context, + ConvertAccuracyGraph(weapDef->aiVsAiAccuracyGraphName, + weapDef->originalAiVsAiAccuracyGraphKnots, + weapDef->originalAiVsAiAccuracyGraphKnotCount)); + } + + if (weapDef->aiVsPlayerAccuracyGraphName && weapDef->originalAiVsPlayerAccuracyGraphKnots + && accuracyGraphWriter->ShouldDumpAiVsPlayerGraph(weapDef->aiVsPlayerAccuracyGraphName)) + { + AccuracyGraphWriter::DumpAiVsPlayerGraph(context, + ConvertAccuracyGraph(weapDef->aiVsPlayerAccuracyGraphName, + weapDef->originalAiVsPlayerAccuracyGraphKnots, + weapDef->originalAiVsPlayerAccuracyGraphKnotCount)); + } + } +} // namespace + +namespace weapon +{ + bool DumperIW5::ShouldDump(XAssetInfo* asset) + { + return true; } - DumpAccuracyGraphs(context, asset); -} + void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // TODO: only dump infostring fields when non-default + + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_WEAPON, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_WEAPON); + stream.write(stringValue.c_str(), stringValue.size()); + } + + DumpAccuracyGraphs(context, asset); + } +} // namespace weapon diff --git a/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.h b/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.h new file mode 100644 index 00000000..3e80c669 --- /dev/null +++ b/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" +#include "InfoString/InfoString.h" + +namespace weapon +{ + class DumperIW5 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace weapon diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperGfxImage.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperGfxImage.h deleted file mode 100644 index b9f89b36..00000000 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperGfxImage.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T5/T5.h" -#include "Image/IImageWriter.h" - -#include - -namespace T5 -{ - class AssetDumperGfxImage final : public AbstractAssetDumper - { - std::unique_ptr m_writer; - - [[nodiscard]] std::string GetAssetFileName(const XAssetInfo& asset) const; - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - - public: - AssetDumperGfxImage(); - }; -} // namespace T5 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperLocalizeEntry.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperLocalizeEntry.cpp deleted file mode 100644 index 0377f5cf..00000000 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperLocalizeEntry.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "AssetDumperLocalizeEntry.h" - -#include "Dumping/Localize/StringFileDumper.h" -#include "Localize/LocalizeCommon.h" - -#include -#include - -using namespace T5; - -void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - if (pool->m_asset_lookup.empty()) - return; - - const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); - const auto assetFile = context.OpenAssetFile(std::format("{}/localizedstrings/{}.str", language, context.m_zone.m_name)); - - if (assetFile) - { - StringFileDumper stringFileDumper(context.m_zone, *assetFile); - - stringFileDumper.SetLanguageName(language); - - // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. - stringFileDumper.SetConfigFile(R"(C:/projects/cod/t5/bin/StringEd.cfg)"); - - stringFileDumper.SetNotes(""); - - for (auto* localizeEntry : *pool) - { - stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); - } - - stringFileDumper.Finalize(); - } - else - { - std::cerr << std::format("Could not create string file for dumping localized strings of zone '{}'\n", context.m_zone.m_name); - } -} diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperLocalizeEntry.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperLocalizeEntry.h deleted file mode 100644 index e4aec7e8..00000000 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperLocalizeEntry.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T5/T5.h" - -namespace T5 -{ - class AssetDumperLocalizeEntry final : public IAssetDumper - { - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - }; -} // namespace T5 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperPhysConstraints.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperPhysConstraints.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperPhysConstraints.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperPhysConstraints.h deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperPhysPreset.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperPhysPreset.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperPhysPreset.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperPhysPreset.h deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.cpp deleted file mode 100644 index 45827890..00000000 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#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 << "\"\n"; - 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 << "\n"; - return; - } - - if (outLen > GSC_MAX_SIZE) - { - std::cout << "Invalid size of gsc file \"" << rawFile->name << "\": " << outLen << "\n"; - 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 << "\"\n"; - 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; -} - -void AssetDumperRawFile::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* rawFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - 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); - } -} diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.h deleted file mode 100644 index b2d10763..00000000 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperRawFile.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T5/T5.h" - -namespace T5 -{ - class AssetDumperRawFile final : public AbstractAssetDumper - { - constexpr static size_t GSC_MAX_SIZE = 0xC000000; - - static void DumpGsc(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T5 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperSndBank.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperSndBank.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperSndBank.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperSndBank.h deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperStringTable.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperStringTable.cpp deleted file mode 100644 index 77899c6f..00000000 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperStringTable.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "AssetDumperStringTable.h" - -#include "Csv/CsvStream.h" - -using namespace T5; - -bool AssetDumperStringTable::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperStringTable::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* stringTable = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - CsvOutputStream csv(*assetFile); - - for (auto row = 0; row < stringTable->rowCount; row++) - { - for (auto column = 0; column < stringTable->columnCount; column++) - { - const auto* cell = &stringTable->values[column + row * stringTable->columnCount]; - if (cell->string != nullptr) - { - csv.WriteColumn(cell->string); - } - else - { - csv.WriteColumn(""); - } - } - - csv.NextRow(); - } -} diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperStringTable.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperStringTable.h deleted file mode 100644 index 64b2739c..00000000 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperStringTable.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T5/T5.h" - -namespace T5 -{ - class AssetDumperStringTable final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T5 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperWeapon.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperWeapon.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperWeapon.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperWeapon.h deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp deleted file mode 100644 index 8eb958bb..00000000 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "AssetDumperXModel.h" - -#include "Game/T5/XModel/XModelDumperT5.h" - -using namespace T5; - -bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) -{ - return !asset->m_name.empty() && asset->m_name[0] != ','; -} - -void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - DumpXModel(context, asset); -} diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h deleted file mode 100644 index 08a1a274..00000000 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T5/T5.h" - -namespace T5 -{ - class AssetDumperXModel final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T5 diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperGfxImage.cpp b/src/ObjWriting/Game/T5/Image/ImageDumperT5.cpp similarity index 52% rename from src/ObjWriting/Game/T5/AssetDumpers/AssetDumperGfxImage.cpp rename to src/ObjWriting/Game/T5/Image/ImageDumperT5.cpp index 6bb99fcf..6843efa8 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperGfxImage.cpp +++ b/src/ObjWriting/Game/T5/Image/ImageDumperT5.cpp @@ -1,10 +1,12 @@ -#include "AssetDumperGfxImage.h" +#include "ImageDumperT5.h" #include "Image/DdsWriter.h" #include "Image/Dx9TextureLoader.h" +#include "Image/ImageCommon.h" #include "Image/IwiLoader.h" #include "Image/IwiWriter13.h" #include "ObjWriting.h" +#include "Utils/Logging/Log.h" #include #include @@ -35,11 +37,11 @@ namespace std::unique_ptr LoadImageFromIwi(const GfxImage& image, ISearchPath& searchPath) { - const auto imageFileName = std::format("images/{}.iwi", image.name); + const auto imageFileName = image::GetFileNameForAsset(image.name, ".iwi"); const auto filePathImage = searchPath.Open(imageFileName); if (!filePathImage.IsOpen()) { - std::cerr << std::format("Could not find data for image \"{}\"\n", image.name); + con::error("Could not find data for image \"{}\"", image.name); return nullptr; } @@ -55,48 +57,43 @@ namespace } } // namespace -AssetDumperGfxImage::AssetDumperGfxImage() +namespace image { - switch (ObjWriting::Configuration.ImageOutputFormat) + DumperT5::DumperT5() { - case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: - m_writer = std::make_unique(); - break; - case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: - m_writer = std::make_unique(); - break; - default: - assert(false); - m_writer = nullptr; - break; + switch (ObjWriting::Configuration.ImageOutputFormat) + { + case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: + m_writer = std::make_unique(); + break; + case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: + m_writer = std::make_unique(); + break; + default: + assert(false); + m_writer = nullptr; + break; + } } -} -bool AssetDumperGfxImage::ShouldDump(XAssetInfo* asset) -{ - return true; -} + bool DumperT5::ShouldDump(XAssetInfo* asset) + { + return true; + } -std::string AssetDumperGfxImage::GetAssetFileName(const XAssetInfo& asset) const -{ - auto cleanAssetName = asset.m_name; - std::ranges::replace(cleanAssetName, '*', '_'); + void DumperT5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* image = asset->Asset(); + const auto texture = LoadImageData(context.m_obj_search_path, *image); + if (!texture) + return; - return std::format("images/{}{}", cleanAssetName, m_writer->GetFileExtension()); -} + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name, m_writer->GetFileExtension())); -void AssetDumperGfxImage::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* image = asset->Asset(); - const auto texture = LoadImageData(context.m_obj_search_path, *image); - if (!texture) - return; + if (!assetFile) + return; - const auto assetFile = context.OpenAssetFile(GetAssetFileName(*asset)); - - if (!assetFile) - return; - - auto& stream = *assetFile; - m_writer->DumpImage(stream, texture.get()); -} + auto& stream = *assetFile; + m_writer->DumpImage(stream, texture.get()); + } +} // namespace image diff --git a/src/ObjWriting/Game/T5/Image/ImageDumperT5.h b/src/ObjWriting/Game/T5/Image/ImageDumperT5.h new file mode 100644 index 00000000..0be6b9bb --- /dev/null +++ b/src/ObjWriting/Game/T5/Image/ImageDumperT5.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T5/T5.h" +#include "Image/IImageWriter.h" + +#include + +namespace image +{ + class DumperT5 final : public AbstractAssetDumper + { + public: + DumperT5(); + + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + + private: + std::unique_ptr m_writer; + }; +} // namespace image diff --git a/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.cpp b/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.cpp new file mode 100644 index 00000000..0ed7388b --- /dev/null +++ b/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.cpp @@ -0,0 +1,45 @@ +#include "LocalizeDumperT5.h" + +#include "Dumping/Localize/StringFileDumper.h" +#include "Localize/LocalizeCommon.h" +#include "Utils/Logging/Log.h" + +#include +#include + +using namespace T5; + +namespace localize +{ + void DumperT5::DumpPool(AssetDumpingContext& context, AssetPool* pool) + { + if (pool->m_asset_lookup.empty()) + return; + + const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); + const auto assetFile = context.OpenAssetFile(std::format("{}/localizedstrings/{}.str", language, context.m_zone.m_name)); + + if (assetFile) + { + StringFileDumper stringFileDumper(context.m_zone, *assetFile); + + stringFileDumper.SetLanguageName(language); + + // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. + stringFileDumper.SetConfigFile(R"(C:/projects/cod/t5/bin/StringEd.cfg)"); + + stringFileDumper.SetNotes(""); + + for (auto* localizeEntry : *pool) + { + stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); + } + + stringFileDumper.Finalize(); + } + else + { + con::error("Could not create string file for dumping localized strings of zone '{}'", context.m_zone.m_name); + } + } +} // namespace localize diff --git a/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.h b/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.h new file mode 100644 index 00000000..321a9aef --- /dev/null +++ b/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T5/T5.h" + +namespace localize +{ + class DumperT5 final : public IAssetDumper + { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + }; +} // namespace localize diff --git a/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.cpp b/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.cpp new file mode 100644 index 00000000..44804ceb --- /dev/null +++ b/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.cpp @@ -0,0 +1,525 @@ +#include "MaterialConstantZoneStateT5.h" + +#include "Game/T5/CommonT5.h" +#include "Game/T5/GameAssetPoolT5.h" +#include "Game/T5/GameT5.h" +#include "ObjWriting.h" +#include "Zone/ZoneRegistry.h" + +namespace T5 +{ + const char* KNOWN_CONSTANT_NAMES[]{ + "AngularVelocityScale", + "AnimSpeed", + "Background", + "BackgroundColor", + "BackgroundNoise", + "BakedLightingIntensity", + "BloodBrightness", + "BloodIntensity", + "BlurAmount", + "CapWidth", + "Char_Size", + "Char_Width", + "Coarseness", + "Color", + "ColorAmount", + "ColorBias", + "Color_Map_Noise", + "Color_Map_Scale", + "Color_Map_Size_Scale", + "DDXScale", + "DDYScale", + "DarkenAmount", + "DarkenPower", + "Detail_Amount", + "Detail_Normal_Tile", + "Diffuse_Normal_Height_Facing", + "Dimensions", + "DispersionAmount", + "Dolly", + "EdgeColor", + "EdgeHarshness", + "EdgeIntensity", + "EdgeMaxDist", + "EdgeMinDist", + "EdgeSize", + "Edge_Color_Multiplier", + "Emissive_Amount", + "EnemiesColor", + "Exposure", + "FPS", + "Fade_Distance", + "Fill_Direction", + "Fill_Direction2", + "FirstFrame", + "FlareIntensity", + "FlareScale", + "FlattenEdges", + "Flicker_Max", + "Flicker_Min", + "Flicker_Seed", + "Flicker_Speed", + "Font_Color", + "Gamma", + "GlossAmount", + "Gloss_Amount", + "Glow_Alt_Color", + "Glow_Color", + "Glow_Falloff", + "GradientColor", + "GradientMax", + "GradientMin", + "Grain_Amount", + "Grain_Color", + "Grid", + "Hardness", + "Heart_Rate_Offset", + "Heart_Rate_Scale", + "Highlight_1_Brightness", + "Highlight_1_Sharpness", + "Highlight_2_Brightness", + "Highlight_2_Sharpness", + "Highlight_2_Size", + "Hightlight_1_Size", + "Holo_Scale", + "LastFrame", + "Layer1Alpha", + "Layer1Depth", + "Layer1Offset", + "Layer1OffsetBobbleDelay", + "Layer1OffsetBobbleSpeedAndSize", + "Layer1Origin", + "Layer1Rotation", + "Layer1Scale", + "Layer1ScaleBobbleDelay", + "Layer1ScaleBobbleSpeedAndSize", + "Layer1Scroll", + "Layer2Alpha", + "Layer2Depth", + "Layer2Offset", + "Layer2OffsetBobbleDelay", + "Layer2OffsetBobbleSpeedAndSize", + "Layer2Origin", + "Layer2Rotation", + "Layer2Scale", + "Layer2ScaleBobbleDelay", + "Layer2ScaleBobbleSpeedAndSize", + "Layer2Scroll", + "Layer3Alpha", + "Layer3Depth", + "Layer3Offset", + "Layer3Origin", + "Layer3Rotation", + "Layer3Scale", + "Layer3Scroll", + "Layer4Alpha", + "Layer4Depth", + "Layer4Offset", + "Layer4Origin", + "Layer4Rotation", + "Layer4Scale", + "Layer4Scroll", + "LineColor", + "LineNoise", + "LineWidth", + "MaxDepth", + "MaxFlickerColor", + "MaxPulseDepth", + "MaxResolution", + "Max_Color", + "Maximum_Distance", + "Midlayer_Depth", + "MinDepth", + "MinFlickerColor", + "MinResolution", + "MinStatic", + "MinVelocityFraction", + "Min_Color", + "Min_Player_Intensity", + "MomentumColor", + "NegativeColor", + "NoisePower", + "Noise_Scale", + "NormalHeightMultiplier", + "Normal_Detail_Height", + "Normal_Detail_Scale", + "Normal_Map_Size_Scale", + "Normal_Variance_Scale", + "NumFrames", + "Outline_Lookup_Scale", + "OverallAmount", + "OverallBrightness", + "Overlay_Color", + "P1", + "P2", + "Padding", + "Player_Color_Multiplier", + "Player_Lookup_Scale", + "PositiveColor", + "Power", + "PulseColor", + "PulseInterval", + "PulseTime", + "Pulse_Color_Multiplier", + "Pulse_Lookup_Scale", + "Radius", + "ReflectionAmount", + "Reflection_Amount", + "Reflection_Blur", + "Reticle_Alt_Color", + "Reticle_Color", + "Row_Chars_", + "Scale", + "ScanlineColor", + "ScanlineIntensity", + "ScanlineOffset", + "ScanlinePower", + "ScanlineSpeed", + "ScatterAmount", + "ScatterSize", + "SceneNoise", + "SparkleBrightness", + "SparkleDensity", + "SparklePower", + "SparkleProbeAmount", + "SparkleScale", + "SparkleSpecAmount", + "SparkleWash", + "SpecGloss_Map_Size_Scale", + "SpecularAmount", + "SpecularColor", + "Specular_Amount", + "Specular_Decay_Threshold", + "Speed", + "StaticAmount", + "StaticLookupSpeed", + "StaticLookupX", + "StaticScale", + "Static_Size", + "Static_amount", + "TearLookupMaxX", + "TearLookupMinX", + "TearLookupSpeed", + "TearMultiplier", + "TearPower", + "Thickness", + "TickMarkColorAndHarshness", + "Tint", + "VelocityScale", + "VignetteMultiplier", + "VignettePower", + "WarpAmount", + "WarpHeight", + "WarpScale", + "WarpSpeed", + "WashOut", + "WashoutMultiply", + "WaterDirection", + "WaterHeight", + "WaterRefraction", + "WaterScale1", + "WaterScale2", + "WaterSpeed1", + "WaterSpeed2", + "Zoom", + "alphaDissolveParms", + "alphaRevealParms", + "alphaRevealParms1", + "alphaRevealParms2", + "alphaRevealParms3", + "alphaRevealParms4", + "clipSpaceLookupOffset", + "clipSpaceLookupScale", + "cloudsFeather", + "cloudsHeights", + "cloudsUVMad1", + "cloudsUVMad2", + "cloudsUVMul1", + "cloudsUVMul2", + "codeMeshArg", + "colorDetailScale", + "colorObjMax", + "colorObjMaxBaseBlend", + "colorObjMin", + "colorObjMinBaseBlend", + "colorTint", + "debugBumpmap", + "debugPerformance", + "detailScale", + "detailScale1", + "detailScale2", + "detailScale3", + "detailScale4", + "distortionScale", + "dofEquationScene", + "dofEquationViewModelAndFarBlur", + "dofLerpBias", + "dofLerpDownBias", + "dofLerpDownScale", + "dofLerpScale", + "dofLerpUpBias", + "dofLerpUpScale", + "dofRowDelta", + "eyeOffsetParms", + "falloffBeginColor", + "falloffEndColor", + "falloffParms", + "featherParms", + "flagParams", + "framebufferRead", + "gameTime", + "hdrAmount", + "inverseTransposeWorldMatrix", + "inverseTransposeWorldViewMatrix", + "inverseWorldMatrix", + "inverseWorldViewMatrix", + "motionblurDirectionAndMagnitude", + "occlusionAmount", + "occlusionAmount1", + "occlusionAmount2", + "occlusionAmount3", + "occlusionAmount4", + "particleCloudColor", + "particleCloudMatrix", + "particleCloudVelWorld", + "resizeParams1", + "resizeParams2", + "scaleRGB", + "scriptVector0", + "scriptVector1", + "scriptVector2", + "scriptVector3", + "scriptVector4", + "scriptVector5", + "scriptVector6", + "scriptVector7", + "skyBoxCloudWeights", + "skyBoxRotationSize", + "skyColorParms", + "spotLightWeight", + "treeCanopyLightingParms", + "treeCanopyScatterColor", + "treeCanopySwayParms", + "ui3dUVSetup0", + "ui3dUVSetup1", + "ui3dUVSetup2", + "ui3dUVSetup3", + "ui3dUVSetup4", + "ui3dUVSetup5", + "uvAnimParms", + "uvScroll", + "viewMatrix", + "weaponParam0", + "weaponParam1", + "weaponParam2", + "weaponParam3", + "weaponParam4", + "weaponParam5", + "weaponParam6", + "weaponParam7", + "weaponParam8", + "weaponParam9", + "worldViewMatrix", + "worldViewProjectionMatrix", + }; + + const char* KNOWN_TEXTURE_DEF_NAMES[]{ + "AddMap", + "Blip_Mask", + "BlockNoise", + "CS_Z_buffer", + "Camo_Detail_Map", + "Color_Map", + "CompassMap", + "Detail_Map", + "Diffuse", + "Diffuse_Map", + "DpadTexture", + "FontTextutre", + "Grain_Map", + "GridTexture", + "GrimeMap", + "Heart_Rate_Image", + "Hologram_Diffuse", + "Image", + "Layer1Map", + "Layer2Map", + "Layer3Map", + "Layer4Map", + "Lookup", + "Lookup2", + "LookupMap", + "Mask", + "Noise", + "Noise_Texture", + "NormalDetailMap", + "Normal_Detail_Map", + "Normal_Map", + "Overlay_Map", + "Reflection_Mask", + "Reveal_Map", + "Rim_Color_Mask", + "Rim_Specular_Mask", + "Rim_Occlusion_Mask", + "Scanline", + "SparkleMap", + "SpecularAndGloss", + "SpecularAndGloss2", + "Specular_Color_Map", + "Specular_Gloss_Map", + "Specular_Map", + "SpotShadowSamplerState", + "SpotShadowState", + "SpriteMap", + "Static", + "StaticMap", + "Static_Noise_Map", + "SunShadowSamplerState", + "SunShadowState", + "Surface_Normal_Map", + "ThermalMapMask", + "Thermal_Gradient", + "Thermal_Map", + "TickMarkMaterial", + "Tile", + "WarpMap", + "WaterNormalMap", + "Weapon_Normal_Map", + "Weapon_Specular_Map", + "Wireframe", + "ZBuffer_Map", + "attenuation", + "attenuationSampler", + "baseLut2D", + "baseLut2DSampler", + "cinematicA", + "cinematicASampler", + "cinematicCb", + "cinematicCbSampler", + "cinematicCr", + "cinematicCrSampler", + "cinematicY", + "cinematicYSampler", + "codeTexture0", + "codeTexture1", + "codeTexture2", + "color", + "colorDetailMap", + "colorDetailMapSampler", + "colorMap", + "colorMap1", + "colorMap2", + "colorMap2D", + "colorMapPostSun", + "colorMapPostSunSampler", + "colorMapSampler", + "colorMapSampler1", + "colorMapSampler2", + "colorSampler", + "detailMap", + "detailMapSampler", + "dlightAttenuation", + "dlightAttenuationSampler", + "floatZ", + "floatZSampler", + "imageSampler", + "lightmapSamplerSecondary", + "lightmapSecondary", + "lut2D", + "lut2DSampler", + "lut3D", + "lut3DSampler", + "missileCam", + "missileCamSampler", + "modelLighting", + "modelLightingSampler", + "normalMap", + "normalMap1", + "normalMap2", + "normalMapSampler", + "normalMapSampler1", + "normalMapSampler2", + "occlusionMap", + "occlusionMapSampler", + "occMap", + "occMapSampler", + "outdoorMap", + "outdoorMapSampler", + "radiantDiffuseMap", + "rawFloatZ", + "rawFloatZSampler", + "reflectionProbe", + "reflectionProbeSampler", + "shadowmapSamplerSpot", + "shadowmapSamplerSun", + "shadowmapSpot", + "shadowmapSun", + "sonarColor", + "sonarColorSampler", + "sonarDepth", + "sonarDepthSampler", + "source", + "specularMap", + "specularMap1", + "specularMap2", + "specularMapSampler", + "specularMapSampler1", + "specularMapSampler2", + "stencil", + "stencilSampler", + "ui3d", + "ui3dSampler", + }; + + void MaterialConstantZoneState::ExtractNamesFromZoneInternal() + { + for (const auto* zone : ZoneRegistry::GetRegistryForGame(GameId::T5)->Zones()) + { + const auto* assetPools = dynamic_cast(zone->m_pools.get()); + if (!assetPools) + return; + + for (const auto* techniqueSetInfo : *assetPools->m_technique_set) + { + const auto* techniqueSet = techniqueSetInfo->Asset(); + + for (const auto* technique : techniqueSet->techniques) + { + if (technique) + ExtractNamesFromTechnique(technique); + } + } + } + } + + unsigned MaterialConstantZoneState::HashString(const std::string& str) + { + return Common::R_HashString(str.c_str()); + } + + void MaterialConstantZoneState::ExtractNamesFromTechnique(const MaterialTechnique* technique) + { + if (!ShouldDumpFromStruct(technique)) + return; + + for (auto passIndex = 0u; passIndex < technique->passCount; passIndex++) + { + const auto& pass = technique->passArray[passIndex]; + + if (pass.vertexShader && pass.vertexShader->prog.loadDef.program) + ExtractNamesFromShader(pass.vertexShader->prog.loadDef.program, pass.vertexShader->prog.loadDef.programSize); + + if (pass.pixelShader && pass.pixelShader->prog.loadDef.program) + ExtractNamesFromShader(pass.pixelShader->prog.loadDef.program, pass.pixelShader->prog.loadDef.programSize); + } + } + + void MaterialConstantZoneState::AddStaticKnownNames() + { + for (const auto* knownConstantName : KNOWN_CONSTANT_NAMES) + AddConstantName(knownConstantName); + for (const auto* knownTextureDefName : KNOWN_TEXTURE_DEF_NAMES) + AddTextureDefName(knownTextureDefName); + } +} // namespace T5 diff --git a/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.h b/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.h new file mode 100644 index 00000000..f9c83922 --- /dev/null +++ b/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Game/T5/T5.h" +#include "Material/AbstractMaterialConstantZoneState.h" + +#include + +namespace T5 +{ + class MaterialConstantZoneState final : public AbstractMaterialConstantZoneStateDx11 + { + protected: + void ExtractNamesFromZoneInternal() override; + void ExtractNamesFromTechnique(const MaterialTechnique* technique); + void AddStaticKnownNames() override; + unsigned HashString(const std::string& str) override; + }; +} // namespace T5 diff --git a/src/ObjWriting/Game/T5/ObjWriterT5.cpp b/src/ObjWriting/Game/T5/ObjWriterT5.cpp index b1c06e2e..208f14ba 100644 --- a/src/ObjWriting/Game/T5/ObjWriterT5.cpp +++ b/src/ObjWriting/Game/T5/ObjWriterT5.cpp @@ -1,16 +1,13 @@ #include "ObjWriterT5.h" -#include "AssetDumpers/AssetDumperGfxImage.h" -#include "AssetDumpers/AssetDumperLocalizeEntry.h" -#include "AssetDumpers/AssetDumperPhysConstraints.h" -#include "AssetDumpers/AssetDumperPhysPreset.h" -#include "AssetDumpers/AssetDumperRawFile.h" -#include "AssetDumpers/AssetDumperSndBank.h" -#include "AssetDumpers/AssetDumperStringTable.h" -#include "AssetDumpers/AssetDumperWeapon.h" -#include "AssetDumpers/AssetDumperXModel.h" #include "Game/T5/GameAssetPoolT5.h" +#include "Game/T5/Material/MaterialJsonDumperT5.h" +#include "Game/T5/XModel/XModelDumperT5.h" +#include "Image/ImageDumperT5.h" +#include "Localize/LocalizeDumperT5.h" #include "ObjWriting.h" +#include "RawFile/RawFileDumperT5.h" +#include "StringTable/StringTableDumperT5.h" using namespace T5; @@ -29,10 +26,10 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const // DUMP_ASSET_POOL(AssetDumperPhysConstraints, m_phys_constraints, ASSET_TYPE_PHYSCONSTRAINTS) // DUMP_ASSET_POOL(AssetDumperDestructibleDef, m_destructible_def, ASSET_TYPE_DESTRUCTIBLEDEF) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) - DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) - // DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) + DUMP_ASSET_POOL(xmodel::DumperT5, m_xmodel, ASSET_TYPE_XMODEL) + DUMP_ASSET_POOL(material::JsonDumperT5, m_material, ASSET_TYPE_MATERIAL) // DUMP_ASSET_POOL(AssetDumperTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) - DUMP_ASSET_POOL(AssetDumperGfxImage, m_image, ASSET_TYPE_IMAGE) + DUMP_ASSET_POOL(image::DumperT5, m_image, ASSET_TYPE_IMAGE) // DUMP_ASSET_POOL(AssetDumperSndBank, m_sound_bank, ASSET_TYPE_SOUND) // DUMP_ASSET_POOL(AssetDumperSndPatch, m_sound_patch, ASSET_TYPE_SOUND_PATCH) // DUMP_ASSET_POOL(AssetDumperClipMap, m_clip_map, ASSET_TYPE_CLIPMAP_PVS) @@ -45,13 +42,13 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const // DUMP_ASSET_POOL(AssetDumperFont, m_font, ASSET_TYPE_FONT) // DUMP_ASSET_POOL(AssetDumperMenuList, m_menu_list, ASSET_TYPE_MENULIST) // DUMP_ASSET_POOL(AssetDumperMenuDef, m_menu_def, ASSET_TYPE_MENU) - DUMP_ASSET_POOL(AssetDumperLocalizeEntry, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) + DUMP_ASSET_POOL(localize::DumperT5, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) // DUMP_ASSET_POOL(AssetDumperWeapon, m_weapon, ASSET_TYPE_WEAPON) // DUMP_ASSET_POOL(AssetDumperSndDriverGlobals, m_snd_driver_globals, ASSET_TYPE_SNDDRIVER_GLOBALS) // DUMP_ASSET_POOL(AssetDumperFxEffectDef, m_fx, ASSET_TYPE_FX) // DUMP_ASSET_POOL(AssetDumperFxImpactTable, m_fx_impact_table, ASSET_TYPE_IMPACT_FX) - DUMP_ASSET_POOL(AssetDumperRawFile, m_raw_file, ASSET_TYPE_RAWFILE) - DUMP_ASSET_POOL(AssetDumperStringTable, m_string_table, ASSET_TYPE_STRINGTABLE) + DUMP_ASSET_POOL(raw_file::DumperT5, m_raw_file, ASSET_TYPE_RAWFILE) + DUMP_ASSET_POOL(string_table::DumperT5, m_string_table, ASSET_TYPE_STRINGTABLE) // DUMP_ASSET_POOL(AssetDumperPackIndex, m_pack_index, ASSET_TYPE_PACK_INDEX) // DUMP_ASSET_POOL(AssetDumperXGlobals, m_xglobals, ASSET_TYPE_XGLOBALS) // DUMP_ASSET_POOL(AssetDumperDDLRoot, m_ddl, ASSET_TYPE_DDL) diff --git a/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.cpp b/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.cpp new file mode 100644 index 00000000..a3d41751 --- /dev/null +++ b/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.cpp @@ -0,0 +1,122 @@ +#include "RawFileDumperT5.h" + +#include "Utils/Logging/Log.h" +#include "Utils/StringUtils.h" + +#include +#include +#include + +using namespace T5; + +namespace fs = std::filesystem; + +namespace +{ + constexpr static size_t GSC_MAX_SIZE = 0xC000000; + + void DumpGsc(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) + { + const auto* rawFile = asset->Asset(); + + if (rawFile->len <= 8) + { + con::error("Invalid len of gsc file \"{}\"", rawFile->name); + 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) + { + con::error("Invalid compression of gsc file \"{}\": {}", rawFile->name, inLen); + return; + } + + if (outLen > GSC_MAX_SIZE) + { + con::error("Invalid size of gsc file \"{}\": {}", rawFile->name, outLen); + 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) + { + con::error("Inflate failed for dumping gsc file \"{}\"", rawFile->name); + 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); + } +} // namespace + +namespace raw_file +{ + bool DumperT5::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* rawFile = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const fs::path rawFilePath(rawFile->name); + auto extension = rawFilePath.extension().string(); + utils::MakeStringLowerCase(extension); + + if (extension == ".gsc" || extension == ".csc") + DumpGsc(context, asset, stream); + else + stream.write(rawFile->buffer, rawFile->len); + } +} // namespace raw_file diff --git a/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.h b/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.h new file mode 100644 index 00000000..056266ae --- /dev/null +++ b/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T5/T5.h" + +namespace raw_file +{ + class DumperT5 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace raw_file diff --git a/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.cpp b/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.cpp new file mode 100644 index 00000000..0acc3587 --- /dev/null +++ b/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.cpp @@ -0,0 +1,42 @@ +#include "StringTableDumperT5.h" + +#include "Csv/CsvStream.h" + +using namespace T5; + +namespace string_table +{ + bool DumperT5::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* stringTable = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + CsvOutputStream csv(*assetFile); + + for (auto row = 0; row < stringTable->rowCount; row++) + { + for (auto column = 0; column < stringTable->columnCount; column++) + { + const auto* cell = &stringTable->values[column + row * stringTable->columnCount]; + if (cell->string != nullptr) + { + csv.WriteColumn(cell->string); + } + else + { + csv.WriteColumn(""); + } + } + + csv.NextRow(); + } + } +} // namespace string_table diff --git a/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.h b/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.h new file mode 100644 index 00000000..12ee953b --- /dev/null +++ b/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T5/T5.h" + +namespace string_table +{ + class DumperT5 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace string_table diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperFontIcon.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperFontIcon.cpp deleted file mode 100644 index 20e375f7..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperFontIcon.cpp +++ /dev/null @@ -1,264 +0,0 @@ -#include "AssetDumperFontIcon.h" - -#include "Csv/CsvStream.h" -#include "Game/T6/CommonT6.h" - -#include - -using namespace T6; - -class AssetDumperFontIconInternal -{ - class KnownAlias - { - public: - std::string m_name; - int m_hash; - - explicit KnownAlias(std::string aliasName) - : m_name(std::move(aliasName)) - { - m_hash = Common::Com_HashString(m_name.c_str(), 0); - } - }; - - inline static const std::string TYPE_ICON = "icon"; - inline static const std::string ICON_HEADERS[]{ - "# index", - "# type", - "# name", - "# material", - "# size", - "# xScale", - "# yScale", - }; - - inline static const std::string TYPE_ALIAS = "alias"; - inline static const std::string ALIAS_HEADERS[]{ - "# index", - "# type", - "# alias", - "# button", - }; - - inline static const KnownAlias KNOWN_ALIASES[]{ - KnownAlias("BUTTON_ADS"), - KnownAlias("BUTTON_CAC_NEXT"), - KnownAlias("BUTTON_CAC_PREV"), - KnownAlias("BUTTON_CANCEL"), - KnownAlias("BUTTON_CAROUSEL_STICK"), - KnownAlias("BUTTON_CREATE"), - KnownAlias("BUTTON_CYCLE_LEFT"), - KnownAlias("BUTTON_CYCLE_LEFT_ACTIVE"), - KnownAlias("BUTTON_CYCLE_RIGHT"), - KnownAlias("BUTTON_CYCLE_RIGHT_ACTIVE"), - KnownAlias("BUTTON_DELETE"), - KnownAlias("BUTTON_EDIT"), - KnownAlias("BUTTON_EMBLEM_BACKWARD"), - KnownAlias("BUTTON_EMBLEM_FLIP"), - KnownAlias("BUTTON_EMBLEM_FORWARD"), - KnownAlias("BUTTON_EMBLEM_FORWARD_BACKWARD"), - KnownAlias("BUTTON_EMBLEM_MOVE"), - KnownAlias("BUTTON_EMBLEM_OUTLINE"), - KnownAlias("BUTTON_EMBLEM_PALETTE_CYCLE"), - KnownAlias("BUTTON_EMBLEM_PALETTE_NEXT"), - KnownAlias("BUTTON_EMBLEM_PALETTE_PREV"), - KnownAlias("BUTTON_EMBLEM_RESET"), - KnownAlias("BUTTON_EMBLEM_ROTATE_LEFT"), - KnownAlias("BUTTON_EMBLEM_ROTATE_RIGHT"), - KnownAlias("BUTTON_EMBLEM_SCALE"), - KnownAlias("BUTTON_FIRE"), - KnownAlias("BUTTON_FRIENDSLIST"), - KnownAlias("BUTTON_INTERACT"), - KnownAlias("BUTTON_LOOKSTICK"), - KnownAlias("BUTTON_LOOK"), - KnownAlias("BUTTON_LUI_ALT1"), - KnownAlias("BUTTON_LUI_ALT2"), - KnownAlias("BUTTON_LUI_DPAD_ALL"), - KnownAlias("BUTTON_LUI_DPAD_D"), - KnownAlias("BUTTON_LUI_DPAD_L"), - KnownAlias("BUTTON_LUI_DPAD_RL"), - KnownAlias("BUTTON_LUI_DPAD_R"), - KnownAlias("BUTTON_LUI_DPAD_UD"), - KnownAlias("BUTTON_LUI_DPAD_U"), - KnownAlias("BUTTON_LUI_LEFT_STICK_UP"), - KnownAlias("BUTTON_LUI_LEFT_TRIGGER"), - KnownAlias("BUTTON_LUI_PRIMARY"), - KnownAlias("BUTTON_LUI_RIGHT_STICK"), - KnownAlias("BUTTON_LUI_RIGHT_TRIGGER"), - KnownAlias("BUTTON_LUI_SECONDARY"), - KnownAlias("BUTTON_LUI_SELECT"), - KnownAlias("BUTTON_LUI_SHOULDERL"), - KnownAlias("BUTTON_LUI_SHOULDERR"), - KnownAlias("BUTTON_LUI_START"), - KnownAlias("BUTTON_MOUSE_CLICK"), - KnownAlias("BUTTON_MOUSE_CLICK_ACTIVE"), - KnownAlias("BUTTON_MOUSE_EDIT"), - KnownAlias("BUTTON_MOUSE_EDIT_ACTIVE"), - KnownAlias("BUTTON_MOUSE_LEFT"), - KnownAlias("BUTTON_MOUSE_MIDDLE"), - KnownAlias("BUTTON_MOUSE_RIGHT"), - KnownAlias("BUTTON_MOVESTICK"), - KnownAlias("BUTTON_MOVE"), - KnownAlias("BUTTON_MP_CANCELCOMMAND"), - KnownAlias("BUTTON_MP_CHANGESETTINGS"), - KnownAlias("BUTTON_MP_GAMERCARD"), - KnownAlias("BUTTON_MP_GAMERREVIEW"), - KnownAlias("BUTTON_MP_JOINGAME"), - KnownAlias("BUTTON_MP_KICKPLAYER"), - KnownAlias("BUTTON_MP_LEAVEGAME"), - KnownAlias("BUTTON_MP_LOBBY_GAMERCARD"), - KnownAlias("BUTTON_MP_NOTREADY"), - KnownAlias("BUTTON_MP_PGDOWN"), - KnownAlias("BUTTON_MP_PGUP"), - KnownAlias("BUTTON_MP_READY"), - KnownAlias("BUTTON_MP_REFRESH"), - KnownAlias("BUTTON_MP_SCOREBOARD"), - KnownAlias("BUTTON_MP_SIGNIN"), - KnownAlias("BUTTON_MP_SPECNEXT"), - KnownAlias("BUTTON_MP_SPECPREV"), - KnownAlias("BUTTON_MP_STARTGAME"), - KnownAlias("BUTTON_MP_TOGGLECHASECAM"), - KnownAlias("BUTTON_MP_TOGGLEVIEW"), - KnownAlias("BUTTON_NO"), - KnownAlias("BUTTON_RECORD_VIEW_NEXT"), - KnownAlias("BUTTON_RECORD_VIEW_PREV"), - KnownAlias("BUTTON_SELECTCHOICE"), - KnownAlias("BUTTON_SP_TOGGLEMENU"), - KnownAlias("BUTTON_YES"), - KnownAlias("CP"), - KnownAlias("FONT_CAPITAL_I"), - KnownAlias("FONT_NUMBER_ZERO"), - KnownAlias("KEY_DOWN_ARROW"), - KnownAlias("KEY_LEFT_ARROW"), - KnownAlias("KEY_RIGHT_ARROW"), - KnownAlias("KEY_UP_ARROW"), - KnownAlias("MOUSE_WHEEL_DOWN"), - KnownAlias("MOUSE_WHEEL_UP"), - KnownAlias("Remote_LStick"), - }; - - CsvOutputStream m_csv; - - static FontIconEntry* FindEntryByHash(const FontIcon* fontIcon, const int hash) - { - auto lowerBound = 0u; - auto upperBound = fontIcon->numEntries; - - while (true) - { - const auto entryIndex = (lowerBound + upperBound) / 2u; - auto* entry = &fontIcon->fontIconEntry[entryIndex]; - - if (entry->fontIconName.hash == hash) - return entry; - - if (lowerBound == upperBound) - return nullptr; - - if (entry->fontIconName.hash < hash) - lowerBound = entryIndex + 1u; - else - upperBound = entryIndex - 1u; - } - } - - static const KnownAlias* FindKnownAliasByHash(const int hash) - { - for (const auto& i : KNOWN_ALIASES) - { - if (i.m_hash == hash) - return &i; - } - - return nullptr; - } - - void WriteFontIconEntries(const FontIcon* fontIcon) - { - for (const auto& header : ICON_HEADERS) - m_csv.WriteColumn(header); - m_csv.NextRow(); - - for (auto iconIndex = 0u; iconIndex < fontIcon->numEntries; iconIndex++) - { - const auto* entry = &fontIcon->fontIconEntry[iconIndex]; - m_csv.WriteColumn(std::to_string(iconIndex)); - m_csv.WriteColumn(TYPE_ICON); - - if (entry->fontIconName.string) - m_csv.WriteColumn(entry->fontIconName.string); - else - m_csv.WriteColumn(""); - - if (entry->fontIconMaterialHandle && entry->fontIconMaterialHandle->info.name) - m_csv.WriteColumn(entry->fontIconMaterialHandle->info.name); - else - m_csv.WriteColumn(""); - - m_csv.WriteColumn(std::to_string(entry->fontIconSize)); - m_csv.WriteColumn(std::to_string(entry->xScale)); - m_csv.WriteColumn(std::to_string(entry->yScale)); - - m_csv.NextRow(); - } - } - - void WriteFontIconAliases(const FontIcon* fontIcon) - { - for (const auto& header : ALIAS_HEADERS) - m_csv.WriteColumn(header); - m_csv.NextRow(); - - for (auto aliasIndex = 0u; aliasIndex < fontIcon->numAliasEntries; aliasIndex++) - { - const auto* alias = &fontIcon->fontIconAlias[aliasIndex]; - m_csv.WriteColumn(std::to_string(aliasIndex)); - m_csv.WriteColumn(TYPE_ALIAS); - - auto* knownAlias = FindKnownAliasByHash(alias->aliasHash); - if (knownAlias) - m_csv.WriteColumn(knownAlias->m_name); - else - m_csv.WriteColumn(std::format("@{:x}", alias->aliasHash)); - - const auto* referencedEntry = FindEntryByHash(fontIcon, alias->buttonHash); - if (referencedEntry && referencedEntry->fontIconName.string) - m_csv.WriteColumn(referencedEntry->fontIconName.string); - else - m_csv.WriteColumn(""); - - m_csv.NextRow(); - } - } - -public: - explicit AssetDumperFontIconInternal(std::ostream& stream) - : m_csv(stream) - { - } - - void DumpFontIcon(const FontIcon* fontIcon) - { - WriteFontIconEntries(fontIcon); - m_csv.NextRow(); - - WriteFontIconAliases(fontIcon); - } -}; - -bool AssetDumperFontIcon::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperFontIcon::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - AssetDumperFontIconInternal dumper(*assetFile); - dumper.DumpFontIcon(asset->Asset()); -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperFontIcon.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperFontIcon.h deleted file mode 100644 index 0581829f..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperFontIcon.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperFontIcon final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.h deleted file mode 100644 index 401aae5e..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" -#include "Image/IImageWriter.h" - -#include - -namespace T6 -{ - class AssetDumperGfxImage final : public AbstractAssetDumper - { - std::unique_ptr m_writer; - - std::string GetAssetFileName(const XAssetInfo& asset) const; - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - - public: - AssetDumperGfxImage(); - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLeaderboardDef.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLeaderboardDef.cpp deleted file mode 100644 index 2d83e5bd..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLeaderboardDef.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "AssetDumperLeaderboardDef.h" - -#include "Game/T6/Leaderboard/JsonLeaderboardDefWriter.h" - -#include -#include - -using namespace T6; - -std::string AssetDumperLeaderboardDef::GetFileNameForAsset(const std::string& assetName) -{ - return std::format("leaderboards/{}.json", assetName); -} - -bool AssetDumperLeaderboardDef::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperLeaderboardDef::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name)); - - if (!assetFile) - return; - - DumpLeaderboardDefAsJson(*assetFile, asset->Asset()); -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLeaderboardDef.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLeaderboardDef.h deleted file mode 100644 index 057d4252..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLeaderboardDef.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperLeaderboardDef final : public AbstractAssetDumper - { - static std::string GetFileNameForAsset(const std::string& assetName); - - protected: - _NODISCARD bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLocalizeEntry.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLocalizeEntry.cpp deleted file mode 100644 index e9ebf735..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLocalizeEntry.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "AssetDumperLocalizeEntry.h" - -#include "Dumping/Localize/StringFileDumper.h" -#include "Localize/LocalizeCommon.h" - -#include -#include - -using namespace T6; - -void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - if (pool->m_asset_lookup.empty()) - return; - - const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); - const auto assetFile = context.OpenAssetFile(std::format("{}/localizedstrings/{}.str", language, context.m_zone.m_name)); - - if (assetFile) - { - StringFileDumper stringFileDumper(context.m_zone, *assetFile); - - stringFileDumper.SetLanguageName(language); - - // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. - stringFileDumper.SetConfigFile(R"(C:/projects/cod/t6/bin/StringEd.cfg)"); - - stringFileDumper.SetNotes(""); - - for (auto* localizeEntry : *pool) - { - stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); - } - - stringFileDumper.Finalize(); - } - else - { - std::cerr << std::format("Could not create string file for dumping localized strings of zone '{}'\n", context.m_zone.m_name); - } -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLocalizeEntry.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLocalizeEntry.h deleted file mode 100644 index d85de9ac..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperLocalizeEntry.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperLocalizeEntry final : public IAssetDumper - { - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMapEnts.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMapEnts.cpp deleted file mode 100644 index cd4a8165..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMapEnts.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AssetDumperMapEnts.h" - -#include - -using namespace T6; - -bool AssetDumperMapEnts::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMapEnts::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* mapEnts = asset->Asset(); - - const auto mapEntsFile = context.OpenAssetFile(std::format("{}.ents", mapEnts->name)); - - if (!mapEntsFile) - return; - - auto& stream = *mapEntsFile; - stream.write(mapEnts->entityString, mapEnts->numEntityChars - 1); -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMapEnts.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMapEnts.h deleted file mode 100644 index 98b0301a..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMapEnts.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperMapEnts final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysConstraints.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysConstraints.cpp deleted file mode 100644 index 7be1250a..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysConstraints.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "AssetDumperPhysConstraints.h" - -#include "Game/T6/InfoString/InfoStringFromStructConverter.h" -#include "Game/T6/ObjConstantsT6.h" -#include "Game/T6/PhysConstraints/PhysConstraintsFields.h" - -#include -#include - -using namespace T6; - -namespace T6 -{ - class InfoStringFromPhysConstraintsConverter final : public InfoStringFromStructConverter - { - protected: - void FillFromExtensionField(const cspField_t& field) override - { - switch (static_cast(field.iFieldType)) - { - case CFT_TYPE: - FillFromEnumInt(std::string(field.szName), field.iOffset, s_constraintTypeNames, std::extent_v); - break; - - default: - assert(false); - break; - } - } - - public: - InfoStringFromPhysConstraintsConverter(const PhysConstraints* structure, - const cspField_t* fields, - const size_t fieldCount, - std::function scriptStringValueCallback) - : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) - { - } - }; -} // namespace T6 - -InfoString AssetDumperPhysConstraints::CreateInfoString(XAssetInfo* asset) -{ - assert(asset->Asset()->count <= 4); - - InfoStringFromPhysConstraintsConverter converter(asset->Asset(), - phys_constraints_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -bool AssetDumperPhysConstraints::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperPhysConstraints::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_PHYS_CONSTRAINTS); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_PHYS_CONSTRAINTS, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("physconstraints/" + asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_PHYS_CONSTRAINTS); - stream.write(stringValue.c_str(), stringValue.size()); - } -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysConstraints.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysConstraints.h deleted file mode 100644 index 8f3d938e..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysConstraints.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" -#include "InfoString/InfoString.h" - -namespace T6 -{ - class AssetDumperPhysConstraints final : public AbstractAssetDumper - { - static InfoString CreateInfoString(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysPreset.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysPreset.cpp deleted file mode 100644 index d11819f4..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysPreset.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "AssetDumperPhysPreset.h" - -#include "Game/T6/InfoString/InfoStringFromStructConverter.h" -#include "Game/T6/ObjConstantsT6.h" -#include "Game/T6/PhysPreset/PhysPresetFields.h" - -#include -#include -#include -#include - -using namespace T6; - -namespace T6 -{ - class InfoStringFromPhysPresetConverter final : public InfoStringFromStructConverter - { - protected: - void FillFromExtensionField(const cspField_t& field) override - { - assert(false); - } - - public: - InfoStringFromPhysPresetConverter(const PhysPresetInfo* structure, - const cspField_t* fields, - const size_t fieldCount, - std::function scriptStringValueCallback) - : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) - { - } - }; -} // namespace T6 - -void AssetDumperPhysPreset::CopyToPhysPresetInfo(const PhysPreset* physPreset, PhysPresetInfo* physPresetInfo) -{ - physPresetInfo->mass = std::clamp(physPreset->mass * 1000.0f, 1.0f, 2000.0f); - physPresetInfo->bounce = physPreset->bounce; - - if (std::isinf(physPreset->friction)) - { - physPresetInfo->isFrictionInfinity = 1; - physPresetInfo->friction = 0; - } - else - { - physPresetInfo->isFrictionInfinity = 0; - physPresetInfo->friction = physPreset->friction; - } - - physPresetInfo->bulletForceScale = physPreset->bulletForceScale; - physPresetInfo->explosiveForceScale = physPreset->explosiveForceScale; - physPresetInfo->piecesSpreadFraction = physPreset->piecesSpreadFraction; - physPresetInfo->piecesUpwardVelocity = physPreset->piecesUpwardVelocity; - physPresetInfo->canFloat = physPreset->canFloat; - physPresetInfo->gravityScale = std::clamp(physPreset->gravityScale, 0.01f, 10.0f); - physPresetInfo->centerOfMassOffset = physPreset->centerOfMassOffset; - physPresetInfo->buoyancyBoxMin = physPreset->buoyancyBoxMin; - physPresetInfo->buoyancyBoxMax = physPreset->buoyancyBoxMax; -} - -InfoString AssetDumperPhysPreset::CreateInfoString(XAssetInfo* asset) -{ - auto* physPresetInfo = new PhysPresetInfo; - CopyToPhysPresetInfo(asset->Asset(), physPresetInfo); - - InfoStringFromPhysPresetConverter converter(physPresetInfo, - phys_preset_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -bool AssetDumperPhysPreset::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperPhysPreset::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_PHYS_PRESET); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("physic/" + asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET); - stream.write(stringValue.c_str(), stringValue.size()); - } -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysPreset.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysPreset.h deleted file mode 100644 index a760ef45..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperPhysPreset.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" -#include "InfoString/InfoString.h" - -namespace T6 -{ - class AssetDumperPhysPreset final : public AbstractAssetDumper - { - static void CopyToPhysPresetInfo(const PhysPreset* physPreset, PhysPresetInfo* physPresetInfo); - static InfoString CreateInfoString(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperQdb.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperQdb.cpp deleted file mode 100644 index dde4a739..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperQdb.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "AssetDumperQdb.h" - -using namespace T6; - -bool AssetDumperQdb::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperQdb::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* qdb = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - stream.write(qdb->buffer, qdb->len); -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperQdb.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperQdb.h deleted file mode 100644 index fd6a7456..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperQdb.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperQdb final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.cpp deleted file mode 100644 index 291d969e..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "AssetDumperRawFile.h" - -#include -#include -#include - -using namespace T6; - -namespace fs = std::filesystem; - -bool AssetDumperRawFile::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperRawFile::DumpAnimtree(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) -{ - const auto* rawFile = asset->Asset(); - - if (rawFile->len <= 4) - { - std::cerr << "Invalid len of animtree file \"" << rawFile->name << "\"\n"; - return; - } - - const auto outLen = reinterpret_cast(rawFile->buffer)[0]; - const auto inLen = rawFile->len; - - if (outLen > ANIMTREE_MAX_SIZE) - { - std::cerr << "Invalid size of animtree file \"" << rawFile->name << "\": " << outLen << "\n"; - 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 = inflateInit2(&zs, -DEF_WBITS); - - if (ret != Z_OK) - { - throw std::runtime_error("Initializing inflate failed"); - } - - zs.next_in = reinterpret_cast(&rawFile->buffer[4]); - zs.avail_in = inLen - sizeof(uint32_t); - - Bytef buffer[0x1000]; - - while (zs.avail_in > 0) - { - zs.next_out = buffer; - zs.avail_out = sizeof(buffer); - ret = inflate(&zs, Z_SYNC_FLUSH); - - if (ret < 0) - { - std::cerr << "Inflate failed for dumping animtree file \"" << rawFile->name << "\"\n"; - inflateEnd(&zs); - return; - } - - const auto inflateOutSize = sizeof(buffer) - zs.avail_out; - - stream.write(reinterpret_cast(buffer), inflateOutSize); - } - - inflateEnd(&zs); -} - -void AssetDumperRawFile::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* rawFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const fs::path rawFilePath(rawFile->name); - const auto extension = rawFilePath.extension().string(); - - if (extension == ".atr") - { - DumpAnimtree(context, asset, stream); - } - else - { - stream.write(rawFile->buffer, rawFile->len); - } -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.h deleted file mode 100644 index 1f9f033f..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperRawFile.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperRawFile final : public AbstractAssetDumper - { - constexpr static size_t ANIMTREE_MAX_SIZE = 0xC000000; - - void DumpAnimtree(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperScriptParseTree.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperScriptParseTree.cpp deleted file mode 100644 index fb1ff079..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperScriptParseTree.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "AssetDumperScriptParseTree.h" - -using namespace T6; - -bool AssetDumperScriptParseTree::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperScriptParseTree::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* scriptParseTree = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - stream.write(scriptParseTree->buffer, scriptParseTree->len); -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperScriptParseTree.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperScriptParseTree.h deleted file mode 100644 index 8ff1db2e..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperScriptParseTree.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperScriptParseTree final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSlug.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSlug.cpp deleted file mode 100644 index 09dd50a4..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSlug.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "AssetDumperSlug.h" - -using namespace T6; - -bool AssetDumperSlug::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperSlug::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* slug = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - stream.write(slug->buffer, slug->len); -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSlug.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSlug.h deleted file mode 100644 index 01598810..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSlug.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperSlug final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.h deleted file mode 100644 index 61883c58..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperSndBank final : public IAssetDumper - { - class Internal; - - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndDriverGlobals.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndDriverGlobals.cpp deleted file mode 100644 index 452858ce..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndDriverGlobals.cpp +++ /dev/null @@ -1,385 +0,0 @@ -#include "AssetDumperSndDriverGlobals.h" - -#include "Csv/CsvStream.h" -#include "ObjContainer/SoundBank/SoundBank.h" - -using namespace T6; - -class AssetDumperSndDriverGlobals::Internal -{ - AssetDumpingContext& m_context; - - inline static const std::string GROUPS_HEADERS[]{ - "name", - "attenuationSp", - "attenuationMp", - "category", - "parent", - "id", - }; - - inline static const std::string GROUPS_CATEGORIES[]{ - "sfx", - "music", - "void", - "ui", - "cinematic", - "id", - }; - - inline static const std::string CURVE_HEADERS[]{ - "name", - "x0", - "y0", - "x1", - "y1", - "x2", - "y2", - "x3", - "y3", - "x4", - "y4", - "x5", - "y5", - "x6", - "y6", - "x7", - "y7", - "id", - }; - - inline static const std::string PAN_HEADERS[]{ - "name", - "front", - "back", - "center", - "lfe", - "left", - "right", - "id", - }; - - inline static const std::string MASTER_HEADERS[]{ - "name", "lowE", "lowG", "lowF", "lowQ", "peak1E", "peak1G", "peak1F", "peak1Q", "peak2E", "peak2G", - "peak2F", "peak2Q", "hiE", "hiG", "hiF", "hiQ", "eqG", "compE", "compPG", "compMG", "compT", - "compR", "compTA", "compTR", "limitE", "limitPG", "limitMG", "limitT", "limitR", "limitTA", "limitTR", "busReverbG", - "busFxG", "busVoiceG", "busPfutzG", "busHdrfxG", "busUiG", "busMusicG", "busMovieG", "busVcsG", "busReverbE", "busFxE", "busVoiceE", - "busPfutzE", "busHdrfxE", "busUiE", "busMusicE", "busMovieE", "hdrfxCompE", "voiceEqE", "voiceCompE", "id", - }; - - inline static const std::string SIDECHAIN_HEADERS[]{ - "name", - "g", - "f", - "q", - "ta", - "tr", - "tf", - "id", - }; - - inline static const std::string FUTZ_HEADERS[]{ - "name", - "bpfF", - "bpfQ", - "lsG", - "lsF", - "lsQ", - "dist", - "preG", - "postG", - "th", - "tg", - "clippre", - "clippost", - "blend", - "startAliasId", - "stopAliasId", - "loopAliasId", - "id", - }; - - std::unique_ptr OpenAssetFile(const std::string& filename) - { - auto outputFile = this->m_context.OpenAssetFile(filename); - if (outputFile == nullptr) - { - std::cout << "Failed to open sound driver globals output file for: \"" << filename << "\"\n"; - } - - return outputFile; - } - - static void WriteFileHeader(CsvOutputStream& stream, const std::string* headers, size_t count) - { - for (auto i = 0u; i < count; i++) - { - stream.WriteColumn(headers[i]); - } - - stream.NextRow(); - } - - void DumpSndVolumesGroups(const SndVolumeGroup* groups, const size_t count) - { - const auto outputFile = this->OpenAssetFile("soundbank/globals/group.csv"); - - if (outputFile != nullptr) - { - CsvOutputStream csvStream(*outputFile); - WriteFileHeader(csvStream, GROUPS_HEADERS, std::extent_v); - - for (auto i = 0u; i < count; i++) - { - const auto& group = groups[i]; - csvStream.WriteColumn(group.name); - csvStream.WriteColumn(std::to_string(group.attenuationSp)); - csvStream.WriteColumn(std::to_string(group.attenuationMp)); - csvStream.WriteColumn(GROUPS_CATEGORIES[group.category]); - csvStream.WriteColumn(group.parentName); - csvStream.WriteColumn(std::to_string(group.id)); - csvStream.NextRow(); - } - } - } - - void DumpSndCurves(const SndCurve* curves, const size_t count) - { - const auto outputFile = this->OpenAssetFile("soundbank/globals/curves.csv"); - - if (outputFile != nullptr) - { - CsvOutputStream csvStream(*outputFile); - WriteFileHeader(csvStream, CURVE_HEADERS, std::extent_v); - - for (auto i = 0u; i < count; i++) - { - const auto& curve = curves[i]; - csvStream.WriteColumn(curve.name); - - for (auto j = 0u; j < 8; j++) - { - csvStream.WriteColumn(std::to_string(curve.points[j].x)); - csvStream.WriteColumn(std::to_string(curve.points[j].y)); - } - - csvStream.WriteColumn(std::to_string(curve.id)); - - csvStream.NextRow(); - } - } - } - - void DumpSndPans(const SndPan* pans, const size_t count) - { - const auto outputFile = this->OpenAssetFile("soundbank/globals/pan.csv"); - - if (outputFile != nullptr) - { - CsvOutputStream csvStream(*outputFile); - WriteFileHeader(csvStream, PAN_HEADERS, std::extent_v); - - for (auto i = 0u; i < count; i++) - { - const auto& pan = pans[i]; - csvStream.WriteColumn(pan.name); - csvStream.WriteColumn(std::to_string(pan.front)); - csvStream.WriteColumn(std::to_string(pan.back)); - csvStream.WriteColumn(std::to_string(pan.center)); - csvStream.WriteColumn(std::to_string(pan.lfe)); - csvStream.WriteColumn(std::to_string(pan.left)); - csvStream.WriteColumn(std::to_string(pan.right)); - csvStream.WriteColumn(std::to_string(pan.id)); - csvStream.NextRow(); - } - } - } - - void DumpSndDuckGroups(const SndDuckGroup* duckGroups, const size_t count) - { - const auto outputFile = this->OpenAssetFile("soundbank/globals/duck_groups.csv"); - - if (outputFile != nullptr) - { - CsvOutputStream csvStream(*outputFile); - csvStream.WriteColumn("name"); - csvStream.WriteColumn("id"); - csvStream.NextRow(); - - for (auto i = 0u; i < count; i++) - { - const auto& duckGroup = duckGroups[i]; - csvStream.WriteColumn(duckGroup.name); - csvStream.WriteColumn(std::to_string(duckGroup.id)); - csvStream.NextRow(); - } - } - } - - void DumpSndMasters(const SndMaster* masters, const size_t count) - { - const auto outputFile = this->OpenAssetFile("soundbank/globals/master.csv"); - - if (outputFile != nullptr) - { - CsvOutputStream csvStream(*outputFile); - WriteFileHeader(csvStream, MASTER_HEADERS, std::extent_v); - - for (auto i = 0u; i < count; i++) - { - const auto& master = masters[i]; - csvStream.WriteColumn(master.name); - csvStream.WriteColumn(std::to_string(master.lowE)); - csvStream.WriteColumn(std::to_string(master.lowG)); - csvStream.WriteColumn(std::to_string(master.lowF)); - csvStream.WriteColumn(std::to_string(master.lowQ)); - csvStream.WriteColumn(std::to_string(master.peak1E)); - csvStream.WriteColumn(std::to_string(master.peak1G)); - csvStream.WriteColumn(std::to_string(master.peak1F)); - csvStream.WriteColumn(std::to_string(master.peak1Q)); - csvStream.WriteColumn(std::to_string(master.peak2E)); - csvStream.WriteColumn(std::to_string(master.peak2G)); - csvStream.WriteColumn(std::to_string(master.peak2F)); - csvStream.WriteColumn(std::to_string(master.peak2Q)); - csvStream.WriteColumn(std::to_string(master.hiE)); - csvStream.WriteColumn(std::to_string(master.hiG)); - csvStream.WriteColumn(std::to_string(master.hiF)); - csvStream.WriteColumn(std::to_string(master.hiQ)); - csvStream.WriteColumn(std::to_string(master.eqG)); - csvStream.WriteColumn(std::to_string(master.compE)); - csvStream.WriteColumn(std::to_string(master.compPG)); - csvStream.WriteColumn(std::to_string(master.compMG)); - csvStream.WriteColumn(std::to_string(master.compT)); - csvStream.WriteColumn(std::to_string(master.compR)); - csvStream.WriteColumn(std::to_string(master.compTA)); - csvStream.WriteColumn(std::to_string(master.compTR)); - csvStream.WriteColumn(std::to_string(master.limitE)); - csvStream.WriteColumn(std::to_string(master.limitPG)); - csvStream.WriteColumn(std::to_string(master.limitMG)); - csvStream.WriteColumn(std::to_string(master.limitT)); - csvStream.WriteColumn(std::to_string(master.limitR)); - csvStream.WriteColumn(std::to_string(master.limitTA)); - csvStream.WriteColumn(std::to_string(master.limitTR)); - csvStream.WriteColumn(std::to_string(master.busReverbG)); - csvStream.WriteColumn(std::to_string(master.busFxG)); - csvStream.WriteColumn(std::to_string(master.busVoiceG)); - csvStream.WriteColumn(std::to_string(master.busPfutzG)); - csvStream.WriteColumn(std::to_string(master.busHdrfxG)); - csvStream.WriteColumn(std::to_string(master.busUiG)); - csvStream.WriteColumn(std::to_string(master.busMusicG)); - csvStream.WriteColumn(std::to_string(master.busMovieG)); - csvStream.WriteColumn(std::to_string(master.busVcsG)); - csvStream.WriteColumn(std::to_string(master.busReverbE)); - csvStream.WriteColumn(std::to_string(master.busFxE)); - csvStream.WriteColumn(std::to_string(master.busVoiceE)); - csvStream.WriteColumn(std::to_string(master.busPfutzE)); - csvStream.WriteColumn(std::to_string(master.busHdrfxE)); - csvStream.WriteColumn(std::to_string(master.busUiE)); - csvStream.WriteColumn(std::to_string(master.busMusicE)); - csvStream.WriteColumn(std::to_string(master.busMovieE)); - csvStream.WriteColumn(std::to_string(master.hdrfxCompE)); - csvStream.WriteColumn(std::to_string(master.voiceEqE)); - csvStream.WriteColumn(std::to_string(master.voiceCompE)); - csvStream.WriteColumn(std::to_string(master.id)); - csvStream.NextRow(); - } - } - } - - void DumpSndSidechainDucks(const SndSidechainDuck* sidechains, const size_t count) - { - const auto outputFile = this->OpenAssetFile("soundbank/globals/sidechain_duck.csv"); - - if (outputFile != nullptr) - { - CsvOutputStream csvStream(*outputFile); - WriteFileHeader(csvStream, SIDECHAIN_HEADERS, std::extent_v); - - for (auto i = 0u; i < count; i++) - { - const auto& sidechain = sidechains[i]; - csvStream.WriteColumn(sidechain.name); - csvStream.WriteColumn(std::to_string(sidechain.g)); - csvStream.WriteColumn(std::to_string(sidechain.f)); - csvStream.WriteColumn(std::to_string(sidechain.q)); - csvStream.WriteColumn(std::to_string(sidechain.ta)); - csvStream.WriteColumn(std::to_string(sidechain.tr)); - csvStream.WriteColumn(std::to_string(sidechain.tf)); - csvStream.WriteColumn(std::to_string(sidechain.id)); - csvStream.NextRow(); - } - } - } - - void DumpSndFutz(const SndFutz* futzes, const size_t count) - { - const auto outputFile = this->OpenAssetFile("soundbank/globals/futz.csv"); - - if (outputFile != nullptr) - { - CsvOutputStream csvStream(*outputFile); - WriteFileHeader(csvStream, FUTZ_HEADERS, std::extent_v); - - for (auto i = 0u; i < count; i++) - { - const auto& futz = futzes[i]; - csvStream.WriteColumn(futz.name); - csvStream.WriteColumn(std::to_string(futz.bpfF)); - csvStream.WriteColumn(std::to_string(futz.bpfQ)); - csvStream.WriteColumn(std::to_string(futz.lsG)); - csvStream.WriteColumn(std::to_string(futz.lsF)); - csvStream.WriteColumn(std::to_string(futz.lsQ)); - csvStream.WriteColumn(std::to_string(futz.dist)); - csvStream.WriteColumn(std::to_string(futz.preG)); - csvStream.WriteColumn(std::to_string(futz.postG)); - csvStream.WriteColumn(std::to_string(futz.th)); - csvStream.WriteColumn(std::to_string(futz.tg)); - csvStream.WriteColumn(std::to_string(futz.clippre)); - csvStream.WriteColumn(std::to_string(futz.clippost)); - csvStream.WriteColumn(std::to_string(futz.blend)); - csvStream.WriteColumn(std::to_string(futz.startAliasId)); - csvStream.WriteColumn(std::to_string(futz.stopAliasId)); - csvStream.WriteColumn(std::to_string(futz.loopAliasId)); - csvStream.WriteColumn(std::to_string(futz.id)); - csvStream.NextRow(); - } - } - } - - void DumpSndDriverGlobals(const XAssetInfo* sndDriverGlobalsInfo) - { - const auto* sndDriverGlobals = sndDriverGlobalsInfo->Asset(); - - DumpSndVolumesGroups(sndDriverGlobals->groups, sndDriverGlobals->groupCount); - DumpSndCurves(sndDriverGlobals->curves, sndDriverGlobals->curveCount); - DumpSndPans(sndDriverGlobals->pans, sndDriverGlobals->panCount); - DumpSndDuckGroups(sndDriverGlobals->duckGroups, sndDriverGlobals->duckGroupCount); - // DumpSndContexts(sndDriverGlobals->contexts, sndDriverGlobals->contextCount); - DumpSndMasters(sndDriverGlobals->masters, sndDriverGlobals->masterCount); - DumpSndSidechainDucks(sndDriverGlobals->voiceDucks, sndDriverGlobals->voiceDuckCount); - DumpSndFutz(sndDriverGlobals->futzes, sndDriverGlobals->futzCount); - } - -public: - explicit Internal(AssetDumpingContext& context) - : m_context(context) - { - } - - void DumpPool(AssetPool* pool) - { - for (const auto* assetInfo : *pool) - { - if (!assetInfo->m_name.empty() && assetInfo->m_name[0] == ',') - continue; - - DumpSndDriverGlobals(assetInfo); - } - } -}; - -void AssetDumperSndDriverGlobals::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - Internal internal(context); - internal.DumpPool(pool); -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndDriverGlobals.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndDriverGlobals.h deleted file mode 100644 index 18f0cf89..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndDriverGlobals.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperSndDriverGlobals final : public IAssetDumper - { - class Internal; - - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperStringTable.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperStringTable.cpp deleted file mode 100644 index 2fb76cec..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperStringTable.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "AssetDumperStringTable.h" - -#include "Csv/CsvStream.h" - -using namespace T6; - -bool AssetDumperStringTable::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperStringTable::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* stringTable = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); - - if (!assetFile) - return; - - CsvOutputStream csv(*assetFile); - - for (auto row = 0; row < stringTable->rowCount; row++) - { - for (auto column = 0; column < stringTable->columnCount; column++) - { - const auto* cell = &stringTable->values[column + row * stringTable->columnCount]; - if (cell->string != nullptr) - { - csv.WriteColumn(cell->string); - } - else - { - csv.WriteColumn(""); - } - } - - csv.NextRow(); - } -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperStringTable.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperStringTable.h deleted file mode 100644 index 1618db88..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperStringTable.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperStringTable final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.cpp deleted file mode 100644 index 9ff914a8..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "AssetDumperTechniqueSet.h" - -#include -#include -#include - -using namespace T6; - -class ShaderZoneState final : public IZoneAssetDumperState -{ -public: - bool ShouldDumpTechnique(const MaterialTechnique* technique) - { - const auto existingTechnique = m_dumped_techniques.find(technique); - if (existingTechnique == m_dumped_techniques.end()) - { - m_dumped_techniques.emplace(technique); - return true; - } - - return false; - } - - bool ShouldDumpPixelShader(const MaterialPixelShader* pixelShader) - { - const auto existingPixelShader = m_dumped_pixel_shaders.find(pixelShader); - if (existingPixelShader == m_dumped_pixel_shaders.end()) - { - m_dumped_pixel_shaders.emplace(pixelShader); - return true; - } - - return false; - } - - bool ShouldDumpVertexShader(const MaterialVertexShader* vertexShader) - { - const auto existingVertexShader = m_dumped_vertex_shaders.find(vertexShader); - if (existingVertexShader == m_dumped_vertex_shaders.end()) - { - m_dumped_vertex_shaders.emplace(vertexShader); - return true; - } - - return false; - } - -private: - std::unordered_set m_dumped_techniques; - std::unordered_set m_dumped_pixel_shaders; - std::unordered_set m_dumped_vertex_shaders; -}; - -bool AssetDumperTechniqueSet::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperTechniqueSet::DumpPixelShader(const AssetDumpingContext& context, const MaterialPixelShader* pixelShader) -{ - std::ostringstream ss; - ss << "techniquesets/shader_bin/ps_" << pixelShader->name; - - const auto shaderFile = context.OpenAssetFile(ss.str()); - - if (!shaderFile) - return; - - shaderFile->write(pixelShader->prog.loadDef.program, pixelShader->prog.loadDef.programSize); -} - -void AssetDumperTechniqueSet::DumpVertexShader(const AssetDumpingContext& context, const MaterialVertexShader* vertexShader) -{ - std::ostringstream ss; - ss << "techniquesets/shader_bin/vs_" << vertexShader->name; - - const auto shaderFile = context.OpenAssetFile(ss.str()); - - if (!shaderFile) - return; - - shaderFile->write(vertexShader->prog.loadDef.program, vertexShader->prog.loadDef.programSize); -} - -void AssetDumperTechniqueSet::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* techniqueSet = asset->Asset(); - auto* shaderState = context.GetZoneAssetDumperState(); - - const auto assetFile = context.OpenAssetFile(std::format("techniquesets/{}.json", techniqueSet->name)); - if (!assetFile) - return; - - nlohmann::json js; - - js["name"] = techniqueSet->name; - js["worldVertFormat"] = techniqueSet->worldVertFormat; - - js["techniques"] = nlohmann::json::array(); - for (const auto* technique : techniqueSet->techniques) - { - nlohmann::json techniqueJs = nlohmann::json::object(); - - if (technique != NULL) - { - techniqueJs["name"] = technique->name; - techniqueJs["flags"] = technique->flags; - techniqueJs["passCount"] = technique->passCount; - - _ASSERT(technique->passCount == 1); - - techniqueJs["passArray"] = nlohmann::json::array(); - for (auto passIndex = 0u; passIndex < technique->passCount; passIndex++) - { - const MaterialPass* currPass = &technique->passArray[passIndex]; - nlohmann::json passJs = nlohmann::json::object(); - - passJs["perPrimArgCount"] = currPass->perPrimArgCount; - passJs["perObjArgCount"] = currPass->perObjArgCount; - passJs["stableArgCount"] = currPass->stableArgCount; - passJs["customSamplerFlags"] = currPass->customSamplerFlags; - passJs["precompiledIndex"] = currPass->precompiledIndex; - passJs["materialType"] = currPass->materialType; - - nlohmann::json vertDeclJs = nlohmann::json::object(); - if (currPass->vertexDecl != NULL) - { - vertDeclJs["streamCount"] = currPass->vertexDecl->streamCount; - vertDeclJs["hasOptionalSource"] = currPass->vertexDecl->hasOptionalSource; - vertDeclJs["isLoaded"] = currPass->vertexDecl->isLoaded; - for (int i = 0; i < 16; i++) - { - vertDeclJs["routing"][i]["source"] = currPass->vertexDecl->routing.data[i].source; - vertDeclJs["routing"][i]["dest"] = currPass->vertexDecl->routing.data[i].dest; - - _ASSERT(currPass->vertexDecl->routing.decl[i] == NULL); - } - } - passJs["vertexDecl"] = vertDeclJs; - - passJs["args"] = nlohmann::json::array(); - if (currPass->args != NULL) - { - for (int i = 0; i < currPass->perPrimArgCount + currPass->perObjArgCount + currPass->stableArgCount; i++) - { - nlohmann::json argsJs = nlohmann::json::object(); - MaterialShaderArgument* currArg = &currPass->args[i]; - - argsJs["type"] = currArg->type; - argsJs["location"] = currArg->location.offset; - argsJs["size"] = currArg->size; - argsJs["buffer"] = currArg->buffer; - if (currArg->type == MTL_ARG_LITERAL_VERTEX_CONST || currArg->type == MTL_ARG_LITERAL_PIXEL_CONST) - { - argsJs["u"]["const0"] = currArg->u.literalConst[0]; - argsJs["u"]["const1"] = currArg->u.literalConst[1]; - argsJs["u"]["const2"] = currArg->u.literalConst[2]; - argsJs["u"]["const3"] = currArg->u.literalConst[3]; - } - else - { - argsJs["u"]["value"] = currArg->u.nameHash; - } - - passJs["args"].push_back(argsJs); - } - } - - nlohmann::json pixelJs = nlohmann::json::object(); - if (currPass->pixelShader != NULL) - { - pixelJs["name"] = currPass->pixelShader->name; - if (shaderState->ShouldDumpPixelShader(currPass->pixelShader)) - DumpPixelShader(context, currPass->pixelShader); - } - passJs["pixelShader"] = pixelJs; - - nlohmann::json vertexJs = nlohmann::json::object(); - if (currPass->vertexShader != NULL) - { - vertexJs["name"] = currPass->vertexShader->name; - if (shaderState->ShouldDumpVertexShader(currPass->vertexShader)) - DumpVertexShader(context, currPass->vertexShader); - } - passJs["vertexShader"] = vertexJs; - - techniqueJs["passArray"].push_back(passJs); - } - } - - js["techniques"].push_back(techniqueJs); - } - - std::string jsonString = js.dump(4); - assetFile->write(jsonString.c_str(), jsonString.size()); -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.h deleted file mode 100644 index 5a34305e..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperTechniqueSet final : public AbstractAssetDumper - { - static void DumpPixelShader(const AssetDumpingContext& context, const MaterialPixelShader* pixelShader); - static void DumpVertexShader(const AssetDumpingContext& context, const MaterialVertexShader* vertexShader); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTracer.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTracer.cpp deleted file mode 100644 index 0c87e4ee..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTracer.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "AssetDumperTracer.h" - -#include "Game/T6/InfoString/InfoStringFromStructConverter.h" -#include "Game/T6/ObjConstantsT6.h" -#include "Game/T6/Tracer/TracerFields.h" - -#include -#include - -using namespace T6; - -namespace T6 -{ - class InfoStringFromTracerConverter final : public InfoStringFromStructConverter - { - protected: - void FillFromExtensionField(const cspField_t& field) override - { - switch (static_cast(field.iFieldType)) - { - case TFT_TRACERTYPE: - FillFromEnumInt(std::string(field.szName), field.iOffset, tracerTypeNames, std::extent_v); - break; - - case TFT_NUM_FIELD_TYPES: - default: - assert(false); - break; - } - } - - public: - InfoStringFromTracerConverter(const TracerDef* structure, - const cspField_t* fields, - const size_t fieldCount, - std::function scriptStringValueCallback) - : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) - { - } - }; -} // namespace T6 - -InfoString AssetDumperTracer::CreateInfoString(XAssetInfo* asset) -{ - InfoStringFromTracerConverter converter(asset->Asset(), - tracer_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -bool AssetDumperTracer::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperTracer::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_TRACER); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_TRACER, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("tracer/" + asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_TRACER); - stream.write(stringValue.c_str(), stringValue.size()); - } -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTracer.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTracer.h deleted file mode 100644 index 51c25c02..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTracer.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" -#include "InfoString/InfoString.h" - -namespace T6 -{ - class AssetDumperTracer final : public AbstractAssetDumper - { - static InfoString CreateInfoString(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperVehicle.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperVehicle.h deleted file mode 100644 index a8c8421e..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperVehicle.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" -#include "InfoString/InfoString.h" - -namespace T6 -{ - class AssetDumperVehicle final : public AbstractAssetDumper - { - static InfoString CreateInfoString(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.h deleted file mode 100644 index d8f9d746..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" -#include "InfoString/InfoString.h" - -namespace T6 -{ - class AssetDumperWeapon final : public AbstractAssetDumper - { - static void CopyToFullDef(const WeaponVariantDef* weapon, WeaponFullDef* fullDef); - static InfoString CreateInfoString(XAssetInfo* asset); - static void DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachment.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachment.cpp deleted file mode 100644 index 0c7fd032..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachment.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "AssetDumperWeaponAttachment.h" - -#include "Game/T6/InfoString/InfoStringFromStructConverter.h" -#include "Game/T6/ObjConstantsT6.h" -#include "Game/T6/Weapon/AttachmentFields.h" -#include "Game/T6/Weapon/WeaponStrings.h" - -#include -#include - -using namespace T6; - -namespace T6 -{ - class InfoStringFromAttachmentConverter final : public InfoStringFromStructConverter - { - protected: - void FillFromExtensionField(const cspField_t& field) override - { - switch (static_cast(field.iFieldType)) - { - case AFT_ATTACHMENTTYPE: - FillFromEnumInt(std::string(field.szName), field.iOffset, szAttachmentTypeNames, std::extent_v); - break; - - case AFT_PENETRATE_TYPE: - FillFromEnumInt(std::string(field.szName), field.iOffset, penetrateTypeNames, std::extent_v); - break; - - case AFT_FIRETYPE: - FillFromEnumInt(std::string(field.szName), field.iOffset, szWeapFireTypeNames, std::extent_v); - break; - - default: - break; - } - } - - public: - InfoStringFromAttachmentConverter(const WeaponAttachment* structure, - const cspField_t* fields, - const size_t fieldCount, - std::function scriptStringValueCallback) - : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) - { - } - }; -} // namespace T6 - -InfoString AssetDumperWeaponAttachment::CreateInfoString(XAssetInfo* asset) -{ - InfoStringFromAttachmentConverter converter(asset->Asset(), - attachment_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -bool AssetDumperWeaponAttachment::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperWeaponAttachment::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON_ATTACHMENT); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("attachment/" + asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT); - stream.write(stringValue.c_str(), stringValue.size()); - } -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachment.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachment.h deleted file mode 100644 index cde27cf5..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachment.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" -#include "InfoString/InfoString.h" - -namespace T6 -{ - class AssetDumperWeaponAttachment final : public AbstractAssetDumper - { - static InfoString CreateInfoString(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachmentUnique.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachmentUnique.cpp deleted file mode 100644 index 6730771a..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachmentUnique.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include "AssetDumperWeaponAttachmentUnique.h" - -#include "Game/T6/InfoString/InfoStringFromStructConverter.h" -#include "Game/T6/ObjConstantsT6.h" -#include "Game/T6/Weapon/AttachmentUniqueFields.h" -#include "Game/T6/Weapon/WeaponStrings.h" - -#include -#include -#include -#include - -using namespace T6; - -namespace T6 -{ - class InfoStringFromWeaponAttachmentUniqueConverter final : public InfoStringFromStructConverter - { - protected: - void FillFromExtensionField(const cspField_t& field) override - { - switch (static_cast(field.iFieldType)) - { - case AUFT_ATTACHMENTTYPE: - FillFromEnumInt(std::string(field.szName), field.iOffset, szAttachmentTypeNames, std::extent_v); - break; - - case AUFT_HIDETAGS: - { - const auto* hideTags = reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset); - std::stringstream ss; - auto first = true; - - for (auto i = 0u; i < std::extent_v; i++) - { - const auto& str = m_get_scr_string(hideTags[i]); - if (!str.empty()) - { - if (!first) - ss << "\n"; - else - first = false; - - ss << str; - } - } - - m_info_string.SetValueForKey(std::string(field.szName), ss.str()); - break; - } - - case AUFT_OVERLAYRETICLE: - FillFromEnumInt(std::string(field.szName), field.iOffset, szWeapOverlayReticleNames, std::extent_v); - break; - - case AUFT_CAMO: - { - const auto* camo = *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset); - - if (camo) - m_info_string.SetValueForKey(std::string(field.szName), std::string(AssetName(camo->name))); - else - m_info_string.SetValueForKey(std::string(field.szName), ""); - break; - } - - default: - assert(false); - break; - } - } - - public: - InfoStringFromWeaponAttachmentUniqueConverter(const WeaponAttachmentUniqueFull* structure, - const cspField_t* fields, - const size_t fieldCount, - std::function scriptStringValueCallback) - : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) - { - } - }; -} // namespace T6 - -void AssetDumperWeaponAttachmentUnique::CopyToFullDef(const WeaponAttachmentUnique* attachment, WeaponAttachmentUniqueFull* fullDef) -{ - fullDef->attachment = *attachment; - - if (attachment->szXAnims) - { - assert(sizeof(WeaponAttachmentUniqueFull::szXAnims) >= sizeof(void*) * NUM_WEAP_ANIMS); - memcpy(fullDef->szXAnims, attachment->szXAnims, sizeof(void*) * NUM_WEAP_ANIMS); - fullDef->attachment.szXAnims = fullDef->szXAnims; - } - - if (attachment->hideTags) - { - assert(sizeof(WeaponAttachmentUniqueFull::hideTags) >= sizeof(scr_string_t) * std::extent_v); - memcpy(fullDef->hideTags, attachment->hideTags, sizeof(scr_string_t) * std::extent_v); - fullDef->attachment.hideTags = fullDef->hideTags; - } - - if (attachment->locationDamageMultipliers) - { - assert(sizeof(WeaponAttachmentUniqueFull::locationDamageMultipliers) >= sizeof(float) * HITLOC_COUNT); - memcpy(fullDef->locationDamageMultipliers, attachment->locationDamageMultipliers, sizeof(float) * HITLOC_COUNT); - fullDef->attachment.locationDamageMultipliers = fullDef->locationDamageMultipliers; - } -} - -InfoString AssetDumperWeaponAttachmentUnique::CreateInfoString(XAssetInfo* asset) -{ - const auto fullDef = std::make_unique(); - memset(fullDef.get(), 0, sizeof(WeaponAttachmentUniqueFull)); - CopyToFullDef(asset->Asset(), fullDef.get()); - - InfoStringFromWeaponAttachmentUniqueConverter converter(fullDef.get(), - attachment_unique_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -bool AssetDumperWeaponAttachmentUnique::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperWeaponAttachmentUnique::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON_ATTACHMENT_UNIQUE); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT_UNIQUE, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("attachmentunique/" + asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT_UNIQUE); - stream.write(stringValue.c_str(), stringValue.size()); - } -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachmentUnique.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachmentUnique.h deleted file mode 100644 index 4d6a9c41..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachmentUnique.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" -#include "InfoString/InfoString.h" - -namespace T6 -{ - class AssetDumperWeaponAttachmentUnique final : public AbstractAssetDumper - { - static void CopyToFullDef(const WeaponAttachmentUnique* attachment, WeaponAttachmentUniqueFull* fullDef); - static InfoString CreateInfoString(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponCamo.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponCamo.cpp deleted file mode 100644 index 4631e962..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponCamo.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AssetDumperWeaponCamo.h" - -#include "Game/T6/WeaponCamo/JsonWeaponCamoWriter.h" - -#include - -using namespace T6; - -bool AssetDumperWeaponCamo::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperWeaponCamo::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto fileName = std::format("camo/{}.json", asset->m_name); - const auto assetFile = context.OpenAssetFile(fileName); - - if (!assetFile) - return; - - DumpWeaponCamoAsJson(*assetFile, asset->Asset()); -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponCamo.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponCamo.h deleted file mode 100644 index 36b2df92..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponCamo.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperWeaponCamo final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp deleted file mode 100644 index 24dc56dc..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "AssetDumperXModel.h" - -#include "Game/T6/XModel/XModelDumperT6.h" - -using namespace T6; - -bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) -{ - return !asset->m_name.empty() && asset->m_name[0] != ','; -} - -void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - DumpXModel(context, asset); -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h deleted file mode 100644 index 75218729..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperXModel final : public AbstractAssetDumper - { - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperZBarrier.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperZBarrier.cpp deleted file mode 100644 index da165648..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperZBarrier.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "AssetDumperZBarrier.h" - -#include "Game/T6/InfoString/InfoStringFromStructConverter.h" -#include "Game/T6/ObjConstantsT6.h" -#include "Game/T6/ZBarrier/ZBarrierFields.h" - -#include -#include - -using namespace T6; - -namespace T6 -{ - class InfoStringFromZBarrierConverter final : public InfoStringFromStructConverter - { - protected: - void FillFromExtensionField(const cspField_t& field) override - { - assert(false); - } - - public: - InfoStringFromZBarrierConverter(const ZBarrierDef* structure, - const cspField_t* fields, - const size_t fieldCount, - std::function scriptStringValueCallback) - : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) - { - } - }; -} // namespace T6 - -InfoString AssetDumperZBarrier::CreateInfoString(XAssetInfo* asset) -{ - InfoStringFromZBarrierConverter converter(asset->Asset(), - zbarrier_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -bool AssetDumperZBarrier::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperZBarrier::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_ZBARRIER); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_ZBARRIER, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("zbarrier/" + asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_ZBARRIER); - stream.write(stringValue.c_str(), stringValue.size()); - } -} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperZBarrier.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperZBarrier.h deleted file mode 100644 index 0a2ba56d..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperZBarrier.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" -#include "InfoString/InfoString.h" - -namespace T6 -{ - class AssetDumperZBarrier final : public AbstractAssetDumper - { - static InfoString CreateInfoString(XAssetInfo* asset); - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.cpp b/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.cpp new file mode 100644 index 00000000..95c6f1cc --- /dev/null +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.cpp @@ -0,0 +1,151 @@ +#include "FontIconCsvDumperT6.h" + +#include "Csv/CsvStream.h" +#include "Game/T6/CommonT6.h" +#include "KnownFontIconAliasesT6.h" + +#include + +using namespace T6; + +namespace +{ + const std::string TYPE_ICON = "icon"; + const std::string ICON_HEADERS[]{ + "# index", + "# type", + "# name", + "# material", + "# size", + "# xScale", + "# yScale", + }; + + const std::string TYPE_ALIAS = "alias"; + const std::string ALIAS_HEADERS[]{ + "# index", + "# type", + "# alias", + "# button", + }; + + FontIconEntry* FindEntryByHash(const FontIcon& fontIcon, const int hash) + { + auto lowerBound = 0u; + auto upperBound = fontIcon.numEntries; + + while (true) + { + const auto entryIndex = (lowerBound + upperBound) / 2u; + auto* entry = &fontIcon.fontIconEntry[entryIndex]; + + if (entry->fontIconName.hash == hash) + return entry; + + if (lowerBound == upperBound) + return nullptr; + + if (entry->fontIconName.hash < hash) + lowerBound = entryIndex + 1u; + else + upperBound = entryIndex - 1u; + } + } + + class Dumper + { + public: + explicit Dumper(std::ostream& stream) + : m_csv(stream) + { + } + + void Dump(const FontIcon& fontIcon) + { + WriteFontIconEntries(fontIcon); + m_csv.NextRow(); + + WriteFontIconAliases(fontIcon); + } + + private: + void WriteFontIconEntries(const FontIcon& fontIcon) + { + for (const auto& header : ICON_HEADERS) + m_csv.WriteColumn(header); + m_csv.NextRow(); + + for (auto iconIndex = 0u; iconIndex < fontIcon.numEntries; iconIndex++) + { + const auto* entry = &fontIcon.fontIconEntry[iconIndex]; + m_csv.WriteColumn(std::to_string(iconIndex)); + m_csv.WriteColumn(TYPE_ICON); + + if (entry->fontIconName.string) + m_csv.WriteColumn(entry->fontIconName.string); + else + m_csv.WriteColumn(""); + + if (entry->fontIconMaterialHandle && entry->fontIconMaterialHandle->info.name) + m_csv.WriteColumn(entry->fontIconMaterialHandle->info.name); + else + m_csv.WriteColumn(""); + + m_csv.WriteColumn(std::to_string(entry->fontIconSize)); + m_csv.WriteColumn(std::to_string(entry->xScale)); + m_csv.WriteColumn(std::to_string(entry->yScale)); + + m_csv.NextRow(); + } + } + + void WriteFontIconAliases(const FontIcon& fontIcon) + { + for (const auto& header : ALIAS_HEADERS) + m_csv.WriteColumn(header); + m_csv.NextRow(); + + for (auto aliasIndex = 0u; aliasIndex < fontIcon.numAliasEntries; aliasIndex++) + { + const auto* alias = &fontIcon.fontIconAlias[aliasIndex]; + m_csv.WriteColumn(std::to_string(aliasIndex)); + m_csv.WriteColumn(TYPE_ALIAS); + + auto* knownAlias = FindKnownFontIconAliasByHash(alias->aliasHash); + if (knownAlias) + m_csv.WriteColumn(knownAlias->m_name); + else + m_csv.WriteColumn(std::format("@{:x}", alias->aliasHash)); + + const auto* referencedEntry = FindEntryByHash(fontIcon, alias->buttonHash); + if (referencedEntry && referencedEntry->fontIconName.string) + m_csv.WriteColumn(referencedEntry->fontIconName.string); + else + m_csv.WriteColumn(""); + + m_csv.NextRow(); + } + } + + CsvOutputStream m_csv; + }; +} // namespace + +namespace font_icon +{ + bool CsvDumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void CsvDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + Dumper dumper(*assetFile); + dumper.Dump(*asset->Asset()); + } +} // namespace font_icon diff --git a/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.h b/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.h new file mode 100644 index 00000000..aeaa8877 --- /dev/null +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace font_icon +{ + class CsvDumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace font_icon diff --git a/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.cpp b/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.cpp new file mode 100644 index 00000000..9f41412b --- /dev/null +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.cpp @@ -0,0 +1,20 @@ +#include "FontIconDumperT6.h" + +#include "FontIconCsvDumperT6.h" +#include "FontIconJsonDumperT6.h" + +using namespace T6; + +// #define DUMP_FONT_ICON_AS_CSV 1 + +namespace font_icon +{ + std::unique_ptr> CreateDumperT6() + { +#ifdef DUMP_FONT_ICON_AS_CSV + return std::make_unique(); +#else + return std::make_unique(); +#endif + } +} // namespace font_icon diff --git a/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.h b/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.h new file mode 100644 index 00000000..55d048f9 --- /dev/null +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +#include + +namespace font_icon +{ + std::unique_ptr> CreateDumperT6(); +} // namespace font_icon diff --git a/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.cpp b/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.cpp new file mode 100644 index 00000000..e82b3b36 --- /dev/null +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.cpp @@ -0,0 +1,95 @@ +#include "FontIconJsonDumperT6.h" + +#include "FontIcon/FontIconCommon.h" +#include "Game/T6/CommonT6.h" +#include "Game/T6/FontIcon/JsonFontIconT6.h" +#include "KnownFontIconAliasesT6.h" + +#include + +using namespace nlohmann; +using namespace T6; + +namespace +{ + void AddFontIconEntryAlias(JsonFontIconEntry& jFontIconEntry, const int aliasHash) + { + auto* knownAlias = FindKnownFontIconAliasByHash(aliasHash); + if (knownAlias) + { + jFontIconEntry.aliases.emplace_back(knownAlias->m_name); + } + else + { + if (!jFontIconEntry.aliasHashes.has_value()) + jFontIconEntry.aliasHashes.emplace(); + + jFontIconEntry.aliasHashes->emplace_back(static_cast(aliasHash)); + } + } + + void CreateJsonFontIconEntry(JsonFontIconEntry& jFontIconEntry, const FontIconEntry& fontIconEntry, const FontIcon& fontIcon) + { + jFontIconEntry.name = fontIconEntry.fontIconName.string; + + if (fontIconEntry.fontIconMaterialHandle && fontIconEntry.fontIconMaterialHandle->info.name) + jFontIconEntry.material = fontIconEntry.fontIconMaterialHandle->info.name; + + jFontIconEntry.size = static_cast(fontIconEntry.fontIconSize); + jFontIconEntry.scale = JsonVec2(); + jFontIconEntry.scale->x = fontIconEntry.xScale; + jFontIconEntry.scale->y = fontIconEntry.yScale; + + if (fontIcon.fontIconAlias) + { + for (auto aliasIndex = 0u; aliasIndex < fontIcon.numAliasEntries; aliasIndex++) + { + const auto& alias = fontIcon.fontIconAlias[aliasIndex]; + if (alias.buttonHash == fontIconEntry.fontIconName.hash) + AddFontIconEntryAlias(jFontIconEntry, alias.aliasHash); + } + } + } + + void CreateJsonFontIconFile(JsonFontIcon& jFontIcon, const FontIcon& fontIcon) + { + if (fontIcon.fontIconEntry == nullptr) + return; + + jFontIcon.entries.resize(fontIcon.numEntries); + for (auto i = 0u; i < fontIcon.numEntries; i++) + CreateJsonFontIconEntry(jFontIcon.entries[i], fontIcon.fontIconEntry[i], fontIcon); + } + + void DumpFontIcon(std::ostream& stream, const FontIcon& fontIcon) + { + JsonFontIcon jFontIcon; + CreateJsonFontIconFile(jFontIcon, fontIcon); + json jRoot = jFontIcon; + + jRoot["$schema"] = "http://openassettools.dev/schema/font-icon.v1.json"; + jRoot["_type"] = "font-icon"; + jRoot["_version"] = 1; + jRoot["_game"] = "t6"; + + stream << std::setw(4) << jRoot << "\n"; + } +} // namespace + +namespace font_icon +{ + bool JsonDumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void JsonDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + DumpFontIcon(*assetFile, *asset->Asset()); + } +} // namespace font_icon diff --git a/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.h b/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.h new file mode 100644 index 00000000..aa1c0017 --- /dev/null +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace font_icon +{ + class JsonDumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace font_icon diff --git a/src/ObjWriting/Game/T6/FontIcon/KnownFontIconAliasesT6.cpp b/src/ObjWriting/Game/T6/FontIcon/KnownFontIconAliasesT6.cpp new file mode 100644 index 00000000..ef391651 --- /dev/null +++ b/src/ObjWriting/Game/T6/FontIcon/KnownFontIconAliasesT6.cpp @@ -0,0 +1,125 @@ +#include "KnownFontIconAliasesT6.h" + +#include "Game/T6/CommonT6.h" + +using namespace T6; + +namespace +{ + inline const KnownAlias KNOWN_ALIASES[]{ + KnownAlias("BUTTON_ADS"), + KnownAlias("BUTTON_CAC_NEXT"), + KnownAlias("BUTTON_CAC_PREV"), + KnownAlias("BUTTON_CANCEL"), + KnownAlias("BUTTON_CAROUSEL_STICK"), + KnownAlias("BUTTON_CREATE"), + KnownAlias("BUTTON_CYCLE_LEFT"), + KnownAlias("BUTTON_CYCLE_LEFT_ACTIVE"), + KnownAlias("BUTTON_CYCLE_RIGHT"), + KnownAlias("BUTTON_CYCLE_RIGHT_ACTIVE"), + KnownAlias("BUTTON_DELETE"), + KnownAlias("BUTTON_EDIT"), + KnownAlias("BUTTON_EMBLEM_BACKWARD"), + KnownAlias("BUTTON_EMBLEM_FLIP"), + KnownAlias("BUTTON_EMBLEM_FORWARD"), + KnownAlias("BUTTON_EMBLEM_FORWARD_BACKWARD"), + KnownAlias("BUTTON_EMBLEM_MOVE"), + KnownAlias("BUTTON_EMBLEM_OUTLINE"), + KnownAlias("BUTTON_EMBLEM_PALETTE_CYCLE"), + KnownAlias("BUTTON_EMBLEM_PALETTE_NEXT"), + KnownAlias("BUTTON_EMBLEM_PALETTE_PREV"), + KnownAlias("BUTTON_EMBLEM_RESET"), + KnownAlias("BUTTON_EMBLEM_ROTATE_LEFT"), + KnownAlias("BUTTON_EMBLEM_ROTATE_RIGHT"), + KnownAlias("BUTTON_EMBLEM_SCALE"), + KnownAlias("BUTTON_FIRE"), + KnownAlias("BUTTON_FRIENDSLIST"), + KnownAlias("BUTTON_INTERACT"), + KnownAlias("BUTTON_LOOKSTICK"), + KnownAlias("BUTTON_LOOK"), + KnownAlias("BUTTON_LUI_ALT1"), + KnownAlias("BUTTON_LUI_ALT2"), + KnownAlias("BUTTON_LUI_DPAD_ALL"), + KnownAlias("BUTTON_LUI_DPAD_D"), + KnownAlias("BUTTON_LUI_DPAD_L"), + KnownAlias("BUTTON_LUI_DPAD_RL"), + KnownAlias("BUTTON_LUI_DPAD_R"), + KnownAlias("BUTTON_LUI_DPAD_UD"), + KnownAlias("BUTTON_LUI_DPAD_U"), + KnownAlias("BUTTON_LUI_LEFT_STICK_UP"), + KnownAlias("BUTTON_LUI_LEFT_TRIGGER"), + KnownAlias("BUTTON_LUI_PRIMARY"), + KnownAlias("BUTTON_LUI_RIGHT_STICK"), + KnownAlias("BUTTON_LUI_RIGHT_TRIGGER"), + KnownAlias("BUTTON_LUI_SECONDARY"), + KnownAlias("BUTTON_LUI_SELECT"), + KnownAlias("BUTTON_LUI_SHOULDERL"), + KnownAlias("BUTTON_LUI_SHOULDERR"), + KnownAlias("BUTTON_LUI_START"), + KnownAlias("BUTTON_MOUSE_CLICK"), + KnownAlias("BUTTON_MOUSE_CLICK_ACTIVE"), + KnownAlias("BUTTON_MOUSE_EDIT"), + KnownAlias("BUTTON_MOUSE_EDIT_ACTIVE"), + KnownAlias("BUTTON_MOUSE_LEFT"), + KnownAlias("BUTTON_MOUSE_MIDDLE"), + KnownAlias("BUTTON_MOUSE_RIGHT"), + KnownAlias("BUTTON_MOVESTICK"), + KnownAlias("BUTTON_MOVE"), + KnownAlias("BUTTON_MP_CANCELCOMMAND"), + KnownAlias("BUTTON_MP_CHANGESETTINGS"), + KnownAlias("BUTTON_MP_GAMERCARD"), + KnownAlias("BUTTON_MP_GAMERREVIEW"), + KnownAlias("BUTTON_MP_JOINGAME"), + KnownAlias("BUTTON_MP_KICKPLAYER"), + KnownAlias("BUTTON_MP_LEAVEGAME"), + KnownAlias("BUTTON_MP_LOBBY_GAMERCARD"), + KnownAlias("BUTTON_MP_NOTREADY"), + KnownAlias("BUTTON_MP_PGDOWN"), + KnownAlias("BUTTON_MP_PGUP"), + KnownAlias("BUTTON_MP_READY"), + KnownAlias("BUTTON_MP_REFRESH"), + KnownAlias("BUTTON_MP_SCOREBOARD"), + KnownAlias("BUTTON_MP_SIGNIN"), + KnownAlias("BUTTON_MP_SPECNEXT"), + KnownAlias("BUTTON_MP_SPECPREV"), + KnownAlias("BUTTON_MP_STARTGAME"), + KnownAlias("BUTTON_MP_TOGGLECHASECAM"), + KnownAlias("BUTTON_MP_TOGGLEVIEW"), + KnownAlias("BUTTON_NO"), + KnownAlias("BUTTON_RECORD_VIEW_NEXT"), + KnownAlias("BUTTON_RECORD_VIEW_PREV"), + KnownAlias("BUTTON_SELECTCHOICE"), + KnownAlias("BUTTON_SP_TOGGLEMENU"), + KnownAlias("BUTTON_YES"), + KnownAlias("CP"), + KnownAlias("FONT_CAPITAL_I"), + KnownAlias("FONT_NUMBER_ZERO"), + KnownAlias("KEY_DOWN_ARROW"), + KnownAlias("KEY_LEFT_ARROW"), + KnownAlias("KEY_RIGHT_ARROW"), + KnownAlias("KEY_UP_ARROW"), + KnownAlias("MOUSE_WHEEL_DOWN"), + KnownAlias("MOUSE_WHEEL_UP"), + KnownAlias("Remote_LStick"), + }; +} + +namespace T6 +{ + KnownAlias::KnownAlias(std::string aliasName) + : m_name(std::move(aliasName)), + m_hash(Common::Com_HashString(m_name.c_str())) + { + } + + const KnownAlias* FindKnownFontIconAliasByHash(const int hash) + { + for (const auto& i : KNOWN_ALIASES) + { + if (i.m_hash == hash) + return &i; + } + + return nullptr; + } +} // namespace T6 diff --git a/src/ObjWriting/Game/T6/FontIcon/KnownFontIconAliasesT6.h b/src/ObjWriting/Game/T6/FontIcon/KnownFontIconAliasesT6.h new file mode 100644 index 00000000..bca5a007 --- /dev/null +++ b/src/ObjWriting/Game/T6/FontIcon/KnownFontIconAliasesT6.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace T6 +{ + class KnownAlias + { + public: + explicit KnownAlias(std::string aliasName); + + std::string m_name; + int m_hash; + }; + + const KnownAlias* FindKnownFontIconAliasByHash(int hash); +} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.cpp b/src/ObjWriting/Game/T6/Image/ImageDumperT6.cpp similarity index 60% rename from src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.cpp rename to src/ObjWriting/Game/T6/Image/ImageDumperT6.cpp index f529cca1..02317695 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperGfxImage.cpp +++ b/src/ObjWriting/Game/T6/Image/ImageDumperT6.cpp @@ -1,11 +1,13 @@ -#include "AssetDumperGfxImage.h" +#include "ImageDumperT6.h" #include "Image/DdsWriter.h" #include "Image/Dx12TextureLoader.h" +#include "Image/ImageCommon.h" #include "Image/IwiLoader.h" #include "Image/IwiWriter27.h" #include "ObjContainer/IPak/IPak.h" #include "ObjWriting.h" +#include "Utils/Logging/Log.h" #include #include @@ -53,11 +55,11 @@ namespace } } - const auto imageFileName = std::format("images/{}.iwi", image.name); + const auto imageFileName = image::GetFileNameForAsset(image.name, ".iwi"); const auto filePathImage = searchPath.Open(imageFileName); if (!filePathImage.IsOpen()) { - std::cerr << std::format("Could not find data for image \"{}\"\n", image.name); + con::error("Could not find data for image \"{}\"", image.name); return nullptr; } @@ -73,48 +75,43 @@ namespace } } // namespace -AssetDumperGfxImage::AssetDumperGfxImage() +namespace image { - switch (ObjWriting::Configuration.ImageOutputFormat) + DumperT6::DumperT6() { - case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: - m_writer = std::make_unique(); - break; - case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: - m_writer = std::make_unique(); - break; - default: - assert(false); - m_writer = nullptr; - break; + switch (ObjWriting::Configuration.ImageOutputFormat) + { + case ObjWriting::Configuration_t::ImageOutputFormat_e::DDS: + m_writer = std::make_unique(); + break; + case ObjWriting::Configuration_t::ImageOutputFormat_e::IWI: + m_writer = std::make_unique(); + break; + default: + assert(false); + m_writer = nullptr; + break; + } } -} -bool AssetDumperGfxImage::ShouldDump(XAssetInfo* asset) -{ - return true; -} + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } -std::string AssetDumperGfxImage::GetAssetFileName(const XAssetInfo& asset) const -{ - auto cleanAssetName = asset.m_name; - std::ranges::replace(cleanAssetName, '*', '_'); + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* image = asset->Asset(); + const auto texture = LoadImageData(context.m_obj_search_path, *image); + if (!texture) + return; - return std::format("images/{}{}", cleanAssetName, m_writer->GetFileExtension()); -} + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name, m_writer->GetFileExtension())); -void AssetDumperGfxImage::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* image = asset->Asset(); - const auto texture = LoadImageData(context.m_obj_search_path, *image); - if (!texture) - return; + if (!assetFile) + return; - const auto assetFile = context.OpenAssetFile(GetAssetFileName(*asset)); - - if (!assetFile) - return; - - auto& stream = *assetFile; - m_writer->DumpImage(stream, texture.get()); -} + auto& stream = *assetFile; + m_writer->DumpImage(stream, texture.get()); + } +} // namespace image diff --git a/src/ObjWriting/Game/T6/Image/ImageDumperT6.h b/src/ObjWriting/Game/T6/Image/ImageDumperT6.h new file mode 100644 index 00000000..5fac0233 --- /dev/null +++ b/src/ObjWriting/Game/T6/Image/ImageDumperT6.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" +#include "Image/IImageWriter.h" + +#include + +namespace image +{ + class DumperT6 final : public AbstractAssetDumper + { + public: + DumperT6(); + + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + + private: + std::unique_ptr m_writer; + }; +} // namespace image diff --git a/src/ObjWriting/Game/T6/Leaderboard/JsonLeaderboardDefWriter.h b/src/ObjWriting/Game/T6/Leaderboard/JsonLeaderboardDefWriter.h deleted file mode 100644 index cdc31389..00000000 --- a/src/ObjWriting/Game/T6/Leaderboard/JsonLeaderboardDefWriter.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Dumping/AssetDumpingContext.h" -#include "Game/T6/T6.h" - -#include - -namespace T6 -{ - void DumpLeaderboardDefAsJson(std::ostream& stream, const LeaderboardDef* leaderboardDef); -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/Leaderboard/JsonLeaderboardDefWriter.cpp b/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.cpp similarity index 78% rename from src/ObjWriting/Game/T6/Leaderboard/JsonLeaderboardDefWriter.cpp rename to src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.cpp index a559881f..4a90d7b2 100644 --- a/src/ObjWriting/Game/T6/Leaderboard/JsonLeaderboardDefWriter.cpp +++ b/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.cpp @@ -1,28 +1,30 @@ -#include "JsonLeaderboardDefWriter.h" +#include "LeaderboardJsonDumperT6.h" #include "Game/T6/CommonT6.h" #include "Game/T6/Leaderboard/JsonLeaderboardDef.h" +#include "Leaderboard/LeaderboardCommon.h" #include #include +#include using namespace nlohmann; using namespace T6; namespace { - class JsonDumper + class Dumper { public: - explicit JsonDumper(std::ostream& stream) + explicit Dumper(std::ostream& stream) : m_stream(stream) { } - void Dump(const LeaderboardDef* leaderboardDef) const + void Dump(const LeaderboardDef& leaderboardDef) const { JsonLeaderboardDef jsonLeaderboardDef; - CreateJsonLeaderboardDef(jsonLeaderboardDef, *leaderboardDef); + CreateJsonLeaderboardDef(jsonLeaderboardDef, leaderboardDef); json jRoot = jsonLeaderboardDef; @@ -94,11 +96,21 @@ namespace }; } // namespace -namespace T6 +namespace leaderboard { - void DumpLeaderboardDefAsJson(std::ostream& stream, const LeaderboardDef* leaderboardDef) + bool JsonDumperT6::ShouldDump(XAssetInfo* asset) { - JsonDumper dumper(stream); - dumper.Dump(leaderboardDef); + return true; } -} // namespace T6 + + void JsonDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAsset(asset->m_name)); + + if (!assetFile) + return; + + Dumper dumper(*assetFile); + dumper.Dump(*asset->Asset()); + } +} // namespace leaderboard diff --git a/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.h b/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.h new file mode 100644 index 00000000..ebbe4c9f --- /dev/null +++ b/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace leaderboard +{ + class JsonDumperT6 final : public AbstractAssetDumper + { + protected: + [[nodiscard]] bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace leaderboard diff --git a/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.cpp b/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.cpp new file mode 100644 index 00000000..c6281264 --- /dev/null +++ b/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.cpp @@ -0,0 +1,45 @@ +#include "LocalizeDumperT6.h" + +#include "Dumping/Localize/StringFileDumper.h" +#include "Localize/LocalizeCommon.h" +#include "Utils/Logging/Log.h" + +#include +#include + +using namespace T6; + +namespace localize +{ + void DumperT6::DumpPool(AssetDumpingContext& context, AssetPool* pool) + { + if (pool->m_asset_lookup.empty()) + return; + + const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); + const auto assetFile = context.OpenAssetFile(std::format("{}/localizedstrings/{}.str", language, context.m_zone.m_name)); + + if (assetFile) + { + StringFileDumper stringFileDumper(context.m_zone, *assetFile); + + stringFileDumper.SetLanguageName(language); + + // Magic string. Original string files do have this config file. The purpose of the config file is unknown though. + stringFileDumper.SetConfigFile(R"(C:/projects/cod/t6/bin/StringEd.cfg)"); + + stringFileDumper.SetNotes(""); + + for (auto* localizeEntry : *pool) + { + stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); + } + + stringFileDumper.Finalize(); + } + else + { + con::error("Could not create string file for dumping localized strings of zone '{}'", context.m_zone.m_name); + } + } +} // namespace localize diff --git a/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.h b/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.h new file mode 100644 index 00000000..1a39defe --- /dev/null +++ b/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace localize +{ + class DumperT6 final : public IAssetDumper + { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + }; +} // namespace localize diff --git a/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.cpp b/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.cpp new file mode 100644 index 00000000..caf8abd5 --- /dev/null +++ b/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.cpp @@ -0,0 +1,26 @@ +#include "MapEntsDumperT6.h" + +#include + +using namespace T6; + +namespace map_ents +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* mapEnts = asset->Asset(); + + const auto mapEntsFile = context.OpenAssetFile(std::format("{}.ents", mapEnts->name)); + + if (!mapEntsFile) + return; + + auto& stream = *mapEntsFile; + stream.write(mapEnts->entityString, mapEnts->numEntityChars - 1); + } +} // namespace map_ents diff --git a/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.h b/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.h new file mode 100644 index 00000000..9dbf36bb --- /dev/null +++ b/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace map_ents +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace map_ents diff --git a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp deleted file mode 100644 index a245cc18..00000000 --- a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "DumperMaterialT6.h" - -#include "Game/T6/Material/JsonMaterialWriterT6.h" -#include "Game/T6/Material/MaterialConstantZoneStateT6.h" -#include "Material/MaterialCommon.h" - -using namespace T6; - -void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - auto* materialConstantState = context.GetZoneAssetDumperState(); - materialConstantState->ExtractNamesFromZone(); - - AbstractAssetDumper::DumpPool(context, pool); -} - -bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto assetFile = context.OpenAssetFile(material::GetFileNameForAssetName(asset->m_name)); - - if (!assetFile) - return; - - DumpMaterialAsJson(*assetFile, *asset->Asset(), context); -} diff --git a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.h b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.h deleted file mode 100644 index 99de31e7..00000000 --- a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Dumping/AbstractAssetDumper.h" -#include "Game/T6/T6.h" - -namespace T6 -{ - class AssetDumperMaterial final : public AbstractAssetDumper - { - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; - - protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - }; -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/Material/MaterialConstantZoneStateT6.cpp b/src/ObjWriting/Game/T6/Material/MaterialConstantZoneStateT6.cpp index 352824f8..966a0ff4 100644 --- a/src/ObjWriting/Game/T6/Material/MaterialConstantZoneStateT6.cpp +++ b/src/ObjWriting/Game/T6/Material/MaterialConstantZoneStateT6.cpp @@ -4,6 +4,7 @@ #include "Game/T6/GameAssetPoolT6.h" #include "Game/T6/GameT6.h" #include "ObjWriting.h" +#include "Zone/ZoneRegistry.h" namespace T6 { @@ -473,7 +474,7 @@ namespace T6 void MaterialConstantZoneState::ExtractNamesFromZoneInternal() { - for (const auto* zone : IGame::GetGameById(GameId::T6)->GetZones()) + for (const auto* zone : ZoneRegistry::GetRegistryForGame(GameId::T6)->Zones()) { const auto* t6AssetPools = dynamic_cast(zone->m_pools.get()); if (!t6AssetPools) diff --git a/src/ObjWriting/Game/T6/ObjWriterT6.cpp b/src/ObjWriting/Game/T6/ObjWriterT6.cpp index bb4bcb15..06c66520 100644 --- a/src/ObjWriting/Game/T6/ObjWriterT6.cpp +++ b/src/ObjWriting/Game/T6/ObjWriterT6.cpp @@ -1,31 +1,31 @@ #include "ObjWriterT6.h" -#include "AssetDumpers/AssetDumperFontIcon.h" -#include "AssetDumpers/AssetDumperGfxImage.h" -#include "AssetDumpers/AssetDumperLeaderboardDef.h" -#include "AssetDumpers/AssetDumperLocalizeEntry.h" -#include "AssetDumpers/AssetDumperMapEnts.h" -#include "AssetDumpers/AssetDumperPhysConstraints.h" -#include "AssetDumpers/AssetDumperPhysPreset.h" -#include "AssetDumpers/AssetDumperQdb.h" -#include "AssetDumpers/AssetDumperRawFile.h" -#include "AssetDumpers/AssetDumperScriptParseTree.h" -#include "AssetDumpers/AssetDumperSlug.h" -#include "AssetDumpers/AssetDumperSndBank.h" -#include "AssetDumpers/AssetDumperSndDriverGlobals.h" -#include "AssetDumpers/AssetDumperStringTable.h" -#include "AssetDumpers/AssetDumperTechniqueSet.h" -#include "AssetDumpers/AssetDumperTracer.h" -#include "AssetDumpers/AssetDumperVehicle.h" -#include "AssetDumpers/AssetDumperWeapon.h" -#include "AssetDumpers/AssetDumperWeaponAttachment.h" -#include "AssetDumpers/AssetDumperWeaponAttachmentUnique.h" -#include "AssetDumpers/AssetDumperWeaponCamo.h" -#include "AssetDumpers/AssetDumperXModel.h" -#include "AssetDumpers/AssetDumperZBarrier.h" +#include "FontIcon/FontIconDumperT6.h" #include "Game/T6/GameAssetPoolT6.h" -#include "Material/DumperMaterialT6.h" +#include "Game/T6/Material/MaterialJsonDumperT6.h" +#include "Game/T6/XModel/XModelDumperT6.h" +#include "Image/ImageDumperT6.h" +#include "Leaderboard/LeaderboardJsonDumperT6.h" +#include "Localize/LocalizeDumperT6.h" +#include "Maps/MapEntsDumperT6.h" #include "ObjWriting.h" +#include "PhysConstraints/PhysConstraintsInfoStringDumperT6.h" +#include "PhysPreset/PhysPresetInfoStringDumperT6.h" +#include "Qdb/QdbDumperT6.h" +#include "RawFile/RawFileDumperT6.h" +#include "Script/ScriptDumperT6.h" +#include "Slug/SlugDumperT6.h" +#include "Sound/SndBankDumperT6.h" +#include "Sound/SndDriverGlobalsDumperT6.h" +#include "StringTable/StringTableDumperT6.h" +#include "Techset/TechsetDumperT6.h" +#include "Tracer/TracerDumperT6.h" +#include "Vehicle/VehicleDumperT6.h" +#include "Weapon/AttachmentDumperT6.h" +#include "Weapon/AttachmentUniqueDumperT6.h" +#include "Weapon/CamoJsonDumperT6.h" +#include "Weapon/WeaponDumperT6.h" +#include "ZBarrier/ZBarrierDumperT6.h" using namespace T6; @@ -37,57 +37,63 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const dumperType dumper; \ dumper.DumpPool(context, assetPools->poolName.get()); \ } +#define DUMP_ASSET_POOL_WITH_FACTORY(createDumper, poolName, assetType) \ + if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(assetType)) \ + { \ + const auto dumper = createDumper; \ + dumper->DumpPool(context, assetPools->poolName.get()); \ + } const auto* assetPools = dynamic_cast(context.m_zone.m_pools.get()); - DUMP_ASSET_POOL(AssetDumperPhysPreset, m_phys_preset, ASSET_TYPE_PHYSPRESET) - DUMP_ASSET_POOL(AssetDumperPhysConstraints, m_phys_constraints, ASSET_TYPE_PHYSCONSTRAINTS) + DUMP_ASSET_POOL(phys_preset::InfoStringDumperT6, m_phys_preset, ASSET_TYPE_PHYSPRESET) + DUMP_ASSET_POOL(phys_constraints::InfoStringDumperT6, m_phys_constraints, ASSET_TYPE_PHYSCONSTRAINTS) // DUMP_ASSET_POOL(AssetDumperDestructibleDef, m_destructible_def, ASSET_TYPE_DESTRUCTIBLEDEF) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) - DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) - DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) - DUMP_ASSET_POOL(AssetDumperTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) - DUMP_ASSET_POOL(AssetDumperGfxImage, m_image, ASSET_TYPE_IMAGE) - DUMP_ASSET_POOL(AssetDumperSndBank, m_sound_bank, ASSET_TYPE_SOUND) + DUMP_ASSET_POOL(xmodel::DumperT6, m_xmodel, ASSET_TYPE_XMODEL) + DUMP_ASSET_POOL(material::JsonDumperT6, m_material, ASSET_TYPE_MATERIAL) + DUMP_ASSET_POOL(techset::DumperT6, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) + DUMP_ASSET_POOL(image::DumperT6, m_image, ASSET_TYPE_IMAGE) + DUMP_ASSET_POOL(sound::SndBankDumperT6, m_sound_bank, ASSET_TYPE_SOUND) // DUMP_ASSET_POOL(AssetDumperSndPatch, m_sound_patch, ASSET_TYPE_SOUND_PATCH) // DUMP_ASSET_POOL(AssetDumperClipMap, m_clip_map, ASSET_TYPE_CLIPMAP_PVS) // DUMP_ASSET_POOL(AssetDumperComWorld, m_com_world, ASSET_TYPE_COMWORLD) // DUMP_ASSET_POOL(AssetDumperGameWorldSp, m_game_world_sp, ASSET_TYPE_GAMEWORLD_SP) // DUMP_ASSET_POOL(AssetDumperGameWorldMp, m_game_world_mp, ASSET_TYPE_GAMEWORLD_MP) - DUMP_ASSET_POOL(AssetDumperMapEnts, m_map_ents, ASSET_TYPE_MAP_ENTS) + DUMP_ASSET_POOL(map_ents::DumperT6, m_map_ents, ASSET_TYPE_MAP_ENTS) // DUMP_ASSET_POOL(AssetDumperGfxWorld, m_gfx_world, ASSET_TYPE_GFXWORLD) // DUMP_ASSET_POOL(AssetDumperGfxLightDef, m_gfx_light_def, ASSET_TYPE_LIGHT_DEF) // DUMP_ASSET_POOL(AssetDumperFont, m_font, ASSET_TYPE_FONT) - DUMP_ASSET_POOL(AssetDumperFontIcon, m_font_icon, ASSET_TYPE_FONTICON) + DUMP_ASSET_POOL_WITH_FACTORY(font_icon::CreateDumperT6(), m_font_icon, ASSET_TYPE_FONTICON) // DUMP_ASSET_POOL(AssetDumperMenuList, m_menu_list, ASSET_TYPE_MENULIST) // DUMP_ASSET_POOL(AssetDumperMenuDef, m_menu_def, ASSET_TYPE_MENU) - DUMP_ASSET_POOL(AssetDumperLocalizeEntry, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) - DUMP_ASSET_POOL(AssetDumperWeapon, m_weapon, ASSET_TYPE_WEAPON) - DUMP_ASSET_POOL(AssetDumperWeaponAttachment, m_attachment, ASSET_TYPE_ATTACHMENT) - DUMP_ASSET_POOL(AssetDumperWeaponAttachmentUnique, m_attachment_unique, ASSET_TYPE_ATTACHMENT_UNIQUE) - DUMP_ASSET_POOL(AssetDumperWeaponCamo, m_camo, ASSET_TYPE_WEAPON_CAMO) - DUMP_ASSET_POOL(AssetDumperSndDriverGlobals, m_snd_driver_globals, ASSET_TYPE_SNDDRIVER_GLOBALS) + DUMP_ASSET_POOL(localize::DumperT6, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) + DUMP_ASSET_POOL(weapon::DumperT6, m_weapon, ASSET_TYPE_WEAPON) + DUMP_ASSET_POOL(attachment::DumperT6, m_attachment, ASSET_TYPE_ATTACHMENT) + DUMP_ASSET_POOL(attachment_unique::DumperT6, m_attachment_unique, ASSET_TYPE_ATTACHMENT_UNIQUE) + DUMP_ASSET_POOL(camo::JsonDumperT6, m_camo, ASSET_TYPE_WEAPON_CAMO) + DUMP_ASSET_POOL(sound::SndDriverGlobalsDumperT6, m_snd_driver_globals, ASSET_TYPE_SNDDRIVER_GLOBALS) // DUMP_ASSET_POOL(AssetDumperFxEffectDef, m_fx, ASSET_TYPE_FX) // DUMP_ASSET_POOL(AssetDumperFxImpactTable, m_fx_impact_table, ASSET_TYPE_IMPACT_FX) - DUMP_ASSET_POOL(AssetDumperRawFile, m_raw_file, ASSET_TYPE_RAWFILE) - DUMP_ASSET_POOL(AssetDumperStringTable, m_string_table, ASSET_TYPE_STRINGTABLE) - DUMP_ASSET_POOL(AssetDumperLeaderboardDef, m_leaderboard, ASSET_TYPE_LEADERBOARD) + DUMP_ASSET_POOL(raw_file::DumperT6, m_raw_file, ASSET_TYPE_RAWFILE) + DUMP_ASSET_POOL(string_table::DumperT6, m_string_table, ASSET_TYPE_STRINGTABLE) + DUMP_ASSET_POOL(leaderboard::JsonDumperT6, m_leaderboard, ASSET_TYPE_LEADERBOARD) // DUMP_ASSET_POOL(AssetDumperXGlobals, m_xglobals, ASSET_TYPE_XGLOBALS) // DUMP_ASSET_POOL(AssetDumperDDLRoot, m_ddl, ASSET_TYPE_DDL) // DUMP_ASSET_POOL(AssetDumperGlasses, m_glasses, ASSET_TYPE_GLASSES) // DUMP_ASSET_POOL(AssetDumperEmblemSet, m_emblem_set, ASSET_TYPE_EMBLEMSET) - DUMP_ASSET_POOL(AssetDumperScriptParseTree, m_script, ASSET_TYPE_SCRIPTPARSETREE) + DUMP_ASSET_POOL(script::DumperT6, m_script, ASSET_TYPE_SCRIPTPARSETREE) // DUMP_ASSET_POOL(AssetDumperKeyValuePairs, m_key_value_pairs, ASSET_TYPE_KEYVALUEPAIRS) - DUMP_ASSET_POOL(AssetDumperVehicle, m_vehicle, ASSET_TYPE_VEHICLEDEF) + DUMP_ASSET_POOL(vehicle::DumperT6, m_vehicle, ASSET_TYPE_VEHICLEDEF) // DUMP_ASSET_POOL(AssetDumperMemoryBlock, m_memory_block, ASSET_TYPE_MEMORYBLOCK) // DUMP_ASSET_POOL(AssetDumperAddonMapEnts, m_addon_map_ents, ASSET_TYPE_ADDON_MAP_ENTS) - DUMP_ASSET_POOL(AssetDumperTracer, m_tracer, ASSET_TYPE_TRACER) + DUMP_ASSET_POOL(tracer::DumperT6, m_tracer, ASSET_TYPE_TRACER) // DUMP_ASSET_POOL(AssetDumperSkinnedVertsDef, m_skinned_verts, ASSET_TYPE_SKINNEDVERTS) - DUMP_ASSET_POOL(AssetDumperQdb, m_qdb, ASSET_TYPE_QDB) - DUMP_ASSET_POOL(AssetDumperSlug, m_slug, ASSET_TYPE_SLUG) + DUMP_ASSET_POOL(qdb::DumperT6, m_qdb, ASSET_TYPE_QDB) + DUMP_ASSET_POOL(slug::DumperT6, m_slug, ASSET_TYPE_SLUG) // DUMP_ASSET_POOL(AssetDumperFootstepTableDef, m_footstep_table, ASSET_TYPE_FOOTSTEP_TABLE) // DUMP_ASSET_POOL(AssetDumperFootstepFXTableDef, m_footstep_fx_table, ASSET_TYPE_FOOTSTEPFX_TABLE) - DUMP_ASSET_POOL(AssetDumperZBarrier, m_zbarrier, ASSET_TYPE_ZBARRIER) + DUMP_ASSET_POOL(z_barrier::DumperT6, m_zbarrier, ASSET_TYPE_ZBARRIER) return true; diff --git a/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.cpp b/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.cpp new file mode 100644 index 00000000..c0c2d7ce --- /dev/null +++ b/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.cpp @@ -0,0 +1,92 @@ +#include "PhysConstraintsInfoStringDumperT6.h" + +#include "Game/T6/InfoString/InfoStringFromStructConverter.h" +#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/PhysConstraints/PhysConstraintsFields.h" +#include "PhysConstraints/PhysConstraintsCommon.h" + +#include +#include + +using namespace T6; + +namespace +{ + class InfoStringFromPhysConstraintsConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + switch (static_cast(field.iFieldType)) + { + case CFT_TYPE: + FillFromEnumInt(std::string(field.szName), field.iOffset, s_constraintTypeNames, std::extent_v); + break; + + default: + assert(false); + break; + } + } + + public: + InfoStringFromPhysConstraintsConverter(const PhysConstraints* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateInfoString(XAssetInfo* asset) + { + assert(asset->Asset()->count <= 4); + + InfoStringFromPhysConstraintsConverter converter(asset->Asset(), + phys_constraints_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace phys_constraints +{ + bool InfoStringDumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void InfoStringDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_PHYS_CONSTRAINTS); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_PHYS_CONSTRAINTS, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_PHYS_CONSTRAINTS); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace phys_constraints diff --git a/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.h b/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.h new file mode 100644 index 00000000..4d1c5e70 --- /dev/null +++ b/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" +#include "InfoString/InfoString.h" + +namespace phys_constraints +{ + class InfoStringDumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace phys_constraints diff --git a/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.cpp b/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.cpp new file mode 100644 index 00000000..c4f5dd85 --- /dev/null +++ b/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.cpp @@ -0,0 +1,113 @@ +#include "PhysPresetInfoStringDumperT6.h" + +#include "Game/T6/InfoString/InfoStringFromStructConverter.h" +#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/PhysPreset/PhysPresetFields.h" +#include "PhysPreset/PhysPresetCommon.h" + +#include +#include +#include +#include + +using namespace T6; + +namespace +{ + class InfoStringFromPhysPresetConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + assert(false); + } + + public: + InfoStringFromPhysPresetConverter(const PhysPresetInfo* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + void CopyToPhysPresetInfo(const PhysPreset* physPreset, PhysPresetInfo* physPresetInfo) + { + physPresetInfo->mass = std::clamp(physPreset->mass * 1000.0f, 1.0f, 2000.0f); + physPresetInfo->bounce = physPreset->bounce; + + if (std::isinf(physPreset->friction)) + { + physPresetInfo->isFrictionInfinity = 1; + physPresetInfo->friction = 0; + } + else + { + physPresetInfo->isFrictionInfinity = 0; + physPresetInfo->friction = physPreset->friction; + } + + physPresetInfo->bulletForceScale = physPreset->bulletForceScale; + physPresetInfo->explosiveForceScale = physPreset->explosiveForceScale; + physPresetInfo->piecesSpreadFraction = physPreset->piecesSpreadFraction; + physPresetInfo->piecesUpwardVelocity = physPreset->piecesUpwardVelocity; + physPresetInfo->canFloat = physPreset->canFloat; + physPresetInfo->gravityScale = std::clamp(physPreset->gravityScale, 0.01f, 10.0f); + physPresetInfo->centerOfMassOffset = physPreset->centerOfMassOffset; + physPresetInfo->buoyancyBoxMin = physPreset->buoyancyBoxMin; + physPresetInfo->buoyancyBoxMax = physPreset->buoyancyBoxMax; + } + + InfoString CreateInfoString(XAssetInfo* asset) + { + auto* physPresetInfo = new PhysPresetInfo; + CopyToPhysPresetInfo(asset->Asset(), physPresetInfo); + + InfoStringFromPhysPresetConverter converter(physPresetInfo, + phys_preset_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace phys_preset +{ + bool InfoStringDumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void InfoStringDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_PHYS_PRESET); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_PHYS_PRESET); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace phys_preset diff --git a/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.h b/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.h new file mode 100644 index 00000000..df52d539 --- /dev/null +++ b/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" +#include "InfoString/InfoString.h" + +namespace phys_preset +{ + class InfoStringDumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace phys_preset diff --git a/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.cpp b/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.cpp new file mode 100644 index 00000000..f990de0d --- /dev/null +++ b/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.cpp @@ -0,0 +1,23 @@ +#include "QdbDumperT6.h" + +using namespace T6; + +namespace qdb +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* qdb = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto& stream = *assetFile; + stream.write(qdb->buffer, qdb->len); + } +} // namespace qdb diff --git a/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.h b/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.h new file mode 100644 index 00000000..eb452510 --- /dev/null +++ b/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace qdb +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace qdb diff --git a/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.cpp b/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.cpp new file mode 100644 index 00000000..9388c216 --- /dev/null +++ b/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.cpp @@ -0,0 +1,106 @@ +#include "RawFileDumperT6.h" + +#include "Utils/Logging/Log.h" + +#include +#include +#include + +using namespace T6; + +namespace fs = std::filesystem; + +namespace +{ + constexpr size_t ANIMTREE_MAX_SIZE = 0xC000000; + + void DumpAnimtree(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) + { + const auto* rawFile = asset->Asset(); + + if (rawFile->len <= 4) + { + con::error("Invalid len of animtree file \"{}\"", rawFile->name); + return; + } + + const auto outLen = reinterpret_cast(rawFile->buffer)[0]; + const auto inLen = rawFile->len; + + if (outLen > ANIMTREE_MAX_SIZE) + { + con::error("Invalid size of animtree file \"{}\": {}", rawFile->name, outLen); + 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 = inflateInit2(&zs, -DEF_WBITS); + + if (ret != Z_OK) + { + throw std::runtime_error("Initializing inflate failed"); + } + + zs.next_in = reinterpret_cast(&rawFile->buffer[4]); + zs.avail_in = inLen - sizeof(uint32_t); + + Bytef buffer[0x1000]; + + while (zs.avail_in > 0) + { + zs.next_out = buffer; + zs.avail_out = sizeof(buffer); + ret = inflate(&zs, Z_SYNC_FLUSH); + + if (ret < 0) + { + con::error("Inflate failed for dumping animtree file \"{}\"", rawFile->name); + inflateEnd(&zs); + return; + } + + const auto inflateOutSize = sizeof(buffer) - zs.avail_out; + + stream.write(reinterpret_cast(buffer), inflateOutSize); + } + + inflateEnd(&zs); + } +} // namespace + +namespace raw_file +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* rawFile = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const fs::path rawFilePath(rawFile->name); + const auto extension = rawFilePath.extension().string(); + + if (extension == ".atr") + { + DumpAnimtree(context, asset, stream); + } + else + { + stream.write(rawFile->buffer, rawFile->len); + } + } +} // namespace raw_file diff --git a/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.h b/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.h new file mode 100644 index 00000000..c2faa0f9 --- /dev/null +++ b/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace raw_file +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace raw_file diff --git a/src/ObjWriting/Game/T6/Script/ScriptDumperT6.cpp b/src/ObjWriting/Game/T6/Script/ScriptDumperT6.cpp new file mode 100644 index 00000000..2b2be090 --- /dev/null +++ b/src/ObjWriting/Game/T6/Script/ScriptDumperT6.cpp @@ -0,0 +1,23 @@ +#include "ScriptDumperT6.h" + +using namespace T6; + +namespace script +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* scriptParseTree = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto& stream = *assetFile; + stream.write(scriptParseTree->buffer, scriptParseTree->len); + } +} // namespace script diff --git a/src/ObjWriting/Game/T6/Script/ScriptDumperT6.h b/src/ObjWriting/Game/T6/Script/ScriptDumperT6.h new file mode 100644 index 00000000..895b55b4 --- /dev/null +++ b/src/ObjWriting/Game/T6/Script/ScriptDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace script +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace script diff --git a/src/ObjWriting/Game/T6/Slug/SlugDumperT6.cpp b/src/ObjWriting/Game/T6/Slug/SlugDumperT6.cpp new file mode 100644 index 00000000..d02a11d0 --- /dev/null +++ b/src/ObjWriting/Game/T6/Slug/SlugDumperT6.cpp @@ -0,0 +1,23 @@ +#include "SlugDumperT6.h" + +using namespace T6; + +namespace slug +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* slug = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + auto& stream = *assetFile; + stream.write(slug->buffer, slug->len); + } +} // namespace slug diff --git a/src/ObjWriting/Game/T6/Slug/SlugDumperT6.h b/src/ObjWriting/Game/T6/Slug/SlugDumperT6.h new file mode 100644 index 00000000..3dd202e5 --- /dev/null +++ b/src/ObjWriting/Game/T6/Slug/SlugDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace slug +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace slug diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp b/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.cpp similarity index 94% rename from src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp rename to src/ObjWriting/Game/T6/Sound/SndBankDumperT6.cpp index 5d407b86..aca65ad7 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp +++ b/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.cpp @@ -1,4 +1,4 @@ -#include "AssetDumperSndBank.h" +#include "SndBankDumperT6.h" #include "Csv/CsvStream.h" #include "Game/T6/CommonT6.h" @@ -7,13 +7,15 @@ #include "Game/T6/SoundConstantsT6.h" #include "ObjContainer/SoundBank/SoundBank.h" #include "Sound/WavWriter.h" -#include "nlohmann/json.hpp" +#include "Utils/Logging/Log.h" +#include "Zone/ZoneRegistry.h" #include #include #include #include #include +#include #include #include @@ -201,7 +203,7 @@ namespace public: void Initialize() { - for (const auto& zone : IGame::GetGameById(GameId::T6)->GetZones()) + for (const auto& zone : ZoneRegistry::GetRegistryForGame(GameId::T6)->Zones()) { auto& sndBankPool = *dynamic_cast(zone->m_pools.get())->m_sound_bank; for (auto* entry : sndBankPool) @@ -669,7 +671,7 @@ namespace const auto outFile = OpenAssetOutputFile(context, assetFileName, ".wav"); if (!outFile) { - std::cerr << std::format("Failed to open sound output file: \"{}\"\n", assetFileName); + con::error("Failed to open sound output file: \"{}\"", assetFileName); return; } @@ -699,7 +701,7 @@ namespace const auto outFile = OpenAssetOutputFile(context, assetFileName, extension); if (!outFile) { - std::cerr << std::format("Failed to open sound output file: \"{}\"\n", assetFileName); + con::error("Failed to open sound output file: \"{}\"", assetFileName); return; } @@ -736,19 +738,19 @@ namespace case SND_ASSET_FORMAT_WMA: case SND_ASSET_FORMAT_WIIUADPCM: case SND_ASSET_FORMAT_MPC: - std::cerr << std::format("Cannot dump sound (Unknown sound format {}): \"{}\"\n", static_cast(format), alias.assetFileName); + con::error("Cannot dump sound (Unknown sound format {}): \"{}\"", static_cast(format), alias.assetFileName); break; default: assert(false); - std::cerr << std::format("Cannot dump sound (Unknown sound format {}): \"{}\"\n", static_cast(format), alias.assetFileName); + con::error("Cannot dump sound (Unknown sound format {}): \"{}\"", static_cast(format), alias.assetFileName); break; } return format; } - std::cerr << std::format("Could not find data for sound \"{}\"\n", alias.assetFileName); + con::warn("Could not find data for sound \"{}\"", alias.assetFileName); return {}; } @@ -759,7 +761,7 @@ namespace const auto outFile = OpenAssetOutputFile(context, std::format("soundbank/{}.aliases", sndBank.name), ".csv"); if (!outFile) { - std::cerr << std::format("Failed to open sound alias output file: \"\"\n", sndBank.name); + con::error("Failed to open sound alias output file: \"\"", sndBank.name); return; } @@ -803,7 +805,7 @@ namespace const auto outFile = OpenAssetOutputFile(context, std::format("soundbank/{}.reverbs", sndBank.name), ".csv"); if (!outFile) { - std::cerr << std::format("Failed to open sound reverb output file: \"{}\"\n", sndBank.name); + con::error("Failed to open sound reverb output file: \"{}\"", sndBank.name); return; } @@ -842,7 +844,7 @@ namespace const auto outFile = OpenAssetOutputFile(context, std::format("soundbank/{}.ducklist", sndBank.name), ".csv"); if (!outFile) { - std::cerr << std::format("Failed to open sound reverb output file: \"{}\"\n", sndBank.name); + con::error("Failed to open sound reverb output file: \"{}\"", sndBank.name); return; } @@ -859,7 +861,7 @@ namespace const auto duckFile = OpenAssetOutputFile(context, std::format("soundbank/ducks/{}", duck.name), ".duk"); if (!outFile) { - std::cerr << std::format("Failed to open sound duck output file: \"{}\"\n", duck.name); + con::error("Failed to open sound duck output file: \"{}\"", duck.name); return; } @@ -910,15 +912,18 @@ namespace } } // namespace -void AssetDumperSndBank::DumpPool(AssetDumpingContext& context, AssetPool* pool) +namespace sound { - LoadedSoundBankHashes soundBankHashes; - soundBankHashes.Initialize(); - for (const auto* assetInfo : *pool) + void SndBankDumperT6::DumpPool(AssetDumpingContext& context, AssetPool* pool) { - if (!assetInfo->m_name.empty() && assetInfo->m_name[0] == ',') - continue; + LoadedSoundBankHashes soundBankHashes; + soundBankHashes.Initialize(); + for (const auto* assetInfo : *pool) + { + if (!assetInfo->m_name.empty() && assetInfo->m_name[0] == ',') + continue; - DumpSndBank(context, soundBankHashes, *assetInfo); + DumpSndBank(context, soundBankHashes, *assetInfo); + } } -} +} // namespace sound diff --git a/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.h b/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.h new file mode 100644 index 00000000..f5d4f59d --- /dev/null +++ b/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace sound +{ + class SndBankDumperT6 final : public IAssetDumper + { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + }; +} // namespace sound diff --git a/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.cpp b/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.cpp new file mode 100644 index 00000000..57649091 --- /dev/null +++ b/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.cpp @@ -0,0 +1,393 @@ +#include "SndDriverGlobalsDumperT6.h" + +#include "Csv/CsvStream.h" +#include "ObjContainer/SoundBank/SoundBank.h" +#include "Utils/Logging/Log.h" + +#include + +using namespace T6; + +namespace +{ + const std::string GROUPS_HEADERS[]{ + "name", + "attenuationSp", + "attenuationMp", + "category", + "parent", + "id", + }; + + const std::string GROUPS_CATEGORIES[]{ + "sfx", + "music", + "void", + "ui", + "cinematic", + "id", + }; + + const std::string CURVE_HEADERS[]{ + "name", + "x0", + "y0", + "x1", + "y1", + "x2", + "y2", + "x3", + "y3", + "x4", + "y4", + "x5", + "y5", + "x6", + "y6", + "x7", + "y7", + "id", + }; + + const std::string PAN_HEADERS[]{ + "name", + "front", + "back", + "center", + "lfe", + "left", + "right", + "id", + }; + + const std::string MASTER_HEADERS[]{ + "name", "lowE", "lowG", "lowF", "lowQ", "peak1E", "peak1G", "peak1F", "peak1Q", "peak2E", "peak2G", + "peak2F", "peak2Q", "hiE", "hiG", "hiF", "hiQ", "eqG", "compE", "compPG", "compMG", "compT", + "compR", "compTA", "compTR", "limitE", "limitPG", "limitMG", "limitT", "limitR", "limitTA", "limitTR", "busReverbG", + "busFxG", "busVoiceG", "busPfutzG", "busHdrfxG", "busUiG", "busMusicG", "busMovieG", "busVcsG", "busReverbE", "busFxE", "busVoiceE", + "busPfutzE", "busHdrfxE", "busUiE", "busMusicE", "busMovieE", "hdrfxCompE", "voiceEqE", "voiceCompE", "id", + }; + + const std::string SIDECHAIN_HEADERS[]{ + "name", + "g", + "f", + "q", + "ta", + "tr", + "tf", + "id", + }; + + const std::string FUTZ_HEADERS[]{ + "name", + "bpfF", + "bpfQ", + "lsG", + "lsF", + "lsQ", + "dist", + "preG", + "postG", + "th", + "tg", + "clippre", + "clippost", + "blend", + "startAliasId", + "stopAliasId", + "loopAliasId", + "id", + }; + + class Internal + { + public: + explicit Internal(AssetDumpingContext& context) + : m_context(context) + { + } + + void DumpPool(AssetPool* pool) + { + for (const auto* assetInfo : *pool) + { + if (!assetInfo->m_name.empty() && assetInfo->m_name[0] == ',') + continue; + + DumpSndDriverGlobals(assetInfo); + } + } + + private: + std::unique_ptr OpenAssetFile(const std::string& filename) + { + auto outputFile = this->m_context.OpenAssetFile(filename); + if (outputFile == nullptr) + con::error("Failed to open sound driver globals output file for: \"{}\"", filename); + + return outputFile; + } + + static void WriteFileHeader(CsvOutputStream& stream, const std::string* headers, size_t count) + { + for (auto i = 0u; i < count; i++) + { + stream.WriteColumn(headers[i]); + } + + stream.NextRow(); + } + + void DumpSndVolumesGroups(const SndVolumeGroup* groups, const size_t count) + { + const auto outputFile = this->OpenAssetFile("soundbank/globals/group.csv"); + + if (outputFile != nullptr) + { + CsvOutputStream csvStream(*outputFile); + WriteFileHeader(csvStream, GROUPS_HEADERS, std::extent_v); + + for (auto i = 0u; i < count; i++) + { + const auto& group = groups[i]; + csvStream.WriteColumn(group.name); + csvStream.WriteColumn(std::to_string(group.attenuationSp)); + csvStream.WriteColumn(std::to_string(group.attenuationMp)); + csvStream.WriteColumn(GROUPS_CATEGORIES[group.category]); + csvStream.WriteColumn(group.parentName); + csvStream.WriteColumn(std::to_string(group.id)); + csvStream.NextRow(); + } + } + } + + void DumpSndCurves(const SndCurve* curves, const size_t count) + { + const auto outputFile = this->OpenAssetFile("soundbank/globals/curves.csv"); + + if (outputFile != nullptr) + { + CsvOutputStream csvStream(*outputFile); + WriteFileHeader(csvStream, CURVE_HEADERS, std::extent_v); + + for (auto i = 0u; i < count; i++) + { + const auto& curve = curves[i]; + csvStream.WriteColumn(curve.name); + + for (auto j = 0u; j < 8; j++) + { + csvStream.WriteColumn(std::to_string(curve.points[j].x)); + csvStream.WriteColumn(std::to_string(curve.points[j].y)); + } + + csvStream.WriteColumn(std::to_string(curve.id)); + + csvStream.NextRow(); + } + } + } + + void DumpSndPans(const SndPan* pans, const size_t count) + { + const auto outputFile = this->OpenAssetFile("soundbank/globals/pan.csv"); + + if (outputFile != nullptr) + { + CsvOutputStream csvStream(*outputFile); + WriteFileHeader(csvStream, PAN_HEADERS, std::extent_v); + + for (auto i = 0u; i < count; i++) + { + const auto& pan = pans[i]; + csvStream.WriteColumn(pan.name); + csvStream.WriteColumn(std::to_string(pan.front)); + csvStream.WriteColumn(std::to_string(pan.back)); + csvStream.WriteColumn(std::to_string(pan.center)); + csvStream.WriteColumn(std::to_string(pan.lfe)); + csvStream.WriteColumn(std::to_string(pan.left)); + csvStream.WriteColumn(std::to_string(pan.right)); + csvStream.WriteColumn(std::to_string(pan.id)); + csvStream.NextRow(); + } + } + } + + void DumpSndDuckGroups(const SndDuckGroup* duckGroups, const size_t count) + { + const auto outputFile = this->OpenAssetFile("soundbank/globals/duck_groups.csv"); + + if (outputFile != nullptr) + { + CsvOutputStream csvStream(*outputFile); + csvStream.WriteColumn("name"); + csvStream.WriteColumn("id"); + csvStream.NextRow(); + + for (auto i = 0u; i < count; i++) + { + const auto& duckGroup = duckGroups[i]; + csvStream.WriteColumn(duckGroup.name); + csvStream.WriteColumn(std::to_string(duckGroup.id)); + csvStream.NextRow(); + } + } + } + + void DumpSndMasters(const SndMaster* masters, const size_t count) + { + const auto outputFile = this->OpenAssetFile("soundbank/globals/master.csv"); + + if (outputFile != nullptr) + { + CsvOutputStream csvStream(*outputFile); + WriteFileHeader(csvStream, MASTER_HEADERS, std::extent_v); + + for (auto i = 0u; i < count; i++) + { + const auto& master = masters[i]; + csvStream.WriteColumn(master.name); + csvStream.WriteColumn(std::to_string(master.lowE)); + csvStream.WriteColumn(std::to_string(master.lowG)); + csvStream.WriteColumn(std::to_string(master.lowF)); + csvStream.WriteColumn(std::to_string(master.lowQ)); + csvStream.WriteColumn(std::to_string(master.peak1E)); + csvStream.WriteColumn(std::to_string(master.peak1G)); + csvStream.WriteColumn(std::to_string(master.peak1F)); + csvStream.WriteColumn(std::to_string(master.peak1Q)); + csvStream.WriteColumn(std::to_string(master.peak2E)); + csvStream.WriteColumn(std::to_string(master.peak2G)); + csvStream.WriteColumn(std::to_string(master.peak2F)); + csvStream.WriteColumn(std::to_string(master.peak2Q)); + csvStream.WriteColumn(std::to_string(master.hiE)); + csvStream.WriteColumn(std::to_string(master.hiG)); + csvStream.WriteColumn(std::to_string(master.hiF)); + csvStream.WriteColumn(std::to_string(master.hiQ)); + csvStream.WriteColumn(std::to_string(master.eqG)); + csvStream.WriteColumn(std::to_string(master.compE)); + csvStream.WriteColumn(std::to_string(master.compPG)); + csvStream.WriteColumn(std::to_string(master.compMG)); + csvStream.WriteColumn(std::to_string(master.compT)); + csvStream.WriteColumn(std::to_string(master.compR)); + csvStream.WriteColumn(std::to_string(master.compTA)); + csvStream.WriteColumn(std::to_string(master.compTR)); + csvStream.WriteColumn(std::to_string(master.limitE)); + csvStream.WriteColumn(std::to_string(master.limitPG)); + csvStream.WriteColumn(std::to_string(master.limitMG)); + csvStream.WriteColumn(std::to_string(master.limitT)); + csvStream.WriteColumn(std::to_string(master.limitR)); + csvStream.WriteColumn(std::to_string(master.limitTA)); + csvStream.WriteColumn(std::to_string(master.limitTR)); + csvStream.WriteColumn(std::to_string(master.busReverbG)); + csvStream.WriteColumn(std::to_string(master.busFxG)); + csvStream.WriteColumn(std::to_string(master.busVoiceG)); + csvStream.WriteColumn(std::to_string(master.busPfutzG)); + csvStream.WriteColumn(std::to_string(master.busHdrfxG)); + csvStream.WriteColumn(std::to_string(master.busUiG)); + csvStream.WriteColumn(std::to_string(master.busMusicG)); + csvStream.WriteColumn(std::to_string(master.busMovieG)); + csvStream.WriteColumn(std::to_string(master.busVcsG)); + csvStream.WriteColumn(std::to_string(master.busReverbE)); + csvStream.WriteColumn(std::to_string(master.busFxE)); + csvStream.WriteColumn(std::to_string(master.busVoiceE)); + csvStream.WriteColumn(std::to_string(master.busPfutzE)); + csvStream.WriteColumn(std::to_string(master.busHdrfxE)); + csvStream.WriteColumn(std::to_string(master.busUiE)); + csvStream.WriteColumn(std::to_string(master.busMusicE)); + csvStream.WriteColumn(std::to_string(master.busMovieE)); + csvStream.WriteColumn(std::to_string(master.hdrfxCompE)); + csvStream.WriteColumn(std::to_string(master.voiceEqE)); + csvStream.WriteColumn(std::to_string(master.voiceCompE)); + csvStream.WriteColumn(std::to_string(master.id)); + csvStream.NextRow(); + } + } + } + + void DumpSndSidechainDucks(const SndSidechainDuck* sidechains, const size_t count) + { + const auto outputFile = this->OpenAssetFile("soundbank/globals/sidechain_duck.csv"); + + if (outputFile != nullptr) + { + CsvOutputStream csvStream(*outputFile); + WriteFileHeader(csvStream, SIDECHAIN_HEADERS, std::extent_v); + + for (auto i = 0u; i < count; i++) + { + const auto& sidechain = sidechains[i]; + csvStream.WriteColumn(sidechain.name); + csvStream.WriteColumn(std::to_string(sidechain.g)); + csvStream.WriteColumn(std::to_string(sidechain.f)); + csvStream.WriteColumn(std::to_string(sidechain.q)); + csvStream.WriteColumn(std::to_string(sidechain.ta)); + csvStream.WriteColumn(std::to_string(sidechain.tr)); + csvStream.WriteColumn(std::to_string(sidechain.tf)); + csvStream.WriteColumn(std::to_string(sidechain.id)); + csvStream.NextRow(); + } + } + } + + void DumpSndFutz(const SndFutz* futzes, const size_t count) + { + const auto outputFile = this->OpenAssetFile("soundbank/globals/futz.csv"); + + if (outputFile != nullptr) + { + CsvOutputStream csvStream(*outputFile); + WriteFileHeader(csvStream, FUTZ_HEADERS, std::extent_v); + + for (auto i = 0u; i < count; i++) + { + const auto& futz = futzes[i]; + csvStream.WriteColumn(futz.name); + csvStream.WriteColumn(std::to_string(futz.bpfF)); + csvStream.WriteColumn(std::to_string(futz.bpfQ)); + csvStream.WriteColumn(std::to_string(futz.lsG)); + csvStream.WriteColumn(std::to_string(futz.lsF)); + csvStream.WriteColumn(std::to_string(futz.lsQ)); + csvStream.WriteColumn(std::to_string(futz.dist)); + csvStream.WriteColumn(std::to_string(futz.preG)); + csvStream.WriteColumn(std::to_string(futz.postG)); + csvStream.WriteColumn(std::to_string(futz.th)); + csvStream.WriteColumn(std::to_string(futz.tg)); + csvStream.WriteColumn(std::to_string(futz.clippre)); + csvStream.WriteColumn(std::to_string(futz.clippost)); + csvStream.WriteColumn(std::to_string(futz.blend)); + csvStream.WriteColumn(std::to_string(futz.startAliasId)); + csvStream.WriteColumn(std::to_string(futz.stopAliasId)); + csvStream.WriteColumn(std::to_string(futz.loopAliasId)); + csvStream.WriteColumn(std::to_string(futz.id)); + csvStream.NextRow(); + } + } + } + + void DumpSndDriverGlobals(const XAssetInfo* sndDriverGlobalsInfo) + { + const auto* sndDriverGlobals = sndDriverGlobalsInfo->Asset(); + + DumpSndVolumesGroups(sndDriverGlobals->groups, sndDriverGlobals->groupCount); + DumpSndCurves(sndDriverGlobals->curves, sndDriverGlobals->curveCount); + DumpSndPans(sndDriverGlobals->pans, sndDriverGlobals->panCount); + DumpSndDuckGroups(sndDriverGlobals->duckGroups, sndDriverGlobals->duckGroupCount); + // DumpSndContexts(sndDriverGlobals->contexts, sndDriverGlobals->contextCount); + DumpSndMasters(sndDriverGlobals->masters, sndDriverGlobals->masterCount); + DumpSndSidechainDucks(sndDriverGlobals->voiceDucks, sndDriverGlobals->voiceDuckCount); + DumpSndFutz(sndDriverGlobals->futzes, sndDriverGlobals->futzCount); + } + + AssetDumpingContext& m_context; + }; +} // namespace + +namespace sound +{ + void SndDriverGlobalsDumperT6::DumpPool(AssetDumpingContext& context, AssetPool* pool) + { + Internal internal(context); + internal.DumpPool(pool); + } +} // namespace sound diff --git a/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.h b/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.h new file mode 100644 index 00000000..042de43c --- /dev/null +++ b/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace sound +{ + class SndDriverGlobalsDumperT6 final : public IAssetDumper + { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + }; +} // namespace sound diff --git a/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.cpp b/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.cpp new file mode 100644 index 00000000..4bd96479 --- /dev/null +++ b/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.cpp @@ -0,0 +1,42 @@ +#include "StringTableDumperT6.h" + +#include "Csv/CsvStream.h" + +using namespace T6; + +namespace string_table +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* stringTable = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + CsvOutputStream csv(*assetFile); + + for (auto row = 0; row < stringTable->rowCount; row++) + { + for (auto column = 0; column < stringTable->columnCount; column++) + { + const auto* cell = &stringTable->values[column + row * stringTable->columnCount]; + if (cell->string != nullptr) + { + csv.WriteColumn(cell->string); + } + else + { + csv.WriteColumn(""); + } + } + + csv.NextRow(); + } + } +} // namespace string_table diff --git a/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.h b/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.h new file mode 100644 index 00000000..b61a4380 --- /dev/null +++ b/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace string_table +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace string_table diff --git a/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.cpp b/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.cpp new file mode 100644 index 00000000..a2d7273c --- /dev/null +++ b/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.cpp @@ -0,0 +1,107 @@ +#include "TechsetDumperT6.h" + +#include "Shader/ShaderCommon.h" + +#include +#include + +using namespace T6; + +namespace +{ + class ShaderZoneState final : public IZoneAssetDumperState + { + public: + bool ShouldDumpTechnique(const MaterialTechnique* technique) + { + const auto existingTechnique = m_dumped_techniques.find(technique); + if (existingTechnique == m_dumped_techniques.end()) + { + m_dumped_techniques.emplace(technique); + return true; + } + + return false; + } + + bool ShouldDumpPixelShader(const MaterialPixelShader* pixelShader) + { + const auto existingPixelShader = m_dumped_pixel_shaders.find(pixelShader); + if (existingPixelShader == m_dumped_pixel_shaders.end()) + { + m_dumped_pixel_shaders.emplace(pixelShader); + return true; + } + + return false; + } + + bool ShouldDumpVertexShader(const MaterialVertexShader* vertexShader) + { + const auto existingVertexShader = m_dumped_vertex_shaders.find(vertexShader); + if (existingVertexShader == m_dumped_vertex_shaders.end()) + { + m_dumped_vertex_shaders.emplace(vertexShader); + return true; + } + + return false; + } + + private: + std::unordered_set m_dumped_techniques; + std::unordered_set m_dumped_pixel_shaders; + std::unordered_set m_dumped_vertex_shaders; + }; + + void DumpPixelShader(const AssetDumpingContext& context, const MaterialPixelShader& pixelShader) + { + const auto shaderFile = context.OpenAssetFile(shader::GetFileNameForPixelShaderAssetName(pixelShader.name)); + + if (!shaderFile) + return; + + shaderFile->write(pixelShader.prog.loadDef.program, pixelShader.prog.loadDef.programSize); + } + + void DumpVertexShader(const AssetDumpingContext& context, const MaterialVertexShader& vertexShader) + { + const auto shaderFile = context.OpenAssetFile(shader::GetFileNameForVertexShaderAssetName(vertexShader.name)); + + if (!shaderFile) + return; + + shaderFile->write(vertexShader.prog.loadDef.program, vertexShader.prog.loadDef.programSize); + } +} // namespace + +namespace techset +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto* techniqueSet = asset->Asset(); + auto* shaderState = context.GetZoneAssetDumperState(); + + for (const auto* technique : techniqueSet->techniques) + { + if (!technique || !shaderState->ShouldDumpTechnique(technique)) + continue; + + for (auto passIndex = 0u; passIndex < technique->passCount; passIndex++) + { + const auto* pixelShader = technique->passArray[passIndex].pixelShader; + if (pixelShader && shaderState->ShouldDumpPixelShader(pixelShader)) + DumpPixelShader(context, *pixelShader); + + const auto* vertexShader = technique->passArray[passIndex].vertexShader; + if (vertexShader && shaderState->ShouldDumpVertexShader(vertexShader)) + DumpVertexShader(context, *vertexShader); + } + } + } +} // namespace techset diff --git a/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.h b/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.h new file mode 100644 index 00000000..59a48f3f --- /dev/null +++ b/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace techset +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace techset diff --git a/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.cpp b/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.cpp new file mode 100644 index 00000000..854a45af --- /dev/null +++ b/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.cpp @@ -0,0 +1,91 @@ +#include "TracerDumperT6.h" + +#include "Game/T6/InfoString/InfoStringFromStructConverter.h" +#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/Tracer/TracerFields.h" +#include "Tracer/TracerCommon.h" + +#include +#include + +using namespace T6; + +namespace +{ + class InfoStringFromTracerConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + switch (static_cast(field.iFieldType)) + { + case TFT_TRACERTYPE: + FillFromEnumInt(std::string(field.szName), field.iOffset, tracerTypeNames, std::extent_v); + break; + + case TFT_NUM_FIELD_TYPES: + default: + assert(false); + break; + } + } + + public: + InfoStringFromTracerConverter(const TracerDef* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateInfoString(XAssetInfo* asset) + { + InfoStringFromTracerConverter converter(asset->Asset(), + tracer_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace tracer +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_TRACER); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_TRACER, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_TRACER); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace tracer diff --git a/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.h b/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.h new file mode 100644 index 00000000..0bfb6d75 --- /dev/null +++ b/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" +#include "InfoString/InfoString.h" + +namespace tracer +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace tracer diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperVehicle.cpp b/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.cpp similarity index 61% rename from src/ObjWriting/Game/T6/AssetDumpers/AssetDumperVehicle.cpp rename to src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.cpp index cccf418a..1178b394 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperVehicle.cpp +++ b/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.cpp @@ -1,15 +1,17 @@ -#include "AssetDumperVehicle.h" +#include "VehicleDumperT6.h" #include "Game/T6/InfoString/InfoStringFromStructConverter.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/Vehicle/VehicleFields.h" +#include "Vehicle/VehicleCommon.h" #include #include using namespace T6; +using namespace ::vehicle; -namespace T6 +namespace { class InfoStringFromVehicleConverter final : public InfoStringFromStructConverter { @@ -88,50 +90,53 @@ namespace T6 { } }; -} // namespace T6 -InfoString AssetDumperVehicle::CreateInfoString(XAssetInfo* asset) -{ - InfoStringFromVehicleConverter converter(asset->Asset(), - vehicle_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -bool AssetDumperVehicle::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperVehicle::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) + InfoString CreateInfoString(XAssetInfo* asset) { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_VEHICLE); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_VEHICLE, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); + InfoStringFromVehicleConverter converter(asset->Asset(), + vehicle_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); } - else +} // namespace + +namespace vehicle +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) { - const auto assetFile = context.OpenAssetFile("vehicles/" + asset->m_name); - - if (!assetFile) - return; - - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_VEHICLE); - stream.write(stringValue.c_str(), stringValue.size()); + return true; } -} + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_VEHICLE); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_VEHICLE, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_VEHICLE); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace vehicle diff --git a/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.h b/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.h new file mode 100644 index 00000000..5d328e87 --- /dev/null +++ b/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" +#include "InfoString/InfoString.h" + +namespace vehicle +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace vehicle diff --git a/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.cpp b/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.cpp new file mode 100644 index 00000000..d7fcc4c7 --- /dev/null +++ b/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.cpp @@ -0,0 +1,98 @@ +#include "AttachmentDumperT6.h" + +#include "Game/T6/InfoString/InfoStringFromStructConverter.h" +#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/Weapon/AttachmentFields.h" +#include "Game/T6/Weapon/WeaponStrings.h" +#include "Weapon/AttachmentCommon.h" + +#include +#include + +using namespace T6; + +namespace +{ + class InfoStringFromAttachmentConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + switch (static_cast(field.iFieldType)) + { + case AFT_ATTACHMENTTYPE: + FillFromEnumInt(std::string(field.szName), field.iOffset, szAttachmentTypeNames, std::extent_v); + break; + + case AFT_PENETRATE_TYPE: + FillFromEnumInt(std::string(field.szName), field.iOffset, penetrateTypeNames, std::extent_v); + break; + + case AFT_FIRETYPE: + FillFromEnumInt(std::string(field.szName), field.iOffset, szWeapFireTypeNames, std::extent_v); + break; + + default: + break; + } + } + + public: + InfoStringFromAttachmentConverter(const WeaponAttachment* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateInfoString(XAssetInfo* asset) + { + InfoStringFromAttachmentConverter converter(asset->Asset(), + attachment_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace attachment +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON_ATTACHMENT); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetInfoStringFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace attachment diff --git a/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.h b/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.h new file mode 100644 index 00000000..0472e2a0 --- /dev/null +++ b/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" +#include "InfoString/InfoString.h" + +namespace attachment +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace attachment diff --git a/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.cpp b/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.cpp new file mode 100644 index 00000000..c10605ac --- /dev/null +++ b/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.cpp @@ -0,0 +1,162 @@ +#include "AttachmentUniqueDumperT6.h" + +#include "Game/T6/InfoString/InfoStringFromStructConverter.h" +#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/Weapon/AttachmentUniqueFields.h" +#include "Game/T6/Weapon/WeaponStrings.h" +#include "Weapon/AttachmentUniqueCommon.h" + +#include +#include +#include +#include + +using namespace T6; + +namespace +{ + class InfoStringFromWeaponAttachmentUniqueConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + switch (static_cast(field.iFieldType)) + { + case AUFT_ATTACHMENTTYPE: + FillFromEnumInt(std::string(field.szName), field.iOffset, szAttachmentTypeNames, std::extent_v); + break; + + case AUFT_HIDETAGS: + { + const auto* hideTags = reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset); + std::stringstream ss; + auto first = true; + + for (auto i = 0u; i < std::extent_v; i++) + { + const auto& str = m_get_scr_string(hideTags[i]); + if (!str.empty()) + { + if (!first) + ss << "\n"; + else + first = false; + + ss << str; + } + } + + m_info_string.SetValueForKey(std::string(field.szName), ss.str()); + break; + } + + case AUFT_OVERLAYRETICLE: + FillFromEnumInt(std::string(field.szName), field.iOffset, szWeapOverlayReticleNames, std::extent_v); + break; + + case AUFT_CAMO: + { + const auto* camo = *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset); + + if (camo) + m_info_string.SetValueForKey(std::string(field.szName), std::string(AssetName(camo->name))); + else + m_info_string.SetValueForKey(std::string(field.szName), ""); + break; + } + + default: + assert(false); + break; + } + } + + public: + InfoStringFromWeaponAttachmentUniqueConverter(const WeaponAttachmentUniqueFull* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + void CopyToFullDef(const WeaponAttachmentUnique* attachment, WeaponAttachmentUniqueFull* fullDef) + { + fullDef->attachment = *attachment; + + if (attachment->szXAnims) + { + assert(sizeof(WeaponAttachmentUniqueFull::szXAnims) >= sizeof(void*) * NUM_WEAP_ANIMS); + memcpy(fullDef->szXAnims, attachment->szXAnims, sizeof(void*) * NUM_WEAP_ANIMS); + fullDef->attachment.szXAnims = fullDef->szXAnims; + } + + if (attachment->hideTags) + { + assert(sizeof(WeaponAttachmentUniqueFull::hideTags) >= sizeof(scr_string_t) * std::extent_v); + memcpy(fullDef->hideTags, attachment->hideTags, sizeof(scr_string_t) * std::extent_v); + fullDef->attachment.hideTags = fullDef->hideTags; + } + + if (attachment->locationDamageMultipliers) + { + assert(sizeof(WeaponAttachmentUniqueFull::locationDamageMultipliers) >= sizeof(float) * HITLOC_COUNT); + memcpy(fullDef->locationDamageMultipliers, attachment->locationDamageMultipliers, sizeof(float) * HITLOC_COUNT); + fullDef->attachment.locationDamageMultipliers = fullDef->locationDamageMultipliers; + } + } + + InfoString CreateInfoString(XAssetInfo* asset) + { + const auto fullDef = std::make_unique(); + memset(fullDef.get(), 0, sizeof(WeaponAttachmentUniqueFull)); + CopyToFullDef(asset->Asset(), fullDef.get()); + + InfoStringFromWeaponAttachmentUniqueConverter converter(fullDef.get(), + attachment_unique_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace attachment_unique +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON_ATTACHMENT_UNIQUE); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT_UNIQUE, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT_UNIQUE); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace attachment_unique diff --git a/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.h b/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.h new file mode 100644 index 00000000..b3e2ae1a --- /dev/null +++ b/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" +#include "InfoString/InfoString.h" + +namespace attachment_unique +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace attachment_unique diff --git a/src/ObjWriting/Game/T6/WeaponCamo/JsonWeaponCamoWriter.cpp b/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.cpp similarity index 87% rename from src/ObjWriting/Game/T6/WeaponCamo/JsonWeaponCamoWriter.cpp rename to src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.cpp index 3438dae8..4a68492e 100644 --- a/src/ObjWriting/Game/T6/WeaponCamo/JsonWeaponCamoWriter.cpp +++ b/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.cpp @@ -1,7 +1,8 @@ -#include "JsonWeaponCamoWriter.h" +#include "CamoJsonDumperT6.h" #include "Game/T6/CommonT6.h" #include "Game/T6/Json/JsonWeaponCamo.h" +#include "Weapon/CamoCommon.h" #include #include @@ -11,10 +12,10 @@ using namespace T6; namespace { - class JsonDumper + class JsonDumperImpl { public: - explicit JsonDumper(std::ostream& stream) + explicit JsonDumperImpl(std::ostream& stream) : m_stream(stream) { } @@ -101,11 +102,22 @@ namespace }; } // namespace -namespace T6 +namespace camo { - void DumpWeaponCamoAsJson(std::ostream& stream, const WeaponCamo* weaponCamo) + bool JsonDumperT6::ShouldDump(XAssetInfo* asset) { - const JsonDumper dumper(stream); - dumper.Dump(weaponCamo); + return true; } -} // namespace T6 + + void JsonDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto fileName = GetJsonFileNameForAssetName(asset->m_name); + const auto assetFile = context.OpenAssetFile(fileName); + + if (!assetFile) + return; + + const JsonDumperImpl dumper(*assetFile); + dumper.Dump(asset->Asset()); + } +} // namespace camo diff --git a/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.h b/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.h new file mode 100644 index 00000000..8bb91f12 --- /dev/null +++ b/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace camo +{ + class JsonDumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace camo diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp b/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp similarity index 53% rename from src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp rename to src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp index 24223dc4..96c5bcdc 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp +++ b/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp @@ -1,10 +1,12 @@ -#include "AssetDumperWeapon.h" +#include "WeaponDumperT6.h" #include "Game/T6/InfoString/InfoStringFromStructConverter.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/Weapon/WeaponFields.h" #include "Game/T6/Weapon/WeaponStrings.h" +#include "InfoString/InfoString.h" #include "Weapon/AccuracyGraphWriter.h" +#include "Weapon/WeaponCommon.h" #include #include @@ -13,7 +15,7 @@ using namespace T6; -namespace T6 +namespace { class InfoStringFromWeaponConverter final : public InfoStringFromStructConverter { @@ -281,208 +283,212 @@ namespace T6 return graph; } -} // namespace T6 -void AssetDumperWeapon::CopyToFullDef(const WeaponVariantDef* weapon, WeaponFullDef* fullDef) -{ - fullDef->weapVariantDef = *weapon; - - if (weapon->weapDef) + void CopyToFullDef(const WeaponVariantDef* weapon, WeaponFullDef* fullDef) { - fullDef->weapDef = *weapon->weapDef; - fullDef->weapVariantDef.weapDef = &fullDef->weapDef; + fullDef->weapVariantDef = *weapon; + + if (weapon->weapDef) + { + fullDef->weapDef = *weapon->weapDef; + fullDef->weapVariantDef.weapDef = &fullDef->weapDef; + } + + if (weapon->attachments) + { + assert(sizeof(WeaponFullDef::attachments) >= sizeof(void*) * std::extent_v); + memcpy(fullDef->attachments, weapon->attachments, sizeof(void*) * std::extent_v); + fullDef->weapVariantDef.attachments = fullDef->attachments; + } + + if (weapon->attachmentUniques) + { + assert(sizeof(WeaponFullDef::attachmentUniques) >= sizeof(void*) * std::extent_v); + memcpy(fullDef->attachmentUniques, weapon->attachmentUniques, sizeof(void*) * std::extent_v); + fullDef->weapVariantDef.attachmentUniques = fullDef->attachmentUniques; + } + + if (fullDef->weapDef.gunXModel) + { + assert(sizeof(WeaponFullDef::gunXModel) >= sizeof(void*) * std::extent_v); + memcpy(fullDef->gunXModel, fullDef->weapDef.gunXModel, sizeof(void*) * std::extent_v); + fullDef->weapDef.gunXModel = fullDef->gunXModel; + } + + if (weapon->szXAnims) + { + assert(sizeof(WeaponFullDef::szXAnims) >= sizeof(void*) * NUM_WEAP_ANIMS); + memcpy(fullDef->szXAnims, weapon->szXAnims, sizeof(void*) * NUM_WEAP_ANIMS); + fullDef->weapVariantDef.szXAnims = fullDef->szXAnims; + } + + if (weapon->hideTags) + { + assert(sizeof(WeaponFullDef::hideTags) >= sizeof(scr_string_t) * std::extent_v); + memcpy(fullDef->hideTags, weapon->hideTags, sizeof(scr_string_t) * std::extent_v); + fullDef->weapVariantDef.hideTags = fullDef->hideTags; + } + + if (fullDef->weapDef.notetrackSoundMapKeys) + { + assert(sizeof(WeaponFullDef::notetrackSoundMapKeys) >= sizeof(scr_string_t) * std::extent_v); + memcpy(fullDef->notetrackSoundMapKeys, + fullDef->weapDef.notetrackSoundMapKeys, + sizeof(scr_string_t) * std::extent_v); + fullDef->weapDef.notetrackSoundMapKeys = fullDef->notetrackSoundMapKeys; + } + + if (fullDef->weapDef.notetrackSoundMapValues) + { + assert(sizeof(WeaponFullDef::notetrackSoundMapValues) >= sizeof(scr_string_t) * std::extent_v); + memcpy(fullDef->notetrackSoundMapValues, + fullDef->weapDef.notetrackSoundMapValues, + sizeof(scr_string_t) * std::extent_v); + fullDef->weapDef.notetrackSoundMapValues = fullDef->notetrackSoundMapValues; + } + + if (fullDef->weapDef.worldModel) + { + assert(sizeof(WeaponFullDef::worldModel) >= sizeof(void*) * std::extent_v); + memcpy(fullDef->worldModel, fullDef->weapDef.worldModel, sizeof(void*) * std::extent_v); + fullDef->weapDef.worldModel = fullDef->worldModel; + } + + if (weapon->attachViewModel) + { + assert(sizeof(WeaponFullDef::attachViewModel) >= sizeof(void*) * std::extent_v); + memcpy(fullDef->attachViewModel, weapon->attachViewModel, sizeof(void*) * std::extent_v); + fullDef->weapVariantDef.attachViewModel = fullDef->attachViewModel; + } + + if (weapon->attachWorldModel) + { + assert(sizeof(WeaponFullDef::attachWorldModel) >= sizeof(void*) * std::extent_v); + memcpy(fullDef->attachWorldModel, weapon->attachWorldModel, sizeof(void*) * std::extent_v); + fullDef->weapVariantDef.attachWorldModel = fullDef->attachWorldModel; + } + + if (weapon->attachViewModelTag) + { + assert(sizeof(WeaponFullDef::attachViewModelTag) >= sizeof(void*) * std::extent_v); + memcpy(fullDef->attachViewModelTag, weapon->attachViewModelTag, sizeof(void*) * std::extent_v); + fullDef->weapVariantDef.attachViewModelTag = fullDef->attachViewModelTag; + } + + if (weapon->attachWorldModelTag) + { + assert(sizeof(WeaponFullDef::attachWorldModelTag) >= sizeof(void*) * std::extent_v); + memcpy(fullDef->attachWorldModelTag, weapon->attachWorldModelTag, sizeof(void*) * std::extent_v); + fullDef->weapVariantDef.attachWorldModelTag = fullDef->attachWorldModelTag; + } + + if (fullDef->weapDef.parallelBounce) + { + assert(sizeof(WeaponFullDef::parallelBounce) >= sizeof(float) * SURF_TYPE_NUM); + memcpy(fullDef->parallelBounce, fullDef->weapDef.parallelBounce, sizeof(float) * SURF_TYPE_NUM); + fullDef->weapDef.parallelBounce = fullDef->parallelBounce; + } + + if (fullDef->weapDef.perpendicularBounce) + { + assert(sizeof(WeaponFullDef::perpendicularBounce) >= sizeof(float) * SURF_TYPE_NUM); + memcpy(fullDef->perpendicularBounce, fullDef->weapDef.perpendicularBounce, sizeof(float) * SURF_TYPE_NUM); + fullDef->weapDef.perpendicularBounce = fullDef->perpendicularBounce; + } + + if (fullDef->weapDef.locationDamageMultipliers) + { + assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * HITLOC_COUNT); + memcpy(fullDef->locationDamageMultipliers, fullDef->weapDef.locationDamageMultipliers, sizeof(float) * HITLOC_COUNT); + fullDef->weapDef.locationDamageMultipliers = fullDef->locationDamageMultipliers; + } + + if (fullDef->weapDef.weaponCamo && fullDef->weapDef.weaponCamo->name) + { + strncpy(fullDef->weaponCamo, fullDef->weapDef.weaponCamo->name, std::extent_v); + } } - if (weapon->attachments) + InfoString CreateInfoString(XAssetInfo* asset) { - assert(sizeof(WeaponFullDef::attachments) >= sizeof(void*) * std::extent_v); - memcpy(fullDef->attachments, weapon->attachments, sizeof(void*) * std::extent_v); - fullDef->weapVariantDef.attachments = fullDef->attachments; + const auto fullDef = std::make_unique(); + memset(fullDef.get(), 0, sizeof(WeaponFullDef)); + CopyToFullDef(asset->Asset(), fullDef.get()); + + InfoStringFromWeaponConverter converter(fullDef.get(), + weapon_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); } - if (weapon->attachmentUniques) + void DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset) { - assert(sizeof(WeaponFullDef::attachmentUniques) >= sizeof(void*) * std::extent_v); - memcpy(fullDef->attachmentUniques, weapon->attachmentUniques, sizeof(void*) * std::extent_v); - fullDef->weapVariantDef.attachmentUniques = fullDef->attachmentUniques; - } + auto* accuracyGraphWriter = context.GetZoneAssetDumperState(); + const auto weapon = asset->Asset(); + const auto* weapDef = weapon->weapDef; - if (fullDef->weapDef.gunXModel) - { - assert(sizeof(WeaponFullDef::gunXModel) >= sizeof(void*) * std::extent_v); - memcpy(fullDef->gunXModel, fullDef->weapDef.gunXModel, sizeof(void*) * std::extent_v); - fullDef->weapDef.gunXModel = fullDef->gunXModel; - } - - if (weapon->szXAnims) - { - assert(sizeof(WeaponFullDef::szXAnims) >= sizeof(void*) * NUM_WEAP_ANIMS); - memcpy(fullDef->szXAnims, weapon->szXAnims, sizeof(void*) * NUM_WEAP_ANIMS); - fullDef->weapVariantDef.szXAnims = fullDef->szXAnims; - } - - if (weapon->hideTags) - { - assert(sizeof(WeaponFullDef::hideTags) >= sizeof(scr_string_t) * std::extent_v); - memcpy(fullDef->hideTags, weapon->hideTags, sizeof(scr_string_t) * std::extent_v); - fullDef->weapVariantDef.hideTags = fullDef->hideTags; - } - - if (fullDef->weapDef.notetrackSoundMapKeys) - { - assert(sizeof(WeaponFullDef::notetrackSoundMapKeys) >= sizeof(scr_string_t) * std::extent_v); - memcpy(fullDef->notetrackSoundMapKeys, - fullDef->weapDef.notetrackSoundMapKeys, - sizeof(scr_string_t) * std::extent_v); - fullDef->weapDef.notetrackSoundMapKeys = fullDef->notetrackSoundMapKeys; - } - - if (fullDef->weapDef.notetrackSoundMapValues) - { - assert(sizeof(WeaponFullDef::notetrackSoundMapValues) >= sizeof(scr_string_t) * std::extent_v); - memcpy(fullDef->notetrackSoundMapValues, - fullDef->weapDef.notetrackSoundMapValues, - sizeof(scr_string_t) * std::extent_v); - fullDef->weapDef.notetrackSoundMapValues = fullDef->notetrackSoundMapValues; - } - - if (fullDef->weapDef.worldModel) - { - assert(sizeof(WeaponFullDef::worldModel) >= sizeof(void*) * std::extent_v); - memcpy(fullDef->worldModel, fullDef->weapDef.worldModel, sizeof(void*) * std::extent_v); - fullDef->weapDef.worldModel = fullDef->worldModel; - } - - if (weapon->attachViewModel) - { - assert(sizeof(WeaponFullDef::attachViewModel) >= sizeof(void*) * std::extent_v); - memcpy(fullDef->attachViewModel, weapon->attachViewModel, sizeof(void*) * std::extent_v); - fullDef->weapVariantDef.attachViewModel = fullDef->attachViewModel; - } - - if (weapon->attachWorldModel) - { - assert(sizeof(WeaponFullDef::attachWorldModel) >= sizeof(void*) * std::extent_v); - memcpy(fullDef->attachWorldModel, weapon->attachWorldModel, sizeof(void*) * std::extent_v); - fullDef->weapVariantDef.attachWorldModel = fullDef->attachWorldModel; - } - - if (weapon->attachViewModelTag) - { - assert(sizeof(WeaponFullDef::attachViewModelTag) >= sizeof(void*) * std::extent_v); - memcpy(fullDef->attachViewModelTag, weapon->attachViewModelTag, sizeof(void*) * std::extent_v); - fullDef->weapVariantDef.attachViewModelTag = fullDef->attachViewModelTag; - } - - if (weapon->attachWorldModelTag) - { - assert(sizeof(WeaponFullDef::attachWorldModelTag) >= sizeof(void*) * std::extent_v); - memcpy(fullDef->attachWorldModelTag, weapon->attachWorldModelTag, sizeof(void*) * std::extent_v); - fullDef->weapVariantDef.attachWorldModelTag = fullDef->attachWorldModelTag; - } - - if (fullDef->weapDef.parallelBounce) - { - assert(sizeof(WeaponFullDef::parallelBounce) >= sizeof(float) * SURF_TYPE_NUM); - memcpy(fullDef->parallelBounce, fullDef->weapDef.parallelBounce, sizeof(float) * SURF_TYPE_NUM); - fullDef->weapDef.parallelBounce = fullDef->parallelBounce; - } - - if (fullDef->weapDef.perpendicularBounce) - { - assert(sizeof(WeaponFullDef::perpendicularBounce) >= sizeof(float) * SURF_TYPE_NUM); - memcpy(fullDef->perpendicularBounce, fullDef->weapDef.perpendicularBounce, sizeof(float) * SURF_TYPE_NUM); - fullDef->weapDef.perpendicularBounce = fullDef->perpendicularBounce; - } - - if (fullDef->weapDef.locationDamageMultipliers) - { - assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * HITLOC_COUNT); - memcpy(fullDef->locationDamageMultipliers, fullDef->weapDef.locationDamageMultipliers, sizeof(float) * HITLOC_COUNT); - fullDef->weapDef.locationDamageMultipliers = fullDef->locationDamageMultipliers; - } - - if (fullDef->weapDef.weaponCamo && fullDef->weapDef.weaponCamo->name) - { - strncpy(fullDef->weaponCamo, fullDef->weapDef.weaponCamo->name, std::extent_v); - } -} - -InfoString AssetDumperWeapon::CreateInfoString(XAssetInfo* asset) -{ - const auto fullDef = std::make_unique(); - memset(fullDef.get(), 0, sizeof(WeaponFullDef)); - CopyToFullDef(asset->Asset(), fullDef.get()); - - InfoStringFromWeaponConverter converter(fullDef.get(), - weapon_fields, - std::extent_v, - [asset](const scr_string_t scrStr) -> std::string - { - assert(scrStr < asset->m_zone->m_script_strings.Count()); - if (scrStr >= asset->m_zone->m_script_strings.Count()) - return ""; - - return asset->m_zone->m_script_strings[scrStr]; - }); - - return converter.Convert(); -} - -void AssetDumperWeapon::DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset) -{ - auto* accuracyGraphWriter = context.GetZoneAssetDumperState(); - const auto weapon = asset->Asset(); - const auto* weapDef = weapon->weapDef; - - if (!weapDef) - return; - - if (weapDef->aiVsAiAccuracyGraphName && weapDef->originalAiVsAiAccuracyGraphKnots - && accuracyGraphWriter->ShouldDumpAiVsAiGraph(weapDef->aiVsAiAccuracyGraphName)) - { - AccuracyGraphWriter::DumpAiVsAiGraph( - context, - ConvertAccuracyGraph(weapDef->aiVsAiAccuracyGraphName, weapDef->originalAiVsAiAccuracyGraphKnots, weapDef->originalAiVsAiAccuracyGraphKnotCount)); - } - - if (weapDef->aiVsPlayerAccuracyGraphName && weapDef->originalAiVsPlayerAccuracyGraphKnots - && accuracyGraphWriter->ShouldDumpAiVsPlayerGraph(weapDef->aiVsPlayerAccuracyGraphName)) - { - AccuracyGraphWriter::DumpAiVsPlayerGraph(context, - ConvertAccuracyGraph(weapDef->aiVsPlayerAccuracyGraphName, - weapDef->originalAiVsPlayerAccuracyGraphKnots, - weapDef->originalAiVsPlayerAccuracyGraphKnotCount)); - } -} - -bool AssetDumperWeapon::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperWeapon::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - // Only dump raw when no gdt available - if (context.m_gdt) - { - const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON); - infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_WEAPON, gdtEntry); - context.m_gdt->WriteEntry(gdtEntry); - } - else - { - const auto assetFile = context.OpenAssetFile("weapons/" + asset->m_name); - - if (!assetFile) + if (!weapDef) return; - auto& stream = *assetFile; - const auto infoString = CreateInfoString(asset); - const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_WEAPON); - stream.write(stringValue.c_str(), stringValue.size()); + if (weapDef->aiVsAiAccuracyGraphName && weapDef->originalAiVsAiAccuracyGraphKnots + && accuracyGraphWriter->ShouldDumpAiVsAiGraph(weapDef->aiVsAiAccuracyGraphName)) + { + AccuracyGraphWriter::DumpAiVsAiGraph(context, + ConvertAccuracyGraph(weapDef->aiVsAiAccuracyGraphName, + weapDef->originalAiVsAiAccuracyGraphKnots, + weapDef->originalAiVsAiAccuracyGraphKnotCount)); + } + + if (weapDef->aiVsPlayerAccuracyGraphName && weapDef->originalAiVsPlayerAccuracyGraphKnots + && accuracyGraphWriter->ShouldDumpAiVsPlayerGraph(weapDef->aiVsPlayerAccuracyGraphName)) + { + AccuracyGraphWriter::DumpAiVsPlayerGraph(context, + ConvertAccuracyGraph(weapDef->aiVsPlayerAccuracyGraphName, + weapDef->originalAiVsPlayerAccuracyGraphKnots, + weapDef->originalAiVsPlayerAccuracyGraphKnotCount)); + } + } +} // namespace + +namespace weapon +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; } - DumpAccuracyGraphs(context, asset); -} + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_WEAPON, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_WEAPON); + stream.write(stringValue.c_str(), stringValue.size()); + } + + DumpAccuracyGraphs(context, asset); + } +} // namespace weapon diff --git a/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.h b/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.h new file mode 100644 index 00000000..4b3b500f --- /dev/null +++ b/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace weapon +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace weapon diff --git a/src/ObjWriting/Game/T6/WeaponCamo/JsonWeaponCamoWriter.h b/src/ObjWriting/Game/T6/WeaponCamo/JsonWeaponCamoWriter.h deleted file mode 100644 index 0da45568..00000000 --- a/src/ObjWriting/Game/T6/WeaponCamo/JsonWeaponCamoWriter.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Dumping/AssetDumpingContext.h" -#include "Game/T6/T6.h" - -#include - -namespace T6 -{ - void DumpWeaponCamoAsJson(std::ostream& stream, const WeaponCamo* weaponCamo); -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.cpp b/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.cpp new file mode 100644 index 00000000..1a7edb80 --- /dev/null +++ b/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.cpp @@ -0,0 +1,81 @@ +#include "ZBarrierDumperT6.h" + +#include "Game/T6/InfoString/InfoStringFromStructConverter.h" +#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/ZBarrier/ZBarrierFields.h" +#include "ZBarrier/ZBarrierCommon.h" + +#include +#include + +using namespace T6; + +namespace +{ + class InfoStringFromZBarrierConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + assert(false); + } + + public: + InfoStringFromZBarrierConverter(const ZBarrierDef* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateInfoString(XAssetInfo* asset) + { + InfoStringFromZBarrierConverter converter(asset->Asset(), + zbarrier_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset->m_zone->m_script_strings.Count()); + if (scrStr >= asset->m_zone->m_script_strings.Count()) + return ""; + + return asset->m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace z_barrier +{ + bool DumperT6::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_ZBARRIER); + infoString.ToGdtProperties(ObjConstants::INFO_STRING_PREFIX_ZBARRIER, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(ObjConstants::INFO_STRING_PREFIX_ZBARRIER); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace z_barrier diff --git a/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.h b/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.h new file mode 100644 index 00000000..9e60f414 --- /dev/null +++ b/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" +#include "InfoString/InfoString.h" + +namespace z_barrier +{ + class DumperT6 final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace z_barrier diff --git a/src/ObjWriting/Material/AbstractMaterialConstantZoneState.cpp b/src/ObjWriting/Material/AbstractMaterialConstantZoneState.cpp index bdbee0c2..c986878a 100644 --- a/src/ObjWriting/Material/AbstractMaterialConstantZoneState.cpp +++ b/src/ObjWriting/Material/AbstractMaterialConstantZoneState.cpp @@ -3,6 +3,7 @@ #include "ObjWriting.h" #include "Shader/D3D11ShaderAnalyser.h" #include "Shader/D3D9ShaderAnalyser.h" +#include "Utils/Logging/Log.h" #include @@ -15,8 +16,7 @@ namespace void AbstractMaterialConstantZoneState::ExtractNamesFromZone() { - if (ObjWriting::Configuration.Verbose) - std::cout << "Building material constant name lookup...\n"; + con::debug("Building material constant name lookup..."); const auto begin = std::chrono::high_resolution_clock::now(); @@ -26,14 +26,11 @@ void AbstractMaterialConstantZoneState::ExtractNamesFromZone() const auto end = std::chrono::high_resolution_clock::now(); - if (ObjWriting::Configuration.Verbose) - { - const auto durationInMs = std::chrono::duration_cast(end - begin); - std::cout << std::format("Built material constant name lookup in {}ms: {} constant names; {} texture def names\n", - durationInMs.count(), - m_constant_names_from_shaders.size(), - m_texture_def_names_from_shaders.size()); - } + const auto durationInMs = std::chrono::duration_cast(end - begin); + con::debug("Built material constant name lookup in {}ms: {} constant names; {} texture def names", + durationInMs.count(), + m_constant_names_from_shaders.size(), + m_texture_def_names_from_shaders.size()); } bool AbstractMaterialConstantZoneState::GetConstantName(const unsigned hash, std::string& constantName) const diff --git a/src/ObjWriting/Material/JsonMaterialWriter.h.template b/src/ObjWriting/Material/JsonMaterialWriter.h.template deleted file mode 100644 index d44748db..00000000 --- a/src/ObjWriting/Material/JsonMaterialWriter.h.template +++ /dev/null @@ -1,20 +0,0 @@ -#options GAME (IW4, IW5, T6) - -#filename "Game/" + GAME + "/Material/JsonMaterialWriter" + GAME + ".h" - -// This file was templated. -// See JsonMaterialWriter.h.template. -// Do not modify, changes will be lost. - -#pragma once - -#include "Dumping/AssetDumpingContext.h" -#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" -#include GAME_HEADER - -#include - -namespace GAME -{ - void DumpMaterialAsJson(std::ostream& stream, const Material& material, AssetDumpingContext& context); -} // namespace GAME diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/MaterialJsonDumper.cpp.template similarity index 85% rename from src/ObjWriting/Material/JsonMaterialWriter.cpp.template rename to src/ObjWriting/Material/MaterialJsonDumper.cpp.template index 2c0ef0f0..7eb8ad51 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/MaterialJsonDumper.cpp.template @@ -1,8 +1,12 @@ -#options GAME (IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) -#filename "Game/" + GAME + "/Material/JsonMaterialWriter" + GAME + ".cpp" +#filename "Game/" + GAME + "/Material/MaterialJsonDumper" + GAME + ".cpp" -#if GAME == "IW4" +#if GAME == "IW3" +#define FEATURE_IW3 +#define HAS_WATER +#define GAME_LOWER "iw3" +#elif GAME == "IW4" #define FEATURE_IW4 #define HAS_WATER #define GAME_LOWER "iw4" @@ -10,22 +14,27 @@ #define FEATURE_IW5 #define HAS_WATER #define GAME_LOWER "iw5" +#elif GAME == "T5" +#define FEATURE_T5 +#define HAS_WATER +#define GAME_LOWER "t5" #elif GAME == "T6" #define FEATURE_T6 #define GAME_LOWER "t6" #endif // This file was templated. -// See JsonMaterialWriter.cpp.template. +// See MaterialJsonDumper.cpp.template. // Do not modify, changes will be lost. -#set WRITER_HEADER "\"JsonMaterialWriter" + GAME + ".h\"" +#set WRITER_HEADER "\"MaterialJsonDumper" + GAME + ".h\"" #include WRITER_HEADER #ifdef HAS_WATER #include "Base64.h" #endif +#include "Material/MaterialCommon.h" #set COMMON_HEADER "\"Game/" + GAME + "/Common" + GAME + ".h\"" #include COMMON_HEADER #set JSON_HEADER "\"Game/" + GAME + "/Material/JsonMaterial" + GAME + ".h\"" @@ -33,18 +42,21 @@ #set CONSTANTS_HEADER "\"Game/" + GAME + "/Material/MaterialConstantZoneState" + GAME + ".h\"" #include CONSTANTS_HEADER +#include #include #include using namespace nlohmann; using namespace GAME; +#set CLASS_NAME "JsonDumper" + GAME + namespace { - class JsonDumper + class JsonDumperImpl { public: - JsonDumper(AssetDumpingContext& context, std::ostream& stream) + JsonDumperImpl(AssetDumpingContext& context, std::ostream& stream) : m_stream(stream), m_material_constants(*context.GetZoneAssetDumperState()) { @@ -56,6 +68,7 @@ namespace CreateJsonMaterial(jsonMaterial, material); json jRoot = jsonMaterial; + jRoot["$schema"] = "http://openassettools.dev/schema/material.v1.json"; jRoot["_type"] = "material"; jRoot["_version"] = 1; jRoot["_game"] = GAME_LOWER; @@ -140,7 +153,7 @@ namespace } jTextureDef.semantic = static_cast(textureDef.semantic); -#if defined(FEATURE_T6) +#if defined(FEATURE_T5) || defined(FEATURE_T6) jTextureDef.isMatureContent = textureDef.isMatureContent; #endif @@ -192,12 +205,12 @@ namespace jConstantDef.nameFragment = nameFragment; } - jConstantDef.literal = std::vector({ + jConstantDef.literal = { constantDef.literal.x, constantDef.literal.y, constantDef.literal.z, constantDef.literal.w, - }); + }; } static void CreateJsonStencil(JsonStencil& jStencil, const unsigned pass, const unsigned fail, const unsigned zFail, const unsigned func) @@ -218,17 +231,22 @@ namespace assert(structured.alphaTestDisabled || structured.alphaTest == GFXS_ALPHA_TEST_GT_0 -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) || structured.alphaTest == GFXS_ALPHA_TEST_LT_128 +#elif defined(FEATURE_T5) + || structured.alphaTest == GFXS_ALPHA_TEST_GE_255 #endif || structured.alphaTest == GFXS_ALPHA_TEST_GE_128); if (structured.alphaTestDisabled) jStateBitsTableEntry.alphaTest = JsonAlphaTest::DISABLED; else if (structured.alphaTest == GFXS_ALPHA_TEST_GT_0) jStateBitsTableEntry.alphaTest = JsonAlphaTest::GT0; -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) else if (structured.alphaTest == GFXS_ALPHA_TEST_LT_128) jStateBitsTableEntry.alphaTest = JsonAlphaTest::LT128; +#elif defined(FEATURE_T5) + else if (structured.alphaTest == GFXS_ALPHA_TEST_GE_255) + jStateBitsTableEntry.alphaTest = JsonAlphaTest::GE255; #endif else if (structured.alphaTest == GFXS_ALPHA_TEST_GE_128) jStateBitsTableEntry.alphaTest = JsonAlphaTest::GE128; @@ -300,9 +318,10 @@ namespace jMaterial.textureAtlas->columns = material.info.textureAtlasColumnCount; jMaterial.surfaceTypeBits = material.info.surfaceTypeBits; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) jMaterial.layeredSurfaceTypes = material.info.layeredSurfaceTypes; - jMaterial.hashIndex = material.info.hashIndex; +#endif +#if defined(FEATURE_T6) jMaterial.surfaceFlags = material.info.surfaceFlags; jMaterial.contents = material.info.contents; #endif @@ -313,9 +332,6 @@ namespace jMaterial.stateFlags = material.stateFlags; jMaterial.cameraRegion = static_cast(material.cameraRegion); -#ifdef FEATURE_T6 - jMaterial.probeMipBits = material.probeMipBits; -#endif if (material.techniqueSet && material.techniqueSet->name) jMaterial.techniqueSet = AssetName(material.techniqueSet->name); @@ -343,11 +359,40 @@ namespace }; } // namespace -namespace GAME +namespace material { - void DumpMaterialAsJson(std::ostream& stream, const Material& material, AssetDumpingContext& context) + void CLASS_NAME::DumpPool(AssetDumpingContext& context, AssetPool* pool) { - const JsonDumper dumper(context, stream); - dumper.Dump(material); + auto* materialConstantState = context.GetZoneAssetDumperState(); + materialConstantState->ExtractNamesFromZone(); + + AbstractAssetDumper::DumpPool(context, pool); } -} // namespace GAME + + bool CLASS_NAME::ShouldDump(XAssetInfo* asset) + { + return true; + } + + void CLASS_NAME::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + const auto* material = asset->Asset(); +#if defined(FEATURE_T5) + assert(material->info.gameFlags < 0x400); + assert(material->maxStreamedMips == 0); +#elif defined(FEATURE_T6) + assert(material->info.gameFlags < 0x8000); + assert(material->info.hashIndex == 0); + assert(material->probeMipBits == 0); +#endif + + JsonDumperImpl dumper(context, *assetFile); + dumper.Dump(*material); + } +} // namespace material + diff --git a/src/ObjWriting/Material/MaterialJsonDumper.h.template b/src/ObjWriting/Material/MaterialJsonDumper.h.template new file mode 100644 index 00000000..f8a41515 --- /dev/null +++ b/src/ObjWriting/Material/MaterialJsonDumper.h.template @@ -0,0 +1,29 @@ +#options GAME(IW3, IW4, IW5, T5, T6) + +#filename "Game/" + GAME + "/Material/MaterialJsonDumper" + GAME + ".h" + +// This file was templated. +// See MaterialJsonDumper.h.template. +// Do not modify, changes will be lost. + +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Dumping/AssetDumpingContext.h" +#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" +#include GAME_HEADER + +#set CLASS_NAME "JsonDumper" + GAME + +namespace material +{ + class CLASS_NAME final : public AbstractAssetDumper + { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + + protected: + [[nodiscard]] bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace material diff --git a/src/ObjWriting/Menu/AbstractMenuDumper.cpp b/src/ObjWriting/Menu/AbstractMenuDumper.cpp deleted file mode 100644 index 5b00b40b..00000000 --- a/src/ObjWriting/Menu/AbstractMenuDumper.cpp +++ /dev/null @@ -1,298 +0,0 @@ -#include "AbstractMenuDumper.h" - -#include "Parsing/Impl/ParserSingleInputStream.h" -#include "Parsing/Simple/SimpleLexer.h" - -#include -#include -#include - -AbstractMenuDumper::AbstractMenuDumper(std::ostream& stream) - : m_stream(stream), - m_indent(0u) -{ -} - -void AbstractMenuDumper::IncIndent() -{ - m_indent++; -} - -void AbstractMenuDumper::DecIndent() -{ - if (m_indent > 0) - m_indent--; -} - -void AbstractMenuDumper::Indent() const -{ - for (auto i = 0u; i < m_indent; i++) - m_stream << " "; -} - -void AbstractMenuDumper::StartScope(const std::string& scopeName) -{ - Indent(); - m_stream << scopeName << "\n"; - Indent(); - m_stream << "{\n"; - IncIndent(); -} - -void AbstractMenuDumper::StartMenuDefScope() -{ - StartScope("menuDef"); -} - -void AbstractMenuDumper::StartItemDefScope() -{ - StartScope("itemDef"); -} - -void AbstractMenuDumper::StartFunctionDefScope() -{ - StartScope("functionDef"); -} - -void AbstractMenuDumper::EndScope() -{ - DecIndent(); - Indent(); - m_stream << "}\n"; -} - -std::vector AbstractMenuDumper::CreateScriptTokenList(const char* script) -{ - const std::string scriptString(script); - std::istringstream stringStream(scriptString); - ParserSingleInputStream inputStream(stringStream, "MenuScript"); - - SimpleLexer::Config lexerConfig; - lexerConfig.m_emit_new_line_tokens = false; - lexerConfig.m_read_strings = true; - lexerConfig.m_string_escape_sequences = true; - lexerConfig.m_read_integer_numbers = false; - lexerConfig.m_read_floating_point_numbers = false; - SimpleLexer lexer(&inputStream, std::move(lexerConfig)); - - std::vector result; - auto hasLexerTokens = true; - while (hasLexerTokens) - { - const auto& token = lexer.GetToken(0); - switch (token.m_type) - { - case SimpleParserValueType::IDENTIFIER: - result.emplace_back(token.IdentifierValue()); - break; - - case SimpleParserValueType::STRING: - result.emplace_back(token.StringValue()); - break; - - case SimpleParserValueType::CHARACTER: - result.emplace_back(1, token.CharacterValue()); - break; - - case SimpleParserValueType::INVALID: - case SimpleParserValueType::END_OF_FILE: - hasLexerTokens = false; - break; - - default: - assert(false); - break; - } - - lexer.PopTokens(1); - } - - return result; -} - -bool AbstractMenuDumper::DoesTokenNeedQuotationMarks(const std::string& token) -{ - if (token.empty()) - return true; - - const auto hasAlNumCharacter = std::ranges::any_of(token, - [](const char& c) - { - return isalnum(c); - }); - - if (!hasAlNumCharacter) - return false; - - const auto hasNonIdentifierCharacter = std::ranges::any_of(token, - [](const char& c) - { - return !isalnum(c) && c != '_'; - }); - - return hasNonIdentifierCharacter; -} - -void AbstractMenuDumper::WriteEscapedString(const std::string_view& str) const -{ - m_stream << "\""; - - for (const auto& c : str) - { - switch (c) - { - case '\r': - m_stream << "\\r"; - break; - case '\n': - m_stream << "\\n"; - break; - case '\t': - m_stream << "\\t"; - break; - case '\f': - m_stream << "\\f"; - break; - case '"': - m_stream << "\\\""; - break; - default: - m_stream << c; - break; - } - } - - m_stream << "\""; -} - -const std::string& AbstractMenuDumper::BoolValue(const bool value) -{ - return value ? BOOL_VALUE_TRUE : BOOL_VALUE_FALSE; -} - -void AbstractMenuDumper::WriteKey(const std::string& keyName) const -{ - m_stream << keyName; - - if (keyName.size() < MENU_KEY_SPACING) - { - const auto spacingLength = MENU_KEY_SPACING - keyName.size(); - for (auto i = 0u; i < spacingLength; i++) - m_stream << " "; - } -} - -void AbstractMenuDumper::WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const -{ - if (propertyValue.empty()) - return; - - Indent(); - WriteKey(propertyKey); - - WriteEscapedString(propertyValue); - m_stream << "\n"; -} - -void AbstractMenuDumper::WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const -{ - if (propertyValue == nullptr || propertyValue[0] == '\0') - return; - - Indent(); - WriteKey(propertyKey); - - WriteEscapedString(propertyValue); - m_stream << "\n"; -} - -void AbstractMenuDumper::WriteBoolProperty(const std::string& propertyKey, const bool propertyValue, const bool defaultValue) const -{ - if (propertyValue == defaultValue) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << BoolValue(propertyValue) << "\n"; -} - -void AbstractMenuDumper::WriteIntProperty(const std::string& propertyKey, const int propertyValue, const int defaultValue) const -{ - if (propertyValue == defaultValue) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << propertyValue << "\n"; -} - -void AbstractMenuDumper::WriteFloatProperty(const std::string& propertyKey, const float propertyValue, const float defaultValue) const -{ - if (std::fabs(propertyValue - defaultValue) < std::numeric_limits::epsilon()) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << propertyValue << "\n"; -} - -void AbstractMenuDumper::WriteColorProperty(const std::string& propertyKey, const float (&propertyValue)[4], const float (&defaultValue)[4]) const -{ - if (std::fabs(propertyValue[0] - defaultValue[0]) < std::numeric_limits::epsilon() - && std::fabs(propertyValue[1] - defaultValue[1]) < std::numeric_limits::epsilon() - && std::fabs(propertyValue[2] - defaultValue[2]) < std::numeric_limits::epsilon() - && std::fabs(propertyValue[3] - defaultValue[3]) < std::numeric_limits::epsilon()) - { - return; - } - - Indent(); - WriteKey(propertyKey); - m_stream << propertyValue[0] << " " << propertyValue[1] << " " << propertyValue[2] << " " << propertyValue[3] << "\n"; -} - -void AbstractMenuDumper::WriteKeywordProperty(const std::string& propertyKey, const bool shouldWrite) const -{ - if (!shouldWrite) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << "\n"; -} - -void AbstractMenuDumper::WriteFlagsProperty(const std::string& propertyKey, const int flagsValue) const -{ - for (auto i = 0u; i < sizeof(flagsValue) * 8; i++) - { - if (flagsValue & (1 << i)) - { - Indent(); - WriteKey(propertyKey); - m_stream << i << "\n"; - } - } -} - -void AbstractMenuDumper::Start() -{ - Indent(); - m_stream << "{\n"; - IncIndent(); -} - -void AbstractMenuDumper::End() -{ - for (auto i = 0u; i < m_indent; i++) - { - DecIndent(); - Indent(); - m_stream << "}\n"; - } -} - -void AbstractMenuDumper::IncludeMenu(const std::string& menuPath) const -{ - Indent(); - m_stream << "loadMenu { \"" << menuPath << "\" }\n"; -} diff --git a/src/ObjWriting/Menu/AbstractMenuDumper.h b/src/ObjWriting/Menu/AbstractMenuDumper.h deleted file mode 100644 index 10494684..00000000 --- a/src/ObjWriting/Menu/AbstractMenuDumper.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -class AbstractMenuDumper -{ -protected: - static constexpr auto MENU_KEY_SPACING = 28u; - static const inline std::string BOOL_VALUE_TRUE = "1"; - static const inline std::string BOOL_VALUE_FALSE = "0"; - static constexpr inline float COLOR_0000[4]{0.0f, 0.0f, 0.0f, 0.0f}; - static constexpr inline float COLOR_1111[4]{1.0f, 1.0f, 1.0f, 1.0f}; - - std::ostream& m_stream; - size_t m_indent; - - void IncIndent(); - void DecIndent(); - void Indent() const; - - void StartScope(const std::string& scopeName); - void StartMenuDefScope(); - void StartItemDefScope(); - void StartFunctionDefScope(); - void EndScope(); - - static std::vector CreateScriptTokenList(const char* script); - static bool DoesTokenNeedQuotationMarks(const std::string& token); - - void WriteEscapedString(const std::string_view& str) const; - - static const std::string& BoolValue(bool value); - void WriteKey(const std::string& keyName) const; - void WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const; - void WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const; - void WriteBoolProperty(const std::string& propertyKey, bool propertyValue, bool defaultValue) const; - void WriteIntProperty(const std::string& propertyKey, int propertyValue, int defaultValue) const; - void WriteFloatProperty(const std::string& propertyKey, float propertyValue, float defaultValue) const; - void WriteColorProperty(const std::string& propertyKey, const float (&propertyValue)[4], const float (&defaultValue)[4]) const; - void WriteKeywordProperty(const std::string& propertyKey, bool shouldWrite) const; - void WriteFlagsProperty(const std::string& propertyKey, int flagsValue) const; - - explicit AbstractMenuDumper(std::ostream& stream); - -public: - void Start(); - void End(); - - void IncludeMenu(const std::string& menuPath) const; -}; diff --git a/src/ObjWriting/Menu/AbstractMenuWriter.cpp b/src/ObjWriting/Menu/AbstractMenuWriter.cpp new file mode 100644 index 00000000..34c1b567 --- /dev/null +++ b/src/ObjWriting/Menu/AbstractMenuWriter.cpp @@ -0,0 +1,301 @@ +#include "AbstractMenuWriter.h" + +#include "Parsing/Impl/ParserSingleInputStream.h" +#include "Parsing/Simple/SimpleLexer.h" + +#include +#include +#include + +namespace menu +{ + AbstractBaseWriter::AbstractBaseWriter(std::ostream& stream) + : m_stream(stream), + m_indent(0u) + { + } + + void AbstractBaseWriter::IncIndent() + { + m_indent++; + } + + void AbstractBaseWriter::DecIndent() + { + if (m_indent > 0) + m_indent--; + } + + void AbstractBaseWriter::Indent() const + { + for (auto i = 0u; i < m_indent; i++) + m_stream << " "; + } + + void AbstractBaseWriter::StartScope(const std::string& scopeName) + { + Indent(); + m_stream << scopeName << "\n"; + Indent(); + m_stream << "{\n"; + IncIndent(); + } + + void AbstractBaseWriter::StartMenuDefScope() + { + StartScope("menuDef"); + } + + void AbstractBaseWriter::StartItemDefScope() + { + StartScope("itemDef"); + } + + void AbstractBaseWriter::StartFunctionDefScope() + { + StartScope("functionDef"); + } + + void AbstractBaseWriter::EndScope() + { + DecIndent(); + Indent(); + m_stream << "}\n"; + } + + std::vector AbstractBaseWriter::CreateScriptTokenList(const char* script) + { + const std::string scriptString(script); + std::istringstream stringStream(scriptString); + ParserSingleInputStream inputStream(stringStream, "MenuScript"); + + SimpleLexer::Config lexerConfig; + lexerConfig.m_emit_new_line_tokens = false; + lexerConfig.m_read_strings = true; + lexerConfig.m_string_escape_sequences = true; + lexerConfig.m_read_integer_numbers = false; + lexerConfig.m_read_floating_point_numbers = false; + SimpleLexer lexer(&inputStream, std::move(lexerConfig)); + + std::vector result; + auto hasLexerTokens = true; + while (hasLexerTokens) + { + const auto& token = lexer.GetToken(0); + switch (token.m_type) + { + case SimpleParserValueType::IDENTIFIER: + result.emplace_back(token.IdentifierValue()); + break; + + case SimpleParserValueType::STRING: + result.emplace_back(token.StringValue()); + break; + + case SimpleParserValueType::CHARACTER: + result.emplace_back(1, token.CharacterValue()); + break; + + case SimpleParserValueType::INVALID: + case SimpleParserValueType::END_OF_FILE: + hasLexerTokens = false; + break; + + default: + assert(false); + break; + } + + lexer.PopTokens(1); + } + + return result; + } + + bool AbstractBaseWriter::DoesTokenNeedQuotationMarks(const std::string& token) + { + if (token.empty()) + return true; + + const auto hasAlNumCharacter = std::ranges::any_of(token, + [](const char& c) + { + return isalnum(c); + }); + + if (!hasAlNumCharacter) + return false; + + const auto hasNonIdentifierCharacter = std::ranges::any_of(token, + [](const char& c) + { + return !isalnum(c) && c != '_'; + }); + + return hasNonIdentifierCharacter; + } + + void AbstractBaseWriter::WriteEscapedString(const std::string_view& str) const + { + m_stream << "\""; + + for (const auto& c : str) + { + switch (c) + { + case '\r': + m_stream << "\\r"; + break; + case '\n': + m_stream << "\\n"; + break; + case '\t': + m_stream << "\\t"; + break; + case '\f': + m_stream << "\\f"; + break; + case '"': + m_stream << "\\\""; + break; + default: + m_stream << c; + break; + } + } + + m_stream << "\""; + } + + const std::string& AbstractBaseWriter::BoolValue(const bool value) + { + return value ? BOOL_VALUE_TRUE : BOOL_VALUE_FALSE; + } + + void AbstractBaseWriter::WriteKey(const std::string& keyName) const + { + m_stream << keyName; + + if (keyName.size() < MENU_KEY_SPACING) + { + const auto spacingLength = MENU_KEY_SPACING - keyName.size(); + for (auto i = 0u; i < spacingLength; i++) + m_stream << " "; + } + } + + void AbstractBaseWriter::WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const + { + if (propertyValue.empty()) + return; + + Indent(); + WriteKey(propertyKey); + + WriteEscapedString(propertyValue); + m_stream << "\n"; + } + + void AbstractBaseWriter::WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const + { + if (propertyValue == nullptr || propertyValue[0] == '\0') + return; + + Indent(); + WriteKey(propertyKey); + + WriteEscapedString(propertyValue); + m_stream << "\n"; + } + + void AbstractBaseWriter::WriteBoolProperty(const std::string& propertyKey, const bool propertyValue, const bool defaultValue) const + { + if (propertyValue == defaultValue) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << BoolValue(propertyValue) << "\n"; + } + + void AbstractBaseWriter::WriteIntProperty(const std::string& propertyKey, const int propertyValue, const int defaultValue) const + { + if (propertyValue == defaultValue) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << propertyValue << "\n"; + } + + void AbstractBaseWriter::WriteFloatProperty(const std::string& propertyKey, const float propertyValue, const float defaultValue) const + { + if (std::fabs(propertyValue - defaultValue) < std::numeric_limits::epsilon()) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << propertyValue << "\n"; + } + + void AbstractBaseWriter::WriteColorProperty(const std::string& propertyKey, const float (&propertyValue)[4], const float (&defaultValue)[4]) const + { + if (std::fabs(propertyValue[0] - defaultValue[0]) < std::numeric_limits::epsilon() + && std::fabs(propertyValue[1] - defaultValue[1]) < std::numeric_limits::epsilon() + && std::fabs(propertyValue[2] - defaultValue[2]) < std::numeric_limits::epsilon() + && std::fabs(propertyValue[3] - defaultValue[3]) < std::numeric_limits::epsilon()) + { + return; + } + + Indent(); + WriteKey(propertyKey); + m_stream << propertyValue[0] << " " << propertyValue[1] << " " << propertyValue[2] << " " << propertyValue[3] << "\n"; + } + + void AbstractBaseWriter::WriteKeywordProperty(const std::string& propertyKey, const bool shouldWrite) const + { + if (!shouldWrite) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << "\n"; + } + + void AbstractBaseWriter::WriteFlagsProperty(const std::string& propertyKey, const int flagsValue) const + { + for (auto i = 0u; i < sizeof(flagsValue) * 8; i++) + { + if (flagsValue & (1 << i)) + { + Indent(); + WriteKey(propertyKey); + m_stream << i << "\n"; + } + } + } + + void AbstractBaseWriter::Start() + { + Indent(); + m_stream << "{\n"; + IncIndent(); + } + + void AbstractBaseWriter::End() + { + for (auto i = 0u; i < m_indent; i++) + { + DecIndent(); + Indent(); + m_stream << "}\n"; + } + } + + void AbstractBaseWriter::IncludeMenu(const std::string& menuPath) const + { + Indent(); + m_stream << "loadMenu { \"" << menuPath << "\" }\n"; + } +} // namespace menu diff --git a/src/ObjWriting/Menu/AbstractMenuWriter.h b/src/ObjWriting/Menu/AbstractMenuWriter.h new file mode 100644 index 00000000..3127a3e8 --- /dev/null +++ b/src/ObjWriting/Menu/AbstractMenuWriter.h @@ -0,0 +1,59 @@ +#pragma once + +#include "IMenuWriter.h" + +#include +#include +#include +#include + +namespace menu +{ + class AbstractBaseWriter : public IWriter + { + protected: + static constexpr auto MENU_KEY_SPACING = 28u; + static const inline std::string BOOL_VALUE_TRUE = "1"; + static const inline std::string BOOL_VALUE_FALSE = "0"; + static constexpr inline float COLOR_0000[4]{0.0f, 0.0f, 0.0f, 0.0f}; + static constexpr inline float COLOR_1111[4]{1.0f, 1.0f, 1.0f, 1.0f}; + + public: + void Start() override; + void End() override; + + void IncludeMenu(const std::string& menuPath) const override; + + protected: + explicit AbstractBaseWriter(std::ostream& stream); + + void IncIndent(); + void DecIndent(); + void Indent() const; + + void StartScope(const std::string& scopeName); + void StartMenuDefScope(); + void StartItemDefScope(); + void StartFunctionDefScope(); + void EndScope(); + + static std::vector CreateScriptTokenList(const char* script); + static bool DoesTokenNeedQuotationMarks(const std::string& token); + + void WriteEscapedString(const std::string_view& str) const; + + static const std::string& BoolValue(bool value); + void WriteKey(const std::string& keyName) const; + void WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const; + void WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const; + void WriteBoolProperty(const std::string& propertyKey, bool propertyValue, bool defaultValue) const; + void WriteIntProperty(const std::string& propertyKey, int propertyValue, int defaultValue) const; + void WriteFloatProperty(const std::string& propertyKey, float propertyValue, float defaultValue) const; + void WriteColorProperty(const std::string& propertyKey, const float (&propertyValue)[4], const float (&defaultValue)[4]) const; + void WriteKeywordProperty(const std::string& propertyKey, bool shouldWrite) const; + void WriteFlagsProperty(const std::string& propertyKey, int flagsValue) const; + + std::ostream& m_stream; + size_t m_indent; + }; +} // namespace menu diff --git a/src/ObjWriting/Menu/IMenuWriter.h b/src/ObjWriting/Menu/IMenuWriter.h new file mode 100644 index 00000000..393fc33f --- /dev/null +++ b/src/ObjWriting/Menu/IMenuWriter.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace menu +{ + class IWriter + { + public: + IWriter() = default; + virtual ~IWriter() = default; + + virtual void Start() = 0; + virtual void End() = 0; + + virtual void IncludeMenu(const std::string& menuPath) const = 0; + }; +} // namespace menu diff --git a/src/ObjWriting/ObjWriting.h b/src/ObjWriting/ObjWriting.h index 0fe1ec93..200ddab7 100644 --- a/src/ObjWriting/ObjWriting.h +++ b/src/ObjWriting/ObjWriting.h @@ -26,7 +26,6 @@ public: GLB }; - bool Verbose = false; std::vector AssetTypesToHandleBitfield; ImageOutputFormat_e ImageOutputFormat = ImageOutputFormat_e::DDS; diff --git a/src/ObjWriting/Dumping/SndCurve/SndCurveDumper.cpp b/src/ObjWriting/Sound/SndCurveDumper.cpp similarity index 100% rename from src/ObjWriting/Dumping/SndCurve/SndCurveDumper.cpp rename to src/ObjWriting/Sound/SndCurveDumper.cpp diff --git a/src/ObjWriting/Dumping/SndCurve/SndCurveDumper.h b/src/ObjWriting/Sound/SndCurveDumper.h similarity index 100% rename from src/ObjWriting/Dumping/SndCurve/SndCurveDumper.h rename to src/ObjWriting/Sound/SndCurveDumper.h diff --git a/src/ObjWriting/Weapon/AccuracyGraphWriter.cpp b/src/ObjWriting/Weapon/AccuracyGraphWriter.cpp index 4da6e353..29f70050 100644 --- a/src/ObjWriting/Weapon/AccuracyGraphWriter.cpp +++ b/src/ObjWriting/Weapon/AccuracyGraphWriter.cpp @@ -1,5 +1,7 @@ #include "AccuracyGraphWriter.h" +#include "Utils/Logging/Log.h" + #include namespace @@ -24,7 +26,7 @@ namespace const auto file = context.OpenAssetFile(std::format("accuracy/{}/{}", subFolder, graph.name)); if (!file) { - std::cerr << "Failed to open file for accuracy graph: " << subFolder << "/" << graph.name << "\n"; + con::error("Failed to open file for accuracy graph: {}/{}", subFolder, graph.name); return; } diff --git a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp index 45f4a021..b8531a6d 100644 --- a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp +++ b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp @@ -18,16 +18,16 @@ protected: auto vertexOffset = 0u; for (const auto& vertex : xmodel.m_vertices) { - XModelVertexBoneWeights weights{0, 0}; + XModelVertexBoneWeights weights{.weightOffset = 0, .weightCount = 0}; if (vertexOffset < xmodel.m_vertex_bone_weights.size()) weights = xmodel.m_vertex_bone_weights[vertexOffset]; - m_vertex_merger.Add(VertexMergerPos{vertex.coordinates[0], - vertex.coordinates[1], - vertex.coordinates[2], - &xmodel.m_bone_weight_data.weights[weights.weightOffset], - weights.weightCount}); + m_vertex_merger.Add(VertexMergerPos{.x = vertex.coordinates[0], + .y = vertex.coordinates[1], + .z = vertex.coordinates[2], + .weights = &xmodel.m_bone_weight_data.weights[weights.weightOffset], + .weightCount = weights.weightCount}); vertexOffset++; } diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index bec9c598..b3576ed0 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -10,6 +10,7 @@ #include #include +#include using namespace gltf; using namespace nlohmann; @@ -25,6 +26,50 @@ namespace float uv[2]; }; + void LhcToRhcCoordinates(float (&coords)[3]) + { + const float two[3]{coords[0], coords[1], coords[2]}; + + coords[0] = two[0]; + coords[1] = two[2]; + coords[2] = -two[1]; + } + + void LhcToRhcQuaternion(float (&quat)[4]) + { + Eigen::Quaternionf eigenQuat(quat[3], quat[0], quat[1], quat[2]); + const Eigen::Quaternionf eigenRotationQuat(Eigen::AngleAxisf(-std::numbers::pi_v / 2.f, Eigen::Vector3f::UnitX())); + + eigenQuat = eigenRotationQuat * eigenQuat; + + quat[0] = eigenQuat.x(); + quat[1] = eigenQuat.y(); + quat[2] = eigenQuat.z(); + quat[3] = eigenQuat.w(); + } + + void LhcToRhcIndices(unsigned short* indices) + { + const unsigned short two[3]{indices[0], indices[1], indices[2]}; + + indices[0] = two[2]; + indices[1] = two[1]; + indices[2] = two[0]; + } + + void LhcToRhcMatrix(Eigen::Matrix4f& matrix) + { + const Eigen::Matrix4f convertMatrix({ + {1.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 1.0, 0.0}, + {0.0, -1.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 1.0} + }); + + const auto result = convertMatrix * matrix; + matrix = result; + } + class GltfWriterImpl final : public gltf::Writer { public: @@ -238,26 +283,39 @@ namespace JsonNode boneNode; const auto& bone = common.m_bones[boneIndex]; - Eigen::Vector3f translation(bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]); - Eigen::Quaternionf rotation(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.y, bone.globalRotation.z); + float globalTranslationData[3]{bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]}; + LhcToRhcCoordinates(globalTranslationData); + Eigen::Vector3f translation(globalTranslationData[0], globalTranslationData[1], globalTranslationData[2]); + + float globalRotationData[4]{bone.globalRotation.x, bone.globalRotation.y, bone.globalRotation.z, bone.globalRotation.w}; + LhcToRhcQuaternion(globalRotationData); + Eigen::Quaternionf rotation(globalRotationData[3], globalRotationData[0], globalRotationData[1], globalRotationData[2]); + if (bone.parentIndex) { const auto& parentBone = common.m_bones[*bone.parentIndex]; - const auto inverseParentRotation = - Eigen::Quaternionf(parentBone.globalRotation.w, parentBone.globalRotation.x, parentBone.globalRotation.y, parentBone.globalRotation.z) - .normalized() - .inverse() - .normalized(); - translation -= Eigen::Vector3f(parentBone.globalOffset[0], parentBone.globalOffset[1], parentBone.globalOffset[2]); + float parentGlobalTranslationData[3]{parentBone.globalOffset[0], parentBone.globalOffset[1], parentBone.globalOffset[2]}; + LhcToRhcCoordinates(parentGlobalTranslationData); + const Eigen::Vector3f parentTranslation(parentGlobalTranslationData[0], parentGlobalTranslationData[1], parentGlobalTranslationData[2]); + + float parentGlobalRotationData[4]{ + parentBone.globalRotation.x, parentBone.globalRotation.y, parentBone.globalRotation.z, parentBone.globalRotation.w}; + LhcToRhcQuaternion(parentGlobalRotationData); + const Eigen::Quaternionf parentRotation( + parentGlobalRotationData[3], parentGlobalRotationData[0], parentGlobalRotationData[1], parentGlobalRotationData[2]); + const auto inverseParentRotation = parentRotation.inverse(); + + translation -= parentTranslation; translation = inverseParentRotation * translation; rotation = inverseParentRotation * rotation; } rotation.normalize(); boneNode.name = bone.name; - boneNode.translation = std::to_array({translation.x(), translation.z(), -translation.y()}); - boneNode.rotation = std::to_array({rotation.x(), rotation.z(), -rotation.y(), rotation.w()}); + + boneNode.translation = std::to_array({translation.x(), translation.y(), translation.z()}); + boneNode.rotation = std::to_array({rotation.x(), rotation.y(), rotation.z(), rotation.w()}); std::vector children; for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++) @@ -471,8 +529,9 @@ namespace auto* vertex = reinterpret_cast(&bufferData[currentBufferOffset]); vertex->coordinates[0] = commonVertex.coordinates[0]; - vertex->coordinates[1] = commonVertex.coordinates[2]; - vertex->coordinates[2] = -commonVertex.coordinates[1]; + vertex->coordinates[1] = commonVertex.coordinates[1]; + vertex->coordinates[2] = commonVertex.coordinates[2]; + LhcToRhcCoordinates(vertex->coordinates); minPosition[0] = std::min(minPosition[0], vertex->coordinates[0]); minPosition[1] = std::min(minPosition[1], vertex->coordinates[1]); @@ -482,8 +541,9 @@ namespace maxPosition[2] = std::max(maxPosition[2], vertex->coordinates[2]); vertex->normal[0] = commonVertex.normal[0]; - vertex->normal[1] = commonVertex.normal[2]; - vertex->normal[2] = -commonVertex.normal[1]; + vertex->normal[1] = commonVertex.normal[1]; + vertex->normal[2] = commonVertex.normal[2]; + LhcToRhcCoordinates(vertex->normal); vertex->uv[0] = commonVertex.uv[0]; vertex->uv[1] = commonVertex.uv[1]; @@ -531,11 +591,13 @@ namespace auto* inverseBindMatrixData = reinterpret_cast(&bufferData[currentBufferOffset]); for (const auto& bone : xmodel.m_bones) { - const auto translation = Eigen::Translation3f(bone.globalOffset[0], bone.globalOffset[2], -bone.globalOffset[1]); - const auto rotation = Eigen::Quaternionf(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.z, -bone.globalRotation.y); + const auto translation = Eigen::Translation3f(bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]); + const auto rotation = Eigen::Quaternionf(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.y, bone.globalRotation.z); + const auto bindMatrixTransform = translation * rotation; + auto bindMatrix = bindMatrixTransform.matrix(); - const auto bindMatrix = (translation * rotation); - const auto inverseBindMatrix = bindMatrix.matrix().inverse(); + LhcToRhcMatrix(bindMatrix); + const auto inverseBindMatrix = bindMatrix.inverse(); // GLTF matrix is column major inverseBindMatrixData[0] = inverseBindMatrix(0, 0); @@ -565,9 +627,10 @@ namespace for (const auto& face : object.m_faces) { auto* faceIndices = reinterpret_cast(&bufferData[currentBufferOffset]); - faceIndices[0] = static_cast(face.vertexIndex[2]); + faceIndices[0] = static_cast(face.vertexIndex[0]); faceIndices[1] = static_cast(face.vertexIndex[1]); - faceIndices[2] = static_cast(face.vertexIndex[0]); + faceIndices[2] = static_cast(face.vertexIndex[2]); + LhcToRhcIndices(faceIndices); currentBufferOffset += sizeof(unsigned short) * 3u; } diff --git a/src/ObjWriting/XModel/XModelDumper.cpp.template b/src/ObjWriting/XModel/XModelDumper.cpp.template index fee67d24..4a569a56 100644 --- a/src/ObjWriting/XModel/XModelDumper.cpp.template +++ b/src/ObjWriting/XModel/XModelDumper.cpp.template @@ -1,4 +1,4 @@ -#options GAME (IW5, T5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/XModelDumper" + GAME + ".cpp" @@ -6,7 +6,13 @@ #set COMMON_HEADER "\"Game/" + GAME + "/Common" + GAME + ".h\"" #set JSON_HEADER "\"Game/" + GAME + "/XModel/JsonXModel" + GAME + ".h\"" -#if GAME == "IW5" +#if GAME == "IW3" +#define FEATURE_IW3 +#define GAME_LOWER "iw3" +#elif GAME == "IW4" +#define FEATURE_IW4 +#define GAME_LOWER "iw4" +#elif GAME == "IW5" #define FEATURE_IW5 #define GAME_LOWER "iw5" #elif GAME == "T5" @@ -42,6 +48,8 @@ using namespace GAME; +#set CLASS_NAME "Dumper" + GAME + namespace { std::string GetFileNameForLod(const std::string& modelName, const unsigned lod, const std::string& extension) @@ -66,7 +74,7 @@ namespace { MaterialTextureDef* def = &material->textureTable[textureIndex]; -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) if (def->semantic == TS_COLOR_MAP) potentialTextureDefs.push_back(def); #else @@ -155,7 +163,7 @@ namespace bool GetSurfaces(const XModel* model, const unsigned lod, XSurface*& surfs, unsigned& surfCount) { -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) if (!model->lodInfo[lod].modelSurfs || !model->lodInfo[lod].modelSurfs->surfs) return false; @@ -276,7 +284,7 @@ namespace void AddXModelMaterials(XModelCommon& out, DistinctMapper& materialMapper, const XModel* model) { - for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) + for (auto surfaceMaterialNum = 0u; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) { Material* material = model->materialHandles[surfaceMaterialNum]; if (materialMapper.Add(material)) @@ -524,6 +532,12 @@ namespace } } + bool CanOmitDefaultArmature() + { + return ObjWriting::Configuration.ModelOutputFormat != ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT + && ObjWriting::Configuration.ModelOutputFormat != ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_BIN; + } + void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) { DistinctMapper materialMapper(model->numsurfs); @@ -534,8 +548,8 @@ namespace AddXModelObjects(out, model, lod, materialMapper); AddXModelVertices(out, model, lod); AddXModelFaces(out, model, lod); - - if (!HasDefaultArmature(model, lod)) + + if (!CanOmitDefaultArmature() || !HasDefaultArmature(model, lod)) { AddXModelBones(out, context, model); AddXModelVertexBoneWeights(out, model, lod); @@ -554,7 +568,8 @@ namespace if (!mtlFile) return; - const auto writer = obj::CreateMtlWriter(*mtlFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); + const auto* game = IGame::GetGameById(context.m_zone.m_game_id); + const auto writer = obj::CreateMtlWriter(*mtlFile, game->GetShortName(), context.m_zone.m_name); DistinctMapper materialMapper(model->numsurfs); writer->Write(common); @@ -568,8 +583,9 @@ namespace if (!assetFile) return; + const auto* game = IGame::GetGameById(context.m_zone.m_game_id); const auto writer = - obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), context.m_zone.m_game->GetShortName(), context.m_zone.m_name); + obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), game->GetShortName(), context.m_zone.m_name); DistinctMapper materialMapper(model->numsurfs); writer->Write(common); @@ -583,7 +599,8 @@ namespace if (!assetFile) return; - const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); + const auto* game = IGame::GetGameById(context.m_zone.m_game_id); + const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, game->GetShortName(), context.m_zone.m_name); writer->Write(common); } @@ -595,7 +612,8 @@ namespace if (!assetFile) return; - const auto writer = xmodel_bin::CreateWriterForVersion7(*assetFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); + const auto* game = IGame::GetGameById(context.m_zone.m_game_id); + const auto writer = xmodel_bin::CreateWriterForVersion7(*assetFile, game->GetShortName(), context.m_zone.m_name); writer->Write(common); } @@ -609,8 +627,9 @@ namespace if (!assetFile) return; + const auto* game = IGame::GetGameById(context.m_zone.m_game_id); const auto output = std::make_unique(*assetFile); - const auto writer = gltf::Writer::CreateWriter(output.get(), context.m_zone.m_game->GetShortName(), context.m_zone.m_name); + const auto writer = gltf::Writer::CreateWriter(output.get(), game->GetShortName(), context.m_zone.m_name); writer->Write(common); } @@ -669,8 +688,9 @@ namespace CreateJsonXModel(jsonXModel, *xmodel); nlohmann::json jRoot = jsonXModel; + jRoot["$schema"] = "http://openassettools.dev/schema/xmodel.v1.json"; jRoot["_type"] = "xmodel"; - jRoot["_version"] = 1; + jRoot["_version"] = 2; jRoot["_game"] = GAME_LOWER; m_stream << std::setw(4) << jRoot << "\n"; @@ -705,11 +725,87 @@ namespace } } + static bool IsAnimated(const XModel& xmodel) + { +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + for (auto i = 0u; i < xmodel.numLods; i++) + { + const auto& lod = xmodel.lodInfo[i]; + if (lod.modelSurfs == nullptr || lod.modelSurfs->surfs == nullptr) + continue; + + for (auto j = 0u; j < lod.modelSurfs->numsurfs; j++) + { + const auto& surf = xmodel.lodInfo[i].modelSurfs->surfs[j]; + if (surf.vertInfo.vertsBlend) + return true; + } + } +#else + for (auto i = 0u; i < xmodel.numsurfs; i++) + { + const auto& surf = xmodel.surfs[i]; + if (surf.vertInfo.vertsBlend) + return true; + } +#endif + + return false; + } + + static bool HasNulledTrans(const XModel& xmodel) + { + if (xmodel.trans == nullptr) + return true; + + const auto transCount = (xmodel.numBones - xmodel.numRootBones) * 3u; + for (auto i = 0u; i < transCount; i++) + { + if (xmodel.trans[i] != 0) + return false; + } + + return true; + } + + static bool HasNonNullBoneInfoTrans(const XModel& xmodel) + { + if (xmodel.boneInfo == nullptr) + return false; + + for (auto i = 0u; i < xmodel.numBones; i++) + { + const auto& boneInfo = xmodel.boneInfo[i]; +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + if (boneInfo.bounds.midPoint.x != 0 || boneInfo.bounds.midPoint.y != 0 || boneInfo.bounds.midPoint.z != 0) + return true; +#else + if (boneInfo.offset.x != 0 || boneInfo.offset.y != 0 || boneInfo.offset.z != 0) + return true; +#endif + } + + return false; + } + + static JsonXModelType GetType(const XModel& xmodel) + { + if (!IsAnimated(xmodel)) + return JsonXModelType::RIGID; + + if (HasNulledTrans(xmodel) && HasNonNullBoneInfoTrans(xmodel)) + return JsonXModelType::VIEWHANDS; + + return JsonXModelType::ANIMATED; + } + static void CreateJsonXModel(JsonXModel& jXModel, const XModel& xmodel) { if (xmodel.collLod >= 0) jXModel.collLod = xmodel.collLod; + jXModel.type = GetType(xmodel); + for (auto lodNumber = 0u; lodNumber < xmodel.numLods; lodNumber++) { JsonXModelLod lod; @@ -722,7 +818,7 @@ namespace if (xmodel.physPreset && xmodel.physPreset->name) jXModel.physPreset = AssetName(xmodel.physPreset->name); -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) if (xmodel.physCollmap && xmodel.physCollmap->name) jXModel.physCollmap = AssetName(xmodel.physCollmap->name); #endif @@ -747,7 +843,7 @@ namespace void DumpXModelJson(AssetDumpingContext& context, XAssetInfo* asset) { - const auto assetFile = context.OpenAssetFile(std::format("xmodel/{}.json", asset->m_name)); + const auto assetFile = context.OpenAssetFile(xmodel::GetJsonFileNameForAssetName(asset->m_name)); if (!assetFile) return; @@ -756,11 +852,16 @@ namespace } } // namespace -namespace GAME +namespace xmodel { - void DumpXModel(AssetDumpingContext& context, XAssetInfo* asset) + bool CLASS_NAME::ShouldDump(XAssetInfo* asset) + { + return !asset->m_name.empty() && asset->m_name[0] != ','; + } + + void CLASS_NAME::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { DumpXModelJson(context, asset); DumpXModelSurfs(context, asset); } -} // namespace GAME +} diff --git a/src/ObjWriting/XModel/XModelDumper.h.template b/src/ObjWriting/XModel/XModelDumper.h.template index bcb2d5ab..5483e209 100644 --- a/src/ObjWriting/XModel/XModelDumper.h.template +++ b/src/ObjWriting/XModel/XModelDumper.h.template @@ -1,4 +1,4 @@ -#options GAME (IW5, T5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/XModelDumper" + GAME + ".h" @@ -10,10 +10,17 @@ #pragma once -#include "Dumping/AssetDumpingContext.h" +#include "Dumping/AbstractAssetDumper.h" #include GAME_HEADER -namespace GAME +#set CLASS_NAME "Dumper" + GAME + +namespace xmodel { - void DumpXModel(AssetDumpingContext& context, XAssetInfo* asset); -} + class CLASS_NAME final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} // namespace xmodel diff --git a/src/Parser/Parsing/Impl/AbstractParser.h b/src/Parser/Parsing/Impl/AbstractParser.h index a1e28095..b76f2053 100644 --- a/src/Parser/Parsing/Impl/AbstractParser.h +++ b/src/Parser/Parsing/Impl/AbstractParser.h @@ -4,6 +4,7 @@ #include "Parsing/IParser.h" #include "Parsing/ParsingException.h" #include "Parsing/Sequence/AbstractSequence.h" +#include "Utils/Logging/Log.h" #include #include @@ -63,12 +64,12 @@ public: if (!line.IsEof()) { - std::cerr << "Error: " << pos.m_filename.get() << " L" << pos.m_line << ':' << pos.m_column << " Could not parse expression:\n" - << line.m_line.substr(pos.m_column - 1) << "\n"; + con::error( + "{} L{}:{} Could not parse expression:\n{}", pos.m_filename.get(), pos.m_line, pos.m_column, line.m_line.substr(pos.m_column - 1)); } else { - std::cerr << "Error: " << pos.m_filename.get() << " L" << pos.m_line << ':' << pos.m_column << " Could not parse expression.\n"; + con::error("{} L{}:{} Could not parse expression.", pos.m_filename.get(), pos.m_line, pos.m_column); } return false; } @@ -81,11 +82,11 @@ public: if (!line.IsEof() && line.m_line.size() > static_cast(pos.m_column - 1)) { - std::cerr << "Error: " << e.FullMessage() << "\n" << line.m_line.substr(pos.m_column - 1) << "\n"; + con::error("{}\n{}", e.FullMessage(), line.m_line.substr(pos.m_column - 1)); } else { - std::cerr << "Error: " << e.FullMessage() << "\n"; + con::error(e.FullMessage()); } return false; diff --git a/src/RawTemplater/RawTemplater.cpp b/src/RawTemplater/RawTemplater.cpp index 902e199b..57465926 100644 --- a/src/RawTemplater/RawTemplater.cpp +++ b/src/RawTemplater/RawTemplater.cpp @@ -2,6 +2,7 @@ #include "RawTemplaterArguments.h" #include "Templating/Templater.h" +#include "Utils/Logging/Log.h" #include #include @@ -21,7 +22,7 @@ class RawTemplater::Impl std::ifstream file(filename, std::ios::in | std::ios::binary); if (!file.is_open()) { - std::cerr << "Failed to open file \"" << filename << "\"\n"; + con::error("Failed to open file \"{}\"", filename); return false; } @@ -61,7 +62,7 @@ public: m_build_log_file = std::ofstream(m_args.m_build_log_file, std::ios::out | std::ios::binary); if (!m_build_log_file.is_open()) { - std::cerr << "Failed to open build log file \"" << m_args.m_build_log_file << "\"\n"; + con::error("Failed to open build log file \"{}\"", m_args.m_build_log_file); return false; } m_write_build_log = true; diff --git a/src/RawTemplater/RawTemplaterArguments.cpp b/src/RawTemplater/RawTemplaterArguments.cpp index 7962e164..a933b425 100644 --- a/src/RawTemplater/RawTemplaterArguments.cpp +++ b/src/RawTemplater/RawTemplaterArguments.cpp @@ -3,6 +3,7 @@ #include "GitVersion.h" #include "Utils/Arguments/CommandLineOption.h" #include "Utils/Arguments/UsageInformation.h" +#include "Utils/Logging/Log.h" #include #include @@ -29,6 +30,12 @@ const CommandLineOption* const OPTION_VERBOSE = .WithDescription("Outputs a lot more and more detailed messages.") .Build(); +const CommandLineOption* const OPTION_NO_COLOR = + CommandLineOption::Builder::Create() + .WithLongName("no-color") + .WithDescription("Disables colored terminal output.") + .Build(); + const CommandLineOption* const OPTION_OUTPUT_FOLDER = CommandLineOption::Builder::Create() .WithShortName("o") @@ -58,14 +65,14 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ OPTION_HELP, OPTION_VERSION, OPTION_VERBOSE, + OPTION_NO_COLOR, OPTION_OUTPUT_FOLDER, OPTION_BUILD_LOG, OPTION_DEFINE, }; RawTemplaterArguments::RawTemplaterArguments() - : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v), - m_verbose(false) + : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v) { } @@ -83,7 +90,7 @@ void RawTemplaterArguments::PrintUsage() const void RawTemplaterArguments::PrintVersion() { - std::cout << std::format("OpenAssetTools RawTemplater {}\n", GIT_VERSION); + con::info("OpenAssetTools RawTemplater {}", GIT_VERSION); } bool RawTemplaterArguments::ParseArgs(const int argc, const char** argv, bool& shouldContinue) @@ -119,7 +126,13 @@ bool RawTemplaterArguments::ParseArgs(const int argc, const char** argv, bool& s } // -v; --verbose - m_verbose = m_argument_parser.IsOptionSpecified(OPTION_VERBOSE); + if (m_argument_parser.IsOptionSpecified(OPTION_VERBOSE)) + con::globalLogLevel = con::LogLevel::DEBUG; + else + con::globalLogLevel = con::LogLevel::INFO; + + // --no-color + con::globalUseColor = !m_argument_parser.IsOptionSpecified(OPTION_NO_COLOR); // -o; --output if (m_argument_parser.IsOptionSpecified(OPTION_OUTPUT_FOLDER)) diff --git a/src/RawTemplater/RawTemplaterArguments.h b/src/RawTemplater/RawTemplaterArguments.h index cfaac7b3..a0b2696d 100644 --- a/src/RawTemplater/RawTemplaterArguments.h +++ b/src/RawTemplater/RawTemplaterArguments.h @@ -17,8 +17,6 @@ class RawTemplaterArguments static void PrintVersion(); public: - bool m_verbose; - std::vector m_input_files; std::string m_output_directory; diff --git a/src/RawTemplater/Templating/Templater.cpp b/src/RawTemplater/Templating/Templater.cpp index 4ca68e30..40c2203f 100644 --- a/src/RawTemplater/Templating/Templater.cpp +++ b/src/RawTemplater/Templating/Templater.cpp @@ -8,6 +8,7 @@ #include "SetDefineStreamProxy.h" #include "TemplatingStreamProxy.h" #include "Utils/ClassUtils.h" +#include "Utils/Logging/Log.h" #include #include @@ -219,7 +220,7 @@ namespace templating { if (!m_active_variations.empty()) { - std::cerr << "Template with variations must specify a filename\n"; + con::error("Template with variations must specify a filename"); return false; } @@ -231,7 +232,7 @@ namespace templating m_output_stream << cachedData; } - std::cout << "Templated file \"" << m_output_file << "\"\n"; + con::info("Templated file \"{}\"", m_output_file); if (buildLogFile) *buildLogFile << "Templated file \"" << m_output_file << "\"\n"; @@ -276,7 +277,7 @@ namespace templating const auto isValidRedefinition = existingVariation->second->GetVariationType() == TemplatingVariationType::SWITCH; if (!isValidRedefinition) - std::cerr << "Redefinition of \"" << switchName << "\" as switch is invalid\n"; + con::error("Redefinition of \"{}\" as switch is invalid", switchName); return isValidRedefinition; } @@ -299,7 +300,7 @@ namespace templating const auto isValidRedefinition = existingVariation->second->GetVariationType() == TemplatingVariationType::OPTIONS; if (!isValidRedefinition) - std::cerr << "Redefinition of \"" << optionsName << "\" as options is invalid\n"; + con::error("Redefinition of \"{}\" as options is invalid", optionsName); return isValidRedefinition; } @@ -337,7 +338,7 @@ namespace templating { if (m_write_output_to_file) { - std::cerr << "Cannot skip when already writing to file\n"; + con::error("Cannot skip when already writing to file"); return false; } @@ -355,7 +356,7 @@ namespace templating m_output_stream = std::ofstream(m_output_file, std::ios::out | std::ios::binary); if (!m_output_stream.is_open()) { - std::cerr << "Failed to open output file \"" << m_output_file << "\"\n"; + con::error("Failed to open output file \"{}\"", m_output_file); return false; } @@ -412,7 +413,7 @@ bool Templater::TemplateToDirectory(const std::string& outputDirectory) const } catch (ParsingException& e) { - std::cerr << "Error: " << e.FullMessage() << "\n"; + con::error(e.FullMessage()); return false; } diff --git a/src/Unlinker/ContentLister/ContentPrinter.cpp b/src/Unlinker/ContentLister/ContentPrinter.cpp index 99e226d1..2134dcdc 100644 --- a/src/Unlinker/ContentLister/ContentPrinter.cpp +++ b/src/Unlinker/ContentLister/ContentPrinter.cpp @@ -1,5 +1,7 @@ #include "ContentPrinter.h" +#include "Utils/Logging/Log.h" + #include #include @@ -11,11 +13,12 @@ ContentPrinter::ContentPrinter(const Zone& zone) void ContentPrinter::PrintContent() const { const auto* pools = m_zone.m_pools.get(); - std::cout << std::format("Zone '{}' ({})\n", m_zone.m_name, m_zone.m_game->GetShortName()); - std::cout << "Content:\n"; + const auto* game = IGame::GetGameById(m_zone.m_game_id); + con::info("Zone '{}' ({})", m_zone.m_name, game->GetShortName()); + con::info("Content:"); for (const auto& asset : *pools) - std::cout << std::format("{}, {}\n", *pools->GetAssetTypeName(asset->m_type), asset->m_name); + con::info("{}, {}", *pools->GetAssetTypeName(asset->m_type), asset->m_name); - std::cout << "\n"; + con::info(""); } diff --git a/src/Unlinker/ContentLister/ZoneDefWriter.cpp b/src/Unlinker/ContentLister/ZoneDefWriter.cpp index 51ec5539..367199e5 100644 --- a/src/Unlinker/ContentLister/ZoneDefWriter.cpp +++ b/src/Unlinker/ContentLister/ZoneDefWriter.cpp @@ -28,9 +28,10 @@ const IZoneDefWriter* IZoneDefWriter::GetZoneDefWriterForGame(GameId game) void AbstractZoneDefWriter::WriteZoneDef(std::ostream& stream, const UnlinkerArgs& args, const Zone& zone) const { ZoneDefinitionOutputStream out(stream); + const auto* game = IGame::GetGameById(zone.m_game_id); - out.WriteComment(zone.m_game->GetFullName()); - out.WriteMetaData(META_DATA_KEY_GAME, zone.m_game->GetShortName()); + out.WriteComment(game->GetFullName()); + out.WriteMetaData(META_DATA_KEY_GAME, game->GetShortName()); out.EmptyLine(); if (args.m_use_gdt) diff --git a/src/Unlinker/Unlinker.cpp b/src/Unlinker/Unlinker.cpp index c79d0d7b..977c1ad3 100644 --- a/src/Unlinker/Unlinker.cpp +++ b/src/Unlinker/Unlinker.cpp @@ -12,6 +12,7 @@ #include "UnlinkerArgs.h" #include "UnlinkerPaths.h" #include "Utils/ClassUtils.h" +#include "Utils/Logging/Log.h" #include "Utils/ObjFileStream.h" #include "ZoneLoading.h" @@ -67,11 +68,11 @@ private: std::ofstream zoneDefinitionFile(zoneDefinitionFilePath, std::fstream::out | std::fstream::binary); if (!zoneDefinitionFile.is_open()) { - std::cerr << std::format("Failed to open file for zone definition file of zone \"{}\".\n", zone.m_name); + con::error("Failed to open file for zone definition file of zone \"{}\".", zone.m_name); return false; } - const auto* zoneDefWriter = IZoneDefWriter::GetZoneDefWriterForGame(zone.m_game->GetId()); + const auto* zoneDefWriter = IZoneDefWriter::GetZoneDefWriterForGame(zone.m_game_id); zoneDefWriter->WriteZoneDef(zoneDefinitionFile, m_args, zone); zoneDefinitionFile.close(); @@ -92,7 +93,7 @@ private: stream = std::ofstream(gdtFilePath, std::fstream::out | std::fstream::binary); if (!stream.is_open()) { - std::cerr << std::format("Failed to open file for zone definition file of zone \"{}\".\n", zone.m_name); + con::error("Failed to open file for zone definition file of zone \"{}\".", zone.m_name); return false; } @@ -126,16 +127,17 @@ private: { if (!handledSpecifiedAssets[i]) { - std::cerr << std::format("Unknown asset type \"{}\"\n", m_args.m_specified_asset_types[i]); + con::error("Unknown asset type \"{}\"", m_args.m_specified_asset_types[i]); anySpecifiedValueInvalid = true; } } if (anySpecifiedValueInvalid) { - std::cerr << "Valid asset types are:\n"; + con::error("Valid asset types are:"); auto first = true; + std::ostringstream ss; for (auto i = 0u; i < assetTypeCount; i++) { const auto assetTypeName = std::string(*context.m_zone.m_pools->GetAssetTypeName(i)); @@ -143,10 +145,10 @@ private: if (first) first = false; else - std::cerr << ", "; - std::cerr << assetTypeName; + ss << ", "; + ss << assetTypeName; } - std::cerr << "\n"; + con::error(ss.str()); } } @@ -186,13 +188,16 @@ private: return false; auto gdt = std::make_unique(gdtStream); gdt->BeginStream(); - gdt->WriteVersion(GdtVersion(zone.m_game->GetShortName(), 1)); + + const auto* game = IGame::GetGameById(zone.m_game_id); + gdt->WriteVersion(GdtVersion(game->GetShortName(), 1)); + context.m_gdt = std::move(gdt); } UpdateAssetIncludesAndExcludes(context); - const auto* objWriter = IObjWriter::GetObjWriterForGame(zone.m_game->GetId()); + const auto* objWriter = IObjWriter::GetObjWriterForGame(zone.m_game_id); auto result = objWriter->DumpZone(context); @@ -204,7 +209,7 @@ private: if (!result) { - std::cerr << "Dumping zone failed!\n"; + con::error("Dumping zone failed!"); return false; } } @@ -218,7 +223,7 @@ private: { if (!fs::is_regular_file(zonePath)) { - std::cerr << std::format("Could not find file \"{}\".\n", zonePath); + con::error("Could not find file \"{}\".", zonePath); continue; } @@ -228,16 +233,15 @@ private: auto zone = ZoneLoading::LoadZone(zonePath); if (zone == nullptr) { - std::cerr << std::format("Failed to load zone \"{}\".\n", zonePath); + con::error("Failed to load zone \"{}\".", zonePath); return false; } - if (m_args.m_verbose) - std::cout << std::format("Loaded zone \"{}\"\n", zone->m_name); + con::debug("Loaded zone \"{}\"", zone->m_name); if (ShouldLoadObj()) { - const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game->GetId()); + const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game_id); objLoader->LoadReferencedContainersForZone(*searchPathsForZone, *zone); } @@ -258,14 +262,13 @@ private: if (ShouldLoadObj()) { - const auto* objLoader = IObjLoader::GetObjLoaderForGame(loadedZone->m_game->GetId()); + const auto* objLoader = IObjLoader::GetObjLoaderForGame(loadedZone->m_game_id); objLoader->UnloadContainersOfZone(*loadedZone); } loadedZone.reset(); - if (m_args.m_verbose) - std::cout << std::format("Unloaded zone \"{}\"\n", zoneName); + con::debug("Unloaded zone \"{}\"", zoneName); } m_loaded_zones.clear(); } @@ -276,7 +279,7 @@ private: { if (!fs::is_regular_file(zonePath)) { - std::cerr << std::format("Could not find file \"{}\".\n", zonePath); + con::error("Could not find file \"{}\".", zonePath); continue; } @@ -291,15 +294,14 @@ private: auto zone = ZoneLoading::LoadZone(zonePath); if (zone == nullptr) { - std::cerr << std::format("Failed to load zone \"{}\".\n", zonePath); + con::error("Failed to load zone \"{}\".", zonePath); return false; } zoneName = zone->m_name; - if (m_args.m_verbose) - std::cout << std::format("Loaded zone \"{}\"\n", zoneName); + con::debug("Loaded zone \"{}\"", zoneName); - const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game->GetId()); + const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game_id); if (ShouldLoadObj()) objLoader->LoadReferencedContainersForZone(*searchPathsForZone, *zone); @@ -310,8 +312,7 @@ private: objLoader->UnloadContainersOfZone(*zone); zone.reset(); - if (m_args.m_verbose) - std::cout << std::format("Unloaded zone \"{}\"\n", zoneName); + con::debug("Unloaded zone \"{}\"", zoneName); } return true; diff --git a/src/Unlinker/UnlinkerArgs.cpp b/src/Unlinker/UnlinkerArgs.cpp index 08746e50..3ae9224f 100644 --- a/src/Unlinker/UnlinkerArgs.cpp +++ b/src/Unlinker/UnlinkerArgs.cpp @@ -5,6 +5,7 @@ #include "ObjWriting.h" #include "Utils/Arguments/UsageInformation.h" #include "Utils/FileUtils.h" +#include "Utils/Logging/Log.h" #include "Utils/StringUtils.h" #include @@ -33,6 +34,12 @@ const CommandLineOption* const OPTION_VERBOSE = .WithDescription("Outputs a lot more and more detailed messages.") .Build(); +const CommandLineOption* const OPTION_NO_COLOR = + CommandLineOption::Builder::Create() + .WithLongName("no-color") + .WithDescription("Disables colored terminal output.") + .Build(); + const CommandLineOption* const OPTION_MINIMAL_ZONE_FILE = CommandLineOption::Builder::Create() .WithShortName("min") @@ -124,6 +131,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ OPTION_HELP, OPTION_VERSION, OPTION_VERBOSE, + OPTION_NO_COLOR, OPTION_MINIMAL_ZONE_FILE, OPTION_LOAD, OPTION_LIST, @@ -145,8 +153,7 @@ UnlinkerArgs::UnlinkerArgs() m_minimal_zone_def(false), m_asset_type_handling(AssetTypeHandling::EXCLUDE), m_skip_obj(false), - m_use_gdt(false), - m_verbose(false) + m_use_gdt(false) { } @@ -167,14 +174,7 @@ void UnlinkerArgs::PrintUsage() const void UnlinkerArgs::PrintVersion() { - std::cout << std::format("OpenAssetTools Unlinker {}\n", GIT_VERSION); -} - -void UnlinkerArgs::SetVerbose(const bool isVerbose) -{ - m_verbose = isVerbose; - ObjLoading::Configuration.Verbose = isVerbose; - ObjWriting::Configuration.Verbose = isVerbose; + con::info("OpenAssetTools Unlinker {}", GIT_VERSION); } bool UnlinkerArgs::SetImageDumpingMode() const @@ -195,7 +195,7 @@ bool UnlinkerArgs::SetImageDumpingMode() const } const std::string originalValue = m_argument_parser.GetValueForOption(OPTION_IMAGE_FORMAT); - std::cerr << std::format("Illegal value: \"{}\" is not a valid image output format. Use -? to see usage information.\n", originalValue); + con::error("Illegal value: \"{}\" is not a valid image output format. Use -? to see usage information.", originalValue); return false; } @@ -235,7 +235,7 @@ bool UnlinkerArgs::SetModelDumpingMode() const } const std::string originalValue = m_argument_parser.GetValueForOption(OPTION_MODEL_FORMAT); - std::cerr << std::format("Illegal value: \"{}\" is not a valid model output format. Use -? to see usage information.\n", originalValue); + con::error("Illegal value: \"{}\" is not a valid model output format. Use -? to see usage information.", originalValue); return false; } @@ -302,7 +302,13 @@ bool UnlinkerArgs::ParseArgs(const int argc, const char** argv, bool& shouldCont } // -v; --verbose - SetVerbose(m_argument_parser.IsOptionSpecified(OPTION_VERBOSE)); + if (m_argument_parser.IsOptionSpecified(OPTION_VERBOSE)) + con::globalLogLevel = con::LogLevel::DEBUG; + else + con::globalLogLevel = con::LogLevel::INFO; + + // --no-color + con::globalUseColor = !m_argument_parser.IsOptionSpecified(OPTION_NO_COLOR); // -min; --minimal-zone m_minimal_zone_def = m_argument_parser.IsOptionSpecified(OPTION_MINIMAL_ZONE_FILE); @@ -358,7 +364,7 @@ bool UnlinkerArgs::ParseArgs(const int argc, const char** argv, bool& shouldCont // --include-assets if (m_argument_parser.IsOptionSpecified(OPTION_EXCLUDE_ASSETS) && m_argument_parser.IsOptionSpecified(OPTION_INCLUDE_ASSETS)) { - std::cout << "You can only asset types to either exclude or include, not both\n"; + con::error("You can only asset types to either exclude or include, not both"); return false; } diff --git a/src/Unlinker/UnlinkerArgs.h b/src/Unlinker/UnlinkerArgs.h index 0e07dd2b..459cbc60 100644 --- a/src/Unlinker/UnlinkerArgs.h +++ b/src/Unlinker/UnlinkerArgs.h @@ -24,8 +24,6 @@ private: */ void PrintUsage() const; static void PrintVersion(); - - void SetVerbose(bool isVerbose); bool SetImageDumpingMode() const; bool SetModelDumpingMode() const; @@ -60,8 +58,6 @@ public: bool m_skip_obj; bool m_use_gdt; - bool m_verbose; - UnlinkerArgs(); bool ParseArgs(int argc, const char** argv, bool& shouldContinue); diff --git a/src/Unlinker/UnlinkerPaths.cpp b/src/Unlinker/UnlinkerPaths.cpp index 91d235a7..59aec518 100644 --- a/src/Unlinker/UnlinkerPaths.cpp +++ b/src/Unlinker/UnlinkerPaths.cpp @@ -2,6 +2,7 @@ #include "SearchPath/IWD.h" #include "SearchPath/SearchPathFilesystem.h" +#include "Utils/Logging/Log.h" #include "Utils/StringUtils.h" #include @@ -18,7 +19,7 @@ bool UnlinkerPaths::LoadUserPaths(const UnlinkerArgs& args) if (!fs::is_directory(absolutePath)) { - std::cerr << std::format("Could not find directory of search path: \"{}\"\n", path); + con::error("Could not find directory of search path: \"{}\"", path); return false; } @@ -44,12 +45,12 @@ bool UnlinkerPaths::LoadUserPaths(const UnlinkerArgs& args) } } - std::cout << std::format("{} SearchPaths{}\n", m_specified_user_paths.size(), !m_specified_user_paths.empty() ? ":" : ""); + con::info("{} SearchPaths{}", m_specified_user_paths.size(), !m_specified_user_paths.empty() ? ":" : ""); for (const auto& absoluteSearchPath : m_specified_user_paths) - std::cout << std::format(" \"{}\"\n", absoluteSearchPath); + con::info(" \"{}\"", absoluteSearchPath); if (!m_specified_user_paths.empty()) - std::cerr << "\n"; + con::info(""); return true; } diff --git a/src/Utils/Utils/Arguments/ArgumentParser.cpp b/src/Utils/Utils/Arguments/ArgumentParser.cpp index 8192f778..14fc2e32 100644 --- a/src/Utils/Utils/Arguments/ArgumentParser.cpp +++ b/src/Utils/Utils/Arguments/ArgumentParser.cpp @@ -1,5 +1,6 @@ #include "ArgumentParser.h" +#include "Utils/Logging/Log.h" #include "Utils/StringUtils.h" #include @@ -81,7 +82,7 @@ bool ArgumentParser::ParseArguments(std::vector& args) if (matchedOption == nullptr) { - std::cout << std::format("Unknown option '{}'.\n", arg); + con::error("Unknown option '{}'.", arg); return false; } @@ -89,7 +90,7 @@ bool ArgumentParser::ParseArguments(std::vector& args) { if (!matchedOption->m_multi_use) { - std::cout << std::format("Option '{}' already specified.\n", arg); + con::error("Option '{}' already specified.", arg); return false; } } @@ -101,7 +102,7 @@ bool ArgumentParser::ParseArguments(std::vector& args) const auto parameterCount = static_cast(matchedOption->m_parameters.size()); if (argIndex + parameterCount >= argCount) { - std::cout << std::format("Not enough parameters for option '{}'.\n", arg); + con::error("Not enough parameters for option '{}'.", arg); return false; } @@ -112,7 +113,7 @@ bool ArgumentParser::ParseArguments(std::vector& args) if (param.compare(0, std::char_traits::length(PREFIX_SHORT), PREFIX_SHORT) == 0) { - std::cout << std::format("Not enough parameters for option '{}'.\n", arg); + con::error("Not enough parameters for option '{}'.", arg); return false; } diff --git a/src/Utils/Utils/Arguments/UsageInformation.cpp b/src/Utils/Utils/Arguments/UsageInformation.cpp index 28d9097e..e3f393a5 100644 --- a/src/Utils/Utils/Arguments/UsageInformation.cpp +++ b/src/Utils/Utils/Arguments/UsageInformation.cpp @@ -1,5 +1,7 @@ #include "UsageInformation.h" +#include "Utils/Logging/Log.h" + #include #include #include @@ -105,7 +107,7 @@ void UsageInformation::Print() } } - std::cout << str.str() << "\n"; + con::info(str.str()); } void UsageInformation::PrintUsageOverview(std::stringstream& str) diff --git a/src/Utils/Utils/Logging/Log.cpp b/src/Utils/Utils/Logging/Log.cpp new file mode 100644 index 00000000..eee3d6d3 --- /dev/null +++ b/src/Utils/Utils/Logging/Log.cpp @@ -0,0 +1,42 @@ +#include "Log.h" + +#include +#include + +namespace con +{ + LogLevel globalLogLevel = LogLevel::INFO; + bool globalUseColor = true; + + void _debug_internal(const std::string& str) + { + if (globalUseColor) + std::cout << std::format("\x1B[90m{}\x1B[0m\n", str); + else + std::cout << std::format("{}\n", str); + } + + void _info_internal(const std::string& str) + { + if (globalUseColor) + std::cout << std::format("\x1B[37m{}\x1B[0m\n", str); + else + std::cout << std::format("{}\n", str); + } + + void _warn_internal(const std::string& str) + { + if (globalUseColor) + std::cout << std::format("\x1B[33m{}\x1B[0m\n", str); + else + std::cout << std::format("{}\n", str); + } + + void _error_internal(const std::string& str) + { + if (globalUseColor) + std::cerr << std::format("\x1B[31m{}\x1B[0m\n", str); + else + std::cerr << std::format("{}\n", str); + } +} // namespace con diff --git a/src/Utils/Utils/Logging/Log.h b/src/Utils/Utils/Logging/Log.h new file mode 100644 index 00000000..2cd2f746 --- /dev/null +++ b/src/Utils/Utils/Logging/Log.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +namespace con +{ + enum class LogLevel + { + DEBUG = 0, + INFO, + WARN, + ERROR + }; + + extern LogLevel globalLogLevel; + extern bool globalUseColor; + + void _debug_internal(const std::string& str); + void _info_internal(const std::string& str); + void _warn_internal(const std::string& str); + void _error_internal(const std::string& str); + + inline void debug(const std::string& str) + { + if (static_cast(globalLogLevel) > static_cast(LogLevel::DEBUG)) + return; + _debug_internal(str); + } + + template void debug(std::format_string fmt, Arg0&& arg0, OtherArgs&&... otherArgs) + { + if (static_cast(globalLogLevel) > static_cast(LogLevel::DEBUG)) + return; + _debug_internal(std::vformat(fmt.get(), std::make_format_args(arg0, otherArgs...))); + } + + inline void info(const std::string& str) + { + if (static_cast(globalLogLevel) > static_cast(LogLevel::INFO)) + return; + _info_internal(str); + } + + template void info(std::format_string fmt, Arg0&& arg0, OtherArgs&&... otherArgs) + { + if (static_cast(globalLogLevel) > static_cast(LogLevel::INFO)) + return; + _info_internal(std::vformat(fmt.get(), std::make_format_args(arg0, otherArgs...))); + } + + inline void warn(const std::string& str) + { + if (static_cast(globalLogLevel) > static_cast(LogLevel::WARN)) + return; + _warn_internal(str); + } + + template void warn(std::format_string fmt, Arg0&& arg0, OtherArgs&&... otherArgs) + { + if (static_cast(globalLogLevel) > static_cast(LogLevel::WARN)) + return; + _warn_internal(std::vformat(fmt.get(), std::make_format_args(arg0, otherArgs...))); + } + + inline void error(const std::string& str) + { + _error_internal(str); + } + + template void error(std::format_string fmt, Arg0&& arg0, OtherArgs&&... otherArgs) + { + _error_internal(std::vformat(fmt.get(), std::make_format_args(arg0, otherArgs...))); + } +} // namespace con diff --git a/src/ZoneCode.lua b/src/ZoneCode.lua index 339a7e9d..1d418539 100644 --- a/src/ZoneCode.lua +++ b/src/ZoneCode.lua @@ -305,6 +305,7 @@ function ZoneCode:project() buildmessage "Generating ZoneCode for game %{file.basename}" buildcommands { '"' .. TargetDirectoryBuildTools .. '/' .. ExecutableByOs('ZoneCodeGenerator') .. '"' + .. ' --no-color' .. ' -h "' .. path.join(path.getabsolute(ProjectFolder()), 'ZoneCode/Game/%{file.basename}/%{file.basename}_ZoneCode.h') .. '"' .. ' -c "' .. path.join(path.getabsolute(ProjectFolder()), 'ZoneCode/Game/%{file.basename}/%{file.basename}_Commands.txt') .. '"' .. ' -o "%{wks.location}/src/ZoneCode/Game/%{file.basename}/XAssets"' diff --git a/src/ZoneCode/Game/IW3/XAssets/XModel.txt b/src/ZoneCode/Game/IW3/XAssets/XModel.txt index 72538b90..f044e586 100644 --- a/src/ZoneCode/Game/IW3/XAssets/XModel.txt +++ b/src/ZoneCode/Game/IW3/XAssets/XModel.txt @@ -12,7 +12,8 @@ set count parentList numBones - numRootBones; set reusable quats; set count quats numBones - numRootBones; set reusable trans; -set count trans numBones - numRootBones; +// This is actually the count but it looks like a bug? It is used like a vec3, but it takes as much memory as vec4 +set count trans (numBones - numRootBones) * 4; set reusable partClassification; set count partClassification numBones; set reusable baseMat; diff --git a/src/ZoneCode/Game/IW4/XAssets/XModel.txt b/src/ZoneCode/Game/IW4/XAssets/XModel.txt index 6e499781..0870c225 100644 --- a/src/ZoneCode/Game/IW4/XAssets/XModel.txt +++ b/src/ZoneCode/Game/IW4/XAssets/XModel.txt @@ -12,7 +12,7 @@ set count parentList numBones - numRootBones; set reusable quats; set count quats numBones - numRootBones; set reusable trans; -set count trans numBones - numRootBones; +set count trans (numBones - numRootBones) * 3; set reusable partClassification; set count partClassification numBones; set reusable baseMat; diff --git a/src/ZoneCodeGeneratorLib/Generating/CodeGenerator.cpp b/src/ZoneCodeGeneratorLib/Generating/CodeGenerator.cpp index 2b1d9bc6..38064100 100644 --- a/src/ZoneCodeGeneratorLib/Generating/CodeGenerator.cpp +++ b/src/ZoneCodeGeneratorLib/Generating/CodeGenerator.cpp @@ -5,6 +5,7 @@ #include "Templates/ZoneLoadTemplate.h" #include "Templates/ZoneMarkTemplate.h" #include "Templates/ZoneWriteTemplate.h" +#include "Utils/Logging/Log.h" #include #include @@ -42,7 +43,7 @@ bool CodeGenerator::GenerateCodeForTemplate(const RenderingContext& context, ICo if (!stream.is_open()) { - std::cerr << std::format("Failed to open file '{}'\n", p.string()); + con::error("Failed to open file '{}'", p.string()); return false; } @@ -59,7 +60,7 @@ bool CodeGenerator::GetAssetWithName(IDataRepository* repository, const std::str auto* def = repository->GetDataDefinitionByName(name); if (def == nullptr) { - std::cerr << std::format("Could not find type with name '{}'\n", name); + con::error("Could not find type with name '{}'", name); return false; } @@ -67,13 +68,13 @@ bool CodeGenerator::GetAssetWithName(IDataRepository* repository, const std::str asset = defWithMembers != nullptr ? repository->GetInformationFor(defWithMembers) : nullptr; if (asset == nullptr) { - std::cerr << std::format("Could not find type with name '{}'\n", name); + con::error("Could not find type with name '{}'", name); return false; } if (!StructureComputations(asset).IsAsset()) { - std::cerr << std::format("Type is not an asset '{}'\n", name); + con::error("Type is not an asset '{}'", name); return false; } @@ -101,7 +102,7 @@ bool CodeGenerator::GenerateCode(IDataRepository* repository) const auto foundTemplate = m_template_mapping.find(templateName); if (foundTemplate == m_template_mapping.end()) { - std::cerr << std::format("Unknown template '{}'.\n", generationTask.m_template_name); + con::error("Unknown template '{}'.", generationTask.m_template_name); return false; } @@ -112,13 +113,11 @@ bool CodeGenerator::GenerateCode(IDataRepository* repository) auto context = RenderingContext::BuildContext(repository, asset); if (!GenerateCodeForTemplate(*context, foundTemplate->second.get())) { - std::cerr << std::format( - "Failed to generate code for asset '{}' with preset '{}'\n", asset->m_definition->GetFullName(), foundTemplate->first); + con::error("Failed to generate code for asset '{}' with preset '{}'", asset->m_definition->GetFullName(), foundTemplate->first); return false; } - std::cout << std::format( - "Successfully generated code for asset '{}' with preset '{}'\n", asset->m_definition->GetFullName(), foundTemplate->first); + con::info("Successfully generated code for asset '{}' with preset '{}'", asset->m_definition->GetFullName(), foundTemplate->first); } } else @@ -133,8 +132,7 @@ bool CodeGenerator::GenerateCode(IDataRepository* repository) } } const auto end = std::chrono::steady_clock::now(); - if (m_args->m_verbose) - std::cout << std::format("Generating code took {}ms\n", std::chrono::duration_cast(end - start).count()); + con::debug("Generating code took {}ms", std::chrono::duration_cast(end - start).count()); return true; } diff --git a/src/ZoneCodeGeneratorLib/Parsing/Commands/CommandsFileReader.cpp b/src/ZoneCodeGeneratorLib/Parsing/Commands/CommandsFileReader.cpp index 449ead72..a92a8614 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Commands/CommandsFileReader.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/Commands/CommandsFileReader.cpp @@ -13,6 +13,7 @@ #include "Parsing/PostProcessing/MemberLeafsPostProcessor.h" #include "Parsing/PostProcessing/UnionsPostProcessor.h" #include "Parsing/PostProcessing/UsagesPostProcessor.h" +#include "Utils/Logging/Log.h" #include #include @@ -37,7 +38,7 @@ bool CommandsFileReader::OpenBaseStream() auto stream = std::make_unique(m_filename); if (!stream->IsOpen()) { - std::cerr << "Could not open commands file\n"; + con::error("Could not open commands file"); return false; } @@ -74,10 +75,7 @@ void CommandsFileReader::SetupPostProcessors() bool CommandsFileReader::ReadCommandsFile(IDataRepository* repository) { - if (m_args->m_verbose) - { - std::cout << std::format("Reading commands file: {}\n", m_filename); - } + con::debug("Reading commands file: {}", m_filename); if (!OpenBaseStream()) return false; @@ -91,8 +89,7 @@ bool CommandsFileReader::ReadCommandsFile(IDataRepository* repository) const auto result = parser->Parse(); const auto end = std::chrono::steady_clock::now(); - if (m_args->m_verbose) - std::cout << std::format("Processing commands took {}ms\n", std::chrono::duration_cast(end - start).count()); + con::debug("Processing commands took {}ms", std::chrono::duration_cast(end - start).count()); if (!result) return false; diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/HeaderFileReader.cpp b/src/ZoneCodeGeneratorLib/Parsing/Header/HeaderFileReader.cpp index 64ba8d46..6f548feb 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/HeaderFileReader.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/HeaderFileReader.cpp @@ -10,6 +10,7 @@ #include "Parsing/PostProcessing/CreateMemberInformationPostProcessor.h" #include "Parsing/PostProcessing/CreateStructureInformationPostProcessor.h" #include "Parsing/PostProcessing/IPostProcessor.h" +#include "Utils/Logging/Log.h" #include #include @@ -36,7 +37,7 @@ bool HeaderFileReader::OpenBaseStream() auto stream = std::make_unique(m_filename); if (!stream->IsOpen()) { - std::cerr << "Could not open header file\n"; + con::error("Could not open header file"); return false; } @@ -71,8 +72,7 @@ void HeaderFileReader::SetupPostProcessors() bool HeaderFileReader::ReadHeaderFile(IDataRepository* repository) { - if (m_args->m_verbose) - std::cout << std::format("Reading header file: {}\n", m_filename); + con::debug("Reading header file: {}", m_filename); if (!OpenBaseStream()) return false; @@ -88,8 +88,7 @@ bool HeaderFileReader::ReadHeaderFile(IDataRepository* repository) result = parser->SaveToRepository(repository); const auto end = std::chrono::steady_clock::now(); - if (m_args->m_verbose) - std::cout << std::format("Processing header took {}ms\n", std::chrono::duration_cast(end - start).count()); + con::debug("Processing header took {}ms", std::chrono::duration_cast(end - start).count()); if (!result) return false; diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderParserState.cpp b/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderParserState.cpp index e26d68ad..40bc1b87 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderParserState.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderParserState.cpp @@ -2,6 +2,7 @@ #include "Domain/Definition/EnumDefinition.h" #include "Parsing/Header/Block/HeaderBlockNone.h" +#include "Utils/Logging/Log.h" #include #include @@ -97,7 +98,7 @@ bool HeaderParserState::ResolveForwardDeclarations() if (dataDefinition == nullptr) { - std::cerr << std::format("Forward declaration \"{}\" was not defined\n", forwardDeclaration->GetFullName()); + con::error("Forward declaration \"{}\" was not defined", forwardDeclaration->GetFullName()); return false; } diff --git a/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/CalculateSizeAndAlignPostProcessor.cpp b/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/CalculateSizeAndAlignPostProcessor.cpp index e6b9fdc3..65032723 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/CalculateSizeAndAlignPostProcessor.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/CalculateSizeAndAlignPostProcessor.cpp @@ -2,6 +2,7 @@ #include "Domain/Definition/ArrayDeclarationModifier.h" #include "Utils/Alignment.h" +#include "Utils/Logging/Log.h" #include #include @@ -189,7 +190,7 @@ namespace return true; if (structDefinition->m_flags & DefinitionWithMembers::FLAG_FIELDS_CALCULATING) { - std::cerr << "Detected circular dependency:\n"; + con::error("Detected circular dependency:"); return false; } @@ -212,7 +213,7 @@ namespace return true; if (unionDefinition->m_flags & DefinitionWithMembers::FLAG_FIELDS_CALCULATING) { - std::cerr << "Detected circular dependency:\n"; + con::error("Detected circular dependency:"); return false; } @@ -257,35 +258,28 @@ bool CalculateSizeAndAlignPostProcessor::PostProcess(IDataRepository* repository { if (repository->GetArchitecture() == Architecture::UNKNOWN) { - std::cerr << "You must set an architecture!\n"; + con::error("You must set an architecture!"); return false; } for (auto* structDefinition : repository->GetAllStructs()) { if (!CalculateFields(repository, structDefinition)) - { - std::cout << "\n"; + return false; - } } for (auto* unionDefinition : repository->GetAllUnions()) { if (!CalculateFields(repository, unionDefinition)) - { - std::cout << "\n"; + return false; - } } for (auto* typedefDeclaration : repository->GetAllTypedefs()) { if (!CalculateFields(repository, typedefDeclaration->m_type_declaration.get())) - { - std::cout << "\n"; return false; - } } return true; diff --git a/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/UnionsPostProcessor.cpp b/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/UnionsPostProcessor.cpp index 83283aae..bcd8d0e9 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/UnionsPostProcessor.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/UnionsPostProcessor.cpp @@ -1,5 +1,7 @@ #include "UnionsPostProcessor.h" +#include "Utils/Logging/Log.h" + #include #include #include @@ -24,7 +26,7 @@ namespace if (entriesWithoutConditionCount > 1 && !info->m_usages.empty() && !info->m_is_leaf) { - std::cerr << std::format("Union '{}' has more than one entry without a condition!\n", info->m_definition->GetFullName()); + con::error("Union '{}' has more than one entry without a condition!", info->m_definition->GetFullName()); return false; } diff --git a/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.cpp b/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.cpp index 306c58c1..f508ba59 100644 --- a/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.cpp +++ b/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.cpp @@ -3,6 +3,7 @@ #include "GitVersion.h" #include "Utils/Arguments/CommandLineOption.h" #include "Utils/Arguments/UsageInformation.h" +#include "Utils/Logging/Log.h" #include #include @@ -29,6 +30,12 @@ const CommandLineOption* const OPTION_VERBOSE = .WithDescription("Outputs a lot more and more detailed messages.") .Build(); +const CommandLineOption* const OPTION_NO_COLOR = + CommandLineOption::Builder::Create() + .WithLongName("no-color") + .WithDescription("Disables colored terminal output.") + .Build(); + constexpr auto CATEGORY_INPUT = "Input"; const CommandLineOption* const OPTION_HEADER = @@ -86,6 +93,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ OPTION_HELP, OPTION_VERSION, OPTION_VERBOSE, + OPTION_NO_COLOR, OPTION_HEADER, OPTION_COMMANDS_FILE, OPTION_OUTPUT_FOLDER, @@ -121,7 +129,6 @@ ZoneCodeGeneratorArguments::ZoneCodeGeneratorArguments() : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v), m_task_flags(0u) { - m_verbose = false; } void ZoneCodeGeneratorArguments::PrintUsage() const @@ -136,7 +143,7 @@ void ZoneCodeGeneratorArguments::PrintUsage() const void ZoneCodeGeneratorArguments::PrintVersion() { - std::cout << std::format("OpenAssetTools ZoneCodeGenerator {}\n", GIT_VERSION); + con::info("OpenAssetTools ZoneCodeGenerator {}", GIT_VERSION); } bool ZoneCodeGeneratorArguments::ParseArgs(const int argc, const char** argv, bool& shouldContinue) @@ -165,7 +172,13 @@ bool ZoneCodeGeneratorArguments::ParseArgs(const int argc, const char** argv, bo } // -v; --verbose - m_verbose = m_argument_parser.IsOptionSpecified(OPTION_VERBOSE); + if (m_argument_parser.IsOptionSpecified(OPTION_VERBOSE)) + con::globalLogLevel = con::LogLevel::DEBUG; + else + con::globalLogLevel = con::LogLevel::INFO; + + // --no-color + con::globalUseColor = !m_argument_parser.IsOptionSpecified(OPTION_NO_COLOR); // -p; --print if (m_argument_parser.IsOptionSpecified(OPTION_PRINT)) @@ -185,7 +198,7 @@ bool ZoneCodeGeneratorArguments::ParseArgs(const int argc, const char** argv, bo } else { - std::cout << "At least one header file must be specified via -h / --header.\n"; + con::error("At least one header file must be specified via -h / --header."); return false; } @@ -197,7 +210,7 @@ bool ZoneCodeGeneratorArguments::ParseArgs(const int argc, const char** argv, bo } else { - std::cout << "At least one commands file must be specified via -c / --commands-file.\n"; + con::error("At least one commands file must be specified via -c / --commands-file."); return false; } @@ -219,7 +232,7 @@ bool ZoneCodeGeneratorArguments::ParseArgs(const int argc, const char** argv, bo if (m_task_flags == 0) { - std::cout << "There was no output task specified.\n"; + con::warn("There was no output task specified."); PrintUsage(); return false; } diff --git a/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.h b/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.h index 601a7042..fa93a7f2 100644 --- a/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.h +++ b/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.h @@ -26,8 +26,6 @@ public: [[nodiscard]] bool ShouldGenerate() const; [[nodiscard]] bool ShouldPrint() const; - bool m_verbose; - std::vector m_header_paths; std::vector m_command_paths; std::string m_output_directory; diff --git a/src/ZoneCommon.lua b/src/ZoneCommon.lua index 3f005f49..bd887778 100644 --- a/src/ZoneCommon.lua +++ b/src/ZoneCommon.lua @@ -20,6 +20,7 @@ function ZoneCommon:link(links) links:linkto(ObjCommon) links:linkto(Parser) links:linkto(Utils) + links:linkto(lzx) end function ZoneCommon:use() @@ -45,5 +46,6 @@ function ZoneCommon:project() path.join(folder, "ZoneCommon/**.cpp") } + lzx:include(includes) self:include(includes) end diff --git a/src/ZoneCommon/Game/IW3/GameAssetPoolIW3.h b/src/ZoneCommon/Game/IW3/GameAssetPoolIW3.h index 8f6b3bf9..9d675501 100644 --- a/src/ZoneCommon/Game/IW3/GameAssetPoolIW3.h +++ b/src/ZoneCommon/Game/IW3/GameAssetPoolIW3.h @@ -14,9 +14,6 @@ public: std::unique_ptr> m_xanim_parts; std::unique_ptr> m_xmodel; std::unique_ptr> m_material; - std::unique_ptr> m_material_pixel_shader; - std::unique_ptr> m_material_vertex_shader; - std::unique_ptr> m_material_vertex_decl; std::unique_ptr> m_technique_set; std::unique_ptr> m_image; std::unique_ptr> m_sound; diff --git a/src/ZoneCommon/Game/T6/ZoneConstantsT6.h b/src/ZoneCommon/Game/T6/ZoneConstantsT6.h index 10213294..37ea4224 100644 --- a/src/ZoneCommon/Game/T6/ZoneConstantsT6.h +++ b/src/ZoneCommon/Game/T6/ZoneConstantsT6.h @@ -13,20 +13,24 @@ namespace T6 public: static constexpr const char* MAGIC_SIGNED_TREYARCH = "TAff0100"; + static constexpr const char* MAGIC_SIGNED_LZX_TREYARCH = "TAffx100"; static constexpr const char* MAGIC_SIGNED_OAT = "ABff0100"; static constexpr const char* MAGIC_UNSIGNED = "TAffu100"; static constexpr const char* MAGIC_UNSIGNED_SERVER = "TAsvu100"; static_assert(std::char_traits::length(MAGIC_SIGNED_TREYARCH) == sizeof(ZoneHeader::m_magic)); + static_assert(std::char_traits::length(MAGIC_SIGNED_LZX_TREYARCH) == sizeof(ZoneHeader::m_magic)); static_assert(std::char_traits::length(MAGIC_SIGNED_OAT) == sizeof(ZoneHeader::m_magic)); static_assert(std::char_traits::length(MAGIC_UNSIGNED) == sizeof(ZoneHeader::m_magic)); static_assert(std::char_traits::length(MAGIC_UNSIGNED_SERVER) == sizeof(ZoneHeader::m_magic)); - static constexpr int ZONE_VERSION = 147; - static constexpr int STREAM_COUNT = 4; - static constexpr int XCHUNK_SIZE = 0x8000; - static constexpr int XCHUNK_MAX_WRITE_SIZE = XCHUNK_SIZE - 0x40; - static constexpr int VANILLA_BUFFER_SIZE = 0x80000; + static constexpr unsigned ZONE_VERSION_PC = 147; + static constexpr unsigned ZONE_VERSION_XENON = 146; + + static constexpr unsigned STREAM_COUNT = 4; + static constexpr unsigned XCHUNK_SIZE = 0x8000; + static constexpr unsigned XCHUNK_MAX_WRITE_SIZE = XCHUNK_SIZE - 0x40; + static constexpr unsigned VANILLA_BUFFER_SIZE = 0x80000; static constexpr unsigned OFFSET_BLOCK_BIT_COUNT = 3u; static constexpr block_t INSERT_BLOCK = XFILE_BLOCK_VIRTUAL; @@ -34,10 +38,14 @@ namespace T6 static constexpr size_t FILE_SUFFIX_ZERO_ALIGN = 0x40; static constexpr const char* MAGIC_AUTH_HEADER = "PHEEBs71"; - inline static const uint8_t SALSA20_KEY_TREYARCH[]{ + inline static const uint8_t SALSA20_KEY_TREYARCH_PC[]{ 0x64, 0x1D, 0x8A, 0x2F, 0xE3, 0x1D, 0x3A, 0xA6, 0x36, 0x22, 0xBB, 0xC9, 0xCE, 0x85, 0x87, 0x22, 0x9D, 0x42, 0xB0, 0xF8, 0xED, 0x9B, 0x92, 0x41, 0x30, 0xBF, 0x88, 0xB6, 0x5E, 0xDC, 0x50, 0xBE, }; + inline static const uint8_t SALSA20_KEY_TREYARCH_XENON[]{ + 0x0E, 0x50, 0xF4, 0x9F, 0x41, 0x23, 0x17, 0x09, 0x60, 0x38, 0x66, 0x56, 0x22, 0xDD, 0x09, 0x13, + 0x32, 0xA2, 0x09, 0xBA, 0x0A, 0x05, 0xA0, 0x0E, 0x13, 0x77, 0xCE, 0xDB, 0x0A, 0x3C, 0xB1, 0xD3, + }; inline static const uint8_t RSA_PUBLIC_KEY_TREYARCH[]{ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc7, 0x9d, 0x33, 0xe0, 0x75, 0xaf, 0xef, 0x08, 0x08, 0x2b, 0x89, 0xd9, 0x3b, 0xf3, diff --git a/src/ZoneCommon/Zone/AssetList/AssetListReader.cpp b/src/ZoneCommon/Zone/AssetList/AssetListReader.cpp index fbc616bc..84ee2270 100644 --- a/src/ZoneCommon/Zone/AssetList/AssetListReader.cpp +++ b/src/ZoneCommon/Zone/AssetList/AssetListReader.cpp @@ -2,6 +2,7 @@ #include "Csv/CsvStream.h" #include "Game/IGame.h" +#include "Utils/Logging/Log.h" #include "Zone/AssetNameResolver.h" #include @@ -35,7 +36,7 @@ namespace const auto maybeType = m_asset_name_resolver->GetAssetTypeByName(typeName); if (!maybeType) { - std::cerr << std::format("Unknown asset type name \"{}\"\n", typeName); + con::error("Unknown asset type name \"{}\"", typeName); if (failure) *failure = true; return false; @@ -90,7 +91,7 @@ std::optional AssetListReader::ReadAssetList(const std::string& zoneN return assetList; } else if (logMissing) - std::cerr << std::format("Failed to open file for assetlist: {}\n", assetListFileName); + con::error("Failed to open file for assetlist: {}", assetListFileName); return std::nullopt; } diff --git a/src/ZoneCommon/Zone/Definition/Parsing/Sequence/SequenceZoneDefinitionMetaData.cpp b/src/ZoneCommon/Zone/Definition/Parsing/Sequence/SequenceZoneDefinitionMetaData.cpp index 1ffa80d8..f60b2f20 100644 --- a/src/ZoneCommon/Zone/Definition/Parsing/Sequence/SequenceZoneDefinitionMetaData.cpp +++ b/src/ZoneCommon/Zone/Definition/Parsing/Sequence/SequenceZoneDefinitionMetaData.cpp @@ -1,5 +1,6 @@ #include "SequenceZoneDefinitionMetaData.h" +#include "Utils/Logging/Log.h" #include "Utils/StringUtils.h" #include "Zone/Definition/Parsing/Matcher/ZoneDefinitionMatcherFactory.h" @@ -114,11 +115,11 @@ namespace } const auto keyPos = keyToken.GetPos(); - std::cerr << std::format("Warning: {} L{}: Zone definition \">type,{}\" is deprecated and should be removed. {}\n", - keyPos.m_filename.get(), - keyPos.m_line, - keyToken.FieldValue(), - deprecationSuggestedAction); + con::error("Warning: {} L{}: Zone definition \">type,{}\" is deprecated and should be removed. {}", + keyPos.m_filename.get(), + keyPos.m_line, + keyToken.FieldValue(), + deprecationSuggestedAction); } } // namespace diff --git a/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp b/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp index 4b54c7cc..d8df6641 100644 --- a/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp +++ b/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp @@ -4,6 +4,7 @@ #include "Parsing/Impl/DefinesStreamProxy.h" #include "Parsing/Impl/IncludingStreamProxy.h" #include "Parsing/Impl/ParserSingleInputStream.h" +#include "Utils/Logging/Log.h" #include "Zone/Definition/Parsing/ZoneDefinitionLexer.h" #include "Zone/Definition/Parsing/ZoneDefinitionParser.h" @@ -44,7 +45,7 @@ void ZoneDefinitionInputStream::SetPreviouslySetGame(GameId game) std::unique_ptr ZoneDefinitionInputStream::ReadDefinition() { - std::cout << std::format("Reading zone definition file: {}\n", m_file_name); + con::info("Reading zone definition file: {}", m_file_name); const auto lexer = std::make_unique(m_stream); const auto parser = std::make_unique(lexer.get(), m_target_name, m_search_path, *m_stream, m_previously_set_game); @@ -55,7 +56,7 @@ std::unique_ptr ZoneDefinitionInputStream::ReadDefinition() definition = parser->GetParsedValue(); const auto end = std::chrono::steady_clock::now(); - std::cout << std::format("Processing zone definition took {}ms\n", std::chrono::duration_cast(end - start).count()); + con::info("Processing zone definition took {}ms", std::chrono::duration_cast(end - start).count()); return std::move(definition); } diff --git a/src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.cpp b/src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.cpp index 377a74be..01968187 100644 --- a/src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.cpp +++ b/src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.cpp @@ -1,25 +1,18 @@ #include "AbstractSalsa20Processor.h" #include +#include -AbstractSalsa20Processor::AbstractSalsa20Processor(const int streamCount, const std::string& zoneName, const uint8_t* salsa20Key, const size_t keySize) +AbstractSalsa20Processor::AbstractSalsa20Processor(const unsigned streamCount, const std::string& zoneName, const uint8_t* salsa20Key, const size_t keySize) : m_stream_count(streamCount), - m_stream_contexts(std::make_unique(streamCount)), - m_stream_block_indices(std::make_unique(streamCount)) + m_stream_contexts(streamCount), + m_block_hashes(BLOCK_HASHES_COUNT * streamCount * SHA1_HASH_SIZE), + m_stream_block_indices(streamCount) { - m_block_hashes = std::make_unique(BLOCK_HASHES_COUNT * streamCount * SHA1_HASH_SIZE); InitStreams(zoneName, salsa20Key, keySize); } -uint8_t* AbstractSalsa20Processor::GetHashBlock(const int streamNumber) const -{ - const auto blockIndexOffset = m_stream_block_indices[streamNumber] * m_stream_count * SHA1_HASH_SIZE; - const auto streamOffset = static_cast(streamNumber) * SHA1_HASH_SIZE; - - return &m_block_hashes[blockIndexOffset + streamOffset]; -} - -void AbstractSalsa20Processor::InitStreams(const std::string& zoneName, const uint8_t* salsa20Key, const size_t keySize) const +void AbstractSalsa20Processor::InitStreams(const std::string& zoneName, const uint8_t* salsa20Key, const size_t keySize) { // Original buffer must have been 32 bytes because the zoneName can at most be 31 characters be long before being cut off const auto zoneNameLength = std::min(zoneName.length(), 31uz); @@ -29,14 +22,14 @@ void AbstractSalsa20Processor::InitStreams(const std::string& zoneName, const ui assert(blockHashBufferSize % 4 == 0); size_t zoneNameOffset = 0; - for (size_t i = 0; i < blockHashBufferSize; i += 4) + for (auto i = 0uz; i < blockHashBufferSize; i += 4) { - *reinterpret_cast(&m_block_hashes[i]) = 0x1010101 * zoneName[zoneNameOffset++]; + memset(&m_block_hashes[i], zoneName[zoneNameOffset++], 4u); zoneNameOffset %= zoneNameLength; } - for (auto stream = 0; stream < m_stream_count; stream++) + for (auto stream = 0u; stream < m_stream_count; stream++) { m_stream_block_indices[stream] = 0; @@ -45,11 +38,19 @@ void AbstractSalsa20Processor::InitStreams(const std::string& zoneName, const ui } } +uint8_t* AbstractSalsa20Processor::GetHashBlock(const unsigned streamNumber) +{ + const auto blockIndexOffset = m_stream_block_indices[streamNumber] * m_stream_count * SHA1_HASH_SIZE; + const auto streamOffset = static_cast(streamNumber) * SHA1_HASH_SIZE; + + return &m_block_hashes[blockIndexOffset + streamOffset]; +} + void AbstractSalsa20Processor::GetCapturedData(const uint8_t** pCapturedData, size_t* pSize) { assert(pCapturedData != nullptr); assert(pSize != nullptr); - *pCapturedData = m_block_hashes.get(); + *pCapturedData = m_block_hashes.data(); *pSize = BLOCK_HASHES_COUNT * m_stream_count * SHA1_HASH_SIZE; } diff --git a/src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.h b/src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.h index 01d56a63..d4b3775d 100644 --- a/src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.h +++ b/src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.h @@ -1,40 +1,22 @@ #pragma once #include "Cryptography.h" -#include "Utils/ClassUtils.h" #include "Utils/ICapturedDataProvider.h" #include #include #include +#include + +class Salsa20StreamContext +{ +public: + std::unique_ptr m_salsa20; + std::unique_ptr m_sha1; +}; class AbstractSalsa20Processor : public ICapturedDataProvider { -protected: - static constexpr int BLOCK_HASHES_COUNT = 200; - static constexpr int SHA1_HASH_SIZE = 20; - static constexpr int SALSA20_IV_SIZE = 8; - - class StreamContext - { - public: - std::unique_ptr m_salsa20; - std::unique_ptr m_sha1; - }; - - int m_stream_count; - std::unique_ptr m_stream_contexts; - - // m_block_hashes[BLOCK_HASHES_COUNT][numStreams][HASH_SIZE] - std::unique_ptr m_block_hashes; - std::unique_ptr m_stream_block_indices; - - AbstractSalsa20Processor(int streamCount, const std::string& zoneName, const uint8_t* salsa20Key, size_t keySize); - - _NODISCARD uint8_t* GetHashBlock(int streamNumber) const; - - void InitStreams(const std::string& zoneName, const uint8_t* salsa20Key, size_t keySize) const; - public: virtual ~AbstractSalsa20Processor() = default; AbstractSalsa20Processor(const AbstractSalsa20Processor& other) = delete; @@ -43,4 +25,21 @@ public: AbstractSalsa20Processor& operator=(AbstractSalsa20Processor&& other) noexcept = default; void GetCapturedData(const uint8_t** pCapturedData, size_t* pSize) override; + +protected: + static constexpr auto BLOCK_HASHES_COUNT = 200u; + static constexpr auto SHA1_HASH_SIZE = 20u; + static constexpr auto SALSA20_IV_SIZE = 8u; + + AbstractSalsa20Processor(unsigned streamCount, const std::string& zoneName, const uint8_t* salsa20Key, size_t keySize); + void InitStreams(const std::string& zoneName, const uint8_t* salsa20Key, size_t keySize); + + [[nodiscard]] uint8_t* GetHashBlock(unsigned streamNumber); + + unsigned m_stream_count; + std::vector m_stream_contexts; + + // m_block_hashes[BLOCK_HASHES_COUNT][numStreams][SHA1_HASH_SIZE] + std::vector m_block_hashes; + std::vector m_stream_block_indices; }; diff --git a/src/ZoneCommon/Zone/XChunk/IXChunkProcessor.h b/src/ZoneCommon/Zone/XChunk/IXChunkProcessor.h index 22e444b1..bcc744ff 100644 --- a/src/ZoneCommon/Zone/XChunk/IXChunkProcessor.h +++ b/src/ZoneCommon/Zone/XChunk/IXChunkProcessor.h @@ -6,6 +6,12 @@ class IXChunkProcessor { public: + IXChunkProcessor() = default; virtual ~IXChunkProcessor() = default; - virtual size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) = 0; + IXChunkProcessor(const IXChunkProcessor& other) = default; + IXChunkProcessor(IXChunkProcessor&& other) noexcept = default; + IXChunkProcessor& operator=(const IXChunkProcessor& other) = default; + IXChunkProcessor& operator=(IXChunkProcessor&& other) noexcept = default; + + virtual size_t Process(unsigned streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) = 0; }; diff --git a/src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.cpp b/src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.cpp index 5cff8933..ed546ab3 100644 --- a/src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.cpp +++ b/src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.cpp @@ -6,7 +6,7 @@ #include #include -size_t XChunkProcessorDeflate::Process(int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize) +size_t XChunkProcessorDeflate::Process(unsigned streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize) { z_stream stream{}; stream.zalloc = Z_NULL; diff --git a/src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.h b/src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.h index 7c158c86..9dda1869 100644 --- a/src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.h +++ b/src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.h @@ -4,5 +4,5 @@ class XChunkProcessorDeflate final : public IXChunkProcessor { public: - size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override; + size_t Process(unsigned streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override; }; diff --git a/src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.cpp b/src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.cpp index bc8420ff..9d1058ce 100644 --- a/src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.cpp +++ b/src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.cpp @@ -1,11 +1,14 @@ #include "XChunkProcessorInflate.h" +#include "Utils/Logging/Log.h" #include "XChunkException.h" +#include +#include #include #include -size_t XChunkProcessorInflate::Process(int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize) +size_t XChunkProcessorInflate::Process(unsigned streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize) { z_stream stream{}; stream.zalloc = Z_NULL; @@ -23,7 +26,10 @@ size_t XChunkProcessorInflate::Process(int streamNumber, const uint8_t* input, c ret = inflate(&stream, Z_FULL_FLUSH); if (ret != Z_STREAM_END) - throw XChunkException("Zone has invalid or unsupported compression. Inflate failed"); + { + con::error("inflate of stream failed with error code {}: {}", streamNumber, ret, stream.msg); + throw XChunkException(std::format("Zone has invalid or unsupported compression: {}", stream.msg)); + } const size_t outputSize = stream.total_out; diff --git a/src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.h b/src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.h index c328219f..c6e24a1c 100644 --- a/src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.h +++ b/src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.h @@ -4,5 +4,5 @@ class XChunkProcessorInflate final : public IXChunkProcessor { public: - size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override; + size_t Process(unsigned streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override; }; diff --git a/src/ZoneCommon/Zone/XChunk/XChunkProcessorLzxDecompress.cpp b/src/ZoneCommon/Zone/XChunk/XChunkProcessorLzxDecompress.cpp new file mode 100644 index 00000000..6778150e --- /dev/null +++ b/src/ZoneCommon/Zone/XChunk/XChunkProcessorLzxDecompress.cpp @@ -0,0 +1,134 @@ +#include "XChunkProcessorLzxDecompress.h" + +#include "Utils/Logging/Log.h" + +#include +#include +#include +#include + +namespace +{ + uint8_t NextByte(const uint8_t* input, size_t& offset, size_t& remainingSize) + { + const auto value = input[offset]; + offset++; + remainingSize--; + return value; + } + + uint16_t CombineHighLow(const uint8_t highByte, const uint8_t lowByte) + { + return static_cast(static_cast(static_cast(highByte) << 8u) | static_cast(lowByte)); + } + + void LogErrorHeaderSpace(size_t remainingInputSize) + { + con::error("XMemCompress: Not enough data for header: {}", remainingInputSize); + } +} // namespace + +XChunkProcessorLzxDecompress::XChunkProcessorLzxDecompress(const unsigned streamCount) + : m_lzx_states(streamCount) +{ + // T6 uses 17 for window bits + for (auto& lzxState : m_lzx_states) + lzxState = lzx_init(17); +} + +XChunkProcessorLzxDecompress::~XChunkProcessorLzxDecompress() +{ + for (auto* lzxState : m_lzx_states) + lzx_teardown(static_cast(lzxState)); +} + +size_t XChunkProcessorLzxDecompress::Process( + const unsigned streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize) +{ + auto* state = static_cast(m_lzx_states[streamNumber]); + + // lzx state is reset before each chunk + lzx_reset(state); + + size_t curInputOffset = 0uz; + size_t curInputSize = inputLength; + + size_t curOutputOffset = 0uz; + size_t curOutputSize = outputBufferSize; + + uint8_t lowByte; + uint16_t dstSize, srcSize; + + while (curInputSize > 0) + { + uint8_t highByte = NextByte(input, curInputOffset, curInputSize); + + uint8_t suffixSize; + if (highByte == 0xFF) // magic number: output is smaller than 0x8000 + { + if (curInputSize < 4) + { + LogErrorHeaderSpace(curInputSize); + return curOutputOffset; + } + + highByte = NextByte(input, curInputOffset, curInputSize); + lowByte = NextByte(input, curInputOffset, curInputSize); + dstSize = CombineHighLow(highByte, lowByte); + + highByte = NextByte(input, curInputOffset, curInputSize); + lowByte = NextByte(input, curInputOffset, curInputSize); + srcSize = CombineHighLow(highByte, lowByte); + + // The game seems to skip a 5 byte suffix after these blocks, not sure why. + suffixSize = 5u; + } + else + { + if (curInputSize < 1) + { + LogErrorHeaderSpace(curInputSize); + return curOutputOffset; + } + + dstSize = 0x8000u; + lowByte = NextByte(input, curInputOffset, curInputSize); + srcSize = CombineHighLow(highByte, lowByte); + + suffixSize = 0u; + } + + if (srcSize == 0 || dstSize == 0) + { + // Other implementations do not handle this as a failure, game code suggests otherwise though + con::error("XMemCompress: EOF: {} {}, {}", srcSize, dstSize, curInputSize); + return curOutputOffset; + } + + if (static_cast(srcSize) + suffixSize > curInputSize) + { + con::error("XMemCompress: block size bigger than remaining data: {} > {}", srcSize, curInputSize); + return curOutputOffset; + } + + if (dstSize > curOutputSize) + { + con::error("XMemCompress: output size bigger than remaining data: {} > {}", dstSize, curOutputSize); + return curOutputOffset; + } + + auto ret = lzx_decompress(state, &input[curInputOffset], &output[curOutputOffset], srcSize, dstSize); + curInputOffset += srcSize + suffixSize; + curInputSize -= (srcSize + suffixSize); + curOutputOffset += dstSize; + curOutputSize -= srcSize; + + if (ret != DECR_OK) + { + con::error("XMemCompress: lzx decompression failed: {}", ret); + return curOutputOffset; + } + } + + return curOutputOffset; +} diff --git a/src/ZoneCommon/Zone/XChunk/XChunkProcessorLzxDecompress.h b/src/ZoneCommon/Zone/XChunk/XChunkProcessorLzxDecompress.h new file mode 100644 index 00000000..e32ee92a --- /dev/null +++ b/src/ZoneCommon/Zone/XChunk/XChunkProcessorLzxDecompress.h @@ -0,0 +1,16 @@ +#pragma once + +#include "IXChunkProcessor.h" + +#include + +class XChunkProcessorLzxDecompress final : public IXChunkProcessor +{ +public: + explicit XChunkProcessorLzxDecompress(unsigned streamCount); + ~XChunkProcessorLzxDecompress(); + size_t Process(unsigned streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override; + +private: + std::vector m_lzx_states; +}; diff --git a/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Decryption.cpp b/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Decryption.cpp index cf1bfb05..72de9270 100644 --- a/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Decryption.cpp +++ b/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Decryption.cpp @@ -4,9 +4,11 @@ #include "Cryptography.h" #include +#include +#include -XChunkProcessorSalsa20Decryption::XChunkProcessorSalsa20Decryption(const int streamCount, - std::string& zoneName, +XChunkProcessorSalsa20Decryption::XChunkProcessorSalsa20Decryption(const unsigned streamCount, + const std::string& zoneName, const uint8_t* salsa20Key, const size_t keySize) : AbstractSalsa20Processor(streamCount, zoneName, salsa20Key, keySize) @@ -14,9 +16,9 @@ XChunkProcessorSalsa20Decryption::XChunkProcessorSalsa20Decryption(const int str } size_t XChunkProcessorSalsa20Decryption::Process( - const int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize) + const unsigned streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize) { - assert(streamNumber >= 0 && streamNumber < m_stream_count); + assert(streamNumber < m_stream_count); assert(input != nullptr); assert(output != nullptr); assert(inputLength <= outputBufferSize); diff --git a/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Decryption.h b/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Decryption.h index c5c57d51..fa32e9b0 100644 --- a/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Decryption.h +++ b/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Decryption.h @@ -7,7 +7,7 @@ class XChunkProcessorSalsa20Decryption final : public IXChunkProcessor, public AbstractSalsa20Processor { public: - XChunkProcessorSalsa20Decryption(int streamCount, std::string& zoneName, const uint8_t* salsa20Key, size_t keySize); + XChunkProcessorSalsa20Decryption(unsigned streamCount, const std::string& zoneName, const uint8_t* salsa20Key, size_t keySize); - size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override; + size_t Process(unsigned streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override; }; diff --git a/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Encryption.cpp b/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Encryption.cpp index 80ca8928..43899e18 100644 --- a/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Encryption.cpp +++ b/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Encryption.cpp @@ -2,7 +2,7 @@ #include -XChunkProcessorSalsa20Encryption::XChunkProcessorSalsa20Encryption(const int streamCount, +XChunkProcessorSalsa20Encryption::XChunkProcessorSalsa20Encryption(const unsigned streamCount, const std::string& zoneName, const uint8_t* salsa20Key, const size_t keySize) @@ -11,14 +11,14 @@ XChunkProcessorSalsa20Encryption::XChunkProcessorSalsa20Encryption(const int str } size_t XChunkProcessorSalsa20Encryption::Process( - const int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize) + const unsigned streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize) { - assert(streamNumber >= 0 && streamNumber < m_stream_count); + assert(streamNumber < m_stream_count); assert(input != nullptr); assert(output != nullptr); assert(inputLength <= outputBufferSize); - auto& streamContext = m_stream_contexts[streamNumber]; + const auto& streamContext = m_stream_contexts[streamNumber]; // Hash not yet encrypted XChunk uint8_t blockSha1Hash[SHA1_HASH_SIZE]; diff --git a/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Encryption.h b/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Encryption.h index 89272df3..12249613 100644 --- a/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Encryption.h +++ b/src/ZoneCommon/Zone/XChunk/XChunkProcessorSalsa20Encryption.h @@ -8,7 +8,7 @@ class XChunkProcessorSalsa20Encryption final : public IXChunkProcessor, public AbstractSalsa20Processor { public: - XChunkProcessorSalsa20Encryption(int streamCount, const std::string& zoneName, const uint8_t* salsa20Key, size_t keySize); + XChunkProcessorSalsa20Encryption(unsigned streamCount, const std::string& zoneName, const uint8_t* salsa20Key, size_t keySize); - size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override; + size_t Process(unsigned streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override; }; diff --git a/src/ZoneCommon/Zone/Zone.cpp b/src/ZoneCommon/Zone/Zone.cpp index f5ee6af1..deae50d3 100644 --- a/src/ZoneCommon/Zone/Zone.cpp +++ b/src/ZoneCommon/Zone/Zone.cpp @@ -1,13 +1,14 @@ #include "Zone.h" -Zone::Zone(std::string name, const zone_priority_t priority, IGame* game) +#include "ZoneRegistry.h" + +Zone::Zone(std::string name, const zone_priority_t priority, GameId gameId) : m_name(std::move(name)), m_priority(priority), m_language(GameLanguage::LANGUAGE_NONE), - m_game(game), - m_pools(ZoneAssetPools::CreateForGame(game->GetId(), this, priority)), - m_memory(std::make_unique()), - m_registered(false) + m_game_id(gameId), + m_pools(ZoneAssetPools::CreateForGame(gameId, this, priority)), + m_memory(std::make_unique()) { } @@ -15,7 +16,7 @@ Zone::~Zone() { if (m_registered) { - m_game->RemoveZone(this); + ZoneRegistry::GetRegistryForGame(m_game_id)->RemoveZone(this); } } @@ -23,7 +24,7 @@ void Zone::Register() { if (!m_registered) { - m_game->AddZone(this); + ZoneRegistry::GetRegistryForGame(m_game_id)->AddZone(this); m_registered = true; } } diff --git a/src/ZoneCommon/Zone/Zone.h b/src/ZoneCommon/Zone/Zone.h index 48c4d7d6..658bb412 100644 --- a/src/ZoneCommon/Zone/Zone.h +++ b/src/ZoneCommon/Zone/Zone.h @@ -10,20 +10,12 @@ #include #include -class IGame; class ZoneAssetPools; class Zone { public: - std::string m_name; - zone_priority_t m_priority; - GameLanguage m_language; - IGame* m_game; - ZoneScriptStrings m_script_strings; - std::unique_ptr m_pools; - - Zone(std::string name, zone_priority_t priority, IGame* game); + Zone(std::string name, zone_priority_t priority, GameId gameId); ~Zone(); Zone(const Zone& other) = delete; Zone(Zone&& other) noexcept = default; @@ -34,6 +26,13 @@ public: [[nodiscard]] ZoneMemory& Memory() const; + std::string m_name; + zone_priority_t m_priority; + GameLanguage m_language; + GameId m_game_id; + ZoneScriptStrings m_script_strings; + std::unique_ptr m_pools; + private: std::unique_ptr m_memory; diff --git a/src/ZoneCommon/Zone/ZoneRegistry.cpp b/src/ZoneCommon/Zone/ZoneRegistry.cpp new file mode 100644 index 00000000..41ee525b --- /dev/null +++ b/src/ZoneCommon/Zone/ZoneRegistry.cpp @@ -0,0 +1,38 @@ +#include "ZoneRegistry.h" + +#include +#include +#include + +ZoneRegistry::ZoneRegistry() = default; + +void ZoneRegistry::AddZone(Zone* zone) +{ + assert(zone); + + m_zones.emplace_back(zone); +} + +void ZoneRegistry::RemoveZone(Zone* zone) +{ + assert(zone); + + std::erase_if(m_zones, + [zone](const Zone* existingZone) + { + return existingZone == zone; + }); +} + +const std::vector& ZoneRegistry::Zones() const +{ + return m_zones; +} + +ZoneRegistry* ZoneRegistry::GetRegistryForGame(GameId gameId) +{ + static ZoneRegistry registries[static_cast(GameId::COUNT)]; + + assert(static_cast(gameId) < static_cast(GameId::COUNT)); + return ®istries[static_cast(gameId)]; +} diff --git a/src/ZoneCommon/Zone/ZoneRegistry.h b/src/ZoneCommon/Zone/ZoneRegistry.h new file mode 100644 index 00000000..5d2a03cb --- /dev/null +++ b/src/ZoneCommon/Zone/ZoneRegistry.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Game/IGame.h" +#include "Zone.h" + +#include + +class ZoneRegistry +{ +public: + void AddZone(Zone* zone); + void RemoveZone(Zone* zone); + + const std::vector& Zones() const; + + static ZoneRegistry* GetRegistryForGame(GameId gameId); + +private: + ZoneRegistry(); + + std::vector m_zones; +}; diff --git a/src/ZoneLoading.lua b/src/ZoneLoading.lua index 0d555bca..3819a0f6 100644 --- a/src/ZoneLoading.lua +++ b/src/ZoneLoading.lua @@ -14,6 +14,7 @@ function ZoneLoading:link(links) links:linkto(Cryptography) links:linkto(Utils) links:linkto(ZoneCommon) + links:linkto(lzx) links:linkto(zlib) if os.host() == "linux" then @@ -55,6 +56,7 @@ function ZoneLoading:project() self:include(includes) Cryptography:include(includes) Utils:include(includes) + lzx:include(includes) zlib:include(includes) ZoneCode:include(includes) diff --git a/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp b/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp index c7e4e4e4..3a387efe 100644 --- a/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp +++ b/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp @@ -68,7 +68,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, IGame::GetGameById(GameId::IW3)); + auto zone = std::make_unique(fileName, 0, GameId::IW3); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; diff --git a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp index 514e290b..1bfd60d6 100644 --- a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp +++ b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp @@ -23,6 +23,7 @@ #include "Loading/Steps/StepVerifyMagic.h" #include "Loading/Steps/StepVerifySignature.h" #include "Utils/ClassUtils.h" +#include "Utils/Logging/Log.h" #include #include @@ -100,7 +101,7 @@ namespace if (!rsa->SetKey(ZoneConstants::RSA_PUBLIC_KEY_INFINITY_WARD, sizeof(ZoneConstants::RSA_PUBLIC_KEY_INFINITY_WARD))) { - std::cerr << "Invalid public key for signature checking\n"; + con::error("Invalid public key for signature checking"); return nullptr; } @@ -175,7 +176,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, IGame::GetGameById(GameId::IW4)); + auto zone = std::make_unique(fileName, 0, GameId::IW4); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; diff --git a/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp b/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp index 9482a1cb..b7e97184 100644 --- a/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp +++ b/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp @@ -22,6 +22,7 @@ #include "Loading/Steps/StepVerifyMagic.h" #include "Loading/Steps/StepVerifySignature.h" #include "Utils/ClassUtils.h" +#include "Utils/Logging/Log.h" #include #include @@ -84,7 +85,7 @@ namespace if (!rsa->SetKey(ZoneConstants::RSA_PUBLIC_KEY_INFINITY_WARD, sizeof(ZoneConstants::RSA_PUBLIC_KEY_INFINITY_WARD))) { - std::cerr << "Invalid public key for signature checking\n"; + con::error("Invalid public key for signature checking"); return nullptr; } @@ -158,7 +159,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, IGame::GetGameById(GameId::IW5)); + auto zone = std::make_unique(fileName, 0, GameId::IW5); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; diff --git a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp index 0b96bb58..34ee2025 100644 --- a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp +++ b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp @@ -68,7 +68,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, IGame::GetGameById(GameId::T5)); + auto zone = std::make_unique(fileName, 0, GameId::T5); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; diff --git a/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp b/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp index 0fb7e43e..813d8602 100644 --- a/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp +++ b/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp @@ -9,6 +9,7 @@ #include "Loading/Processor/ProcessorXChunks.h" #include "Loading/Steps/StepAddProcessor.h" #include "Loading/Steps/StepAllocXBlocks.h" +#include "Loading/Steps/StepDumpData.h" #include "Loading/Steps/StepLoadSignature.h" #include "Loading/Steps/StepLoadZoneContent.h" #include "Loading/Steps/StepLoadZoneSizes.h" @@ -16,16 +17,22 @@ #include "Loading/Steps/StepVerifyFileName.h" #include "Loading/Steps/StepVerifyMagic.h" #include "Loading/Steps/StepVerifySignature.h" -#include "Utils/ClassUtils.h" +#include "Utils/Endianness.h" +#include "Utils/Logging/Log.h" #include "Zone/XChunk/XChunkProcessorInflate.h" +#include "Zone/XChunk/XChunkProcessorLzxDecompress.h" #include "Zone/XChunk/XChunkProcessorSalsa20Decryption.h" #include +#include #include +#include +#include #include #include using namespace T6; +namespace fs = std::filesystem; namespace { @@ -44,46 +51,63 @@ namespace return GameLanguage::LANGUAGE_NONE; } - bool CanLoad(const ZoneHeader& header, bool* isSecure, bool* isOfficial, bool* isEncrypted) + bool CanLoad(const ZoneHeader& header, bool& isBigEndian, bool& isSecure, bool& isOfficial, bool& isEncrypted, bool& isLzxCompressed) { - assert(isSecure != nullptr); - assert(isOfficial != nullptr); - - if (header.m_version != ZoneConstants::ZONE_VERSION) + if (endianness::FromLittleEndian(header.m_version) == ZoneConstants::ZONE_VERSION_PC) { - return false; + isBigEndian = false; + isLzxCompressed = false; + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, 8)) + { + isSecure = true; + isOfficial = true; + isEncrypted = true; + return true; + } + + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_OAT, 8)) + { + isSecure = true; + isOfficial = false; + isEncrypted = true; + return true; + } + + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, 8)) + { + isSecure = false; + isOfficial = true; + isEncrypted = true; + return true; + } + + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED_SERVER, 8)) + { + isSecure = false; + isOfficial = true; + isEncrypted = false; + return true; + } } - - if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, 8)) + else if (endianness::FromBigEndian(header.m_version) == ZoneConstants::ZONE_VERSION_XENON) { - *isSecure = true; - *isOfficial = true; - *isEncrypted = true; - return true; - } - - if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_OAT, 8)) - { - *isSecure = true; - *isOfficial = false; - *isEncrypted = true; - return true; - } - - if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, 8)) - { - *isSecure = false; - *isOfficial = true; - *isEncrypted = true; - return true; - } - - if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED_SERVER, 8)) - { - *isSecure = false; - *isOfficial = true; - *isEncrypted = false; - return true; + isBigEndian = true; + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, 8)) + { + isSecure = true; + isOfficial = true; + isEncrypted = true; + isLzxCompressed = false; + return true; + } + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_LZX_TREYARCH, 8)) + { + isSecure = true; + isOfficial = true; + isEncrypted = true; + isLzxCompressed = true; + return true; + } } return false; @@ -113,7 +137,7 @@ namespace if (!rsa->SetKey(ZoneConstants::RSA_PUBLIC_KEY_TREYARCH, sizeof(ZoneConstants::RSA_PUBLIC_KEY_TREYARCH))) { - std::cerr << "Invalid public key for signature checking\n"; + con::error("Invalid public key for signature checking"); return nullptr; } @@ -145,22 +169,45 @@ namespace return signatureLoadStepPtr; } - ICapturedDataProvider* AddXChunkProcessor(const bool isEncrypted, ZoneLoader& zoneLoader, std::string& fileName) + ICapturedDataProvider* + AddXChunkProcessor(const bool isBigEndian, const bool isEncrypted, const bool isLzxCompressed, ZoneLoader& zoneLoader, std::string& fileName) { ICapturedDataProvider* result = nullptr; - auto xChunkProcessor = processor::CreateProcessorXChunks(ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, ZoneConstants::VANILLA_BUFFER_SIZE); + std::unique_ptr xChunkProcessor; + + if (isBigEndian) + { + xChunkProcessor = processor::CreateProcessorXChunks( + ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, GameEndianness::BE, ZoneConstants::VANILLA_BUFFER_SIZE); + } + else + { + xChunkProcessor = processor::CreateProcessorXChunks( + ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, GameEndianness::LE, ZoneConstants::VANILLA_BUFFER_SIZE); + } + + const uint8_t (&salsa20Key)[32] = isBigEndian ? ZoneConstants::SALSA20_KEY_TREYARCH_XENON : ZoneConstants::SALSA20_KEY_TREYARCH_PC; if (isEncrypted) { // If zone is encrypted, the decryption is applied before the decompression. T6 Zones always use Salsa20. - auto chunkProcessorSalsa20 = std::make_unique( - ZoneConstants::STREAM_COUNT, fileName, ZoneConstants::SALSA20_KEY_TREYARCH, sizeof(ZoneConstants::SALSA20_KEY_TREYARCH)); + auto chunkProcessorSalsa20 = + std::make_unique(ZoneConstants::STREAM_COUNT, fileName, salsa20Key, sizeof(salsa20Key)); result = chunkProcessorSalsa20.get(); xChunkProcessor->AddChunkProcessor(std::move(chunkProcessorSalsa20)); } - // Decompress the chunks using zlib - xChunkProcessor->AddChunkProcessor(std::make_unique()); + if (isLzxCompressed) + { + // Decompress the chunks using lzx + xChunkProcessor->AddChunkProcessor(std::make_unique(ZoneConstants::STREAM_COUNT)); + } + else + { + // Decompress the chunks using zlib + xChunkProcessor->AddChunkProcessor(std::make_unique()); + } + zoneLoader.AddLoadingStep(step::CreateStepAddProcessor(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 @@ -170,16 +217,14 @@ namespace std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const { - bool isSecure; - bool isOfficial; - bool isEncrypted; + bool isBigEndian, isSecure, isOfficial, isEncrypted, isLzxCompressed; // Check if this file is a supported T6 zone. - if (!CanLoad(header, &isSecure, &isOfficial, &isEncrypted)) + if (!CanLoad(header, isBigEndian, isSecure, isOfficial, isEncrypted, isLzxCompressed)) return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, IGame::GetGameById(GameId::T6)); + auto zone = std::make_unique(fileName, 0, GameId::T6); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GetZoneLanguage(fileName); @@ -196,26 +241,37 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& ISignatureProvider* signatureProvider = AddAuthHeaderSteps(isSecure, *zoneLoader, fileName); // Setup loading XChunks from the zone from this point on. - ICapturedDataProvider* signatureDataProvider = AddXChunkProcessor(isEncrypted, *zoneLoader, fileName); + ICapturedDataProvider* signatureDataProvider = AddXChunkProcessor(isBigEndian, isEncrypted, isLzxCompressed, *zoneLoader, fileName); - // Start of the XFile struct - zoneLoader->AddLoadingStep(step::CreateStepLoadZoneSizes()); - zoneLoader->AddLoadingStep(step::CreateStepAllocXBlocks()); - - // Start of the zone content - zoneLoader->AddLoadingStep(step::CreateStepLoadZoneContent( - [zonePtr](ZoneInputStream& stream) - { - return std::make_unique(*zonePtr, stream); - }, - 32u, - ZoneConstants::OFFSET_BLOCK_BIT_COUNT, - ZoneConstants::INSERT_BLOCK, - zonePtr->Memory())); - - if (isSecure) + if (!isBigEndian) { - zoneLoader->AddLoadingStep(step::CreateStepVerifySignature(std::move(rsa), signatureProvider, signatureDataProvider)); + // Start of the XFile struct + zoneLoader->AddLoadingStep(step::CreateStepLoadZoneSizes()); + zoneLoader->AddLoadingStep(step::CreateStepAllocXBlocks()); + + // Start of the zone content + zoneLoader->AddLoadingStep(step::CreateStepLoadZoneContent( + [zonePtr](ZoneInputStream& stream) + { + return std::make_unique(*zonePtr, stream); + }, + 32u, + ZoneConstants::OFFSET_BLOCK_BIT_COUNT, + ZoneConstants::INSERT_BLOCK, + zonePtr->Memory())); + + if (isSecure) + { + zoneLoader->AddLoadingStep(step::CreateStepVerifySignature(std::move(rsa), signatureProvider, signatureDataProvider)); + } + } + else + { + fs::path dumpFileNamePath = fs::path(fileName).filename(); + dumpFileNamePath.replace_extension(".dat"); + std::string dumpFileName = dumpFileNamePath.string(); + con::warn("Dumping xbox assets is not supported, making a full fastfile data dump to {}", dumpFileName); + zoneLoader->AddLoadingStep(step::CreateStepDumpData(dumpFileName, 0xFFFFFFFF)); } return zoneLoader; diff --git a/src/ZoneLoading/Loading/Processor/ProcessorXChunks.cpp b/src/ZoneLoading/Loading/Processor/ProcessorXChunks.cpp index 6d73eafc..71aed486 100644 --- a/src/ZoneLoading/Loading/Processor/ProcessorXChunks.cpp +++ b/src/ZoneLoading/Loading/Processor/ProcessorXChunks.cpp @@ -1,17 +1,22 @@ #include "ProcessorXChunks.h" #include "Loading/Exception/InvalidChunkSizeException.h" +#include "Utils/Endianness.h" #include "Zone/ZoneTypes.h" #include #include #include +#include +#include #include #include #include #include #include +#define XCHUNK_ASYNC 1 + namespace { class DbLoadStream @@ -41,17 +46,24 @@ namespace { if (inputSize > 0) { +#ifdef XCHUNK_ASYNC std::unique_lock lock(m_load_mutex); if (m_is_loading) { m_loading_finished.wait(lock); } +#endif m_input_size = inputSize; m_is_loading = true; + +#ifdef XCHUNK_ASYNC m_load_thread = std::thread(&DbLoadStream::Load, this); m_load_thread.detach(); +#else + Load(); +#endif } else { @@ -77,7 +89,9 @@ namespace private: void Load() { +#ifdef XCHUNK_ASYNC std::lock_guard lock(m_load_mutex); +#endif bool firstProcessor = true; @@ -99,7 +113,10 @@ namespace } m_is_loading = false; + +#ifdef XCHUNK_ASYNC m_loading_finished.notify_all(); +#endif } int m_index; @@ -125,8 +142,9 @@ namespace class ProcessorXChunks final : public processor::IProcessorXChunks { public: - ProcessorXChunks(const int numStreams, const size_t xChunkSize, const std::optional vanillaBufferSize) + ProcessorXChunks(const int numStreams, const size_t xChunkSize, const GameEndianness endianness, const std::optional vanillaBufferSize) : m_chunk_size(xChunkSize), + m_endianness(endianness), m_vanilla_buffer_size(vanillaBufferSize), m_initialized_streams(false), m_current_stream(0), @@ -219,8 +237,12 @@ namespace } const size_t readSize = m_base_stream->Load(&chunkSize, sizeof(chunkSize)); + if (m_endianness == GameEndianness::LE) + chunkSize = endianness::FromLittleEndian(chunkSize); + else + chunkSize = endianness::FromBigEndian(chunkSize); - if (readSize == 0) + if (readSize < sizeof(chunkSize) || chunkSize == 0) { m_eof_reached = true; m_eof_stream = streamNum; @@ -280,6 +302,7 @@ namespace std::vector> m_streams; size_t m_chunk_size; + GameEndianness m_endianness; std::optional m_vanilla_buffer_size; std::vector> m_chunk_processors; @@ -297,13 +320,14 @@ namespace namespace processor { - std::unique_ptr CreateProcessorXChunks(int numStreams, const size_t xChunkSize) + std::unique_ptr CreateProcessorXChunks(unsigned numStreams, const size_t xChunkSize, const GameEndianness endianness) { - return std::make_unique(numStreams, xChunkSize, std::nullopt); + return std::make_unique(numStreams, xChunkSize, endianness, std::nullopt); } - std::unique_ptr CreateProcessorXChunks(int numStreams, const size_t xChunkSize, const size_t vanillaBufferSize) + std::unique_ptr + CreateProcessorXChunks(unsigned numStreams, const size_t xChunkSize, GameEndianness endianness, const size_t vanillaBufferSize) { - return std::make_unique(numStreams, xChunkSize, vanillaBufferSize); + return std::make_unique(numStreams, xChunkSize, endianness, vanillaBufferSize); } } // namespace processor diff --git a/src/ZoneLoading/Loading/Processor/ProcessorXChunks.h b/src/ZoneLoading/Loading/Processor/ProcessorXChunks.h index dec161af..6a876231 100644 --- a/src/ZoneLoading/Loading/Processor/ProcessorXChunks.h +++ b/src/ZoneLoading/Loading/Processor/ProcessorXChunks.h @@ -1,4 +1,6 @@ #pragma once + +#include "Game/IGame.h" #include "Loading/StreamProcessor.h" #include "Zone/XChunk/IXChunkProcessor.h" @@ -12,6 +14,6 @@ namespace processor virtual void AddChunkProcessor(std::unique_ptr chunkProcessor) = 0; }; - std::unique_ptr CreateProcessorXChunks(int numStreams, size_t xChunkSize); - std::unique_ptr CreateProcessorXChunks(int numStreams, size_t xChunkSize, size_t vanillaBufferSize); + std::unique_ptr CreateProcessorXChunks(unsigned numStreams, size_t xChunkSize, GameEndianness endianness); + std::unique_ptr CreateProcessorXChunks(unsigned numStreams, size_t xChunkSize, GameEndianness endianness, size_t vanillaBufferSize); } // namespace processor diff --git a/src/ZoneLoading/Loading/Steps/StepDumpData.cpp b/src/ZoneLoading/Loading/Steps/StepDumpData.cpp index 3999dc4d..906fce4b 100644 --- a/src/ZoneLoading/Loading/Steps/StepDumpData.cpp +++ b/src/ZoneLoading/Loading/Steps/StepDumpData.cpp @@ -7,17 +7,18 @@ namespace class StepDumpData final : public ILoadingStep { public: - explicit StepDumpData(const size_t dumpCount) - : m_dump_count(dumpCount) + StepDumpData(std::string fileName, const size_t dumpCount) + : m_file_name(std::move(fileName)), + m_dump_count(dumpCount) { } void PerformStep(ZoneLoader& zoneLoader, ILoadingStream& stream) override { - uint8_t tempBuffer[128]; + uint8_t tempBuffer[0x1000]; auto dumpedBytes = 0uz; - std::ofstream tempFile("dump.dat", std::fstream::out | std::fstream::binary); + std::ofstream tempFile(m_file_name, std::fstream::out | std::fstream::binary); while (dumpedBytes < m_dump_count) { @@ -45,14 +46,15 @@ namespace } private: + std::string m_file_name; size_t m_dump_count; }; } // namespace namespace step { - std::unique_ptr CreateStepDumpData(size_t dumpCount) + std::unique_ptr CreateStepDumpData(std::string fileName, size_t dumpCount) { - return std::make_unique(dumpCount); + return std::make_unique(std::move(fileName), dumpCount); } } // namespace step diff --git a/src/ZoneLoading/Loading/Steps/StepDumpData.h b/src/ZoneLoading/Loading/Steps/StepDumpData.h index 07beac1a..a174a3a3 100644 --- a/src/ZoneLoading/Loading/Steps/StepDumpData.h +++ b/src/ZoneLoading/Loading/Steps/StepDumpData.h @@ -3,8 +3,9 @@ #include "Loading/ILoadingStep.h" #include +#include namespace step { - std::unique_ptr CreateStepDumpData(size_t dumpCount); + std::unique_ptr CreateStepDumpData(std::string fileName, size_t dumpCount); } diff --git a/src/ZoneLoading/Loading/ZoneLoader.cpp b/src/ZoneLoading/Loading/ZoneLoader.cpp index a1f7d0c0..a97f6c6f 100644 --- a/src/ZoneLoading/Loading/ZoneLoader.cpp +++ b/src/ZoneLoading/Loading/ZoneLoader.cpp @@ -2,6 +2,7 @@ #include "Exception/LoadingException.h" #include "LoadingFileStream.h" +#include "Utils/Logging/Log.h" #include #include @@ -87,7 +88,7 @@ std::unique_ptr ZoneLoader::LoadZone(std::istream& stream) } catch (LoadingException& e) { - std::cerr << std::format("Loading fastfile failed: {}\n", e.DetailedMessage()); + con::error("Loading fastfile failed: {}", e.DetailedMessage()); return nullptr; } diff --git a/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp b/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp index 5e843841..296060fc 100644 --- a/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp +++ b/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp @@ -5,6 +5,7 @@ #include "Loading/Exception/InvalidOffsetBlockOffsetException.h" #include "Loading/Exception/OutOfBlockBoundsException.h" #include "Utils/Alignment.h" +#include "Utils/Logging/Log.h" #include #include @@ -414,8 +415,7 @@ namespace ss << " " << m_block_offsets[block->m_index]; } - ss << "\n"; - std::cout << ss.str(); + con::debug(ss.str()); } #endif diff --git a/src/ZoneLoading/ZoneLoading.cpp b/src/ZoneLoading/ZoneLoading.cpp index ad934839..3d15b80c 100644 --- a/src/ZoneLoading/ZoneLoading.cpp +++ b/src/ZoneLoading/ZoneLoading.cpp @@ -2,6 +2,7 @@ #include "Loading/IZoneLoaderFactory.h" #include "Loading/ZoneLoader.h" +#include "Utils/Logging/Log.h" #include "Utils/ObjFileStream.h" #include @@ -18,7 +19,7 @@ std::unique_ptr ZoneLoading::LoadZone(const std::string& path) if (!file.is_open()) { - std::cerr << std::format("Could not open file '{}'.\n", path); + con::error("Could not open file '{}'.", path); return nullptr; } @@ -26,7 +27,7 @@ std::unique_ptr ZoneLoading::LoadZone(const std::string& path) file.read(reinterpret_cast(&header), sizeof(header)); if (file.gcount() != sizeof(header)) { - std::cerr << std::format("Failed to read zone header from file '{}'.\n", path); + con::error("Failed to read zone header from file '{}'.", path); return nullptr; } @@ -42,7 +43,7 @@ std::unique_ptr ZoneLoading::LoadZone(const std::string& path) if (!zoneLoader) { - std::cerr << std::format("Could not create factory for zone '{}'.\n", zoneName); + con::error("Could not create factory for zone '{}'.", zoneName); return nullptr; } diff --git a/src/ZoneWriting/Game/T6/ZoneWriterFactoryT6.cpp b/src/ZoneWriting/Game/T6/ZoneWriterFactoryT6.cpp index 3d0d1d70..536e08b6 100644 --- a/src/ZoneWriting/Game/T6/ZoneWriterFactoryT6.cpp +++ b/src/ZoneWriting/Game/T6/ZoneWriterFactoryT6.cpp @@ -44,7 +44,7 @@ namespace ZoneHeader CreateHeaderForParams(const bool isSecure, const bool isOfficial, const bool isEncrypted) { ZoneHeader header{}; - header.m_version = ZoneConstants::ZONE_VERSION; + header.m_version = ZoneConstants::ZONE_VERSION_PC; if (isSecure) { @@ -82,7 +82,7 @@ namespace { // If zone is encrypted, the decryption is applied before the decompression. T6 Zones always use Salsa20. auto chunkProcessorSalsa20 = std::make_unique( - ZoneConstants::STREAM_COUNT, zone.m_name, ZoneConstants::SALSA20_KEY_TREYARCH, sizeof(ZoneConstants::SALSA20_KEY_TREYARCH)); + ZoneConstants::STREAM_COUNT, zone.m_name, ZoneConstants::SALSA20_KEY_TREYARCH_PC, sizeof(ZoneConstants::SALSA20_KEY_TREYARCH_PC)); // If there is encryption, the signed data of the zone is the final hash blocks provided by the Salsa20 IV adaption algorithm if (dataToSignProviderPtr) diff --git a/src/ZoneWriting/Writing/ZoneWriter.cpp b/src/ZoneWriting/Writing/ZoneWriter.cpp index 82e4287b..1f7a0ca1 100644 --- a/src/ZoneWriting/Writing/ZoneWriter.cpp +++ b/src/ZoneWriting/Writing/ZoneWriter.cpp @@ -1,5 +1,6 @@ #include "ZoneWriter.h" +#include "Utils/Logging/Log.h" #include "WritingException.h" #include "WritingFileStream.h" @@ -73,12 +74,12 @@ bool ZoneWriter::WriteZone(std::ostream& stream) } catch (WritingException& e) { - std::cout << std::format("Writing fastfile failed: {}\n", e.Message()); + con::error("Writing fastfile failed: {}", e.Message()); return false; } catch (std::runtime_error& e) { - std::cout << std::format("Writing fastfile failed: {}\n", e.what()); + con::error("Writing fastfile failed: {}", e.what()); return false; } diff --git a/src/ZoneWriting/ZoneWriting.cpp b/src/ZoneWriting/ZoneWriting.cpp index 18f56a10..d0ee4da7 100644 --- a/src/ZoneWriting/ZoneWriting.cpp +++ b/src/ZoneWriting/ZoneWriting.cpp @@ -1,5 +1,6 @@ #include "ZoneWriting.h" +#include "Utils/Logging/Log.h" #include "Writing/IZoneWriterFactory.h" #include @@ -10,12 +11,12 @@ bool ZoneWriting::WriteZone(std::ostream& stream, const Zone& zone) { const auto start = std::chrono::high_resolution_clock::now(); - const auto factory = IZoneWriterFactory::GetZoneWriterFactoryForGame(zone.m_game->GetId()); + const auto factory = IZoneWriterFactory::GetZoneWriterFactoryForGame(zone.m_game_id); const auto zoneWriter = factory->CreateWriter(zone); if (zoneWriter == nullptr) { - std::cerr << std::format("Could not create ZoneWriter for zone \"{}\".\n", zone.m_name); + con::error("Could not create ZoneWriter for zone \"{}\".", zone.m_name); return false; } @@ -23,7 +24,7 @@ bool ZoneWriting::WriteZone(std::ostream& stream, const Zone& zone) const auto end = std::chrono::high_resolution_clock::now(); - std::cout << std::format("Writing zone \"{}\" took {} ms.\n", zone.m_name, std::chrono::duration_cast(end - start).count()); + con::info("Writing zone \"{}\" took {} ms.", zone.m_name, std::chrono::duration_cast(end - start).count()); return result; } diff --git a/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp b/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp index b25715b9..4d5ab4fa 100644 --- a/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp +++ b/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp @@ -20,19 +20,19 @@ namespace public: TestContext() : m_memory(), - m_zone("test", 0, IGame::GetGameById(GameId::T6)), + m_zone("test", 0, GameId::T6), m_zone_definition(), m_zone_states(m_zone), m_creators(m_zone), m_ignored_assets(), m_context(m_zone, &m_creators, &m_ignored_assets), - m_kvp_creator(m_zone_states.GetZoneAssetCreationState()) + m_kvp_creator(m_zone_states.GetZoneAssetCreationState()) { } std::unique_ptr CreateSut() { - return CreateKeyValuePairsCompiler(m_memory, m_zone, m_zone_definition, m_zone_states); + return key_value_pairs::CreateCompilerT6(m_memory, m_zone, m_zone_definition, m_zone_states); } TestMemoryManager m_memory; @@ -43,7 +43,7 @@ namespace IgnoredAssetLookup m_ignored_assets; AssetCreationContext m_context; - KeyValuePairsCreator& m_kvp_creator; + key_value_pairs::Creator& m_kvp_creator; }; } // namespace @@ -81,7 +81,7 @@ namespace test::game::t6::keyvaluepairs TestContext testContext; const auto sut = testContext.CreateSut(); - testContext.m_kvp_creator.AddKeyValuePair(CommonKeyValuePair("ipak_read", "test_ipak")); + testContext.m_kvp_creator.AddKeyValuePair(key_value_pairs::CommonKeyValuePair("ipak_read", "test_ipak")); sut->FinalizeZone(testContext.m_context); @@ -107,7 +107,7 @@ namespace test::game::t6::keyvaluepairs TestContext testContext; const auto sut = testContext.CreateSut(); - testContext.m_kvp_creator.AddKeyValuePair(CommonKeyValuePair(0xDDEEFFAA, "hello_there")); + testContext.m_kvp_creator.AddKeyValuePair(key_value_pairs::CommonKeyValuePair(0xDDEEFFAA, "hello_there")); sut->FinalizeZone(testContext.m_context); diff --git a/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp b/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp index bd49745f..fc423589 100644 --- a/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp +++ b/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp @@ -20,15 +20,15 @@ namespace { public: TestContext() - : m_zone("test", 0, IGame::GetGameById(GameId::T6)), + : m_zone("test", 0, GameId::T6), m_zone_states(m_zone), m_out_dir() { } - IPakCreator& CreateSut() + image::IPakCreator& CreateSut() { - return m_zone_states.GetZoneAssetCreationState(); + return m_zone_states.GetZoneAssetCreationState(); } Zone m_zone; diff --git a/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp b/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp index 29dd627f..c9c6b82c 100644 --- a/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp +++ b/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp @@ -20,7 +20,7 @@ namespace { public: TestContext() - : m_zone("test", 0, IGame::GetGameById(GameId::T6)), + : m_zone("test", 0, GameId::T6), m_zone_definition(), m_zone_definition_context(m_zone_definition), m_zone_states(m_zone), @@ -28,13 +28,13 @@ namespace m_ignored_assets(), m_out_dir(), m_context(m_zone, &m_creators, &m_ignored_assets), - m_ipak_creator(m_zone_states.GetZoneAssetCreationState()) + m_ipak_creator(m_zone_states.GetZoneAssetCreationState()) { } std::unique_ptr CreateSut() { - return std::make_unique>(m_zone_definition_context, m_search_path, m_zone_states, m_out_dir); + return std::make_unique>(m_zone_definition_context, m_search_path, m_zone_states, m_out_dir); } Zone m_zone; @@ -47,7 +47,7 @@ namespace MockOutputPath m_out_dir; AssetCreationContext m_context; - IPakCreator& m_ipak_creator; + image::IPakCreator& m_ipak_creator; }; } // namespace diff --git a/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp b/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp index 8e971524..adfef729 100644 --- a/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp +++ b/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp @@ -19,7 +19,7 @@ namespace { public: TestContext() - : m_zone("test", 0, IGame::GetGameById(GameId::T6)), + : m_zone("test", 0, GameId::T6), m_zone_definition(), m_zone_definition_context(m_zone_definition), m_zone_states(m_zone), @@ -33,7 +33,7 @@ namespace std::unique_ptr CreateSut() { - return std::make_unique>(m_zone_definition_context, m_search_path, m_zone_states, m_out_dir); + return std::make_unique>(m_zone_definition_context, m_search_path, m_zone_states, m_out_dir); } Zone m_zone; diff --git a/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp b/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp index 63ceeb17..33421760 100644 --- a/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp +++ b/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp @@ -23,7 +23,7 @@ namespace { public: TestContext() - : m_zone("test", 0, IGame::GetGameById(GameId::T6)), + : m_zone("test", 0, GameId::T6), m_zone_states(m_zone), m_out_dir() { diff --git a/test/ObjCompilingTests/KeyValuePairs/KeyValuePairsCreatorTest.cpp b/test/ObjCompilingTests/KeyValuePairs/KeyValuePairsCreatorTest.cpp index 48bffd61..42c21fb1 100644 --- a/test/ObjCompilingTests/KeyValuePairs/KeyValuePairsCreatorTest.cpp +++ b/test/ObjCompilingTests/KeyValuePairs/KeyValuePairsCreatorTest.cpp @@ -13,7 +13,7 @@ namespace test::keyvaluepairs { TEST_CASE("KeyValuePairsCreator: ZoneDefinition with no properties produces no KeyValuePairs", "[keyvaluepairs]") { - KeyValuePairsCreator sut; + key_value_pairs::Creator sut; ZoneDefinition zoneDefinition; sut.Finalize(zoneDefinition); @@ -25,7 +25,7 @@ namespace test::keyvaluepairs TEST_CASE("KeyValuePairsCreator: ZoneDefinition with unrelated properties produce no KeyValuePairs", "[keyvaluepairs]") { - KeyValuePairsCreator sut; + key_value_pairs::Creator sut; ZoneDefinition zoneDefinition; zoneDefinition.m_properties.AddProperty("linker.test", "yes"); @@ -38,7 +38,7 @@ namespace test::keyvaluepairs TEST_CASE("KeyValuePairsCreator: ZoneDefinition with level properties produce KeyValuePairs", "[keyvaluepairs]") { - KeyValuePairsCreator sut; + key_value_pairs::Creator sut; ZoneDefinition zoneDefinition; zoneDefinition.m_properties.AddProperty("linker.test", "yes"); @@ -56,7 +56,7 @@ namespace test::keyvaluepairs TEST_CASE("KeyValuePairsCreator: ZoneDefinition can have level properties with hash", "[keyvaluepairs]") { - KeyValuePairsCreator sut; + key_value_pairs::Creator sut; ZoneDefinition zoneDefinition; zoneDefinition.m_properties.AddProperty("level.@D34DB33F", "yes"); @@ -77,7 +77,7 @@ namespace test::keyvaluepairs TEST_CASE("KeyValuePairsCreator: ZoneDefinition can have level properties with name and/or hash", "[keyvaluepairs]") { - KeyValuePairsCreator sut; + key_value_pairs::Creator sut; ZoneDefinition zoneDefinition; zoneDefinition.m_properties.AddProperty("level.ipak_read", "asdf"); diff --git a/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp b/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp new file mode 100644 index 00000000..4c88b469 --- /dev/null +++ b/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp @@ -0,0 +1,577 @@ +#include "Game/IW3/Material/LoaderMaterialIW3.h" + +#include "Game/IW3/CommonIW3.h" +#include "Game/IW3/GameIW3.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace IW3; +using namespace Catch; +using namespace std::literals; + +namespace +{ + void GivenImage(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* image = memory.Alloc(); + image->name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(image); + context.AddAsset(std::move(registration)); + } + + void GivenTechset(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* techset = memory.Alloc(); + techset->name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(techset); + context.AddAsset(std::move(registration)); + } + + TEST_CASE("LoaderMaterial(IW3): Can parse material", "[iw3][material][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("materials/wc/ch_plasterwall_long.json", + R"MATERIAL( +{ + "_game": "iw3", + "_type": "material", + "_version": 1, + "cameraRegion": "lit", + "constants": [ + { + "literal": [ + 0.800000011920929, + 2.0, + 1.0, + 0.625 + ], + "name": "envMapParms" + }, + { + "literal": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "name": "colorTint" + } + ], + "gameFlags": [ + "2", + "10", + "CASTS_SHADOW" + ], + "sortKey": 4, + "stateBits": [ + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offsetShadowmap", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "invdestalpha", + "stencilFront": { + "fail": "keep", + "func": "equal", + "pass": "keep", + "zfail": "keep" + } + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "invsrccolor", + "polygonOffset": "offset2", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "zero" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 3, + 1, + -1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + 5, + -1, + 1, + 6, + 1, + -1 + ], + "stateFlags": 57, + "surfaceTypeBits": 32768, + "techniqueSet": "wc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~ch_plasterwall_long_spc-r-49~3c124bfe", + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "ch_plasterwall_long_nml", + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "normalMap" + }, + { + "image": "ch_plasterwall_long_col", + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, GameId::IW3); + + MemoryManager memory; + AssetCreatorCollection creatorCollection(zone); + IgnoredAssetLookup ignoredAssetLookup; + AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); + + GivenImage("~ch_plasterwall_long_spc-r-49~3c124bfe", context, memory); + GivenImage("ch_plasterwall_long_nml", context, memory); + GivenImage("ch_plasterwall_long_col", context, memory); + GivenTechset("wc_l_sm_r0c0n0s0", context, memory); + + auto loader = material::CreateLoaderIW3(memory, searchPath); + auto result = loader->CreateAsset("wc/ch_plasterwall_long", context); + REQUIRE(result.HasBeenSuccessful()); + + const auto* assetInfo = reinterpret_cast*>(result.GetAssetInfo()); + const auto* material = assetInfo->Asset(); + + REQUIRE(material->info.name == "wc/ch_plasterwall_long"s); + REQUIRE(material->info.gameFlags == 0x52); + REQUIRE(material->info.sortKey == 4); + REQUIRE(material->info.textureAtlasRowCount == 1); + REQUIRE(material->info.textureAtlasColumnCount == 1); + REQUIRE(material->info.surfaceTypeBits == 0x8000); + + constexpr int8_t expectedStateBitsEntry[]{0, 1, 2, 3, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, + -1, -1, -1, -1, 4, 4, 4, -1, -1, -1, -1, 5, -1, 1, 6, 1, -1}; + REQUIRE(std::memcmp(material->stateBitsEntry, expectedStateBitsEntry, sizeof(expectedStateBitsEntry)) == 0); + + REQUIRE(material->stateFlags == 57); + REQUIRE(material->cameraRegion == CAMERA_REGION_LIT); + + REQUIRE(material->techniqueSet != nullptr); + REQUIRE(material->techniqueSet->name != nullptr); + REQUIRE(material->techniqueSet->name == "wc_l_sm_r0c0n0s0"s); + + REQUIRE(material->textureCount == 3); + REQUIRE(material->textureTable); + + const auto& textureDef0 = material->textureTable[0]; + REQUIRE(textureDef0.nameHash == 0x34ecccb3); + REQUIRE(textureDef0.nameStart == 's'); + REQUIRE(textureDef0.nameEnd == 'p'); + REQUIRE(textureDef0.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef0.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST); + REQUIRE(textureDef0.samplerState.clampU == 0); + REQUIRE(textureDef0.samplerState.clampV == 0); + REQUIRE(textureDef0.samplerState.clampW == 0); + REQUIRE(textureDef0.semantic == TS_SPECULAR_MAP); + REQUIRE(textureDef0.u.image); + REQUIRE(textureDef0.u.image->name); + REQUIRE(textureDef0.u.image->name == "~ch_plasterwall_long_spc-r-49~3c124bfe"s); + + const auto& textureDef1 = material->textureTable[1]; + REQUIRE(textureDef1.nameHash == 0x59d30d0f); + REQUIRE(textureDef1.nameStart == 'n'); + REQUIRE(textureDef1.nameEnd == 'p'); + REQUIRE(textureDef1.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef1.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR); + REQUIRE(textureDef1.samplerState.clampU == 0); + REQUIRE(textureDef1.samplerState.clampV == 0); + REQUIRE(textureDef1.samplerState.clampW == 0); + REQUIRE(textureDef1.semantic == TS_NORMAL_MAP); + REQUIRE(textureDef1.u.image); + REQUIRE(textureDef1.u.image->name); + REQUIRE(textureDef1.u.image->name == "ch_plasterwall_long_nml"s); + + const auto& textureDef2 = material->textureTable[2]; + REQUIRE(textureDef2.nameHash == 0xa0ab1041); + REQUIRE(textureDef2.nameStart == 'c'); + REQUIRE(textureDef2.nameEnd == 'p'); + REQUIRE(textureDef2.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef2.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR); + REQUIRE(textureDef2.samplerState.clampU == 0); + REQUIRE(textureDef2.samplerState.clampV == 0); + REQUIRE(textureDef2.samplerState.clampW == 0); + REQUIRE(textureDef2.semantic == TS_COLOR_MAP); + REQUIRE(textureDef2.u.image); + REQUIRE(textureDef2.u.image->name); + REQUIRE(textureDef2.u.image->name == "ch_plasterwall_long_col"s); + + REQUIRE(material->constantCount == 2); + REQUIRE(material->constantTable); + + const auto& constantDef0 = material->constantTable[0]; + REQUIRE(constantDef0.nameHash == 0x3d9994dc); + REQUIRE(strncmp(constantDef0.name, "envMapParms", std::extent_v) == 0); + REQUIRE(constantDef0.literal.x == Approx(0.8f)); + REQUIRE(constantDef0.literal.y == Approx(2.0f)); + REQUIRE(constantDef0.literal.z == Approx(1.0f)); + REQUIRE(constantDef0.literal.w == Approx(0.625f)); + + const auto& constantDef1 = material->constantTable[1]; + REQUIRE(constantDef1.nameHash == 0xb60c3b3a); + REQUIRE(strncmp(constantDef1.name, "colorTint", std::extent_v) == 0); + REQUIRE(constantDef1.literal.x == Approx(1.0f)); + REQUIRE(constantDef1.literal.y == Approx(1.0f)); + REQUIRE(constantDef1.literal.z == Approx(1.0f)); + REQUIRE(constantDef1.literal.w == Approx(1.0f)); + + REQUIRE(material->stateBitsCount == 7); + REQUIRE(material->stateBitsTable); + + const auto& stateBits0 = material->stateBitsTable[0]; + REQUIRE(stateBits0.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits0.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits0.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits0.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits0.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits0.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits0.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits0.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits0.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits0.loadBits.structured.colorWriteRgb == 0); + REQUIRE(stateBits0.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits0.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits0.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits0.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits0.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits0.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits1 = material->stateBitsTable[1]; + REQUIRE(stateBits1.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits1.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits1.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits1.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits1.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits1.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits1.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits1.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits1.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits1.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits1.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits1.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits1.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits1.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits1.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits1.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits2 = material->stateBitsTable[2]; + REQUIRE(stateBits2.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits2.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits2.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits2.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits2.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits2.loadBits.structured.cullFace == GFXS_CULL_NONE); + REQUIRE(stateBits2.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits2.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits2.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits2.loadBits.structured.colorWriteRgb == 0); + REQUIRE(stateBits2.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits2.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits2.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits2.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits2.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits2.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_SHADOWMAP); + REQUIRE(stateBits2.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits3 = material->stateBitsTable[3]; + REQUIRE(stateBits3.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits3.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits3.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits3.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits3.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits3.loadBits.structured.cullFace == GFXS_CULL_NONE); + REQUIRE(stateBits3.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits3.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits3.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits3.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits3.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits3.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits3.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits3.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits3.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits3.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits4 = material->stateBitsTable[4]; + REQUIRE(stateBits4.loadBits.structured.srcBlendRgb == GFXS_BLEND_INVDESTALPHA); + REQUIRE(stateBits4.loadBits.structured.dstBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.blendOpRgb == GFXS_BLENDOP_ADD); + REQUIRE(stateBits4.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits4.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits4.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits4.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits4.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits4.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits4.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits4.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits4.loadBits.structured.depthWrite == 0); + REQUIRE(stateBits4.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits4.loadBits.structured.depthTest == GFXS_DEPTHTEST_EQUAL); + REQUIRE(stateBits4.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontEnabled == 1); + REQUIRE(stateBits4.loadBits.structured.stencilFrontPass == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits4.loadBits.structured.stencilFrontFail == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits4.loadBits.structured.stencilFrontZFail == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits4.loadBits.structured.stencilFrontFunc == GFXS_STENCILFUNC_EQUAL); + REQUIRE(stateBits4.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits5 = material->stateBitsTable[5]; + REQUIRE(stateBits5.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits5.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits5.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits5.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits5.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits5.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits5.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits5.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits5.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits5.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits5.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits5.loadBits.structured.polymodeLine == 1); + REQUIRE(stateBits5.loadBits.structured.depthWrite == 0); + REQUIRE(stateBits5.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits5.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits5.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_2); + REQUIRE(stateBits5.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits6 = material->stateBitsTable[6]; + REQUIRE(stateBits6.loadBits.structured.srcBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits6.loadBits.structured.dstBlendRgb == GFXS_BLEND_INVSRCCOLOR); + REQUIRE(stateBits6.loadBits.structured.blendOpRgb == GFXS_BLENDOP_ADD); + REQUIRE(stateBits6.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits6.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits6.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits6.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits6.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits6.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits6.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits6.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits6.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits6.loadBits.structured.depthWrite == 0); + REQUIRE(stateBits6.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits6.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits6.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_2); + REQUIRE(stateBits6.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits6.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits6.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits6.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits6.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits6.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits6.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits6.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits6.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits6.loadBits.structured.stencilBackFunc == 0); + } +} // namespace diff --git a/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp b/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp index 36ee0347..4bfd7172 100644 --- a/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp +++ b/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp @@ -20,14 +20,14 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW3)); + Zone zone("MockZone", 0, GameId::IW3); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); IgnoredAssetLookup ignoredAssetLookup; AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); - auto loader = CreateStringTableLoader(memory, searchPath); + auto loader = string_table::CreateLoaderIW3(memory, searchPath); auto result = loader->CreateAsset("mp/cooltable.csv", context); REQUIRE(result.HasBeenSuccessful()); diff --git a/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp b/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp index 88fbe000..e6fdf9db 100644 --- a/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp +++ b/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp @@ -20,14 +20,14 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW4)); + Zone zone("MockZone", 0, GameId::IW4); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); IgnoredAssetLookup ignoredAssetLookup; AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); - auto loader = CreateStringTableLoader(memory, searchPath); + auto loader = string_table::CreateLoaderIW4(memory, searchPath); auto result = loader->CreateAsset("mp/cooltable.csv", context); REQUIRE(result.HasBeenSuccessful()); diff --git a/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp b/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp index 0860550b..39fd6075 100644 --- a/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp +++ b/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp @@ -277,7 +277,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW4)); + Zone zone("MockZone", 0, GameId::IW4); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); @@ -289,7 +289,7 @@ namespace GivenImage("ch_rubble01_col", context, memory); GivenTechset("mc_l_sm_r0c0n0s0", context, memory); - auto loader = CreateMaterialLoader(memory, searchPath); + auto loader = material::CreateLoaderIW4(memory, searchPath); auto result = loader->CreateAsset("mc/ch_rubble01", context); REQUIRE(result.HasBeenSuccessful()); diff --git a/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp b/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp index 69021fe5..dcd495f1 100644 --- a/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp +++ b/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp @@ -11,7 +11,6 @@ #include #include -using namespace menu; using namespace IW4; using namespace std::literals; using namespace Catch::Matchers; @@ -31,12 +30,12 @@ namespace test::game::iw4::menu::parsing::it public: MenuParsingItHelper() - : m_zone("MockZone", 0, IGame::GetGameById(GameId::IW4)), + : m_zone("MockZone", 0, GameId::IW4), m_creator_collection(m_zone), m_ignored_asset_lookup(), m_context(m_zone, &m_creator_collection, &m_ignored_asset_lookup) { - m_asset_creator = CreateMenuListLoader(m_zone.Memory(), m_search_path); + m_asset_creator = ::menu::CreateMenuListLoaderIW4(m_zone.Memory(), m_search_path); } void AddFile(std::string fileName, std::string data) diff --git a/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp b/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp index ab5ac734..6aadc816 100644 --- a/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp +++ b/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp @@ -19,14 +19,14 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW5)); + Zone zone("MockZone", 0, GameId::IW5); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); IgnoredAssetLookup ignoredAssetLookup; AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); - auto loader = CreateStringTableLoader(memory, searchPath); + auto loader = string_table::CreateLoaderIW5(memory, searchPath); auto result = loader->CreateAsset("mp/cooltable.csv", context); REQUIRE(result.HasBeenSuccessful()); diff --git a/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp b/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp index 5de6912b..8105f4ee 100644 --- a/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp +++ b/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp @@ -301,7 +301,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW5)); + Zone zone("MockZone", 0, GameId::IW5); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); @@ -313,7 +313,7 @@ namespace GivenImage("me_metal_rusty02_col", context, memory); GivenTechset("wc_l_sm_r0c0n0s0", context, memory); - auto loader = CreateMaterialLoader(memory, searchPath); + auto loader = material::CreateLoaderIW5(memory, searchPath); auto result = loader->CreateAsset("wc/me_metal_rust_02", context); REQUIRE(result.HasBeenSuccessful()); diff --git a/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp b/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp index 8dca092f..7db27a83 100644 --- a/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp +++ b/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp @@ -19,14 +19,14 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::T5)); + Zone zone("MockZone", 0, GameId::T5); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); IgnoredAssetLookup ignoredAssetLookup; AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); - auto loader = CreateStringTableLoader(memory, searchPath); + auto loader = string_table::CreateLoaderT5(memory, searchPath); auto result = loader->CreateAsset("mp/cooltable.csv", context); REQUIRE(result.HasBeenSuccessful()); diff --git a/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp b/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp new file mode 100644 index 00000000..4847e225 --- /dev/null +++ b/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp @@ -0,0 +1,640 @@ +#include "Game/T5/Material/LoaderMaterialT5.h" + +#include "Game/T5/CommonT5.h" +#include "Game/T5/GameT5.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace T5; +using namespace Catch; +using namespace std::literals; + +namespace +{ + void GivenImage(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* image = memory.Alloc(); + image->name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(image); + context.AddAsset(std::move(registration)); + } + + void GivenTechset(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* techset = memory.Alloc(); + techset->name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(techset); + context.AddAsset(std::move(registration)); + } + + TEST_CASE("LoaderMaterial(T5): Can parse material", "[t5][material][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("materials/mc/jun_art_brick_foundation.json", + R"MATERIAL( +{ + "_game": "t5", + "_type": "material", + "_version": 1, + "cameraRegion": "lit", + "constants": [ + { + "literal": [ + 1.0, + 1.0, + 1.0, + 0.5 + ], + "name": "envMapParms" + }, + { + "literal": [ + 0.0, + 1.0, + 0.0, + 1.0 + ], + "nameFragment": "dynamicFolia", + "nameHash": 3034467714 + } + ], + "gameFlags": [ + "10", + "CASTS_SHADOW" + ], + "layeredSurfaceTypes": 536870914, + "sortKey": 4, + "stateBits": [ + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one", + "stencilFront": { + "fail": "keep", + "func": "equal", + "pass": "keep", + "zfail": "keep" + } + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offsetShadowmap", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "invdestalpha" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 3, + 1, + -1, + -1, + -1, + -1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4, + 4, + 4, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + 5, + -1, + 1, + 1, + -1 + ], + "stateFlags": 121, + "surfaceTypeBits": 2, + "techniqueSet": "mc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~$white-rgb&~-rjun_art_brick_~a05e7627", + "isMatureContent": false, + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "jun_art_brick_foundation_n", + "isMatureContent": false, + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "normalMap" + }, + { + "image": "~-gjun_art_brick_foundation_c", + "isMatureContent": false, + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, GameId::T5); + + MemoryManager memory; + AssetCreatorCollection creatorCollection(zone); + IgnoredAssetLookup ignoredAssetLookup; + AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); + + GivenImage("~$white-rgb&~-rjun_art_brick_~a05e7627", context, memory); + GivenImage("jun_art_brick_foundation_n", context, memory); + GivenImage("~-gjun_art_brick_foundation_c", context, memory); + GivenTechset("mc_l_sm_r0c0n0s0", context, memory); + + auto loader = material::CreateLoaderT5(memory, searchPath); + auto result = loader->CreateAsset("mc/jun_art_brick_foundation", context); + REQUIRE(result.HasBeenSuccessful()); + + const auto* assetInfo = reinterpret_cast*>(result.GetAssetInfo()); + const auto* material = assetInfo->Asset(); + + REQUIRE(material->info.name == "mc/jun_art_brick_foundation"s); + REQUIRE(material->info.gameFlags == 0x50); + REQUIRE(material->info.sortKey == 4); + REQUIRE(material->info.textureAtlasRowCount == 1); + REQUIRE(material->info.textureAtlasColumnCount == 1); + REQUIRE(material->info.surfaceTypeBits == 0x2); + REQUIRE(material->info.layeredSurfaceTypes == 0x20000002); + + constexpr int8_t expectedStateBitsEntry[]{0, 1, 2, 3, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 4, 4, 4, 4, 4, -1, -1, -1, -1, 5, -1, 1, 1, -1}; + REQUIRE(std::memcmp(material->stateBitsEntry, expectedStateBitsEntry, sizeof(expectedStateBitsEntry)) == 0); + + REQUIRE(material->stateFlags == 121); + REQUIRE(material->cameraRegion == CAMERA_REGION_LIT); + + REQUIRE(material->techniqueSet != nullptr); + REQUIRE(material->techniqueSet->name != nullptr); + REQUIRE(material->techniqueSet->name == "mc_l_sm_r0c0n0s0"s); + + REQUIRE(material->textureCount == 3); + REQUIRE(material->textureTable); + + const auto& textureDef0 = material->textureTable[0]; + REQUIRE(textureDef0.nameHash == 0x34ecccb3); + REQUIRE(textureDef0.nameStart == 's'); + REQUIRE(textureDef0.nameEnd == 'p'); + REQUIRE(textureDef0.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef0.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST); + REQUIRE(textureDef0.samplerState.clampU == 0); + REQUIRE(textureDef0.samplerState.clampV == 0); + REQUIRE(textureDef0.samplerState.clampW == 0); + REQUIRE(textureDef0.semantic == TS_SPECULAR_MAP); + REQUIRE(textureDef0.isMatureContent == false); + REQUIRE(textureDef0.u.image); + REQUIRE(textureDef0.u.image->name); + REQUIRE(textureDef0.u.image->name == "~$white-rgb&~-rjun_art_brick_~a05e7627"s); + + const auto& textureDef1 = material->textureTable[1]; + REQUIRE(textureDef1.nameHash == 0x59d30d0f); + REQUIRE(textureDef1.nameStart == 'n'); + REQUIRE(textureDef1.nameEnd == 'p'); + REQUIRE(textureDef1.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef1.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST); + REQUIRE(textureDef1.samplerState.clampU == 0); + REQUIRE(textureDef1.samplerState.clampV == 0); + REQUIRE(textureDef1.samplerState.clampW == 0); + REQUIRE(textureDef1.semantic == TS_NORMAL_MAP); + REQUIRE(textureDef1.isMatureContent == false); + REQUIRE(textureDef1.u.image); + REQUIRE(textureDef1.u.image->name); + REQUIRE(textureDef1.u.image->name == "jun_art_brick_foundation_n"s); + + const auto& textureDef2 = material->textureTable[2]; + REQUIRE(textureDef2.nameHash == 0xa0ab1041); + REQUIRE(textureDef2.nameStart == 'c'); + REQUIRE(textureDef2.nameEnd == 'p'); + REQUIRE(textureDef2.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef2.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST); + REQUIRE(textureDef2.samplerState.clampU == 0); + REQUIRE(textureDef2.samplerState.clampV == 0); + REQUIRE(textureDef2.samplerState.clampW == 0); + REQUIRE(textureDef2.semantic == TS_COLOR_MAP); + REQUIRE(textureDef2.isMatureContent == false); + REQUIRE(textureDef2.u.image); + REQUIRE(textureDef2.u.image->name); + REQUIRE(textureDef2.u.image->name == "~-gjun_art_brick_foundation_c"s); + + REQUIRE(material->constantCount == 2); + REQUIRE(material->constantTable); + + const auto& constantDef0 = material->constantTable[0]; + REQUIRE(constantDef0.nameHash == 0x3d9994dc); + REQUIRE(strncmp(constantDef0.name, "envMapParms", std::extent_v) == 0); + REQUIRE(constantDef0.literal.x == Approx(1.0f)); + REQUIRE(constantDef0.literal.y == Approx(1.0f)); + REQUIRE(constantDef0.literal.z == Approx(1.0f)); + REQUIRE(constantDef0.literal.w == Approx(0.5f)); + + const auto& constantDef1 = material->constantTable[1]; + REQUIRE(constantDef1.nameHash == 0xb4de4d82); + REQUIRE(strncmp(constantDef1.name, "dynamicFolia", std::extent_v) == 0); + REQUIRE(constantDef1.literal.x == Approx(0.0f)); + REQUIRE(constantDef1.literal.y == Approx(1.0f)); + REQUIRE(constantDef1.literal.z == Approx(0.0f)); + REQUIRE(constantDef1.literal.w == Approx(1.0f)); + + REQUIRE(material->stateBitsCount == 6); + REQUIRE(material->stateBitsTable); + + const auto& stateBits0 = material->stateBitsTable[0]; + REQUIRE(stateBits0.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits0.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits0.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits0.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits0.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits0.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits0.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits0.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits0.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits0.loadBits.structured.colorWriteRgb == 0); + REQUIRE(stateBits0.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits0.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits0.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits0.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits0.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits0.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontEnabled == 1); + REQUIRE(stateBits0.loadBits.structured.stencilFrontPass == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits0.loadBits.structured.stencilFrontFail == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits0.loadBits.structured.stencilFrontZFail == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits0.loadBits.structured.stencilFrontFunc == GFXS_STENCILFUNC_EQUAL); + REQUIRE(stateBits0.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits1 = material->stateBitsTable[1]; + REQUIRE(stateBits1.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits1.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits1.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits1.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits1.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits1.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits1.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits1.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits1.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits1.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits1.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits1.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits1.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits1.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits1.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits1.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits2 = material->stateBitsTable[2]; + REQUIRE(stateBits2.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits2.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits2.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits2.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits2.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits2.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits2.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits2.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits2.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits2.loadBits.structured.colorWriteRgb == 0); + REQUIRE(stateBits2.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits2.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits2.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits2.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits2.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits2.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_SHADOWMAP); + REQUIRE(stateBits2.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits3 = material->stateBitsTable[3]; + REQUIRE(stateBits3.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits3.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits3.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits3.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits3.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits3.loadBits.structured.cullFace == GFXS_CULL_NONE); + REQUIRE(stateBits3.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits3.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits3.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits3.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits3.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits3.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits3.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits3.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits3.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits3.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits4 = material->stateBitsTable[4]; + REQUIRE(stateBits4.loadBits.structured.srcBlendRgb == GFXS_BLEND_INVDESTALPHA); + REQUIRE(stateBits4.loadBits.structured.dstBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.blendOpRgb == GFXS_BLENDOP_ADD); + REQUIRE(stateBits4.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits4.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits4.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits4.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits4.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits4.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits4.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits4.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits4.loadBits.structured.depthWrite == 0); + REQUIRE(stateBits4.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits4.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits4.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits5 = material->stateBitsTable[5]; + REQUIRE(stateBits5.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits5.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits5.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits5.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits5.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits5.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits5.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits5.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits5.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits5.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits5.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits5.loadBits.structured.polymodeLine == 1); + REQUIRE(stateBits5.loadBits.structured.depthWrite == 0); + REQUIRE(stateBits5.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits5.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits5.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_2); + REQUIRE(stateBits5.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackFunc == 0); + } +} // namespace diff --git a/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp b/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp index 87004412..e26e8b74 100644 --- a/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp +++ b/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp @@ -19,14 +19,14 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::T6)); + Zone zone("MockZone", 0, GameId::T6); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); IgnoredAssetLookup ignoredAssetLookup; AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); - auto loader = CreateStringTableLoader(memory, searchPath); + auto loader = string_table::CreateLoaderT6(memory, searchPath); auto result = loader->CreateAsset("mp/cooltable.csv", context); REQUIRE(result.HasBeenSuccessful()); diff --git a/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp b/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp new file mode 100644 index 00000000..24311566 --- /dev/null +++ b/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp @@ -0,0 +1,156 @@ +#include "Game/T6/FontIcon/JsonLoaderFontIconT6.h" + +#include "Game/T6/CommonT6.h" +#include "Game/T6/GameT6.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace T6; +using namespace Catch; +using namespace std::literals; + +namespace +{ + void GivenMaterial(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* material = memory.Alloc(); + material->info.name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(material); + context.AddAsset(std::move(registration)); + } + + TEST_CASE("JsonLoaderFontIcon(T6): Can parse font icon", "[t6][font-icon][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("fonticon/test.json", + R"FONT_ICON( +{ + "$schema": "http://openassettools.dev/schema/font-icon.v1.json", + "_game": "t6", + "_type": "font-icon", + "_version": 1, + "entries": [ + { + "aliases": [ + "BUTTON_LUI_DPAD_ALL" + ], + "material": "xenonbutton_dpad_all", + "name": "XenonButtondpadAll", + "scale": { + "x": 1.0, + "y": 1.0 + }, + "size": 32 + }, + { + "aliasHashes": [ + 3463582170 + ], + "aliases": [], + "material": "ui_button_xenon_lstick_anim_d", + "name": "XenonButtonLStickAnimatedD", + "scale": { + "x": 1.5, + "y": 1.5 + }, + "size": 32 + }, + { + "aliases": [ + "BUTTON_MOVE", + "BUTTON_EMBLEM_MOVE", + "BUTTON_LUI_LEFT_STICK_UP", + "BUTTON_MOVESTICK" + ], + "material": "xenonbutton_ls", + "name": "XenonButtonStickAnimatedL", + "scale": { + "x": 1.0, + "y": 1.0 + }, + "size": 32 + } + ] +})FONT_ICON"); + + Zone zone("MockZone", 0, GameId::T6); + + MemoryManager memory; + AssetCreatorCollection creatorCollection(zone); + IgnoredAssetLookup ignoredAssetLookup; + AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); + + GivenMaterial("xenonbutton_dpad_all", context, memory); + GivenMaterial("ui_button_xenon_lstick_anim_d", context, memory); + GivenMaterial("xenonbutton_ls", context, memory); + + auto loader = font_icon::CreateJsonLoaderT6(memory, searchPath); + auto result = loader->CreateAsset("fonticon/test.csv", context); + REQUIRE(result.HasBeenSuccessful()); + + const auto* assetInfo = reinterpret_cast*>(result.GetAssetInfo()); + const auto* fontIcon = assetInfo->Asset(); + + REQUIRE(fontIcon->name == "fonticon/test.csv"s); + + REQUIRE(fontIcon->numEntries == 3); + REQUIRE(fontIcon->fontIconEntry != nullptr); + + const auto& entry0 = fontIcon->fontIconEntry[0]; + REQUIRE(entry0.fontIconName.string == "XenonButtondpadAll"s); + REQUIRE(entry0.fontIconName.hash == static_cast(0x9220177B)); + REQUIRE(entry0.fontIconMaterialHandle->info.name == "xenonbutton_dpad_all"s); + REQUIRE(entry0.fontIconSize == 32); + REQUIRE(entry0.xScale == 1.0f); + REQUIRE(entry0.yScale == 1.0f); + + const auto& entry1 = fontIcon->fontIconEntry[1]; + REQUIRE(entry1.fontIconName.string == "XenonButtonLStickAnimatedD"s); + REQUIRE(entry1.fontIconName.hash == static_cast(0x9E7D535A)); + REQUIRE(entry1.fontIconMaterialHandle->info.name == "ui_button_xenon_lstick_anim_d"s); + REQUIRE(entry1.fontIconSize == 32); + REQUIRE(entry1.xScale == 1.5f); + REQUIRE(entry1.yScale == 1.5f); + + const auto& entry2 = fontIcon->fontIconEntry[2]; + REQUIRE(entry2.fontIconName.string == "XenonButtonStickAnimatedL"s); + REQUIRE(entry2.fontIconName.hash == static_cast(0xABF58CD6)); + REQUIRE(entry2.fontIconMaterialHandle->info.name == "xenonbutton_ls"s); + REQUIRE(entry2.fontIconSize == 32); + REQUIRE(entry2.xScale == 1.0f); + REQUIRE(entry2.yScale == 1.0f); + + REQUIRE(fontIcon->numAliasEntries == 6); + REQUIRE(fontIcon->fontIconAlias != nullptr); + + auto& alias0 = fontIcon->fontIconAlias[0]; + REQUIRE(alias0.aliasHash == static_cast(0xCE7211DA)); + REQUIRE(alias0.buttonHash == entry1.fontIconName.hash); + + const auto& alias1 = fontIcon->fontIconAlias[1]; + REQUIRE(alias1.aliasHash == static_cast(0xDD3B363A)); + REQUIRE(alias1.buttonHash == entry0.fontIconName.hash); + + const auto& alias2 = fontIcon->fontIconAlias[2]; + REQUIRE(alias2.aliasHash == static_cast(0xFFFBB17)); + REQUIRE(alias2.buttonHash == entry2.fontIconName.hash); + + const auto& alias3 = fontIcon->fontIconAlias[3]; + REQUIRE(alias3.aliasHash == static_cast(0x1893B6A8)); + REQUIRE(alias3.buttonHash == entry2.fontIconName.hash); + + const auto& alias4 = fontIcon->fontIconAlias[4]; + REQUIRE(alias4.aliasHash == static_cast(0x18A33DB5)); + REQUIRE(alias4.buttonHash == entry2.fontIconName.hash); + + const auto& alias5 = fontIcon->fontIconAlias[5]; + REQUIRE(alias5.aliasHash == static_cast(0x5A15AB35)); + REQUIRE(alias5.buttonHash == entry2.fontIconName.hash); + } +} // namespace diff --git a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp index 2596dc78..95b3bfa1 100644 --- a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp +++ b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp @@ -62,9 +62,7 @@ namespace "10", "CASTS_SHADOW" ], - "hashIndex": 0, "layeredSurfaceTypes": 536870925, - "probeMipBits": 0, "sortKey": 4, "stateBits": [ { @@ -243,7 +241,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::T6)); + Zone zone("MockZone", 0, GameId::T6); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); @@ -255,7 +253,7 @@ namespace GivenImage("~-gmetal_ac_duct_c", context, memory); GivenTechset("wpc_lit_sm_r0c0n0s0_1zzj1138", context, memory); - auto loader = CreateMaterialLoader(memory, searchPath); + auto loader = material::CreateLoaderT6(memory, searchPath); auto result = loader->CreateAsset("wpc/metal_ac_duct", context); REQUIRE(result.HasBeenSuccessful()); diff --git a/test/ObjWritingTests.lua b/test/ObjWritingTests.lua index 8afcba6c..cda98707 100644 --- a/test/ObjWritingTests.lua +++ b/test/ObjWritingTests.lua @@ -3,6 +3,7 @@ ObjWritingTests = {} function ObjWritingTests:include(includes) if includes:handle(self:name()) then includedirs { + "%{wks.location}/src/ObjWriting", path.join(TestFolder(), "ObjWritingTests") } end diff --git a/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp b/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp new file mode 100644 index 00000000..66b71dd6 --- /dev/null +++ b/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp @@ -0,0 +1,572 @@ +#include "Game/IW3/Material/MaterialJsonDumperIW3.h" + +#include "Asset/AssetRegistration.h" +#include "Game/IW3/CommonIW3.h" +#include "Game/IW3/GameIW3.h" +#include "NormalizedJson.h" +#include "Pool/AssetPoolDynamic.h" +#include "SearchPath/MockOutputPath.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace IW3; +using namespace Catch; +using namespace std::literals; + +namespace +{ + GfxImage* GivenImage(const std::string& name, MemoryManager& memory) + { + auto* image = memory.Alloc(); + image->name = memory.Dup(name.c_str()); + + return image; + } + + MaterialTechniqueSet* GivenTechset(const std::string& name, MemoryManager& memory) + { + auto* techset = memory.Alloc(); + techset->name = memory.Dup(name.c_str()); + + return techset; + } + + void GivenMaterial(const std::string& name, AssetPool& pool, MemoryManager& memory) + { + auto* material = memory.Alloc(); + material->info.name = memory.Dup(name.c_str()); + material->info.gameFlags = 0x52; + material->info.sortKey = 4; + material->info.textureAtlasRowCount = 1; + material->info.textureAtlasColumnCount = 1; + material->info.surfaceTypeBits = 0x8000; + + constexpr int8_t stateBitsEntry[] = {0, 1, 2, 3, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, + -1, -1, -1, -1, 4, 4, 4, -1, -1, -1, -1, 5, -1, 1, 6, 1, -1}; + std::memcpy(material->stateBitsEntry, stateBitsEntry, sizeof(material->stateBitsEntry)); + + material->cameraRegion = CAMERA_REGION_LIT; + material->stateFlags = 57; + material->techniqueSet = GivenTechset("wc_l_sm_r0c0n0s0", memory); + + material->textureCount = 3; + material->textureTable = memory.Alloc(3); + + auto& textureDef0 = material->textureTable[0]; + textureDef0.u.image = GivenImage("~ch_plasterwall_long_spc-r-49~3c124bfe", memory); + textureDef0.nameHash = 0x34ecccb3; + textureDef0.nameStart = 's'; + textureDef0.nameEnd = 'p'; + textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef0.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + textureDef0.samplerState.clampU = 0; + textureDef0.samplerState.clampV = 0; + textureDef0.samplerState.clampW = 0; + textureDef0.semantic = TS_SPECULAR_MAP; + + auto& textureDef1 = material->textureTable[1]; + textureDef1.u.image = GivenImage("ch_plasterwall_long_nml", memory); + textureDef1.nameHash = 0x59d30d0f; + textureDef1.nameStart = 'n'; + textureDef1.nameEnd = 'p'; + textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef1.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; + textureDef1.samplerState.clampU = 0; + textureDef1.samplerState.clampV = 0; + textureDef1.samplerState.clampW = 0; + textureDef1.semantic = TS_NORMAL_MAP; + + auto& textureDef2 = material->textureTable[2]; + textureDef2.u.image = GivenImage("ch_plasterwall_long_col", memory); + textureDef2.nameHash = 0xa0ab1041; + textureDef2.nameStart = 'c'; + textureDef2.nameEnd = 'p'; + textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef2.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; + textureDef2.samplerState.clampU = 0; + textureDef2.samplerState.clampV = 0; + textureDef2.samplerState.clampW = 0; + textureDef2.semantic = TS_COLOR_MAP; + + material->constantCount = 2; + material->constantTable = memory.Alloc(2); + + auto& constantDef0 = material->constantTable[0]; + constantDef0.nameHash = 0x3d9994dc; + strncpy(constantDef0.name, "envMapParms", std::extent_v); + constantDef0.literal.x = 0.8f; + constantDef0.literal.y = 2.0f; + constantDef0.literal.z = 1.0f; + constantDef0.literal.w = 0.625f; + + auto& constantDef1 = material->constantTable[1]; + constantDef1.nameHash = 0xb60c3b3a; + strncpy(constantDef1.name, "colorTint", std::extent_v); + constantDef1.literal.x = 1.0f; + constantDef1.literal.y = 1.0f; + constantDef1.literal.z = 1.0f; + constantDef1.literal.w = 1.0f; + + material->stateBitsCount = 7; + material->stateBitsTable = memory.Alloc(7); + + auto& stateBits0 = material->stateBitsTable[0]; + stateBits0.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits0.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits0.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits0.loadBits.structured.alphaTestDisabled = 1; + stateBits0.loadBits.structured.alphaTest = 0; + stateBits0.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits0.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits0.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits0.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits0.loadBits.structured.colorWriteRgb = 0; + stateBits0.loadBits.structured.colorWriteAlpha = 0; + stateBits0.loadBits.structured.polymodeLine = 0; + stateBits0.loadBits.structured.depthWrite = 1; + stateBits0.loadBits.structured.depthTestDisabled = 0; + stateBits0.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits0.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits0.loadBits.structured.stencilFrontEnabled = 0; + stateBits0.loadBits.structured.stencilFrontPass = 0; + stateBits0.loadBits.structured.stencilFrontFail = 0; + stateBits0.loadBits.structured.stencilFrontZFail = 0; + stateBits0.loadBits.structured.stencilFrontFunc = 0; + stateBits0.loadBits.structured.stencilBackEnabled = 0; + stateBits0.loadBits.structured.stencilBackPass = 0; + stateBits0.loadBits.structured.stencilBackFail = 0; + stateBits0.loadBits.structured.stencilBackZFail = 0; + stateBits0.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits1 = material->stateBitsTable[1]; + stateBits1.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits1.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits1.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits1.loadBits.structured.alphaTestDisabled = 1; + stateBits1.loadBits.structured.alphaTest = 0; + stateBits1.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits1.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits1.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits1.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits1.loadBits.structured.colorWriteRgb = 1; + stateBits1.loadBits.structured.colorWriteAlpha = 1; + stateBits1.loadBits.structured.polymodeLine = 0; + stateBits1.loadBits.structured.depthWrite = 1; + stateBits1.loadBits.structured.depthTestDisabled = 0; + stateBits1.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits1.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits1.loadBits.structured.stencilFrontEnabled = 0; + stateBits1.loadBits.structured.stencilFrontPass = 0; + stateBits1.loadBits.structured.stencilFrontFail = 0; + stateBits1.loadBits.structured.stencilFrontZFail = 0; + stateBits1.loadBits.structured.stencilFrontFunc = 0; + stateBits1.loadBits.structured.stencilBackEnabled = 0; + stateBits1.loadBits.structured.stencilBackPass = 0; + stateBits1.loadBits.structured.stencilBackFail = 0; + stateBits1.loadBits.structured.stencilBackZFail = 0; + stateBits1.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits2 = material->stateBitsTable[2]; + stateBits2.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits2.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits2.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits2.loadBits.structured.alphaTestDisabled = 1; + stateBits2.loadBits.structured.alphaTest = 0; + stateBits2.loadBits.structured.cullFace = GFXS_CULL_NONE; + stateBits2.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits2.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits2.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits2.loadBits.structured.colorWriteRgb = 0; + stateBits2.loadBits.structured.colorWriteAlpha = 0; + stateBits2.loadBits.structured.polymodeLine = 0; + stateBits2.loadBits.structured.depthWrite = 1; + stateBits2.loadBits.structured.depthTestDisabled = 0; + stateBits2.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits2.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_SHADOWMAP; + stateBits2.loadBits.structured.stencilFrontEnabled = 0; + stateBits2.loadBits.structured.stencilFrontPass = 0; + stateBits2.loadBits.structured.stencilFrontFail = 0; + stateBits2.loadBits.structured.stencilFrontZFail = 0; + stateBits2.loadBits.structured.stencilFrontFunc = 0; + stateBits2.loadBits.structured.stencilBackEnabled = 0; + stateBits2.loadBits.structured.stencilBackPass = 0; + stateBits2.loadBits.structured.stencilBackFail = 0; + stateBits2.loadBits.structured.stencilBackZFail = 0; + stateBits2.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits3 = material->stateBitsTable[3]; + stateBits3.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits3.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits3.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits3.loadBits.structured.alphaTestDisabled = 1; + stateBits3.loadBits.structured.alphaTest = 0; + stateBits3.loadBits.structured.cullFace = GFXS_CULL_NONE; + stateBits3.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits3.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits3.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits3.loadBits.structured.colorWriteRgb = 1; + stateBits3.loadBits.structured.colorWriteAlpha = 1; + stateBits3.loadBits.structured.polymodeLine = 0; + stateBits3.loadBits.structured.depthWrite = 1; + stateBits3.loadBits.structured.depthTestDisabled = 0; + stateBits3.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits3.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits3.loadBits.structured.stencilFrontEnabled = 0; + stateBits3.loadBits.structured.stencilFrontPass = 0; + stateBits3.loadBits.structured.stencilFrontFail = 0; + stateBits3.loadBits.structured.stencilFrontZFail = 0; + stateBits3.loadBits.structured.stencilFrontFunc = 0; + stateBits3.loadBits.structured.stencilBackEnabled = 0; + stateBits3.loadBits.structured.stencilBackPass = 0; + stateBits3.loadBits.structured.stencilBackFail = 0; + stateBits3.loadBits.structured.stencilBackZFail = 0; + stateBits3.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits4 = material->stateBitsTable[4]; + stateBits4.loadBits.structured.srcBlendRgb = GFXS_BLEND_INVDESTALPHA; + stateBits4.loadBits.structured.dstBlendRgb = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.blendOpRgb = GFXS_BLENDOP_ADD; + stateBits4.loadBits.structured.alphaTestDisabled = 1; + stateBits4.loadBits.structured.alphaTest = 0; + stateBits4.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits4.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits4.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits4.loadBits.structured.colorWriteRgb = 1; + stateBits4.loadBits.structured.colorWriteAlpha = 1; + stateBits4.loadBits.structured.polymodeLine = 0; + stateBits4.loadBits.structured.depthWrite = 0; + stateBits4.loadBits.structured.depthTestDisabled = 0; + stateBits4.loadBits.structured.depthTest = GFXS_DEPTHTEST_EQUAL; + stateBits4.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits4.loadBits.structured.stencilFrontEnabled = 1; + stateBits4.loadBits.structured.stencilFrontPass = GFXS_STENCILOP_KEEP; + stateBits4.loadBits.structured.stencilFrontFail = GFXS_STENCILOP_KEEP; + stateBits4.loadBits.structured.stencilFrontZFail = GFXS_STENCILOP_KEEP; + stateBits4.loadBits.structured.stencilFrontFunc = GFXS_STENCILFUNC_EQUAL; + stateBits4.loadBits.structured.stencilBackEnabled = 0; + stateBits4.loadBits.structured.stencilBackPass = 0; + stateBits4.loadBits.structured.stencilBackFail = 0; + stateBits4.loadBits.structured.stencilBackZFail = 0; + stateBits4.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits5 = material->stateBitsTable[5]; + stateBits5.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits5.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits5.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits5.loadBits.structured.alphaTestDisabled = 1; + stateBits5.loadBits.structured.alphaTest = 0; + stateBits5.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits5.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits5.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits5.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits5.loadBits.structured.colorWriteRgb = 1; + stateBits5.loadBits.structured.colorWriteAlpha = 0; + stateBits5.loadBits.structured.polymodeLine = 1; + stateBits5.loadBits.structured.depthWrite = 0; + stateBits5.loadBits.structured.depthTestDisabled = 0; + stateBits5.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits5.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_2; + stateBits5.loadBits.structured.stencilFrontEnabled = 0; + stateBits5.loadBits.structured.stencilFrontPass = 0; + stateBits5.loadBits.structured.stencilFrontFail = 0; + stateBits5.loadBits.structured.stencilFrontZFail = 0; + stateBits5.loadBits.structured.stencilFrontFunc = 0; + stateBits5.loadBits.structured.stencilBackEnabled = 0; + stateBits5.loadBits.structured.stencilBackPass = 0; + stateBits5.loadBits.structured.stencilBackFail = 0; + stateBits5.loadBits.structured.stencilBackZFail = 0; + stateBits5.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits6 = material->stateBitsTable[6]; + stateBits6.loadBits.structured.srcBlendRgb = GFXS_BLEND_ZERO; + stateBits6.loadBits.structured.dstBlendRgb = GFXS_BLEND_INVSRCCOLOR; + stateBits6.loadBits.structured.blendOpRgb = GFXS_BLENDOP_ADD; + stateBits6.loadBits.structured.alphaTestDisabled = 1; + stateBits6.loadBits.structured.alphaTest = 0; + stateBits6.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits6.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits6.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits6.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits6.loadBits.structured.colorWriteRgb = 1; + stateBits6.loadBits.structured.colorWriteAlpha = 1; + stateBits6.loadBits.structured.polymodeLine = 0; + stateBits6.loadBits.structured.depthWrite = 0; + stateBits6.loadBits.structured.depthTestDisabled = 0; + stateBits6.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits6.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_2; + stateBits6.loadBits.structured.stencilFrontEnabled = 0; + stateBits6.loadBits.structured.stencilFrontPass = 0; + stateBits6.loadBits.structured.stencilFrontFail = 0; + stateBits6.loadBits.structured.stencilFrontZFail = 0; + stateBits6.loadBits.structured.stencilFrontFunc = 0; + stateBits6.loadBits.structured.stencilBackEnabled = 0; + stateBits6.loadBits.structured.stencilBackPass = 0; + stateBits6.loadBits.structured.stencilBackFail = 0; + stateBits6.loadBits.structured.stencilBackZFail = 0; + stateBits6.loadBits.structured.stencilBackFunc = 0; + + pool.AddAsset(std::make_unique>(ASSET_TYPE_MATERIAL, name, material)); + } + + TEST_CASE("DumperMaterial(IW3): Can dump material", "[iw3][material][assetdumper]") + { + std::string expected(R"MATERIAL( +{ + "$schema": "http://openassettools.dev/schema/material.v1.json", + "_game": "iw3", + "_type": "material", + "_version": 1, + "cameraRegion": "lit", + "constants": [ + { + "literal": [ + 0.800000011920929, + 2.0, + 1.0, + 0.625 + ], + "name": "envMapParms" + }, + { + "literal": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "name": "colorTint" + } + ], + "gameFlags": [ + "2", + "10", + "CASTS_SHADOW" + ], + "sortKey": 4, + "stateBits": [ + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offsetShadowmap", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "invdestalpha", + "stencilFront": { + "fail": "keep", + "func": "equal", + "pass": "keep", + "zfail": "keep" + } + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "invsrccolor", + "polygonOffset": "offset2", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "zero" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 3, + 1, + -1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + 5, + -1, + 1, + 6, + 1, + -1 + ], + "stateFlags": 57, + "surfaceTypeBits": 32768, + "techniqueSet": "wc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~ch_plasterwall_long_spc-r-49~3c124bfe", + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "ch_plasterwall_long_nml", + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "normalMap" + }, + { + "image": "ch_plasterwall_long_col", + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, GameId::IW3); + + MemoryManager memory; + MockSearchPath mockObjPath; + MockOutputPath mockOutput; + AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + + AssetPoolDynamic materialPool(0); + + GivenMaterial("wc/ch_plasterwall_long", materialPool, memory); + + material::JsonDumperIW3 dumper; + dumper.DumpPool(context, &materialPool); + + const auto* file = mockOutput.GetMockedFile("materials/wc/ch_plasterwall_long.json"); + REQUIRE(file); + REQUIRE(JsonNormalized(file->AsString()) == JsonNormalized(expected)); + } +} // namespace diff --git a/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp b/test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp similarity index 98% rename from test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp rename to test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp index 2d33bd2d..ea108592 100644 --- a/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp +++ b/test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp @@ -1,4 +1,4 @@ -#include "Game/IW4/Material/DumperMaterialIW4.h" +#include "Game/IW4/Material/MaterialJsonDumperIW4.h" #include "Asset/AssetRegistration.h" #include "Game/IW4/CommonIW4.h" @@ -62,7 +62,7 @@ namespace textureDef0.nameStart = 's'; textureDef0.nameEnd = 'p'; textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef0.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef0.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; textureDef0.samplerState.clampU = 0; textureDef0.samplerState.clampV = 0; textureDef0.samplerState.clampW = 0; @@ -74,7 +74,7 @@ namespace textureDef1.nameStart = 'n'; textureDef1.nameEnd = 'p'; textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef1.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef1.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; textureDef1.samplerState.clampU = 0; textureDef1.samplerState.clampV = 0; textureDef1.samplerState.clampW = 0; @@ -86,7 +86,7 @@ namespace textureDef2.nameStart = 'c'; textureDef2.nameEnd = 'p'; textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef2.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef2.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; textureDef2.samplerState.clampU = 0; textureDef2.samplerState.clampV = 0; textureDef2.samplerState.clampW = 0; @@ -295,6 +295,7 @@ namespace { std::string expected(R"MATERIAL( { + "$schema": "http://openassettools.dev/schema/material.v1.json", "_game": "iw4", "_type": "material", "_version": 1, @@ -531,7 +532,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW4)); + Zone zone("MockZone", 0, GameId::IW4); MemoryManager memory; MockSearchPath mockObjPath; @@ -542,7 +543,7 @@ namespace GivenMaterial("mc/ch_rubble01", materialPool, memory); - AssetDumperMaterial dumper; + material::JsonDumperIW4 dumper; dumper.DumpPool(context, &materialPool); const auto* file = mockOutput.GetMockedFile("materials/mc/ch_rubble01.json"); diff --git a/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp b/test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp similarity index 98% rename from test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp rename to test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp index cf545055..941c961d 100644 --- a/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp +++ b/test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp @@ -1,4 +1,4 @@ -#include "Game/IW5/Material/DumperMaterialIW5.h" +#include "Game/IW5/Material/MaterialJsonDumperIW5.h" #include "Asset/AssetRegistration.h" #include "Game/IW5/CommonIW5.h" @@ -62,7 +62,7 @@ namespace textureDef0.nameStart = 's'; textureDef0.nameEnd = 'p'; textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef0.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef0.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; textureDef0.samplerState.clampU = 0; textureDef0.samplerState.clampV = 0; textureDef0.samplerState.clampW = 0; @@ -74,7 +74,7 @@ namespace textureDef1.nameStart = 'n'; textureDef1.nameEnd = 'p'; textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef1.samplerState.mipMap = TEXTURE_FILTER_LINEAR; + textureDef1.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; textureDef1.samplerState.clampU = 0; textureDef1.samplerState.clampV = 0; textureDef1.samplerState.clampW = 0; @@ -86,7 +86,7 @@ namespace textureDef2.nameStart = 'c'; textureDef2.nameEnd = 'p'; textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef2.samplerState.mipMap = TEXTURE_FILTER_LINEAR; + textureDef2.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; textureDef2.samplerState.clampU = 0; textureDef2.samplerState.clampV = 0; textureDef2.samplerState.clampW = 0; @@ -324,6 +324,7 @@ namespace { std::string expected(R"MATERIAL( { + "$schema": "http://openassettools.dev/schema/material.v1.json", "_game": "iw5", "_type": "material", "_version": 1, @@ -584,7 +585,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW5)); + Zone zone("MockZone", 0, GameId::IW5); MemoryManager memory; MockSearchPath mockObjPath; @@ -595,7 +596,7 @@ namespace GivenMaterial("wc/me_metal_rust_02", materialPool, memory); - AssetDumperMaterial dumper; + material::JsonDumperIW5 dumper; dumper.DumpPool(context, &materialPool); const auto* file = mockOutput.GetMockedFile("materials/wc/me_metal_rust_02.json"); diff --git a/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp b/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp new file mode 100644 index 00000000..704ea0d5 --- /dev/null +++ b/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp @@ -0,0 +1,635 @@ +#include "Game/T5/Material/MaterialJsonDumperT5.h" + +#include "Asset/AssetRegistration.h" +#include "Game/T5/CommonT5.h" +#include "Game/T5/GameT5.h" +#include "NormalizedJson.h" +#include "Pool/AssetPoolDynamic.h" +#include "SearchPath/MockOutputPath.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace T5; +using namespace Catch; +using namespace std::literals; + +namespace +{ + GfxImage* GivenImage(const std::string& name, MemoryManager& memory) + { + auto* image = memory.Alloc(); + image->name = memory.Dup(name.c_str()); + + return image; + } + + MaterialTechniqueSet* GivenTechset(const std::string& name, MemoryManager& memory) + { + auto* techset = memory.Alloc(); + techset->name = memory.Dup(name.c_str()); + + return techset; + } + + void GivenMaterial(const std::string& name, AssetPool& pool, MemoryManager& memory) + { + auto* material = memory.Alloc(); + material->info.name = memory.Dup(name.c_str()); + material->info.gameFlags = 0x50; + material->info.sortKey = 4; + material->info.textureAtlasRowCount = 1; + material->info.textureAtlasColumnCount = 1; + material->info.surfaceTypeBits = 0x2; + material->info.layeredSurfaceTypes = 0x20000002; + + constexpr int8_t stateBitsEntry[] = {0, 1, 2, 3, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 4, 4, 4, 4, 4, -1, -1, -1, -1, 5, -1, 1, 1, -1}; + std::memcpy(material->stateBitsEntry, stateBitsEntry, sizeof(material->stateBitsEntry)); + + material->cameraRegion = CAMERA_REGION_LIT; + material->stateFlags = 121; + material->techniqueSet = GivenTechset("mc_l_sm_r0c0n0s0", memory); + + material->textureCount = 3; + material->textureTable = memory.Alloc(3); + + auto& textureDef0 = material->textureTable[0]; + textureDef0.u.image = GivenImage("~$white-rgb&~-rjun_art_brick_~a05e7627", memory); + textureDef0.nameHash = 0x34ecccb3; + textureDef0.nameStart = 's'; + textureDef0.nameEnd = 'p'; + textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef0.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + textureDef0.samplerState.clampU = 0; + textureDef0.samplerState.clampV = 0; + textureDef0.samplerState.clampW = 0; + textureDef0.semantic = TS_SPECULAR_MAP; + textureDef0.isMatureContent = false; + + auto& textureDef1 = material->textureTable[1]; + textureDef1.u.image = GivenImage("jun_art_brick_foundation_n", memory); + textureDef1.nameHash = 0x59d30d0f; + textureDef1.nameStart = 'n'; + textureDef1.nameEnd = 'p'; + textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef1.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + textureDef1.samplerState.clampU = 0; + textureDef1.samplerState.clampV = 0; + textureDef1.samplerState.clampW = 0; + textureDef1.semantic = TS_NORMAL_MAP; + textureDef1.isMatureContent = false; + + auto& textureDef2 = material->textureTable[2]; + textureDef2.u.image = GivenImage("~-gjun_art_brick_foundation_c", memory); + textureDef2.nameHash = 0xa0ab1041; + textureDef2.nameStart = 'c'; + textureDef2.nameEnd = 'p'; + textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef2.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + textureDef2.samplerState.clampU = 0; + textureDef2.samplerState.clampV = 0; + textureDef2.samplerState.clampW = 0; + textureDef2.semantic = TS_COLOR_MAP; + textureDef2.isMatureContent = false; + + material->constantCount = 2; + material->constantTable = memory.Alloc(2); + + auto& constantDef0 = material->constantTable[0]; + constantDef0.nameHash = 0x3d9994dc; + strncpy(constantDef0.name, "envMapParms", std::extent_v); + constantDef0.literal.x = 1.0f; + constantDef0.literal.y = 1.0f; + constantDef0.literal.z = 1.0f; + constantDef0.literal.w = 0.5f; + + auto& constantDef1 = material->constantTable[1]; + constantDef1.nameHash = 0xb4de4d82; + strncpy(constantDef1.name, "dynamicFolia", std::extent_v); + constantDef1.literal.x = 0.0f; + constantDef1.literal.y = 1.0f; + constantDef1.literal.z = 0.0f; + constantDef1.literal.w = 1.0f; + + material->stateBitsCount = 6; + material->stateBitsTable = memory.Alloc(6); + + auto& stateBits0 = material->stateBitsTable[0]; + stateBits0.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits0.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits0.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits0.loadBits.structured.alphaTestDisabled = 1; + stateBits0.loadBits.structured.alphaTest = 0; + stateBits0.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits0.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits0.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits0.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits0.loadBits.structured.colorWriteRgb = 0; + stateBits0.loadBits.structured.colorWriteAlpha = 0; + stateBits0.loadBits.structured.polymodeLine = 0; + stateBits0.loadBits.structured.depthWrite = 1; + stateBits0.loadBits.structured.depthTestDisabled = 0; + stateBits0.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits0.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits0.loadBits.structured.stencilFrontEnabled = 1; + stateBits0.loadBits.structured.stencilFrontPass = GFXS_STENCILOP_KEEP; + stateBits0.loadBits.structured.stencilFrontFail = GFXS_STENCILOP_KEEP; + stateBits0.loadBits.structured.stencilFrontZFail = GFXS_STENCILOP_KEEP; + stateBits0.loadBits.structured.stencilFrontFunc = GFXS_STENCILFUNC_EQUAL; + stateBits0.loadBits.structured.stencilBackEnabled = 0; + stateBits0.loadBits.structured.stencilBackPass = 0; + stateBits0.loadBits.structured.stencilBackFail = 0; + stateBits0.loadBits.structured.stencilBackZFail = 0; + stateBits0.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits1 = material->stateBitsTable[1]; + stateBits1.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits1.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits1.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits1.loadBits.structured.alphaTestDisabled = 1; + stateBits1.loadBits.structured.alphaTest = 0; + stateBits1.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits1.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits1.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits1.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits1.loadBits.structured.colorWriteRgb = 1; + stateBits1.loadBits.structured.colorWriteAlpha = 1; + stateBits1.loadBits.structured.polymodeLine = 0; + stateBits1.loadBits.structured.depthWrite = 1; + stateBits1.loadBits.structured.depthTestDisabled = 0; + stateBits1.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits1.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits1.loadBits.structured.stencilFrontEnabled = 0; + stateBits1.loadBits.structured.stencilFrontPass = 0; + stateBits1.loadBits.structured.stencilFrontFail = 0; + stateBits1.loadBits.structured.stencilFrontZFail = 0; + stateBits1.loadBits.structured.stencilFrontFunc = 0; + stateBits1.loadBits.structured.stencilBackEnabled = 0; + stateBits1.loadBits.structured.stencilBackPass = 0; + stateBits1.loadBits.structured.stencilBackFail = 0; + stateBits1.loadBits.structured.stencilBackZFail = 0; + stateBits1.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits2 = material->stateBitsTable[2]; + stateBits2.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits2.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits2.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits2.loadBits.structured.alphaTestDisabled = 1; + stateBits2.loadBits.structured.alphaTest = 0; + stateBits2.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits2.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits2.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits2.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits2.loadBits.structured.colorWriteRgb = 0; + stateBits2.loadBits.structured.colorWriteAlpha = 0; + stateBits2.loadBits.structured.polymodeLine = 0; + stateBits2.loadBits.structured.depthWrite = 1; + stateBits2.loadBits.structured.depthTestDisabled = 0; + stateBits2.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits2.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_SHADOWMAP; + stateBits2.loadBits.structured.stencilFrontEnabled = 0; + stateBits2.loadBits.structured.stencilFrontPass = 0; + stateBits2.loadBits.structured.stencilFrontFail = 0; + stateBits2.loadBits.structured.stencilFrontZFail = 0; + stateBits2.loadBits.structured.stencilFrontFunc = 0; + stateBits2.loadBits.structured.stencilBackEnabled = 0; + stateBits2.loadBits.structured.stencilBackPass = 0; + stateBits2.loadBits.structured.stencilBackFail = 0; + stateBits2.loadBits.structured.stencilBackZFail = 0; + stateBits2.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits3 = material->stateBitsTable[3]; + stateBits3.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits3.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits3.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits3.loadBits.structured.alphaTestDisabled = 1; + stateBits3.loadBits.structured.alphaTest = 0; + stateBits3.loadBits.structured.cullFace = GFXS_CULL_NONE; + stateBits3.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits3.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits3.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits3.loadBits.structured.colorWriteRgb = 1; + stateBits3.loadBits.structured.colorWriteAlpha = 1; + stateBits3.loadBits.structured.polymodeLine = 0; + stateBits3.loadBits.structured.depthWrite = 1; + stateBits3.loadBits.structured.depthTestDisabled = 0; + stateBits3.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits3.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits3.loadBits.structured.stencilFrontEnabled = 0; + stateBits3.loadBits.structured.stencilFrontPass = 0; + stateBits3.loadBits.structured.stencilFrontFail = 0; + stateBits3.loadBits.structured.stencilFrontZFail = 0; + stateBits3.loadBits.structured.stencilFrontFunc = 0; + stateBits3.loadBits.structured.stencilBackEnabled = 0; + stateBits3.loadBits.structured.stencilBackPass = 0; + stateBits3.loadBits.structured.stencilBackFail = 0; + stateBits3.loadBits.structured.stencilBackZFail = 0; + stateBits3.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits4 = material->stateBitsTable[4]; + stateBits4.loadBits.structured.srcBlendRgb = GFXS_BLEND_INVDESTALPHA; + stateBits4.loadBits.structured.dstBlendRgb = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.blendOpRgb = GFXS_BLENDOP_ADD; + stateBits4.loadBits.structured.alphaTestDisabled = 1; + stateBits4.loadBits.structured.alphaTest = 0; + stateBits4.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits4.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits4.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits4.loadBits.structured.colorWriteRgb = 1; + stateBits4.loadBits.structured.colorWriteAlpha = 1; + stateBits4.loadBits.structured.polymodeLine = 0; + stateBits4.loadBits.structured.depthWrite = 0; + stateBits4.loadBits.structured.depthTestDisabled = 0; + stateBits4.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits4.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits4.loadBits.structured.stencilFrontEnabled = 0; + stateBits4.loadBits.structured.stencilFrontPass = 0; + stateBits4.loadBits.structured.stencilFrontFail = 0; + stateBits4.loadBits.structured.stencilFrontZFail = 0; + stateBits4.loadBits.structured.stencilFrontFunc = 0; + stateBits4.loadBits.structured.stencilBackEnabled = 0; + stateBits4.loadBits.structured.stencilBackPass = 0; + stateBits4.loadBits.structured.stencilBackFail = 0; + stateBits4.loadBits.structured.stencilBackZFail = 0; + stateBits4.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits5 = material->stateBitsTable[5]; + stateBits5.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits5.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits5.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits5.loadBits.structured.alphaTestDisabled = 1; + stateBits5.loadBits.structured.alphaTest = 0; + stateBits5.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits5.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits5.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits5.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits5.loadBits.structured.colorWriteRgb = 1; + stateBits5.loadBits.structured.colorWriteAlpha = 0; + stateBits5.loadBits.structured.polymodeLine = 1; + stateBits5.loadBits.structured.depthWrite = 0; + stateBits5.loadBits.structured.depthTestDisabled = 0; + stateBits5.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits5.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_2; + stateBits5.loadBits.structured.stencilFrontEnabled = 0; + stateBits5.loadBits.structured.stencilFrontPass = 0; + stateBits5.loadBits.structured.stencilFrontFail = 0; + stateBits5.loadBits.structured.stencilFrontZFail = 0; + stateBits5.loadBits.structured.stencilFrontFunc = 0; + stateBits5.loadBits.structured.stencilBackEnabled = 0; + stateBits5.loadBits.structured.stencilBackPass = 0; + stateBits5.loadBits.structured.stencilBackFail = 0; + stateBits5.loadBits.structured.stencilBackZFail = 0; + stateBits5.loadBits.structured.stencilBackFunc = 0; + + pool.AddAsset(std::make_unique>(ASSET_TYPE_MATERIAL, name, material)); + } + + TEST_CASE("DumperMaterial(T5): Can dump material", "[t5][material][assetdumper]") + { + std::string expected(R"MATERIAL( +{ + "$schema": "http://openassettools.dev/schema/material.v1.json", + "_game": "t5", + "_type": "material", + "_version": 1, + "cameraRegion": "lit", + "constants": [ + { + "literal": [ + 1.0, + 1.0, + 1.0, + 0.5 + ], + "name": "envMapParms" + }, + { + "literal": [ + 0.0, + 1.0, + 0.0, + 1.0 + ], + "nameFragment": "dynamicFolia", + "nameHash": 3034467714 + } + ], + "gameFlags": [ + "10", + "CASTS_SHADOW" + ], + "layeredSurfaceTypes": 536870914, + "sortKey": 4, + "stateBits": [ + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one", + "stencilFront": { + "fail": "keep", + "func": "equal", + "pass": "keep", + "zfail": "keep" + } + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offsetShadowmap", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "invdestalpha" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 3, + 1, + -1, + -1, + -1, + -1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4, + 4, + 4, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + 5, + -1, + 1, + 1, + -1 + ], + "stateFlags": 121, + "surfaceTypeBits": 2, + "techniqueSet": "mc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~$white-rgb&~-rjun_art_brick_~a05e7627", + "isMatureContent": false, + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "jun_art_brick_foundation_n", + "isMatureContent": false, + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "normalMap" + }, + { + "image": "~-gjun_art_brick_foundation_c", + "isMatureContent": false, + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, GameId::T5); + + MemoryManager memory; + MockSearchPath mockObjPath; + MockOutputPath mockOutput; + AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + + AssetPoolDynamic materialPool(0); + + GivenMaterial("mc/ch_rubble01", materialPool, memory); + + material::JsonDumperT5 dumper; + dumper.DumpPool(context, &materialPool); + + const auto* file = mockOutput.GetMockedFile("materials/mc/ch_rubble01.json"); + REQUIRE(file); + REQUIRE(JsonNormalized(file->AsString()) == JsonNormalized(expected)); + } +} // namespace diff --git a/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp b/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp new file mode 100644 index 00000000..eaeb798d --- /dev/null +++ b/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp @@ -0,0 +1,161 @@ +#include "Game/T6/FontIcon/FontIconJsonDumperT6.h" + +#include "Asset/AssetRegistration.h" +#include "Game/T6/CommonT6.h" +#include "Game/T6/GameT6.h" +#include "NormalizedJson.h" +#include "Pool/AssetPoolDynamic.h" +#include "SearchPath/MockOutputPath.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace T6; +using namespace Catch; +using namespace std::literals; + +namespace +{ + Material* GivenMaterial(const std::string& name, MemoryManager& memory) + { + auto* material = memory.Alloc(); + material->info.name = memory.Dup(name.c_str()); + + return material; + } + + void GivenFontIcon(const std::string& name, AssetPool& pool, MemoryManager& memory) + { + auto* fontIcon = memory.Alloc(); + fontIcon->name = memory.Dup(name.c_str()); + + fontIcon->numEntries = 3; + fontIcon->fontIconEntry = memory.Alloc(fontIcon->numEntries); + + auto& entry0 = fontIcon->fontIconEntry[0]; + entry0.fontIconName.string = "XenonButtondpadAll"; + entry0.fontIconName.hash = Common::Com_HashString(entry0.fontIconName.string); + entry0.fontIconMaterialHandle = GivenMaterial("xenonbutton_dpad_all", memory); + entry0.fontIconSize = 32; + entry0.xScale = 1.0f; + entry0.yScale = 1.0f; + + auto& entry1 = fontIcon->fontIconEntry[1]; + entry1.fontIconName.string = "XenonButtonLStickAnimatedD"; + entry1.fontIconName.hash = Common::Com_HashString(entry1.fontIconName.string); + entry1.fontIconMaterialHandle = GivenMaterial("ui_button_xenon_lstick_anim_d", memory); + entry1.fontIconSize = 32; + entry1.xScale = 1.5f; + entry1.yScale = 1.5f; + + auto& entry2 = fontIcon->fontIconEntry[2]; + entry2.fontIconName.string = "XenonButtonStickAnimatedL"; + entry2.fontIconName.hash = Common::Com_HashString(entry2.fontIconName.string); + entry2.fontIconMaterialHandle = GivenMaterial("xenonbutton_ls", memory); + entry2.fontIconSize = 32; + entry2.xScale = 1.0f; + entry2.yScale = 1.0f; + + fontIcon->numAliasEntries = 6; + fontIcon->fontIconAlias = memory.Alloc(fontIcon->numAliasEntries); + + auto& alias0 = fontIcon->fontIconAlias[0]; + alias0.aliasHash = Common::Com_HashString("BUTTON_LUI_DPAD_ALL"); + alias0.buttonHash = entry0.fontIconName.hash; + + auto& alias1 = fontIcon->fontIconAlias[1]; + alias1.aliasHash = static_cast(0xCE7211DA); + alias1.buttonHash = entry1.fontIconName.hash; + + auto& alias2 = fontIcon->fontIconAlias[2]; + alias2.aliasHash = Common::Com_HashString("BUTTON_MOVE"); + alias2.buttonHash = entry2.fontIconName.hash; + + auto& alias3 = fontIcon->fontIconAlias[3]; + alias3.aliasHash = Common::Com_HashString("BUTTON_EMBLEM_MOVE"); + alias3.buttonHash = entry2.fontIconName.hash; + + auto& alias4 = fontIcon->fontIconAlias[4]; + alias4.aliasHash = Common::Com_HashString("BUTTON_LUI_LEFT_STICK_UP"); + alias4.buttonHash = entry2.fontIconName.hash; + + auto& alias5 = fontIcon->fontIconAlias[5]; + alias5.aliasHash = Common::Com_HashString("BUTTON_MOVESTICK"); + alias5.buttonHash = entry2.fontIconName.hash; + + pool.AddAsset(std::make_unique>(ASSET_TYPE_FONTICON, name, fontIcon)); + } + + TEST_CASE("DumperFontIconJson(T6): Can dump font icon", "[t6][font-icon][assetdumper]") + { + std::string expected(R"FONT_ICON( +{ + "$schema": "http://openassettools.dev/schema/font-icon.v1.json", + "_game": "t6", + "_type": "font-icon", + "_version": 1, + "entries": [ + { + "aliases": [ + "BUTTON_LUI_DPAD_ALL" + ], + "material": "xenonbutton_dpad_all", + "name": "XenonButtondpadAll", + "scale": { + "x": 1.0, + "y": 1.0 + }, + "size": 32 + }, + { + "aliasHashes": [ + 3463582170 + ], + "aliases": [], + "material": "ui_button_xenon_lstick_anim_d", + "name": "XenonButtonLStickAnimatedD", + "scale": { + "x": 1.5, + "y": 1.5 + }, + "size": 32 + }, + { + "aliases": [ + "BUTTON_MOVE", + "BUTTON_EMBLEM_MOVE", + "BUTTON_LUI_LEFT_STICK_UP", + "BUTTON_MOVESTICK" + ], + "material": "xenonbutton_ls", + "name": "XenonButtonStickAnimatedL", + "scale": { + "x": 1.0, + "y": 1.0 + }, + "size": 32 + } + ] +})FONT_ICON"); + + Zone zone("MockZone", 0, GameId::T6); + + MemoryManager memory; + MockSearchPath mockObjPath; + MockOutputPath mockOutput; + AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + + AssetPoolDynamic fontIconPool(0); + GivenFontIcon("fonticon/test.csv", fontIconPool, memory); + + font_icon::JsonDumperT6 dumper; + dumper.DumpPool(context, &fontIconPool); + + const auto* file = mockOutput.GetMockedFile("fonticon/test.json"); + REQUIRE(file); + REQUIRE(JsonNormalized(file->AsString()) == JsonNormalized(expected)); + } +} // namespace diff --git a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp b/test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp similarity index 98% rename from test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp rename to test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp index 4c6e5f50..f7b70376 100644 --- a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp +++ b/test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp @@ -1,4 +1,4 @@ -#include "Game/T6/Material/DumperMaterialT6.h" +#include "Game/T6/Material/MaterialJsonDumperT6.h" #include "Asset/AssetRegistration.h" #include "Game/T6/CommonT6.h" @@ -261,6 +261,7 @@ namespace { std::string expected(R"MATERIAL( { + "$schema": "http://openassettools.dev/schema/material.v1.json", "_game": "t6", "_type": "material", "_version": 1, @@ -282,9 +283,7 @@ namespace "10", "CASTS_SHADOW" ], - "hashIndex": 0, "layeredSurfaceTypes": 536870925, - "probeMipBits": 0, "sortKey": 4, "stateBits": [ { @@ -463,7 +462,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, IGame::GetGameById(GameId::T6)); + Zone zone("MockZone", 0, GameId::T6); MemoryManager memory; MockSearchPath mockObjPath; @@ -471,10 +470,9 @@ namespace AssetDumpingContext context(zone, "", mockOutput, mockObjPath); AssetPoolDynamic materialPool(0); - GivenMaterial("wpc/metal_ac_duct", materialPool, memory); - AssetDumperMaterial dumper; + material::JsonDumperT6 dumper; dumper.DumpPool(context, &materialPool); const auto* file = mockOutput.GetMockedFile("materials/wpc/metal_ac_duct.json"); diff --git a/thirdparty/catch2 b/thirdparty/catch2 index 038ee6ea..8e4ab5dd 160000 --- a/thirdparty/catch2 +++ b/thirdparty/catch2 @@ -1 +1 @@ -Subproject commit 038ee6ea13f341807547657d42333ad339eac2fe +Subproject commit 8e4ab5dd8f5a9b6fed373c58a9aa07a82e528827 diff --git a/thirdparty/eigen b/thirdparty/eigen index 8ac2fb07..1edf360e 160000 --- a/thirdparty/eigen +++ b/thirdparty/eigen @@ -1 +1 @@ -Subproject commit 8ac2fb077dfcdf9a6fa78a871c494594dfa795b0 +Subproject commit 1edf360e3c2b0c0d4872f6228676e27c42e7f416 diff --git a/thirdparty/json b/thirdparty/json index d33ecd3f..3ed64e50 160000 --- a/thirdparty/json +++ b/thirdparty/json @@ -1 +1 @@ -Subproject commit d33ecd3f3bd11e30aa8bbabb00e0a9cd3f2456d8 +Subproject commit 3ed64e502a6371311af3c2f309e6525b2f5f6f18 diff --git a/thirdparty/libtomcrypt b/thirdparty/libtomcrypt index d448df19..5edb54e5 160000 --- a/thirdparty/libtomcrypt +++ b/thirdparty/libtomcrypt @@ -1 +1 @@ -Subproject commit d448df1938e8988bcdb0eed6591387e82b26874b +Subproject commit 5edb54e52292c90113af6b3a72fd9147ba727f86 diff --git a/thirdparty/lz4 b/thirdparty/lz4 index cacca377..f8e36503 160000 --- a/thirdparty/lz4 +++ b/thirdparty/lz4 @@ -1 +1 @@ -Subproject commit cacca37747572717ceb1f156eb9840644205ca4f +Subproject commit f8e365036efc9e94bda616432eb8ad942c236d76 diff --git a/thirdparty/lzx.lua b/thirdparty/lzx.lua new file mode 100644 index 00000000..1674eb4d --- /dev/null +++ b/thirdparty/lzx.lua @@ -0,0 +1,48 @@ +lzx = {} + +function lzx:include(includes) + if includes:handle(self:name()) then + includedirs { + path.join(ThirdPartyFolder(), "lzx") + } + end +end + +function lzx:link(links) + links:add(self:name()) +end + +function lzx:use() + +end + +function lzx:name() + return "lzx" +end + +function lzx:project() + local folder = ThirdPartyFolder() + local includes = Includes:create() + + project(self:name()) + targetdir(TargetDirectoryLib) + location "%{wks.location}/thirdparty/%{prj.name}" + kind "StaticLib" + language "C" + + files { + path.join(folder, "lzx/*.h"), + path.join(folder, "lzx/*.c") + } + + defines { + "_CRT_SECURE_NO_WARNINGS", + "_CRT_NONSTDC_NO_DEPRECATE" + } + + + self:include(includes) + + -- Disable warnings. They do not have any value to us since it is not our code. + warnings "off" +end diff --git a/thirdparty/lzx/lzx.c b/thirdparty/lzx/lzx.c new file mode 100644 index 00000000..c1199a72 --- /dev/null +++ b/thirdparty/lzx/lzx.c @@ -0,0 +1,808 @@ +// Modified version of the code from Wine: +// https://gitlab.winehq.org/wine/wine/-/blob/fcc40a07909dc7626b6d1e2ec73f823d828a47e8/dlls/itss/lzx.c + +/*************************************************************************** + * lzx.c - LZX decompression routines * + * ------------------- * + * * + * maintainer: Jed Wing * + * source: modified lzx.c from cabextract v0.5 * + * notes: This file was taken from cabextract v0.5, which was, * + * itself, a modified version of the lzx decompression code * + * from unlzx. * + * * + * platforms: In its current incarnation, this file has been tested on * + * two different Linux platforms (one, redhat-based, with a * + * 2.1.2 glibc and gcc 2.95.x, and the other, Debian, with * + * 2.2.4 glibc and both gcc 2.95.4 and gcc 3.0.2). Both were * + * Intel x86 compatible machines. * + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. Note that an exemption to this * + * license has been granted by Stuart Caie for the purposes of * + * distribution with chmlib. This does not, to the best of my * + * knowledge, constitute a change in the license of this (the LZX) code * + * in general. * + * * + ***************************************************************************/ + +#include "lzx.h" +#include +#include +#include +#include + +/* some constants defined by the LZX specification */ +#define LZX_MIN_MATCH 2 +/* #define LZX_MAX_MATCH 257 */ +#define LZX_NUM_CHARS 256 +#define LZX_BLOCKTYPE_INVALID 0 /* also blocktypes 4-7 invalid */ +#define LZX_BLOCKTYPE_VERBATIM 1 +#define LZX_BLOCKTYPE_ALIGNED 2 +#define LZX_BLOCKTYPE_UNCOMPRESSED 3 +#define LZX_PRETREE_NUM_ELEMENTS 20 +#define LZX_ALIGNED_NUM_ELEMENTS 8 /* aligned offset tree #elements */ +#define LZX_NUM_PRIMARY_LENGTHS 7 /* this one missing from spec! */ +#define LZX_NUM_SECONDARY_LENGTHS 249 /* length tree #elements */ + +/* LZX huffman defines: tweak tablebits as desired */ +#define LZX_PRETREE_MAXSYMBOLS LZX_PRETREE_NUM_ELEMENTS +#define LZX_PRETREE_TABLEBITS 6 +#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50 * 8) +#define LZX_MAINTREE_TABLEBITS 12 +#define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS + 1) +#define LZX_LENGTH_TABLEBITS 12 +#define LZX_ALIGNED_MAXSYMBOLS LZX_ALIGNED_NUM_ELEMENTS +#define LZX_ALIGNED_TABLEBITS 7 + +#define LZX_LENTABLE_SAFETY 64 /* we allow length table decoding overruns */ + +#define LZX_DECLARE_TABLE(tbl) \ + uint16_t tbl## _table[(1 << LZX_## tbl## _TABLEBITS) + (LZX_## tbl## _MAXSYMBOLS << 1)]; \ + uint8_t tbl## _len[LZX_## tbl## _MAXSYMBOLS + LZX_LENTABLE_SAFETY] + +struct lzx_state { + uint8_t* window; /* the actual decoding window */ + uint32_t window_size; /* window size (32Kb through 2Mb) */ + uint32_t actual_size; /* window size when it was first allocated */ + uint32_t window_posn; /* current offset within the window */ + uint32_t R0, R1, R2; /* for the LRU offset system */ + uint16_t main_elements; /* number of main tree elements */ + int header_read; /* have we started decoding at all yet? */ + uint16_t block_type; /* type of this block */ + uint32_t block_length; /* uncompressed length of this block */ + uint32_t block_remaining; /* uncompressed bytes still left to decode */ + uint32_t frames_read; /* the number of CFDATA blocks processed */ + int32_t intel_filesize; /* magic header value used for transform */ + int32_t intel_curpos; /* current offset in transform space */ + int intel_started; /* have we seen any translatable data yet? */ + + LZX_DECLARE_TABLE(PRETREE); + LZX_DECLARE_TABLE(MAINTREE); + LZX_DECLARE_TABLE(LENGTH); + LZX_DECLARE_TABLE(ALIGNED); +}; + +/* LZX decruncher */ + +/* Microsoft's LZX document and their implementation of the + * com.ms.util.cab Java package do not concur. + * + * In the LZX document, there is a table showing the correlation between + * window size and the number of position slots. It states that the 1MB + * window = 40 slots and the 2MB window = 42 slots. In the implementation, + * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the + * first slot whose position base is equal to or more than the required + * window size'. This would explain why other tables in the document refer + * to 50 slots rather than 42. + * + * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode + * is not defined in the specification. + * + * The LZX document does not state the uncompressed block has an + * uncompressed length field. Where does this length field come from, so + * we can know how large the block is? The implementation has it as the 24 + * bits following after the 3 blocktype bits, before the alignment + * padding. + * + * The LZX document states that aligned offset blocks have their aligned + * offset huffman tree AFTER the main and length trees. The implementation + * suggests that the aligned offset tree is BEFORE the main and length + * trees. + * + * The LZX document decoding algorithm states that, in an aligned offset + * block, if an extra_bits value is 1, 2 or 3, then that number of bits + * should be read and the result added to the match offset. This is + * correct for 1 and 2, but not 3, where just a huffman symbol (using the + * aligned tree) should be read. + * + * Regarding the E8 preprocessing, the LZX document states 'No translation + * may be performed on the last 6 bytes of the input block'. This is + * correct. However, the pseudocode provided checks for the *E8 leader* + * up to the last 6 bytes. If the leader appears between -10 and -7 bytes + * from the end, this would cause the next four bytes to be modified, at + * least one of which would be in the last 6 bytes, which is not allowed + * according to the spec. + * + * The specification states that the huffman trees must always contain at + * least one element. However, many CAB files contain blocks where the + * length tree is completely empty (because there are no matches), and + * this is expected to succeed. + */ + +/* LZX uses what it calls 'position slots' to represent match offsets. + * What this means is that a small 'position slot' number and a small + * offset from that slot are encoded instead of one large offset for + * every match. + * - position_base is an index to the position slot bases + * - extra_bits states how many bits of offset-from-base data is needed. + */ +static const uint8_t extra_bits[51] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, + 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17}; + +static const uint32_t position_base[51] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, + 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, + 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, + 98304, 131072, 196608, 262144, 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, + 1310720, 1441792, 1572864, 1703936, 1835008, 1966080, 2097152}; + +struct lzx_state* lzx_init(int window) { + struct lzx_state* pState = NULL; + uint32_t wndsize = 1 << window; + int i, posn_slots; + + /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */ + /* if a previously allocated window is big enough, keep it */ + if (window < 15 || window > 21) + return NULL; + + /* allocate state and associated window */ + pState = (struct lzx_state*)malloc(sizeof(struct lzx_state)); + if (!pState || !(pState->window = (uint8_t*)malloc(wndsize))) { + free(pState); + return NULL; + } + pState->actual_size = wndsize; + pState->window_size = wndsize; + + /* calculate required position slots */ + if (window == 20) + posn_slots = 42; + else if (window == 21) + posn_slots = 50; + else + posn_slots = window << 1; + + /** alternatively **/ + /* posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */ + + /* initialize other state */ + pState->R0 = pState->R1 = pState->R2 = 1; + pState->main_elements = LZX_NUM_CHARS + (posn_slots << 3); + pState->header_read = 0; + pState->frames_read = 0; + pState->block_remaining = 0; + pState->block_type = LZX_BLOCKTYPE_INVALID; + pState->intel_curpos = 0; + pState->intel_started = 0; + pState->window_posn = 0; + + /* initialise tables to 0 (because deltas will be applied to them) */ + for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) + pState->MAINTREE_len[i] = 0; + for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) + pState->LENGTH_len[i] = 0; + + return pState; +} + +void lzx_teardown(struct lzx_state* pState) { + if (pState) { + if (pState->window) + free(pState->window); + free(pState); + } +} + +void lzx_reset(struct lzx_state* pState) { + int i; + + pState->R0 = pState->R1 = pState->R2 = 1; + pState->header_read = 0; + pState->frames_read = 0; + pState->block_remaining = 0; + pState->block_type = LZX_BLOCKTYPE_INVALID; + pState->intel_curpos = 0; + pState->intel_started = 0; + pState->window_posn = 0; + + for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) { + pState->MAINTREE_len[i] = 0; + } + + for (i = 0; i < LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) { + pState->LENGTH_len[i] = 0; + } +} + +/* Bitstream reading macros: + * + * INIT_BITSTREAM should be used first to set up the system + * READ_BITS(var,n) takes N bits from the buffer and puts them in var + * + * ENSURE_BITS(n) ensures there are at least N bits in the bit buffer + * PEEK_BITS(n) extracts (without removing) N bits from the bit buffer + * REMOVE_BITS(n) removes N bits from the bit buffer + * + * These bit access routines work by using the area beyond the MSB and the + * LSB as a free source of zeroes. This avoids having to mask any bits. + * So we have to know the bit width of the bitbuffer variable. This is + * sizeof(uint32_t) * 8, also defined as uint32_t_BITS + */ + +/* number of bits in uint32_t. Note: This must be at multiple of 16, and at + * least 32 for the bitbuffer code to work (ie, it must be able to ensure + * up to 17 bits - that's adding 16 bits when there's one bit left, or + * adding 32 bits when there are no bits left. The code should work fine + * for machines where uint32_t >= 32 bits. + */ +#define uint32_t_BITS (sizeof(uint32_t) << 3) + +#define INIT_BITSTREAM \ + do { \ + bitsleft = 0; \ + bitbuf = 0; \ + } while (0) + +#define ENSURE_BITS(n) \ + while (bitsleft < (n)) { \ + bitbuf |= ((inpos[1] << 8) | inpos[0]) << (uint32_t_BITS - 16 - bitsleft); \ + bitsleft += 16; \ + inpos += 2; \ + } + +#define PEEK_BITS(n) (bitbuf >> (uint32_t_BITS - (n))) +#define REMOVE_BITS(n) ((bitbuf <<= (n)), (bitsleft -= (n))) + +#define READ_BITS(v, n) \ + do { \ + ENSURE_BITS(n); \ + (v) = PEEK_BITS(n); \ + REMOVE_BITS(n); \ + } while (0) + +/* Huffman macros */ + +#define TABLEBITS(tbl) (LZX_## tbl## _TABLEBITS) +#define MAXSYMBOLS(tbl) (LZX_## tbl## _MAXSYMBOLS) +#define SYMTABLE(tbl) (pState->tbl## _table) +#define LENTABLE(tbl) (pState->tbl## _len) + +/* BUILD_TABLE(tablename) builds a huffman lookup table from code lengths. + * In reality, it just calls make_decode_table() with the appropriate + * values - they're all fixed by some #defines anyway, so there's no point + * writing each call out in full by hand. + */ +#define BUILD_TABLE(tbl) \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), LENTABLE(tbl), SYMTABLE(tbl))) { \ + return DECR_ILLEGALDATA; \ + } + +/* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the + * bitstream using the stated table and puts it in var. + */ +#define READ_HUFFSYM(tbl, var) \ + do { \ + ENSURE_BITS(16); \ + hufftbl = SYMTABLE(tbl); \ + if ((i = hufftbl[PEEK_BITS(TABLEBITS(tbl))]) >= MAXSYMBOLS(tbl)) { \ + j = 1 << (uint32_t_BITS - TABLEBITS(tbl)); \ + do { \ + j >>= 1; \ + i <<= 1; \ + i |= (bitbuf & j) ? 1 : 0; \ + if (!j) { \ + return DECR_ILLEGALDATA; \ + } \ + } while ((i = hufftbl[i]) >= MAXSYMBOLS(tbl)); \ + } \ + j = LENTABLE(tbl)[(var) = i]; \ + REMOVE_BITS(j); \ + } while (0) + +/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols + * first to last in the given table. The code lengths are stored in their + * own special LZX way. + */ +#define READ_LENGTHS(tbl, first, last) \ + do { \ + lb.bb = bitbuf; \ + lb.bl = bitsleft; \ + lb.ip = inpos; \ + if (lzx_read_lens(pState, LENTABLE(tbl), (first), (last), &lb)) { \ + return DECR_ILLEGALDATA; \ + } \ + bitbuf = lb.bb; \ + bitsleft = lb.bl; \ + inpos = lb.ip; \ + } while (0) + +/* make_decode_table(nsyms, nbits, length[], table[]) + * + * This function was coded by David Tritscher. It builds a fast huffman + * decoding table out of just a canonical huffman code lengths table. + * + * nsyms = total number of symbols in this huffman tree. + * nbits = any symbols with a code length of nbits or less can be decoded + * in one lookup of the table. + * length = A table to get code lengths from [0 to syms-1] + * table = The table to fill up with decoded symbols and pointers. + * + * Returns 0 for OK or 1 for error + */ +static int make_decode_table(uint32_t nsyms, uint32_t nbits, uint8_t* length, uint16_t* table) { + uint16_t sym; + uint32_t leaf; + uint8_t bit_num = 1; + uint32_t fill; + uint32_t pos = 0; /* the current position in the decode table */ + uint32_t table_mask = 1 << nbits; + uint32_t bit_mask = table_mask >> 1; /* don't do 0 length codes */ + uint32_t next_symbol = bit_mask; /* base of allocation for long codes */ + + /* fill entries for codes short enough for a direct mapping */ + while (bit_num <= nbits) { + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] != bit_num) { + continue; + } + leaf = pos; + + if ((pos += bit_mask) > table_mask) + return 1; /* table overrun */ + + /* fill all possible lookups of this symbol with the symbol itself */ + fill = bit_mask; + while (fill-- > 0) { + table[leaf++] = sym; + } + } + bit_mask >>= 1; + bit_num++; + } + + /* if there are any codes longer than nbits */ + if (pos != table_mask) { + /* clear the remainder of the table */ + for (sym = pos; sym < table_mask; sym++) { + table[sym] = 0; + } + + /* give ourselves room for codes to grow by up to 16 more bits */ + pos <<= 16; + table_mask <<= 16; + bit_mask = 1 << 15; + + while (bit_num <= 16) { + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] != bit_num) { + continue; + } + leaf = pos >> 16; + for (fill = 0; fill < bit_num - nbits; fill++) { + /* if this path hasn't been taken yet, 'allocate' two entries */ + if (table[leaf] == 0) { + table[(next_symbol << 1)] = 0; + table[(next_symbol << 1) + 1] = 0; + table[leaf] = next_symbol++; + } + /* follow the path and select either left or right for next bit */ + leaf = table[leaf] << 1; + if ((pos >> (15 - fill)) & 1) + leaf++; + } + table[leaf] = sym; + + if ((pos += bit_mask) > table_mask) + return 1; /* table overflow */ + } + bit_mask >>= 1; + bit_num++; + } + } + + /* full table? */ + if (pos == table_mask) + return 0; + + /* either erroneous table, or all elements are 0 - let's find out. */ + for (sym = 0; sym < nsyms; sym++) { + if (length[sym]) + return 1; + } + return 0; +} + +struct lzx_bits { + uint32_t bb; + int bl; + uint8_t* ip; +}; + +static int lzx_read_lens(struct lzx_state* pState, uint8_t* lens, uint32_t first, uint32_t last, + struct lzx_bits* lb) { + uint32_t i, j, x, y; + int z; + + uint32_t bitbuf = lb->bb; + int bitsleft = lb->bl; + uint8_t* inpos = lb->ip; + uint16_t* hufftbl; + + for (x = 0; x < 20; x++) { + READ_BITS(y, 4); + LENTABLE(PRETREE)[x] = y; + } + BUILD_TABLE(PRETREE); + + for (x = first; x < last;) { + READ_HUFFSYM(PRETREE, z); + if (z == 17) { + READ_BITS(y, 4); + y += 4; + while (y--) + lens[x++] = 0; + } else if (z == 18) { + READ_BITS(y, 5); + y += 20; + while (y--) + lens[x++] = 0; + } else if (z == 19) { + READ_BITS(y, 1); + y += 4; + READ_HUFFSYM(PRETREE, z); + z = lens[x] - z; + if (z < 0) + z += 17; + while (y--) + lens[x++] = z; + } else { + z = lens[x] - z; + if (z < 0) + z += 17; + lens[x++] = z; + } + } + + lb->bb = bitbuf; + lb->bl = bitsleft; + lb->ip = inpos; + return 0; +} + +int lzx_decompress(struct lzx_state* pState, const unsigned char* inpos, unsigned char* outpos, int inlen, + int outlen) { + const uint8_t* endinp = inpos + inlen; + uint8_t* window = pState->window; + uint8_t* runsrc, *rundest; + uint16_t* hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */ + + uint32_t window_posn = pState->window_posn; + uint32_t window_size = pState->window_size; + uint32_t R0 = pState->R0; + uint32_t R1 = pState->R1; + uint32_t R2 = pState->R2; + + uint32_t bitbuf; + int bitsleft; + uint32_t match_offset, i, j, k; /* ijk used in READ_HUFFSYM macro */ + struct lzx_bits lb; /* used in READ_LENGTHS macro */ + + int togo = outlen, this_run, main_element, aligned_bits; + int match_length, length_footer, extra, verbatim_bits; + + INIT_BITSTREAM; + + /* read header if necessary */ + if (!pState->header_read) { + i = j = 0; + READ_BITS(k, 1); + if (k) { + READ_BITS(i, 16); + READ_BITS(j, 16); + } + pState->intel_filesize = (i << 16) | j; /* or 0 if not encoded */ + pState->header_read = 1; + } + + /* main decoding loop */ + while (togo > 0) { + /* last block finished, new block expected */ + if (pState->block_remaining == 0) { + if (pState->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) { + if (pState->block_length & 1) + inpos++; /* realign bitstream to word */ + INIT_BITSTREAM; + } + + READ_BITS(pState->block_type, 3); + READ_BITS(i, 16); + READ_BITS(j, 8); + pState->block_remaining = pState->block_length = (i << 8) | j; + + switch (pState->block_type) { + case LZX_BLOCKTYPE_ALIGNED: + for (i = 0; i < 8; i++) { + READ_BITS(j, 3); + LENTABLE(ALIGNED)[i] = j; + } + BUILD_TABLE(ALIGNED); + /* rest of aligned header is same as verbatim */ + + case LZX_BLOCKTYPE_VERBATIM: + READ_LENGTHS(MAINTREE, 0, 256); + READ_LENGTHS(MAINTREE, 256, pState->main_elements); + BUILD_TABLE(MAINTREE); + if (LENTABLE(MAINTREE)[0xE8] != 0) + pState->intel_started = 1; + + READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS); + BUILD_TABLE(LENGTH); + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + pState->intel_started = 1; /* because we can't assume otherwise */ + ENSURE_BITS(16); /* get up to 16 pad bits into the buffer */ + if (bitsleft > 16) + inpos -= 2; /* and align the bitstream! */ + R0 = inpos[0] | (inpos[1] << 8) | (inpos[2] << 16) | (inpos[3] << 24); + inpos += 4; + R1 = inpos[0] | (inpos[1] << 8) | (inpos[2] << 16) | (inpos[3] << 24); + inpos += 4; + R2 = inpos[0] | (inpos[1] << 8) | (inpos[2] << 16) | (inpos[3] << 24); + inpos += 4; + break; + + default: + return DECR_ILLEGALDATA; + } + } + + /* buffer exhaustion check */ + if (inpos > endinp) { + /* it's possible to have a file where the next run is less than + * 16 bits in size. In this case, the READ_HUFFSYM() macro used + * in building the tables will exhaust the buffer, so we should + * allow for this, but not allow those accidentally read bits to + * be used (so we check that there are at least 16 bits + * remaining - in this boundary case they aren't really part of + * the compressed data) + */ + if (inpos > (endinp + 2) || bitsleft < 16) + return DECR_ILLEGALDATA; + } + + while ((this_run = pState->block_remaining) > 0 && togo > 0) { + if (this_run > togo) + this_run = togo; + togo -= this_run; + pState->block_remaining -= this_run; + + /* apply 2^x-1 mask */ + window_posn &= window_size - 1; + /* runs can't straddle the window wraparound */ + if ((window_posn + this_run) > window_size) + return DECR_DATAFORMAT; + + switch (pState->block_type) { + case LZX_BLOCKTYPE_VERBATIM: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + match_offset = main_element >> 3; + + if (match_offset > 2) { + /* not repeated offset */ + if (match_offset != 3) { + extra = extra_bits[match_offset]; + READ_BITS(verbatim_bits, extra); + match_offset = position_base[match_offset] - 2 + verbatim_bits; + } else { + match_offset = 1; + } + + /* update repeated offset LRU queue */ + R2 = R1; + R1 = R0; + R0 = match_offset; + } else if (match_offset == 0) { + match_offset = R0; + } else if (match_offset == 1) { + match_offset = R1; + R1 = R0; + R0 = match_offset; + } else /* match_offset == 2 */ { + match_offset = R2; + R2 = R0; + R0 = match_offset; + } + + rundest = window + window_posn; + runsrc = rundest - match_offset; + window_posn += match_length; + if (window_posn > window_size) + return DECR_ILLEGALDATA; + this_run -= match_length; + + /* copy any wrapped around source data */ + while ((runsrc < window) && (match_length-- > 0)) { + *rundest++ = *(runsrc + window_size); + runsrc++; + } + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) + *rundest++ = *runsrc++; + } + } + break; + + case LZX_BLOCKTYPE_ALIGNED: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + match_offset = main_element >> 3; + + if (match_offset > 2) { + /* not repeated offset */ + extra = extra_bits[match_offset]; + match_offset = position_base[match_offset] - 2; + if (extra > 3) { + /* verbatim and aligned bits */ + extra -= 3; + READ_BITS(verbatim_bits, extra); + match_offset += (verbatim_bits << 3); + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } else if (extra == 3) { + /* aligned bits only */ + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } else if (extra > 0) { /* extra==1, extra==2 */ + /* verbatim bits only */ + READ_BITS(verbatim_bits, extra); + match_offset += (uint32_t)verbatim_bits; + } else /* extra == 0 */ { + /* ??? */ + match_offset = 1; + } + + /* update repeated offset LRU queue */ + R2 = R1; + R1 = R0; + R0 = match_offset; + } else if (match_offset == 0) { + match_offset = R0; + } else if (match_offset == 1) { + match_offset = R1; + R1 = R0; + R0 = match_offset; + } else /* match_offset == 2 */ { + match_offset = R2; + R2 = R0; + R0 = match_offset; + } + + rundest = window + window_posn; + runsrc = rundest - match_offset; + window_posn += (uint32_t)match_length; + if (window_posn > window_size) + return DECR_ILLEGALDATA; + this_run -= match_length; + + /* copy any wrapped around source data */ + while ((runsrc < window) && (match_length-- > 0)) { + *rundest++ = *(runsrc + window_size); + runsrc++; + } + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) + *rundest++ = *runsrc++; + } + } + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + if ((inpos + this_run) > endinp) + return DECR_ILLEGALDATA; + memcpy(window + window_posn, inpos, (size_t)this_run); + inpos += this_run; + window_posn += (uint32_t)this_run; + break; + + default: + return DECR_ILLEGALDATA; /* might as well */ + } + } + } + + if (togo != 0) + return DECR_ILLEGALDATA; + memcpy(outpos, window + ((!window_posn) ? window_size : window_posn) - outlen, (size_t)outlen); + + pState->window_posn = window_posn; + pState->R0 = R0; + pState->R1 = R1; + pState->R2 = R2; + + /* intel E8 decoding */ + if ((pState->frames_read++ < 32768) && pState->intel_filesize != 0) { + if (outlen <= 6 || !pState->intel_started) { + pState->intel_curpos += outlen; + } else { + uint8_t* data = outpos; + uint8_t* dataend = data + outlen - 10; + int32_t curpos = pState->intel_curpos; + int32_t filesize = pState->intel_filesize; + int32_t abs_off, rel_off; + + pState->intel_curpos = curpos + outlen; + + while (data < dataend) { + if (*data++ != 0xE8) { + curpos++; + continue; + } + abs_off = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + if ((abs_off >= -curpos) && (abs_off < filesize)) { + rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; + data[0] = (uint8_t)rel_off; + data[1] = (uint8_t)(rel_off >> 8); + data[2] = (uint8_t)(rel_off >> 16); + data[3] = (uint8_t)(rel_off >> 24); + } + data += 4; + curpos += 5; + } + } + } + return DECR_OK; +} + diff --git a/thirdparty/lzx/lzx.h b/thirdparty/lzx/lzx.h new file mode 100644 index 00000000..ce9dd5a4 --- /dev/null +++ b/thirdparty/lzx/lzx.h @@ -0,0 +1,61 @@ +// Modified version of the code from Wine: +// https://gitlab.winehq.org/wine/wine/-/blob/fcc40a07909dc7626b6d1e2ec73f823d828a47e8/dlls/itss/lzx.h + +/*************************************************************************** +* lzx.h - LZX decompression routines * +* ------------------- * +* * +* maintainer: Jed Wing * +* source: modified lzx.c from cabextract v0.5 * +* notes: This file was taken from cabextract v0.5, which was, * +* itself, a modified version of the lzx decompression code * +* from unlzx. * +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. Note that an exemption to this * + * license has been granted by Stuart Caie for the purposes of * + * distribution with chmlib. This does not, to the best of my * + * knowledge, constitute a change in the license of this (the LZX) code * + * in general. * + * * + ***************************************************************************/ + +#ifndef INCLUDED_LZX_H +#define INCLUDED_LZX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* return codes */ +#define DECR_OK (0) +#define DECR_DATAFORMAT (1) +#define DECR_ILLEGALDATA (2) +#define DECR_NOMEMORY (3) + +/* opaque state structure */ +struct lzx_state; + +/* create an lzx state object */ +struct lzx_state* lzx_init(int window); + +/* destroy an lzx state object */ +void lzx_teardown(struct lzx_state* pState); + +/* reset an lzx stream */ +void lzx_reset(struct lzx_state* pState); + +/* decompress an LZX compressed block */ +int lzx_decompress(struct lzx_state* pState, const unsigned char* inpos, unsigned char* outpos, int inlen, + int outlen); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_LZX_H */ diff --git a/thirdparty/webview b/thirdparty/webview new file mode 160000 index 00000000..0ae23fc2 --- /dev/null +++ b/thirdparty/webview @@ -0,0 +1 @@ +Subproject commit 0ae23fc2da788d5089bc01aea60ef1ac2543fc50 diff --git a/thirdparty/webview.lua b/thirdparty/webview.lua new file mode 100644 index 00000000..5fef3927 --- /dev/null +++ b/thirdparty/webview.lua @@ -0,0 +1,123 @@ +webview = {} + +function webview:include(includes) + if includes:handle(self:name()) then + includedirs { + path.join(ThirdPartyFolder(), "webview/core/include"), + path.join(self:msWebviewDir(), "build/native/include") + } + end +end + +function webview:link(links) + + if os.host() == "windows" then + links:add("WebView2LoaderStatic") + + filter "platforms:x86" + libdirs { + path.join(self:msWebviewDir(), "build/native/x86") + } + filter {} + filter "platforms:x64" + libdirs { + path.join(self:msWebviewDir(), "build/native/x64") + } + filter {} + end + + links:add(self:name()) +end + +function webview:use() + +end + +function webview:name() + return "webview" +end + +function webview:project() + local folder = ThirdPartyFolder() + local includes = Includes:create() + + project(self:name()) + targetdir(TargetDirectoryLib) + location "%{wks.location}/thirdparty/%{prj.name}" + kind "StaticLib" + language "C++" + + files { + path.join(folder, "webview/core/include/**.h"), + path.join(folder, "webview/core/include/**.hh"), + path.join(folder, "webview/core/src/**.cc") + } + + defines { + "WEBVIEW_STATIC" + } + + if os.host() == "windows" then + self:installWebview2() + end + + filter { "system:linux", "action:gmake" } + buildoptions { "`pkg-config --cflags gtk4 webkitgtk-6.0`" } + linkoptions { "`pkg-config --libs gtk4 webkitgtk-6.0`" } + filter {} + + self:include(includes) + + -- Disable warnings. They do not have any value to us since it is not our code. + warnings "off" +end + +function webview:msWebviewDir() + return path.join(BuildFolder(), "thirdparty/ms-webview2") +end + +function webview:installWebview2() + local version = "1.0.3485.44" + local webviewDir = self:msWebviewDir() + local versionFile = path.join(webviewDir, "ms-webview2.txt") + local nuspecFile = path.join(webviewDir, "Microsoft.Web.WebView2.nuspec") + local nupkgFile = path.join(webviewDir, "microsoft.web.webview2.nupkg.zip") + local url = "https://www.nuget.org/api/v2/package/Microsoft.Web.WebView2/" .. version + + if not os.isdir(webviewDir) then + os.mkdir(webviewDir) + end + + local installedVersion = io.readfile(versionFile) + if installedVersion == version and os.isfile(nuspecFile) then + return + end + + function progress(total, current) + local ratio = current / total; + ratio = math.min(math.max(ratio, 0), 1); + local percent = math.floor(ratio * 100); + io.write("\rDownload progress (" .. percent .. "%/100%)") + end + + print("Downloading Microsoft.Web.WebView2 " .. version .. "...") + local result_str, response_code = http.download(url, nupkgFile, { + progress = progress + }) + + io.write("\n") + + if result_str ~= "OK" then + premake.error("Failed to download Microsoft.Web.WebView2") + end + + -- local hash = string.sha1(io.readfile(nupkgFile)) + -- print(hash) + + print("Extracting Microsoft.Web.WebView2 " .. version .. "...") + zip.extract(nupkgFile, webviewDir) + + os.remove(nupkgFile) + + io.writefile(versionFile, version) +end diff --git a/tools/scripts/options.lua b/tools/scripts/options.lua index 28ed3633..794a315e 100644 --- a/tools/scripts/options.lua +++ b/tools/scripts/options.lua @@ -1,3 +1,9 @@ +newoption { + trigger = "oat-version", + description = "Set the OAT version being compiled.", + value = "VERSION", + default = "_" -- An empty string is not recognized as a default +} newoption { trigger = "debug-structureddatadef", description = "Activate additional debugging logic for StructuredDataDef assets" @@ -10,3 +16,10 @@ newoption { trigger = "experimental-material-compilation", description = "Activate experimental material compilation support" } + +-- ModMan is currently experimental and deactivated by default. +-- For more information see src/ModMan/README.md +newoption { + trigger = "modman", + description = "Activate experimental compilation of ModMan (OAT GUI)" +} diff --git a/tools/scripts/version.lua b/tools/scripts/version.lua index dc0087f9..5b32b51f 100644 --- a/tools/scripts/version.lua +++ b/tools/scripts/version.lua @@ -2,13 +2,17 @@ local BuildSubFolderFolder = "premake" local HeaderFileName = "GitVersion.h" function GetGitVersion() + if _OPTIONS["oat-version"] ~= "_" then + return _OPTIONS["oat-version"] + end + result, errorCode = os.outputof("git describe --tags") if errorCode == 0 then return result end - return "Unknown" + return "dev" end function GetVersionHeaderFolder()