diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a3a770d6..48ba8ece 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,4 +10,10 @@ updates: directory: "/" schedule: interval: "weekly" - day: "friday" \ No newline at end of file + day: "friday" + + - package-ecosystem: "npm" + directory: "src/ModManUi" + schedule: + interval: "weekly" + day: "friday" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index eec3044e..e7fcf213 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -35,7 +35,7 @@ jobs: update-alternatives --set g++ /usr/bin/g++-13 - name: Install node - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: 24 @@ -88,7 +88,7 @@ jobs: runs-on: windows-latest steps: - name: Install node - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: 24 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6f3a5094..ab799216 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -40,7 +40,7 @@ jobs: chmod +x build/bin/Release_x86/{ImageConverter,Unlinker,Linker} - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: oat-linux path: | @@ -68,7 +68,7 @@ jobs: run: msbuild /m /p:Configuration=Release /p:Platform=Win32 build - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: oat-windows path: | @@ -83,7 +83,7 @@ jobs: actions: read contents: write steps: - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@v6 - name: Zip artifacts run: | 7z a oat-linux.tar ./oat-linux/* diff --git a/.gitignore b/.gitignore index 12fa1242..76921ac5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ local/ /build/ .vscode .idea -user*.* \ No newline at end of file +user*.* +*.aps diff --git a/repo/logo.ico b/repo/logo.ico new file mode 100644 index 00000000..5b05ede3 Binary files /dev/null and b/repo/logo.ico differ diff --git a/repo/logo.svg b/repo/logo.svg new file mode 100644 index 00000000..5f051e9b --- /dev/null +++ b/repo/logo.svg @@ -0,0 +1,41 @@ + + + + diff --git a/repo/logo_circle.ico b/repo/logo_circle.ico new file mode 100644 index 00000000..b2f2e9f9 Binary files /dev/null and b/repo/logo_circle.ico differ diff --git a/repo/logo_circle.svg b/repo/logo_circle.svg new file mode 100644 index 00000000..98d88ab3 --- /dev/null +++ b/repo/logo_circle.svg @@ -0,0 +1,77 @@ + + + + diff --git a/src/Common/Game/CommonAsset.cpp b/src/Common/Game/CommonAsset.cpp new file mode 100644 index 00000000..30bdfd7d --- /dev/null +++ b/src/Common/Game/CommonAsset.cpp @@ -0,0 +1,26 @@ +#include "CommonAsset.h" + +#include "IW3/CommonAssetIW3.h" +#include "IW4/CommonAssetIW4.h" +#include "IW5/CommonAssetIW5.h" +#include "T5/CommonAssetT5.h" +#include "T6/CommonAssetT6.h" + +#include + +ICommonAssetTypeMapper* ICommonAssetTypeMapper::GetCommonAssetMapperByGame(GameId gameId) +{ + static ICommonAssetTypeMapper* assetTypeMappers[static_cast(GameId::COUNT)]{ + new IW3::CommonAssetTypeMapper(), + new IW4::CommonAssetTypeMapper(), + new IW5::CommonAssetTypeMapper(), + new T5::CommonAssetTypeMapper(), + new T6::CommonAssetTypeMapper(), + }; + + assert(static_cast(gameId) < static_cast(GameId::COUNT)); + auto* result = assetTypeMappers[static_cast(gameId)]; + assert(result); + + return result; +} diff --git a/src/Common/Game/CommonAsset.h b/src/Common/Game/CommonAsset.h new file mode 100644 index 00000000..24371e49 --- /dev/null +++ b/src/Common/Game/CommonAsset.h @@ -0,0 +1,181 @@ +#pragma once + +#include "IGame.h" +#include "Zone/ZoneTypes.h" + +#include +#include + +enum class CommonAssetType : std::uint8_t +{ + // IW3, IW4, IW5, T5, T6 + PHYS_PRESET, + // IW3, IW4, IW5, T5, T6 + XANIM, + // IW3, IW4, IW5, T5, T6 + XMODEL, + // IW3, IW4, IW5, T5, T6 + MATERIAL, + // IW3, IW4, IW5, T5, T6 + TECHNIQUE_SET, + // IW3, IW4, IW5, T5, T6 + IMAGE, + // IW3, IW4, IW5, T5, T6 + SOUND, + // IW3, IW4, IW5 + SOUND_CURVE, + // IW3, IW4, IW5 + LOADED_SOUND, + // IW3, IW4, IW5, T5, T6 + CLIP_MAP, + // IW3, IW4, IW5, T5, T6 + COM_WORLD, + // IW3, IW4, T5, T6 + GAME_WORLD_SP, + // IW3, IW4, T5, T6 + GAME_WORLD_MP, + // IW3, IW4, IW5, T5, T6 + MAP_ENTS, + // IW3, IW4, IW5, T5, T6 + GFX_WORLD, + // IW3, IW4, IW5, T5, T6 + LIGHT_DEF, + // IW3, IW4, IW5, T5, T6 + UI_MAP, + // IW3, IW4, IW5, T5, T6 + FONT, + // IW3, IW4, IW5, T5, T6 + MENU_LIST, + // IW3, IW4, IW5, T5, T6 + MENU, + // IW3, IW4, IW5, T5, T6 + LOCALIZE_ENTRY, + // IW3, IW4, IW5, T5, T6 + WEAPON, + // IW3, IW4, IW5, T5, T6 + SOUND_DRIVER_GLOBALS, + // IW3, IW4, IW5, T5, T6 + FX, + // IW3, IW4, IW5, T5, T6 + IMPACT_FX, + // IW3, IW4, IW5, T5, T6 + AI_TYPE, + // IW3, IW4, IW5, T5, T6 + MP_TYPE, + // IW3, IW4, IW5, T5, T6 + CHARACTER, + // IW3, IW4, IW5, T5, T6 + XMODEL_ALIAS, + // IW3, IW4, IW5, T5, T6 + RAW_FILE, + // IW3, IW4, IW5, T5, T6 + STRING_TABLE, + + // IW3, T5, T6 + XMODEL_PIECES, + + // IW4, IW5 + PHYS_COLL_MAP, + // IW4, IW5 + XMODEL_SURFS, + // IW4, IW5 + PIXEL_SHADER, + // IW4, IW5 + VERTEX_SHADER, + // IW4, IW5 + VERTEX_DECL, + // IW4, IW5 + FX_WORLD, + // IW4, IW5, T6 + LEADERBOARD, + // IW4, IW5 + STRUCTURED_DATA_DEF, + // IW4, IW5, T6 + TRACER, + // IW4, IW5, T6 + VEHICLE, + // IW4, IW5, T6 + ADDON_MAP_ENTS, + + // IW5 + GLASS_WORLD, + // IW5 + PATH_DATA, + // IW5 + VEHICLE_TRACK, + // IW5, T6 + ATTACHMENT, + // IW5 + SURFACE_FX, + // IW5, T6 + SCRIPT, + + // T5, T6 + PHYS_CONSTRAINTS, + // T5, T6 + DESTRUCTIBLE_DEF, + // T5, T6 + SOUND_PATCH, + // T5, T6 + WEAPON_DEF, + // T5, T6 + WEAPON_VARIANT, + // T5, T6 + MP_BODY, + // T5, T6 + MP_HEAD, + // T5 + PACK_INDEX, + // T5, T6 + XGLOBALS, + // T5, T6 + DDL, + // T5, T6 + GLASSES, + // T5, T6 + EMBLEM_SET, + + // T6 + FONT_ICON, + // T6 + WEAPON_FULL, + // T6 + ATTACHMENT_UNIQUE, + // T6 + WEAPON_CAMO, + // T6 + KEY_VALUE_PAIRS, + // T6 + MEMORY_BLOCK, + // T6 + SKINNED_VERTS, + // T6 + QDB, + // T6 + SLUG, + // T6 + FOOTSTEP_TABLE, + // T6 + FOOTSTEP_FX_TABLE, + // T6 + ZBARRIER, + + COUNT, +}; + +class ICommonAssetTypeMapper +{ +public: + [[nodiscard]] virtual CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const = 0; + [[nodiscard]] virtual std::optional CommonToGameAssetType(CommonAssetType commonAssetType) const = 0; + + static ICommonAssetTypeMapper* GetCommonAssetMapperByGame(GameId gameId); + +protected: + ICommonAssetTypeMapper() = default; + virtual ~ICommonAssetTypeMapper() = default; + ICommonAssetTypeMapper(const ICommonAssetTypeMapper& other) = default; + ICommonAssetTypeMapper(ICommonAssetTypeMapper&& other) noexcept = default; + ICommonAssetTypeMapper& operator=(const ICommonAssetTypeMapper& other) = default; + ICommonAssetTypeMapper& operator=(ICommonAssetTypeMapper&& other) noexcept = default; +}; diff --git a/src/Common/Game/IGame.h b/src/Common/Game/IGame.h index d8021ee2..a53ed7b6 100644 --- a/src/Common/Game/IGame.h +++ b/src/Common/Game/IGame.h @@ -2,10 +2,11 @@ #include "GameLanguage.h" +#include #include #include -enum class GameId +enum class GameId : std::uint8_t { IW3, IW4, @@ -18,7 +19,7 @@ enum class GameId // The full uppercase names are macros in the standard lib // So unfortunately not usable as values in the enum -enum class GameEndianness +enum class GameEndianness : std::uint8_t { /* Little endian */ LE, @@ -26,12 +27,19 @@ enum class GameEndianness BE }; -enum class GameWordSize +enum class GameWordSize : std::uint8_t { ARCH_32, ARCH_64 }; +enum class GamePlatform : std::uint8_t +{ + PC, + XBOX, + PS3 +}; + static constexpr const char* GameId_Names[]{ "IW3", "IW4", diff --git a/src/Common/Game/IW3/CommonAssetIW3.cpp b/src/Common/Game/IW3/CommonAssetIW3.cpp new file mode 100644 index 00000000..485657f2 --- /dev/null +++ b/src/Common/Game/IW3/CommonAssetIW3.cpp @@ -0,0 +1,100 @@ +#include "CommonAssetIW3.h" + +#include "IW3.h" + +#include + +namespace IW3 +{ + CommonAssetTypeMapper::CommonAssetTypeMapper() = default; + + CommonAssetType CommonAssetTypeMapper::GameToCommonAssetType(const asset_type_t gameAssetType) const + { + static CommonAssetType lookupTable[static_cast(ASSET_TYPE_COUNT)]{ + CommonAssetType::XMODEL_PIECES, // ASSET_TYPE_XMODELPIECES + CommonAssetType::PHYS_PRESET, // ASSET_TYPE_PHYSPRESET + CommonAssetType::XANIM, // ASSET_TYPE_XANIMPARTS + CommonAssetType::XMODEL, // ASSET_TYPE_XMODEL + CommonAssetType::MATERIAL, // ASSET_TYPE_MATERIAL + CommonAssetType::TECHNIQUE_SET, // ASSET_TYPE_TECHNIQUE_SET + CommonAssetType::IMAGE, // ASSET_TYPE_IMAGE + CommonAssetType::SOUND, // ASSET_TYPE_SOUND + CommonAssetType::SOUND_CURVE, // ASSET_TYPE_SOUND_CURVE + CommonAssetType::LOADED_SOUND, // ASSET_TYPE_LOADED_SOUND + CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP + CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP_PVS + CommonAssetType::COM_WORLD, // ASSET_TYPE_COMWORLD + CommonAssetType::GAME_WORLD_SP, // ASSET_TYPE_GAMEWORLD_SP + CommonAssetType::GAME_WORLD_MP, // ASSET_TYPE_GAMEWORLD_MP + CommonAssetType::MAP_ENTS, // ASSET_TYPE_MAP_ENTS + CommonAssetType::GFX_WORLD, // ASSET_TYPE_GFXWORLD + CommonAssetType::LIGHT_DEF, // ASSET_TYPE_LIGHT_DEF + CommonAssetType::UI_MAP, // ASSET_TYPE_UI_MAP + CommonAssetType::FONT, // ASSET_TYPE_FONT + CommonAssetType::MENU_LIST, // ASSET_TYPE_MENULIST + CommonAssetType::MENU, // ASSET_TYPE_MENU + CommonAssetType::LOCALIZE_ENTRY, // ASSET_TYPE_LOCALIZE_ENTRY + CommonAssetType::WEAPON, // ASSET_TYPE_WEAPON + CommonAssetType::SOUND_DRIVER_GLOBALS, // ASSET_TYPE_SNDDRIVER_GLOBALS + CommonAssetType::FX, // ASSET_TYPE_FX + CommonAssetType::IMPACT_FX, // ASSET_TYPE_IMPACT_FX + CommonAssetType::AI_TYPE, // ASSET_TYPE_AITYPE + CommonAssetType::MP_TYPE, // ASSET_TYPE_MPTYPE + CommonAssetType::CHARACTER, // ASSET_TYPE_CHARACTER + CommonAssetType::XMODEL_ALIAS, // ASSET_TYPE_XMODELALIAS + CommonAssetType::RAW_FILE, // ASSET_TYPE_RAWFILE + CommonAssetType::STRING_TABLE, // ASSET_TYPE_STRINGTABLE + }; + + assert(gameAssetType < ASSET_TYPE_COUNT); + return lookupTable[gameAssetType]; + } + + std::optional CommonAssetTypeMapper::CommonToGameAssetType(const CommonAssetType commonAssetType) const + { +#define MAP_COMMON(common, game) \ + case common: \ + return game; + + switch (commonAssetType) + { + MAP_COMMON(CommonAssetType::XMODEL_PIECES, ASSET_TYPE_XMODELPIECES) + MAP_COMMON(CommonAssetType::PHYS_PRESET, ASSET_TYPE_PHYSPRESET) + MAP_COMMON(CommonAssetType::XANIM, ASSET_TYPE_XANIMPARTS) + MAP_COMMON(CommonAssetType::XMODEL, ASSET_TYPE_XMODEL) + MAP_COMMON(CommonAssetType::MATERIAL, ASSET_TYPE_MATERIAL) + MAP_COMMON(CommonAssetType::TECHNIQUE_SET, ASSET_TYPE_TECHNIQUE_SET) + MAP_COMMON(CommonAssetType::IMAGE, ASSET_TYPE_IMAGE) + MAP_COMMON(CommonAssetType::SOUND, ASSET_TYPE_SOUND) + MAP_COMMON(CommonAssetType::SOUND_CURVE, ASSET_TYPE_SOUND_CURVE) + MAP_COMMON(CommonAssetType::LOADED_SOUND, ASSET_TYPE_LOADED_SOUND) + MAP_COMMON(CommonAssetType::CLIP_MAP, ASSET_TYPE_CLIPMAP_PVS) + MAP_COMMON(CommonAssetType::COM_WORLD, ASSET_TYPE_COMWORLD) + MAP_COMMON(CommonAssetType::GAME_WORLD_SP, ASSET_TYPE_GAMEWORLD_SP) + MAP_COMMON(CommonAssetType::GAME_WORLD_MP, ASSET_TYPE_GAMEWORLD_MP) + MAP_COMMON(CommonAssetType::MAP_ENTS, ASSET_TYPE_MAP_ENTS) + MAP_COMMON(CommonAssetType::GFX_WORLD, ASSET_TYPE_GFXWORLD) + MAP_COMMON(CommonAssetType::LIGHT_DEF, ASSET_TYPE_LIGHT_DEF) + MAP_COMMON(CommonAssetType::UI_MAP, ASSET_TYPE_UI_MAP) + MAP_COMMON(CommonAssetType::FONT, ASSET_TYPE_FONT) + MAP_COMMON(CommonAssetType::MENU_LIST, ASSET_TYPE_MENULIST) + MAP_COMMON(CommonAssetType::MENU, ASSET_TYPE_MENU) + MAP_COMMON(CommonAssetType::LOCALIZE_ENTRY, ASSET_TYPE_LOCALIZE_ENTRY) + MAP_COMMON(CommonAssetType::WEAPON, ASSET_TYPE_WEAPON) + MAP_COMMON(CommonAssetType::SOUND_DRIVER_GLOBALS, ASSET_TYPE_SNDDRIVER_GLOBALS) + MAP_COMMON(CommonAssetType::FX, ASSET_TYPE_FX) + MAP_COMMON(CommonAssetType::IMPACT_FX, ASSET_TYPE_IMPACT_FX) + MAP_COMMON(CommonAssetType::AI_TYPE, ASSET_TYPE_AITYPE) + MAP_COMMON(CommonAssetType::MP_TYPE, ASSET_TYPE_MPTYPE) + MAP_COMMON(CommonAssetType::CHARACTER, ASSET_TYPE_CHARACTER) + MAP_COMMON(CommonAssetType::XMODEL_ALIAS, ASSET_TYPE_XMODELALIAS) + MAP_COMMON(CommonAssetType::RAW_FILE, ASSET_TYPE_RAWFILE) + MAP_COMMON(CommonAssetType::STRING_TABLE, ASSET_TYPE_STRINGTABLE) + + default: + return std::nullopt; + } + +#undef MAP_COMMON + } +} // namespace IW3 diff --git a/src/Common/Game/IW3/CommonAssetIW3.h b/src/Common/Game/IW3/CommonAssetIW3.h new file mode 100644 index 00000000..bc19e468 --- /dev/null +++ b/src/Common/Game/IW3/CommonAssetIW3.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Game/CommonAsset.h" + +namespace IW3 +{ + class CommonAssetTypeMapper final : public ICommonAssetTypeMapper + { + public: + CommonAssetTypeMapper(); + + [[nodiscard]] CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const override; + [[nodiscard]] std::optional CommonToGameAssetType(CommonAssetType commonAssetType) const override; + }; +} // namespace IW3 diff --git a/src/Common/Game/IW4/CommonAssetIW4.cpp b/src/Common/Game/IW4/CommonAssetIW4.cpp new file mode 100644 index 00000000..b8f4172e --- /dev/null +++ b/src/Common/Game/IW4/CommonAssetIW4.cpp @@ -0,0 +1,120 @@ +#include "CommonAssetIW4.h" + +#include "IW4.h" + +#include + +namespace IW4 +{ + CommonAssetTypeMapper::CommonAssetTypeMapper() = default; + + CommonAssetType CommonAssetTypeMapper::GameToCommonAssetType(const asset_type_t gameAssetType) const + { + static CommonAssetType lookupTable[static_cast(ASSET_TYPE_COUNT)]{ + CommonAssetType::PHYS_PRESET, // ASSET_TYPE_PHYSPRESET + CommonAssetType::PHYS_COLL_MAP, // ASSET_TYPE_PHYSCOLLMAP + CommonAssetType::XANIM, // ASSET_TYPE_XANIMPARTS + CommonAssetType::XMODEL_SURFS, // ASSET_TYPE_XMODEL_SURFS + CommonAssetType::XMODEL, // ASSET_TYPE_XMODEL + CommonAssetType::MATERIAL, // ASSET_TYPE_MATERIAL + CommonAssetType::PIXEL_SHADER, // ASSET_TYPE_PIXELSHADER + CommonAssetType::VERTEX_SHADER, // ASSET_TYPE_VERTEXSHADER + CommonAssetType::VERTEX_DECL, // ASSET_TYPE_VERTEXDECL + CommonAssetType::TECHNIQUE_SET, // ASSET_TYPE_TECHNIQUE_SET + CommonAssetType::IMAGE, // ASSET_TYPE_IMAGE + CommonAssetType::SOUND, // ASSET_TYPE_SOUND + CommonAssetType::SOUND_CURVE, // ASSET_TYPE_SOUND_CURVE + CommonAssetType::LOADED_SOUND, // ASSET_TYPE_LOADED_SOUND + CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP_SP + CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP_MP + CommonAssetType::COM_WORLD, // ASSET_TYPE_COMWORLD + CommonAssetType::GAME_WORLD_SP, // ASSET_TYPE_GAMEWORLD_SP + CommonAssetType::GAME_WORLD_MP, // ASSET_TYPE_GAMEWORLD_MP + CommonAssetType::MAP_ENTS, // ASSET_TYPE_MAP_ENTS + CommonAssetType::FX_WORLD, // ASSET_TYPE_FXWORLD + CommonAssetType::GFX_WORLD, // ASSET_TYPE_GFXWORLD + CommonAssetType::LIGHT_DEF, // ASSET_TYPE_LIGHT_DEF + CommonAssetType::UI_MAP, // ASSET_TYPE_UI_MAP + CommonAssetType::FONT, // ASSET_TYPE_FONT + CommonAssetType::MENU_LIST, // ASSET_TYPE_MENULIST + CommonAssetType::MENU, // ASSET_TYPE_MENU + CommonAssetType::LOCALIZE_ENTRY, // ASSET_TYPE_LOCALIZE_ENTRY + CommonAssetType::WEAPON, // ASSET_TYPE_WEAPON + CommonAssetType::SOUND_DRIVER_GLOBALS, // ASSET_TYPE_SNDDRIVER_GLOBALS + CommonAssetType::FX, // ASSET_TYPE_FX + CommonAssetType::IMPACT_FX, // ASSET_TYPE_IMPACT_FX + CommonAssetType::AI_TYPE, // ASSET_TYPE_AITYPE + CommonAssetType::MP_TYPE, // ASSET_TYPE_MPTYPE + CommonAssetType::CHARACTER, // ASSET_TYPE_CHARACTER + CommonAssetType::XMODEL_ALIAS, // ASSET_TYPE_XMODELALIAS + CommonAssetType::RAW_FILE, // ASSET_TYPE_RAWFILE + CommonAssetType::STRING_TABLE, // ASSET_TYPE_STRINGTABLE + CommonAssetType::LEADERBOARD, // ASSET_TYPE_LEADERBOARD + CommonAssetType::STRUCTURED_DATA_DEF, // ASSET_TYPE_STRUCTURED_DATA_DEF + CommonAssetType::TRACER, // ASSET_TYPE_TRACER + CommonAssetType::VEHICLE, // ASSET_TYPE_VEHICLE + CommonAssetType::ADDON_MAP_ENTS, // ASSET_TYPE_ADDON_MAP_ENTS + }; + + assert(gameAssetType < ASSET_TYPE_COUNT); + return lookupTable[gameAssetType]; + } + + std::optional CommonAssetTypeMapper::CommonToGameAssetType(const CommonAssetType commonAssetType) const + { +#define MAP_COMMON(common, game) \ + case common: \ + return game; + + switch (commonAssetType) + { + MAP_COMMON(CommonAssetType::PHYS_PRESET, ASSET_TYPE_PHYSPRESET) + MAP_COMMON(CommonAssetType::PHYS_COLL_MAP, ASSET_TYPE_PHYSCOLLMAP) + MAP_COMMON(CommonAssetType::XANIM, ASSET_TYPE_XANIMPARTS) + MAP_COMMON(CommonAssetType::XMODEL_SURFS, ASSET_TYPE_XMODEL_SURFS) + MAP_COMMON(CommonAssetType::XMODEL, ASSET_TYPE_XMODEL) + MAP_COMMON(CommonAssetType::MATERIAL, ASSET_TYPE_MATERIAL) + MAP_COMMON(CommonAssetType::PIXEL_SHADER, ASSET_TYPE_PIXELSHADER) + MAP_COMMON(CommonAssetType::VERTEX_SHADER, ASSET_TYPE_VERTEXSHADER) + MAP_COMMON(CommonAssetType::VERTEX_DECL, ASSET_TYPE_VERTEXDECL) + MAP_COMMON(CommonAssetType::TECHNIQUE_SET, ASSET_TYPE_TECHNIQUE_SET) + MAP_COMMON(CommonAssetType::IMAGE, ASSET_TYPE_IMAGE) + MAP_COMMON(CommonAssetType::SOUND, ASSET_TYPE_SOUND) + MAP_COMMON(CommonAssetType::SOUND_CURVE, ASSET_TYPE_SOUND_CURVE) + MAP_COMMON(CommonAssetType::LOADED_SOUND, ASSET_TYPE_LOADED_SOUND) + MAP_COMMON(CommonAssetType::CLIP_MAP, ASSET_TYPE_CLIPMAP_MP) + MAP_COMMON(CommonAssetType::COM_WORLD, ASSET_TYPE_COMWORLD) + MAP_COMMON(CommonAssetType::GAME_WORLD_SP, ASSET_TYPE_GAMEWORLD_SP) + MAP_COMMON(CommonAssetType::GAME_WORLD_MP, ASSET_TYPE_GAMEWORLD_MP) + MAP_COMMON(CommonAssetType::MAP_ENTS, ASSET_TYPE_MAP_ENTS) + MAP_COMMON(CommonAssetType::FX_WORLD, ASSET_TYPE_FXWORLD) + MAP_COMMON(CommonAssetType::GFX_WORLD, ASSET_TYPE_GFXWORLD) + MAP_COMMON(CommonAssetType::LIGHT_DEF, ASSET_TYPE_LIGHT_DEF) + MAP_COMMON(CommonAssetType::UI_MAP, ASSET_TYPE_UI_MAP) + MAP_COMMON(CommonAssetType::FONT, ASSET_TYPE_FONT) + MAP_COMMON(CommonAssetType::MENU_LIST, ASSET_TYPE_MENULIST) + MAP_COMMON(CommonAssetType::MENU, ASSET_TYPE_MENU) + MAP_COMMON(CommonAssetType::LOCALIZE_ENTRY, ASSET_TYPE_LOCALIZE_ENTRY) + MAP_COMMON(CommonAssetType::WEAPON, ASSET_TYPE_WEAPON) + MAP_COMMON(CommonAssetType::SOUND_DRIVER_GLOBALS, ASSET_TYPE_SNDDRIVER_GLOBALS) + MAP_COMMON(CommonAssetType::FX, ASSET_TYPE_FX) + MAP_COMMON(CommonAssetType::IMPACT_FX, ASSET_TYPE_IMPACT_FX) + MAP_COMMON(CommonAssetType::AI_TYPE, ASSET_TYPE_AITYPE) + MAP_COMMON(CommonAssetType::MP_TYPE, ASSET_TYPE_MPTYPE) + MAP_COMMON(CommonAssetType::CHARACTER, ASSET_TYPE_CHARACTER) + MAP_COMMON(CommonAssetType::XMODEL_ALIAS, ASSET_TYPE_XMODELALIAS) + MAP_COMMON(CommonAssetType::RAW_FILE, ASSET_TYPE_RAWFILE) + MAP_COMMON(CommonAssetType::STRING_TABLE, ASSET_TYPE_STRINGTABLE) + MAP_COMMON(CommonAssetType::LEADERBOARD, ASSET_TYPE_LEADERBOARD) + MAP_COMMON(CommonAssetType::STRUCTURED_DATA_DEF, ASSET_TYPE_STRUCTURED_DATA_DEF) + MAP_COMMON(CommonAssetType::TRACER, ASSET_TYPE_TRACER) + MAP_COMMON(CommonAssetType::VEHICLE, ASSET_TYPE_VEHICLE) + MAP_COMMON(CommonAssetType::ADDON_MAP_ENTS, ASSET_TYPE_ADDON_MAP_ENTS) + + default: + return std::nullopt; + } + +#undef MAP_COMMON + } +} // namespace IW4 diff --git a/src/Common/Game/IW4/CommonAssetIW4.h b/src/Common/Game/IW4/CommonAssetIW4.h new file mode 100644 index 00000000..e4d84543 --- /dev/null +++ b/src/Common/Game/IW4/CommonAssetIW4.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Game/CommonAsset.h" + +namespace IW4 +{ + class CommonAssetTypeMapper final : public ICommonAssetTypeMapper + { + public: + CommonAssetTypeMapper(); + + [[nodiscard]] CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const override; + [[nodiscard]] std::optional CommonToGameAssetType(CommonAssetType commonAssetType) const override; + }; +} // namespace IW4 diff --git a/src/Common/Game/IW5/CommonAssetIW5.cpp b/src/Common/Game/IW5/CommonAssetIW5.cpp new file mode 100644 index 00000000..bc558b23 --- /dev/null +++ b/src/Common/Game/IW5/CommonAssetIW5.cpp @@ -0,0 +1,127 @@ +#include "CommonAssetIW5.h" + +#include "IW5.h" + +#include + +namespace IW5 +{ + CommonAssetTypeMapper::CommonAssetTypeMapper() = default; + + CommonAssetType CommonAssetTypeMapper::GameToCommonAssetType(const asset_type_t gameAssetType) const + { + static CommonAssetType lookupTable[static_cast(ASSET_TYPE_COUNT)]{ + CommonAssetType::PHYS_PRESET, // ASSET_TYPE_PHYSPRESET + CommonAssetType::PHYS_COLL_MAP, // ASSET_TYPE_PHYSCOLLMAP + CommonAssetType::XANIM, // ASSET_TYPE_XANIMPARTS + CommonAssetType::XMODEL_SURFS, // ASSET_TYPE_XMODEL_SURFS + CommonAssetType::XMODEL, // ASSET_TYPE_XMODEL + CommonAssetType::MATERIAL, // ASSET_TYPE_MATERIAL + CommonAssetType::PIXEL_SHADER, // ASSET_TYPE_PIXELSHADER + CommonAssetType::VERTEX_SHADER, // ASSET_TYPE_VERTEXSHADER + CommonAssetType::VERTEX_DECL, // ASSET_TYPE_VERTEXDECL + CommonAssetType::TECHNIQUE_SET, // ASSET_TYPE_TECHNIQUE_SET + CommonAssetType::IMAGE, // ASSET_TYPE_IMAGE + CommonAssetType::SOUND, // ASSET_TYPE_SOUND + CommonAssetType::SOUND_CURVE, // ASSET_TYPE_SOUND_CURVE + CommonAssetType::LOADED_SOUND, // ASSET_TYPE_LOADED_SOUND + CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP + CommonAssetType::COM_WORLD, // ASSET_TYPE_COMWORLD + CommonAssetType::GLASS_WORLD, // ASSET_TYPE_GLASSWORLD + CommonAssetType::PATH_DATA, // ASSET_TYPE_PATHDATA + CommonAssetType::VEHICLE_TRACK, // ASSET_TYPE_VEHICLE_TRACK + CommonAssetType::MAP_ENTS, // ASSET_TYPE_MAP_ENTS + CommonAssetType::FX_WORLD, // ASSET_TYPE_FXWORLD + CommonAssetType::GFX_WORLD, // ASSET_TYPE_GFXWORLD + CommonAssetType::LIGHT_DEF, // ASSET_TYPE_LIGHT_DEF + CommonAssetType::UI_MAP, // ASSET_TYPE_UI_MAP + CommonAssetType::FONT, // ASSET_TYPE_FONT + CommonAssetType::MENU_LIST, // ASSET_TYPE_MENULIST + CommonAssetType::MENU, // ASSET_TYPE_MENU + CommonAssetType::LOCALIZE_ENTRY, // ASSET_TYPE_LOCALIZE_ENTRY + CommonAssetType::ATTACHMENT, // ASSET_TYPE_ATTACHMENT + CommonAssetType::WEAPON, // ASSET_TYPE_WEAPON + CommonAssetType::SOUND_DRIVER_GLOBALS, // ASSET_TYPE_SNDDRIVER_GLOBALS + CommonAssetType::FX, // ASSET_TYPE_FX + CommonAssetType::IMPACT_FX, // ASSET_TYPE_IMPACT_FX + CommonAssetType::SURFACE_FX, // ASSET_TYPE_SURFACE_FX + CommonAssetType::AI_TYPE, // ASSET_TYPE_AITYPE + CommonAssetType::MP_TYPE, // ASSET_TYPE_MPTYPE + CommonAssetType::CHARACTER, // ASSET_TYPE_CHARACTER + CommonAssetType::XMODEL_ALIAS, // ASSET_TYPE_XMODELALIAS + CommonAssetType::RAW_FILE, // ASSET_TYPE_RAWFILE + CommonAssetType::SCRIPT, // ASSET_TYPE_SCRIPTFILE + CommonAssetType::STRING_TABLE, // ASSET_TYPE_STRINGTABLE + CommonAssetType::LEADERBOARD, // ASSET_TYPE_LEADERBOARD + CommonAssetType::STRUCTURED_DATA_DEF, // ASSET_TYPE_STRUCTURED_DATA_DEF + CommonAssetType::TRACER, // ASSET_TYPE_TRACER + CommonAssetType::VEHICLE, // ASSET_TYPE_VEHICLE + CommonAssetType::ADDON_MAP_ENTS, // ASSET_TYPE_ADDON_MAP_ENTS + }; + + assert(gameAssetType < ASSET_TYPE_COUNT); + return lookupTable[gameAssetType]; + } + + std::optional CommonAssetTypeMapper::CommonToGameAssetType(const CommonAssetType commonAssetType) const + { +#define MAP_COMMON(common, game) \ + case common: \ + return game; + + switch (commonAssetType) + { + MAP_COMMON(CommonAssetType::PHYS_PRESET, ASSET_TYPE_PHYSPRESET) + MAP_COMMON(CommonAssetType::PHYS_COLL_MAP, ASSET_TYPE_PHYSCOLLMAP) + MAP_COMMON(CommonAssetType::XANIM, ASSET_TYPE_XANIMPARTS) + MAP_COMMON(CommonAssetType::XMODEL_SURFS, ASSET_TYPE_XMODEL_SURFS) + MAP_COMMON(CommonAssetType::XMODEL, ASSET_TYPE_XMODEL) + MAP_COMMON(CommonAssetType::MATERIAL, ASSET_TYPE_MATERIAL) + MAP_COMMON(CommonAssetType::PIXEL_SHADER, ASSET_TYPE_PIXELSHADER) + MAP_COMMON(CommonAssetType::VERTEX_SHADER, ASSET_TYPE_VERTEXSHADER) + MAP_COMMON(CommonAssetType::VERTEX_DECL, ASSET_TYPE_VERTEXDECL) + MAP_COMMON(CommonAssetType::TECHNIQUE_SET, ASSET_TYPE_TECHNIQUE_SET) + MAP_COMMON(CommonAssetType::IMAGE, ASSET_TYPE_IMAGE) + MAP_COMMON(CommonAssetType::SOUND, ASSET_TYPE_SOUND) + MAP_COMMON(CommonAssetType::SOUND_CURVE, ASSET_TYPE_SOUND_CURVE) + MAP_COMMON(CommonAssetType::LOADED_SOUND, ASSET_TYPE_LOADED_SOUND) + MAP_COMMON(CommonAssetType::CLIP_MAP, ASSET_TYPE_CLIPMAP) + MAP_COMMON(CommonAssetType::COM_WORLD, ASSET_TYPE_COMWORLD) + MAP_COMMON(CommonAssetType::GLASS_WORLD, ASSET_TYPE_GLASSWORLD) + MAP_COMMON(CommonAssetType::PATH_DATA, ASSET_TYPE_PATHDATA) + MAP_COMMON(CommonAssetType::VEHICLE_TRACK, ASSET_TYPE_VEHICLE_TRACK) + MAP_COMMON(CommonAssetType::MAP_ENTS, ASSET_TYPE_MAP_ENTS) + MAP_COMMON(CommonAssetType::FX_WORLD, ASSET_TYPE_FXWORLD) + MAP_COMMON(CommonAssetType::GFX_WORLD, ASSET_TYPE_GFXWORLD) + MAP_COMMON(CommonAssetType::LIGHT_DEF, ASSET_TYPE_LIGHT_DEF) + MAP_COMMON(CommonAssetType::UI_MAP, ASSET_TYPE_UI_MAP) + MAP_COMMON(CommonAssetType::FONT, ASSET_TYPE_FONT) + MAP_COMMON(CommonAssetType::MENU_LIST, ASSET_TYPE_MENULIST) + MAP_COMMON(CommonAssetType::MENU, ASSET_TYPE_MENU) + MAP_COMMON(CommonAssetType::LOCALIZE_ENTRY, ASSET_TYPE_LOCALIZE_ENTRY) + MAP_COMMON(CommonAssetType::ATTACHMENT, ASSET_TYPE_ATTACHMENT) + MAP_COMMON(CommonAssetType::WEAPON, ASSET_TYPE_WEAPON) + MAP_COMMON(CommonAssetType::SOUND_DRIVER_GLOBALS, ASSET_TYPE_SNDDRIVER_GLOBALS) + MAP_COMMON(CommonAssetType::FX, ASSET_TYPE_FX) + MAP_COMMON(CommonAssetType::IMPACT_FX, ASSET_TYPE_IMPACT_FX) + MAP_COMMON(CommonAssetType::SURFACE_FX, ASSET_TYPE_SURFACE_FX) + MAP_COMMON(CommonAssetType::AI_TYPE, ASSET_TYPE_AITYPE) + MAP_COMMON(CommonAssetType::MP_TYPE, ASSET_TYPE_MPTYPE) + MAP_COMMON(CommonAssetType::CHARACTER, ASSET_TYPE_CHARACTER) + MAP_COMMON(CommonAssetType::XMODEL_ALIAS, ASSET_TYPE_XMODELALIAS) + MAP_COMMON(CommonAssetType::RAW_FILE, ASSET_TYPE_RAWFILE) + MAP_COMMON(CommonAssetType::SCRIPT, ASSET_TYPE_SCRIPTFILE) + MAP_COMMON(CommonAssetType::STRING_TABLE, ASSET_TYPE_STRINGTABLE) + MAP_COMMON(CommonAssetType::LEADERBOARD, ASSET_TYPE_LEADERBOARD) + MAP_COMMON(CommonAssetType::STRUCTURED_DATA_DEF, ASSET_TYPE_STRUCTURED_DATA_DEF) + MAP_COMMON(CommonAssetType::TRACER, ASSET_TYPE_TRACER) + MAP_COMMON(CommonAssetType::VEHICLE, ASSET_TYPE_VEHICLE) + MAP_COMMON(CommonAssetType::ADDON_MAP_ENTS, ASSET_TYPE_ADDON_MAP_ENTS) + + default: + return std::nullopt; + } + +#undef MAP_COMMON + } +} // namespace IW5 diff --git a/src/Common/Game/IW5/CommonAssetIW5.h b/src/Common/Game/IW5/CommonAssetIW5.h new file mode 100644 index 00000000..2dbf5c1f --- /dev/null +++ b/src/Common/Game/IW5/CommonAssetIW5.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Game/CommonAsset.h" + +namespace IW5 +{ + class CommonAssetTypeMapper final : public ICommonAssetTypeMapper + { + public: + CommonAssetTypeMapper(); + + [[nodiscard]] CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const override; + [[nodiscard]] std::optional CommonToGameAssetType(CommonAssetType commonAssetType) const override; + }; +} // namespace IW5 diff --git a/src/Common/Game/T5/CommonAssetT5.cpp b/src/Common/Game/T5/CommonAssetT5.cpp new file mode 100644 index 00000000..d671bf1a --- /dev/null +++ b/src/Common/Game/T5/CommonAssetT5.cpp @@ -0,0 +1,120 @@ +#include "CommonAssetT5.h" + +#include "T5.h" + +#include + +namespace T5 +{ + CommonAssetTypeMapper::CommonAssetTypeMapper() = default; + + CommonAssetType CommonAssetTypeMapper::GameToCommonAssetType(const asset_type_t gameAssetType) const + { + static CommonAssetType lookupTable[static_cast(ASSET_TYPE_COUNT)]{ + CommonAssetType::XMODEL_PIECES, // ASSET_TYPE_XMODELPIECES + CommonAssetType::PHYS_PRESET, // ASSET_TYPE_PHYSPRESET + CommonAssetType::PHYS_CONSTRAINTS, // ASSET_TYPE_PHYSCONSTRAINTS + CommonAssetType::DESTRUCTIBLE_DEF, // ASSET_TYPE_DESTRUCTIBLEDEF + CommonAssetType::XANIM, // ASSET_TYPE_XANIMPARTS + CommonAssetType::XMODEL, // ASSET_TYPE_XMODEL + CommonAssetType::MATERIAL, // ASSET_TYPE_MATERIAL + CommonAssetType::TECHNIQUE_SET, // ASSET_TYPE_TECHNIQUE_SET + CommonAssetType::IMAGE, // ASSET_TYPE_IMAGE + CommonAssetType::SOUND, // ASSET_TYPE_SOUND + CommonAssetType::SOUND_PATCH, // ASSET_TYPE_SOUND_PATCH + CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP + CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP_PVS + CommonAssetType::COM_WORLD, // ASSET_TYPE_COMWORLD + CommonAssetType::GAME_WORLD_SP, // ASSET_TYPE_GAMEWORLD_SP + CommonAssetType::GAME_WORLD_MP, // ASSET_TYPE_GAMEWORLD_MP + CommonAssetType::MAP_ENTS, // ASSET_TYPE_MAP_ENTS + CommonAssetType::GFX_WORLD, // ASSET_TYPE_GFXWORLD + CommonAssetType::LIGHT_DEF, // ASSET_TYPE_LIGHT_DEF + CommonAssetType::UI_MAP, // ASSET_TYPE_UI_MAP + CommonAssetType::FONT, // ASSET_TYPE_FONT + CommonAssetType::MENU_LIST, // ASSET_TYPE_MENULIST + CommonAssetType::MENU, // ASSET_TYPE_MENU + CommonAssetType::LOCALIZE_ENTRY, // ASSET_TYPE_LOCALIZE_ENTRY + CommonAssetType::WEAPON, // ASSET_TYPE_WEAPON + CommonAssetType::WEAPON_DEF, // ASSET_TYPE_WEAPONDEF + CommonAssetType::WEAPON_VARIANT, // ASSET_TYPE_WEAPON_VARIANT + CommonAssetType::SOUND_DRIVER_GLOBALS, // ASSET_TYPE_SNDDRIVER_GLOBALS + CommonAssetType::FX, // ASSET_TYPE_FX + CommonAssetType::IMPACT_FX, // ASSET_TYPE_IMPACT_FX + CommonAssetType::AI_TYPE, // ASSET_TYPE_AITYPE + CommonAssetType::MP_TYPE, // ASSET_TYPE_MPTYPE + CommonAssetType::MP_BODY, // ASSET_TYPE_MPBODY + CommonAssetType::MP_HEAD, // ASSET_TYPE_MPHEAD + CommonAssetType::CHARACTER, // ASSET_TYPE_CHARACTER + CommonAssetType::XMODEL_ALIAS, // ASSET_TYPE_XMODELALIAS + CommonAssetType::RAW_FILE, // ASSET_TYPE_RAWFILE + CommonAssetType::STRING_TABLE, // ASSET_TYPE_STRINGTABLE + CommonAssetType::PACK_INDEX, // ASSET_TYPE_PACK_INDEX + CommonAssetType::XGLOBALS, // ASSET_TYPE_XGLOBALS + CommonAssetType::DDL, // ASSET_TYPE_DDL + CommonAssetType::GLASSES, // ASSET_TYPE_GLASSES + CommonAssetType::EMBLEM_SET, // ASSET_TYPE_EMBLEMSET + }; + + assert(gameAssetType < ASSET_TYPE_COUNT); + return lookupTable[gameAssetType]; + } + + std::optional CommonAssetTypeMapper::CommonToGameAssetType(const CommonAssetType commonAssetType) const + { +#define MAP_COMMON(common, game) \ + case common: \ + return game; + + switch (commonAssetType) + { + MAP_COMMON(CommonAssetType::XMODEL_PIECES, ASSET_TYPE_XMODELPIECES) + MAP_COMMON(CommonAssetType::PHYS_PRESET, ASSET_TYPE_PHYSPRESET) + MAP_COMMON(CommonAssetType::PHYS_CONSTRAINTS, ASSET_TYPE_PHYSCONSTRAINTS) + MAP_COMMON(CommonAssetType::DESTRUCTIBLE_DEF, ASSET_TYPE_DESTRUCTIBLEDEF) + MAP_COMMON(CommonAssetType::XANIM, ASSET_TYPE_XANIMPARTS) + MAP_COMMON(CommonAssetType::XMODEL, ASSET_TYPE_XMODEL) + MAP_COMMON(CommonAssetType::MATERIAL, ASSET_TYPE_MATERIAL) + MAP_COMMON(CommonAssetType::TECHNIQUE_SET, ASSET_TYPE_TECHNIQUE_SET) + MAP_COMMON(CommonAssetType::IMAGE, ASSET_TYPE_IMAGE) + MAP_COMMON(CommonAssetType::SOUND, ASSET_TYPE_SOUND) + MAP_COMMON(CommonAssetType::SOUND_PATCH, ASSET_TYPE_SOUND_PATCH) + MAP_COMMON(CommonAssetType::CLIP_MAP, ASSET_TYPE_CLIPMAP_PVS) + MAP_COMMON(CommonAssetType::COM_WORLD, ASSET_TYPE_COMWORLD) + MAP_COMMON(CommonAssetType::GAME_WORLD_SP, ASSET_TYPE_GAMEWORLD_SP) + MAP_COMMON(CommonAssetType::GAME_WORLD_MP, ASSET_TYPE_GAMEWORLD_MP) + MAP_COMMON(CommonAssetType::MAP_ENTS, ASSET_TYPE_MAP_ENTS) + MAP_COMMON(CommonAssetType::GFX_WORLD, ASSET_TYPE_GFXWORLD) + MAP_COMMON(CommonAssetType::LIGHT_DEF, ASSET_TYPE_LIGHT_DEF) + MAP_COMMON(CommonAssetType::UI_MAP, ASSET_TYPE_UI_MAP) + MAP_COMMON(CommonAssetType::FONT, ASSET_TYPE_FONT) + MAP_COMMON(CommonAssetType::MENU_LIST, ASSET_TYPE_MENULIST) + MAP_COMMON(CommonAssetType::MENU, ASSET_TYPE_MENU) + MAP_COMMON(CommonAssetType::LOCALIZE_ENTRY, ASSET_TYPE_LOCALIZE_ENTRY) + MAP_COMMON(CommonAssetType::WEAPON, ASSET_TYPE_WEAPON) + MAP_COMMON(CommonAssetType::WEAPON_DEF, ASSET_TYPE_WEAPONDEF) + MAP_COMMON(CommonAssetType::WEAPON_VARIANT, ASSET_TYPE_WEAPON_VARIANT) + MAP_COMMON(CommonAssetType::SOUND_DRIVER_GLOBALS, ASSET_TYPE_SNDDRIVER_GLOBALS) + MAP_COMMON(CommonAssetType::FX, ASSET_TYPE_FX) + MAP_COMMON(CommonAssetType::IMPACT_FX, ASSET_TYPE_IMPACT_FX) + MAP_COMMON(CommonAssetType::AI_TYPE, ASSET_TYPE_AITYPE) + MAP_COMMON(CommonAssetType::MP_TYPE, ASSET_TYPE_MPTYPE) + MAP_COMMON(CommonAssetType::MP_BODY, ASSET_TYPE_MPBODY) + MAP_COMMON(CommonAssetType::MP_HEAD, ASSET_TYPE_MPHEAD) + MAP_COMMON(CommonAssetType::CHARACTER, ASSET_TYPE_CHARACTER) + MAP_COMMON(CommonAssetType::XMODEL_ALIAS, ASSET_TYPE_XMODELALIAS) + MAP_COMMON(CommonAssetType::RAW_FILE, ASSET_TYPE_RAWFILE) + MAP_COMMON(CommonAssetType::STRING_TABLE, ASSET_TYPE_STRINGTABLE) + MAP_COMMON(CommonAssetType::PACK_INDEX, ASSET_TYPE_PACK_INDEX) + MAP_COMMON(CommonAssetType::XGLOBALS, ASSET_TYPE_XGLOBALS) + MAP_COMMON(CommonAssetType::DDL, ASSET_TYPE_DDL) + MAP_COMMON(CommonAssetType::GLASSES, ASSET_TYPE_GLASSES) + MAP_COMMON(CommonAssetType::EMBLEM_SET, ASSET_TYPE_EMBLEMSET) + + default: + return std::nullopt; + } + +#undef MAP_COMMON + } +} // namespace T5 diff --git a/src/Common/Game/T5/CommonAssetT5.h b/src/Common/Game/T5/CommonAssetT5.h new file mode 100644 index 00000000..551f8255 --- /dev/null +++ b/src/Common/Game/T5/CommonAssetT5.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Game/CommonAsset.h" + +namespace T5 +{ + class CommonAssetTypeMapper final : public ICommonAssetTypeMapper + { + public: + CommonAssetTypeMapper(); + + [[nodiscard]] CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const override; + [[nodiscard]] std::optional CommonToGameAssetType(CommonAssetType commonAssetType) const override; + }; +} // namespace T5 diff --git a/src/Common/Game/T6/CommonAssetT6.cpp b/src/Common/Game/T6/CommonAssetT6.cpp new file mode 100644 index 00000000..5ea16b24 --- /dev/null +++ b/src/Common/Game/T6/CommonAssetT6.cpp @@ -0,0 +1,154 @@ +#include "CommonAssetT6.h" + +#include "T6.h" + +#include + +namespace T6 +{ + CommonAssetTypeMapper::CommonAssetTypeMapper() = default; + + CommonAssetType CommonAssetTypeMapper::GameToCommonAssetType(const asset_type_t gameAssetType) const + { + static CommonAssetType lookupTable[static_cast(ASSET_TYPE_COUNT)]{ + CommonAssetType::XMODEL_PIECES, // ASSET_TYPE_XMODELPIECES + CommonAssetType::PHYS_PRESET, // ASSET_TYPE_PHYSPRESET + CommonAssetType::PHYS_CONSTRAINTS, // ASSET_TYPE_PHYSCONSTRAINTS + CommonAssetType::DESTRUCTIBLE_DEF, // ASSET_TYPE_DESTRUCTIBLEDEF + CommonAssetType::XANIM, // ASSET_TYPE_XANIMPARTS + CommonAssetType::XMODEL, // ASSET_TYPE_XMODEL + CommonAssetType::MATERIAL, // ASSET_TYPE_MATERIAL + CommonAssetType::TECHNIQUE_SET, // ASSET_TYPE_TECHNIQUE_SET + CommonAssetType::IMAGE, // ASSET_TYPE_IMAGE + CommonAssetType::SOUND, // ASSET_TYPE_SOUND + CommonAssetType::SOUND_PATCH, // ASSET_TYPE_SOUND_PATCH + CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP + CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP_PVS + CommonAssetType::COM_WORLD, // ASSET_TYPE_COMWORLD + CommonAssetType::GAME_WORLD_SP, // ASSET_TYPE_GAMEWORLD_SP + CommonAssetType::GAME_WORLD_MP, // ASSET_TYPE_GAMEWORLD_MP + CommonAssetType::MAP_ENTS, // ASSET_TYPE_MAP_ENTS + CommonAssetType::GFX_WORLD, // ASSET_TYPE_GFXWORLD + CommonAssetType::LIGHT_DEF, // ASSET_TYPE_LIGHT_DEF + CommonAssetType::UI_MAP, // ASSET_TYPE_UI_MAP + CommonAssetType::FONT, // ASSET_TYPE_FONT + CommonAssetType::FONT_ICON, // ASSET_TYPE_FONTICON + CommonAssetType::MENU_LIST, // ASSET_TYPE_MENULIST + CommonAssetType::MENU, // ASSET_TYPE_MENU + CommonAssetType::LOCALIZE_ENTRY, // ASSET_TYPE_LOCALIZE_ENTRY + CommonAssetType::WEAPON, // ASSET_TYPE_WEAPON + CommonAssetType::WEAPON_DEF, // ASSET_TYPE_WEAPONDEF + CommonAssetType::WEAPON_VARIANT, // ASSET_TYPE_WEAPON_VARIANT + CommonAssetType::WEAPON_FULL, // ASSET_TYPE_WEAPON_FULL + CommonAssetType::ATTACHMENT, // ASSET_TYPE_ATTACHMENT + CommonAssetType::ATTACHMENT_UNIQUE, // ASSET_TYPE_ATTACHMENT_UNIQUE + CommonAssetType::WEAPON_CAMO, // ASSET_TYPE_WEAPON_CAMO + CommonAssetType::SOUND_DRIVER_GLOBALS, // ASSET_TYPE_SNDDRIVER_GLOBALS + CommonAssetType::FX, // ASSET_TYPE_FX + CommonAssetType::IMPACT_FX, // ASSET_TYPE_IMPACT_FX + CommonAssetType::AI_TYPE, // ASSET_TYPE_AITYPE + CommonAssetType::MP_TYPE, // ASSET_TYPE_MPTYPE + CommonAssetType::MP_BODY, // ASSET_TYPE_MPBODY + CommonAssetType::MP_HEAD, // ASSET_TYPE_MPHEAD + CommonAssetType::CHARACTER, // ASSET_TYPE_CHARACTER + CommonAssetType::XMODEL_ALIAS, // ASSET_TYPE_XMODELALIAS + CommonAssetType::RAW_FILE, // ASSET_TYPE_RAWFILE + CommonAssetType::STRING_TABLE, // ASSET_TYPE_STRINGTABLE + CommonAssetType::LEADERBOARD, // ASSET_TYPE_LEADERBOARD + CommonAssetType::XGLOBALS, // ASSET_TYPE_XGLOBALS + CommonAssetType::DDL, // ASSET_TYPE_DDL + CommonAssetType::GLASSES, // ASSET_TYPE_GLASSES + CommonAssetType::EMBLEM_SET, // ASSET_TYPE_EMBLEMSET + CommonAssetType::SCRIPT, // ASSET_TYPE_SCRIPTPARSETREE + CommonAssetType::KEY_VALUE_PAIRS, // ASSET_TYPE_KEYVALUEPAIRS + CommonAssetType::VEHICLE, // ASSET_TYPE_VEHICLEDEF + CommonAssetType::MEMORY_BLOCK, // ASSET_TYPE_MEMORYBLOCK + CommonAssetType::ADDON_MAP_ENTS, // ASSET_TYPE_ADDON_MAP_ENTS + CommonAssetType::TRACER, // ASSET_TYPE_TRACER + CommonAssetType::SKINNED_VERTS, // ASSET_TYPE_SKINNEDVERTS + CommonAssetType::QDB, // ASSET_TYPE_QDB + CommonAssetType::SLUG, // ASSET_TYPE_SLUG + CommonAssetType::FOOTSTEP_TABLE, // ASSET_TYPE_FOOTSTEP_TABLE + CommonAssetType::FOOTSTEP_FX_TABLE, // ASSET_TYPE_FOOTSTEPFX_TABLE + CommonAssetType::ZBARRIER, // ASSET_TYPE_ZBARRIER + }; + + assert(gameAssetType < ASSET_TYPE_COUNT); + return lookupTable[gameAssetType]; + } + + std::optional CommonAssetTypeMapper::CommonToGameAssetType(const CommonAssetType commonAssetType) const + { +#define MAP_COMMON(common, game) \ + case common: \ + return game; + + switch (commonAssetType) + { + MAP_COMMON(CommonAssetType::XMODEL_PIECES, ASSET_TYPE_XMODELPIECES) + MAP_COMMON(CommonAssetType::PHYS_PRESET, ASSET_TYPE_PHYSPRESET) + MAP_COMMON(CommonAssetType::PHYS_CONSTRAINTS, ASSET_TYPE_PHYSCONSTRAINTS) + MAP_COMMON(CommonAssetType::DESTRUCTIBLE_DEF, ASSET_TYPE_DESTRUCTIBLEDEF) + MAP_COMMON(CommonAssetType::XANIM, ASSET_TYPE_XANIMPARTS) + MAP_COMMON(CommonAssetType::XMODEL, ASSET_TYPE_XMODEL) + MAP_COMMON(CommonAssetType::MATERIAL, ASSET_TYPE_MATERIAL) + MAP_COMMON(CommonAssetType::TECHNIQUE_SET, ASSET_TYPE_TECHNIQUE_SET) + MAP_COMMON(CommonAssetType::IMAGE, ASSET_TYPE_IMAGE) + MAP_COMMON(CommonAssetType::SOUND, ASSET_TYPE_SOUND) + MAP_COMMON(CommonAssetType::SOUND_PATCH, ASSET_TYPE_SOUND_PATCH) + MAP_COMMON(CommonAssetType::CLIP_MAP, ASSET_TYPE_CLIPMAP_PVS) + MAP_COMMON(CommonAssetType::COM_WORLD, ASSET_TYPE_COMWORLD) + MAP_COMMON(CommonAssetType::GAME_WORLD_SP, ASSET_TYPE_GAMEWORLD_SP) + MAP_COMMON(CommonAssetType::GAME_WORLD_MP, ASSET_TYPE_GAMEWORLD_MP) + MAP_COMMON(CommonAssetType::MAP_ENTS, ASSET_TYPE_MAP_ENTS) + MAP_COMMON(CommonAssetType::GFX_WORLD, ASSET_TYPE_GFXWORLD) + MAP_COMMON(CommonAssetType::LIGHT_DEF, ASSET_TYPE_LIGHT_DEF) + MAP_COMMON(CommonAssetType::UI_MAP, ASSET_TYPE_UI_MAP) + MAP_COMMON(CommonAssetType::FONT, ASSET_TYPE_FONT) + MAP_COMMON(CommonAssetType::FONT_ICON, ASSET_TYPE_FONTICON) + MAP_COMMON(CommonAssetType::MENU_LIST, ASSET_TYPE_MENULIST) + MAP_COMMON(CommonAssetType::MENU, ASSET_TYPE_MENU) + MAP_COMMON(CommonAssetType::LOCALIZE_ENTRY, ASSET_TYPE_LOCALIZE_ENTRY) + MAP_COMMON(CommonAssetType::WEAPON, ASSET_TYPE_WEAPON) + MAP_COMMON(CommonAssetType::WEAPON_DEF, ASSET_TYPE_WEAPONDEF) + MAP_COMMON(CommonAssetType::WEAPON_VARIANT, ASSET_TYPE_WEAPON_VARIANT) + MAP_COMMON(CommonAssetType::WEAPON_FULL, ASSET_TYPE_WEAPON_FULL) + MAP_COMMON(CommonAssetType::ATTACHMENT, ASSET_TYPE_ATTACHMENT) + MAP_COMMON(CommonAssetType::ATTACHMENT_UNIQUE, ASSET_TYPE_ATTACHMENT_UNIQUE) + MAP_COMMON(CommonAssetType::WEAPON_CAMO, ASSET_TYPE_WEAPON_CAMO) + MAP_COMMON(CommonAssetType::SOUND_DRIVER_GLOBALS, ASSET_TYPE_SNDDRIVER_GLOBALS) + MAP_COMMON(CommonAssetType::FX, ASSET_TYPE_FX) + MAP_COMMON(CommonAssetType::IMPACT_FX, ASSET_TYPE_IMPACT_FX) + MAP_COMMON(CommonAssetType::AI_TYPE, ASSET_TYPE_AITYPE) + MAP_COMMON(CommonAssetType::MP_TYPE, ASSET_TYPE_MPTYPE) + MAP_COMMON(CommonAssetType::MP_BODY, ASSET_TYPE_MPBODY) + MAP_COMMON(CommonAssetType::MP_HEAD, ASSET_TYPE_MPHEAD) + MAP_COMMON(CommonAssetType::CHARACTER, ASSET_TYPE_CHARACTER) + MAP_COMMON(CommonAssetType::XMODEL_ALIAS, ASSET_TYPE_XMODELALIAS) + MAP_COMMON(CommonAssetType::RAW_FILE, ASSET_TYPE_RAWFILE) + MAP_COMMON(CommonAssetType::STRING_TABLE, ASSET_TYPE_STRINGTABLE) + MAP_COMMON(CommonAssetType::LEADERBOARD, ASSET_TYPE_LEADERBOARD) + MAP_COMMON(CommonAssetType::XGLOBALS, ASSET_TYPE_XGLOBALS) + MAP_COMMON(CommonAssetType::DDL, ASSET_TYPE_DDL) + MAP_COMMON(CommonAssetType::GLASSES, ASSET_TYPE_GLASSES) + MAP_COMMON(CommonAssetType::EMBLEM_SET, ASSET_TYPE_EMBLEMSET) + MAP_COMMON(CommonAssetType::SCRIPT, ASSET_TYPE_SCRIPTPARSETREE) + MAP_COMMON(CommonAssetType::KEY_VALUE_PAIRS, ASSET_TYPE_KEYVALUEPAIRS) + MAP_COMMON(CommonAssetType::VEHICLE, ASSET_TYPE_VEHICLEDEF) + MAP_COMMON(CommonAssetType::MEMORY_BLOCK, ASSET_TYPE_MEMORYBLOCK) + MAP_COMMON(CommonAssetType::ADDON_MAP_ENTS, ASSET_TYPE_ADDON_MAP_ENTS) + MAP_COMMON(CommonAssetType::TRACER, ASSET_TYPE_TRACER) + MAP_COMMON(CommonAssetType::SKINNED_VERTS, ASSET_TYPE_SKINNEDVERTS) + MAP_COMMON(CommonAssetType::QDB, ASSET_TYPE_QDB) + MAP_COMMON(CommonAssetType::SLUG, ASSET_TYPE_SLUG) + MAP_COMMON(CommonAssetType::FOOTSTEP_TABLE, ASSET_TYPE_FOOTSTEP_TABLE) + MAP_COMMON(CommonAssetType::FOOTSTEP_FX_TABLE, ASSET_TYPE_FOOTSTEPFX_TABLE) + MAP_COMMON(CommonAssetType::ZBARRIER, ASSET_TYPE_ZBARRIER) + + default: + return std::nullopt; + } + +#undef MAP_COMMON + } +} // namespace T6 diff --git a/src/Common/Game/T6/CommonAssetT6.h b/src/Common/Game/T6/CommonAssetT6.h new file mode 100644 index 00000000..a6b5d242 --- /dev/null +++ b/src/Common/Game/T6/CommonAssetT6.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Game/CommonAsset.h" + +namespace T6 +{ + class CommonAssetTypeMapper final : public ICommonAssetTypeMapper + { + public: + CommonAssetTypeMapper(); + + [[nodiscard]] CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const override; + [[nodiscard]] std::optional CommonToGameAssetType(CommonAssetType commonAssetType) const override; + }; +} // namespace T6 diff --git a/src/Common/Utils/ProgressCallback.h b/src/Common/Utils/ProgressCallback.h new file mode 100644 index 00000000..d2222d84 --- /dev/null +++ b/src/Common/Utils/ProgressCallback.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class ProgressCallback +{ +public: + ProgressCallback() = default; + virtual ~ProgressCallback() = default; + + virtual void OnProgress(size_t current, size_t total) = 0; +}; diff --git a/src/Linker/Linker.cpp b/src/Linker/Linker.cpp index cb02202e..4c98307b 100644 --- a/src/Linker/Linker.cpp +++ b/src/Linker/Linker.cpp @@ -363,14 +363,16 @@ class LinkerImpl final : public Linker zoneDirectory = fs::current_path(); auto absoluteZoneDirectory = absolute(zoneDirectory).string(); - auto zone = ZoneLoading::LoadZone(zonePath); - if (!zone) + auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt); + if (!maybeZone) { - con::error("Failed to load zone \"{}\".", zonePath); + con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error()); return false; } - con::debug("Load zone \"{}\"", zone->m_name); + auto zone = std::move(*maybeZone); + + con::debug("Loaded zone \"{}\"", zone->m_name); m_loaded_zones.emplace_back(std::move(zone)); } diff --git a/src/Linker/ZoneCreation/ZoneCreator.cpp b/src/Linker/ZoneCreation/ZoneCreator.cpp index 1be5335e..efcd60c0 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, gameId); + return std::make_unique(context.m_definition->m_name, 0, gameId, GamePlatform::PC); } std::vector CreateGdtList(const ZoneCreationContext& context) diff --git a/src/ModMan.lua b/src/ModMan.lua index c160f700..bfff6fb1 100644 --- a/src/ModMan.lua +++ b/src/ModMan.lua @@ -36,6 +36,12 @@ function ModMan:project() path.join(folder, "ModMan/**.cpp") } + filter { "system:windows" } + files { + path.join(folder, "ModMan/**.rc") + } + filter {} + includedirs { "%{wks.location}/src/ModMan" } @@ -47,10 +53,16 @@ function ModMan:project() self:include(includes) Utils:include(includes) + ZoneLoading:include(includes) + ObjLoading:include(includes) + ObjWriting:include(includes) json:include(includes) webview:include(includes) links:linkto(Utils) + links:linkto(ZoneLoading) + links:linkto(ObjLoading) + links:linkto(ObjWriting) links:linkto(webview) links:linkall() end diff --git a/src/ModMan/Context/FastFileContext.cpp b/src/ModMan/Context/FastFileContext.cpp new file mode 100644 index 00000000..1027b488 --- /dev/null +++ b/src/ModMan/Context/FastFileContext.cpp @@ -0,0 +1,91 @@ +#include "FastFileContext.h" + +#include "Web/Binds/ZoneBinds.h" +#include "Web/UiCommunication.h" +#include "ZoneLoading.h" + +#include + +namespace fs = std::filesystem; + +namespace +{ + constexpr double MIN_PROGRESS_TO_REPORT = 0.5; + + class LoadingEventProgressReporter : public ProgressCallback + { + public: + explicit LoadingEventProgressReporter(std::string zoneName) + : m_zone_name(std::move(zoneName)), + m_last_progress(0) + { + } + + void OnProgress(const size_t current, const size_t total) override + { + const double percentage = static_cast(current) / static_cast(total) * 100.0; + + if (percentage - m_last_progress >= MIN_PROGRESS_TO_REPORT) + { + m_last_progress = percentage; + ui::NotifyZoneLoadProgress(m_zone_name, percentage); + } + } + + private: + std::string m_zone_name; + double m_last_progress; + }; +} // namespace + +LoadedZone::LoadedZone(std::unique_ptr zone, std::string filePath) + : m_zone(std::move(zone)), + m_file_path(std::move(filePath)) +{ +} + +void FastFileContext::Destroy() +{ + // Unload all zones + m_loaded_zones.clear(); +} + +result::Expected FastFileContext::LoadFastFile(const std::string& path) +{ + auto zone = ZoneLoading::LoadZone(path, std::make_unique(fs::path(path).filename().replace_extension().string())); + if (!zone) + return result::Unexpected(std::move(zone.error())); + + auto loadedZone = std::make_unique(std::move(*zone), path); + + LoadedZone* result; + { + std::lock_guard lock(m_zone_lock); + result = m_loaded_zones.emplace_back(std::move(loadedZone)).get(); + } + + ui::NotifyZoneLoaded(*result); + + return result; +} + +result::Expected FastFileContext::UnloadZone(const std::string& zoneName) +{ + { + std::lock_guard lock(m_zone_lock); + const auto existingZone = std::ranges::find_if(m_loaded_zones, + [&zoneName](const std::unique_ptr& loadedZone) + { + return loadedZone->m_zone->m_name == zoneName; + }); + + if (existingZone != m_loaded_zones.end()) + { + m_loaded_zones.erase(existingZone); + ui::NotifyZoneUnloaded(zoneName); + return NoResult(); + } + } + + return result::Unexpected(std::format("No zone with name {} loaded", zoneName)); +} diff --git a/src/ModMan/Context/FastFileContext.h b/src/ModMan/Context/FastFileContext.h new file mode 100644 index 00000000..e769e34f --- /dev/null +++ b/src/ModMan/Context/FastFileContext.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Utils/Result.h" +#include "Zone/Zone.h" + +#include +#include +#include + +class LoadedZone +{ +public: + std::unique_ptr m_zone; + std::string m_file_path; + + LoadedZone(std::unique_ptr zone, std::string filePath); +}; + +class FastFileContext +{ +public: + void Destroy(); + + result::Expected LoadFastFile(const std::string& path); + result::Expected UnloadZone(const std::string& zoneName); + + std::vector> m_loaded_zones; + std::shared_mutex m_zone_lock; +}; diff --git a/src/ModMan/Context/ModManContext.cpp b/src/ModMan/Context/ModManContext.cpp new file mode 100644 index 00000000..221ebbf6 --- /dev/null +++ b/src/ModMan/Context/ModManContext.cpp @@ -0,0 +1,18 @@ +#include "ModManContext.h" + +ModManContext& ModManContext::Get() +{ + static ModManContext context; + return context; +} + +void ModManContext::Startup() +{ + m_db_thread.Start(); +} + +void ModManContext::Destroy() +{ + m_fast_file.Destroy(); + m_db_thread.Terminate(); +} diff --git a/src/ModMan/Context/ModManContext.h b/src/ModMan/Context/ModManContext.h new file mode 100644 index 00000000..ec09e183 --- /dev/null +++ b/src/ModMan/Context/ModManContext.h @@ -0,0 +1,22 @@ +#pragma once + +#include "FastFileContext.h" +#include "Utils/DispatchableThread.h" +#include "Web/WebViewLib.h" + +#include + +class ModManContext +{ +public: + static ModManContext& Get(); + + void Startup(); + void Destroy(); + + std::unique_ptr m_main_webview; + std::unique_ptr m_dev_tools_webview; + FastFileContext m_fast_file; + + DispatchableThread m_db_thread; +}; diff --git a/src/ModMan/ModMan.rc b/src/ModMan/ModMan.rc new file mode 100644 index 00000000..3806ce99 Binary files /dev/null and b/src/ModMan/ModMan.rc differ diff --git a/src/ModMan/ModManArgs.cpp b/src/ModMan/ModManArgs.cpp new file mode 100644 index 00000000..b5b1f88c --- /dev/null +++ b/src/ModMan/ModManArgs.cpp @@ -0,0 +1,105 @@ +#include "ModManArgs.h" + +#include "GitVersion.h" +#include "Utils/Arguments/UsageInformation.h" +#include "Utils/Logging/Log.h" + +#include +#include + +namespace +{ + // clang-format off + const CommandLineOption* const OPTION_HELP = + CommandLineOption::Builder::Create() + .WithShortName("?") + .WithLongName("help") + .WithDescription("Displays usage information.") + .Build(); + + const CommandLineOption* const OPTION_VERSION = + CommandLineOption::Builder::Create() + .WithLongName("version") + .WithDescription("Prints the application version.") + .Build(); + + const CommandLineOption* const OPTION_VERBOSE = + CommandLineOption::Builder::Create() + .WithShortName("v") + .WithLongName("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(); + // clang-format on + + const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ + OPTION_HELP, + OPTION_VERSION, + OPTION_VERBOSE, + OPTION_NO_COLOR, + }; +} // namespace + +ModManArgs::ModManArgs() + : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v) +{ +} + +void ModManArgs::PrintUsage() const +{ + UsageInformation usage(m_argument_parser.GetExecutableName()); + + for (const auto* commandLineOption : COMMAND_LINE_OPTIONS) + { + usage.AddCommandLineOption(commandLineOption); + } + + usage.Print(); +} + +void ModManArgs::PrintVersion() +{ + con::info("OpenAssetTools ModMan {}", GIT_VERSION); +} + +bool ModManArgs::ParseArgs(const int argc, const char** argv, bool& shouldContinue) +{ + shouldContinue = true; + if (!m_argument_parser.ParseArguments(argc, argv)) + { + PrintUsage(); + return false; + } + + // Check if the user requested help + if (m_argument_parser.IsOptionSpecified(OPTION_HELP)) + { + PrintUsage(); + shouldContinue = false; + return true; + } + + // Check if the user wants to see the version + if (m_argument_parser.IsOptionSpecified(OPTION_VERSION)) + { + PrintVersion(); + shouldContinue = false; + return true; + } + + // -v; --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/ModMan/ModManArgs.h b/src/ModMan/ModManArgs.h new file mode 100644 index 00000000..feb19628 --- /dev/null +++ b/src/ModMan/ModManArgs.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Utils/Arguments/ArgumentParser.h" + +class ModManArgs +{ +public: + ModManArgs(); + bool ParseArgs(int argc, const char** argv, bool& shouldContinue); + +private: + /** + * \brief Prints a command line usage help text for the ModMan tool to stdout. + */ + void PrintUsage() const; + static void PrintVersion(); + + ArgumentParser m_argument_parser; +}; diff --git a/src/ModMan/Utils/DispatchableThread.cpp b/src/ModMan/Utils/DispatchableThread.cpp new file mode 100644 index 00000000..58797b5f --- /dev/null +++ b/src/ModMan/Utils/DispatchableThread.cpp @@ -0,0 +1,77 @@ +#include "DispatchableThread.h" + +DispatchableThread::DispatchableThread() + : m_terminate(false) +{ +} + +DispatchableThread::~DispatchableThread() +{ + Terminate(); +} + +void DispatchableThread::Start() +{ + m_terminate = false; + m_thread = std::thread( + [&] + { + ThreadLoop(); + }); +} + +void DispatchableThread::Terminate() +{ + std::unique_lock lock(m_cb_mutex); + + if (!m_terminate) + { + m_terminate = true; + m_cv.notify_all(); + lock.unlock(); + m_thread.join(); + } + else + { + lock.unlock(); + } +} + +void DispatchableThread::Dispatch(cb_t cb) +{ + std::lock_guard lock(m_cb_mutex); + + m_cb_list.emplace_back(std::move(cb)); + m_cv.notify_one(); +} + +std::optional DispatchableThread::NextCallback() +{ + if (m_terminate || m_cb_list.empty()) + return std::nullopt; + + auto cb = std::move(m_cb_list.front()); + m_cb_list.pop_front(); + + return cb; +} + +void DispatchableThread::ThreadLoop() +{ + while (!m_terminate) + { + std::unique_lock lock(m_cb_mutex); + m_cv.wait(lock, + [&] + { + return !m_cb_list.empty() || m_terminate; + }); + + auto maybeCb = NextCallback(); + + lock.unlock(); + + if (maybeCb) + (*maybeCb)(); + } +} diff --git a/src/ModMan/Utils/DispatchableThread.h b/src/ModMan/Utils/DispatchableThread.h new file mode 100644 index 00000000..383ef586 --- /dev/null +++ b/src/ModMan/Utils/DispatchableThread.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class DispatchableThread +{ +public: + using cb_t = std::function; + + DispatchableThread(); + ~DispatchableThread(); + DispatchableThread(const DispatchableThread& other) = delete; + DispatchableThread(DispatchableThread&& other) noexcept = default; + DispatchableThread& operator=(const DispatchableThread& other) = delete; + DispatchableThread& operator=(DispatchableThread&& other) noexcept = default; + + void Start(); + void Terminate(); + + void Dispatch(cb_t cb); + +private: + std::optional NextCallback(); + void ThreadLoop(); + + std::mutex m_cb_mutex; + std::deque m_cb_list; + + std::condition_variable m_cv; + std::thread m_thread; + bool m_terminate; +}; diff --git a/src/ModMan/Web/Binds/AssetBinds.cpp b/src/ModMan/Web/Binds/AssetBinds.cpp new file mode 100644 index 00000000..31769a01 --- /dev/null +++ b/src/ModMan/Web/Binds/AssetBinds.cpp @@ -0,0 +1,174 @@ +#include "AssetBinds.h" + +#include "Context/ModManContext.h" +#include "Game/CommonAsset.h" +#include "Web/UiCommunication.h" + +#include "Json/JsonExtension.h" +#include + +NLOHMANN_JSON_SERIALIZE_ENUM(CommonAssetType, + { + {CommonAssetType::PHYS_PRESET, "PHYS_PRESET" }, + {CommonAssetType::XANIM, "XANIM" }, + {CommonAssetType::XMODEL, "XMODEL" }, + {CommonAssetType::MATERIAL, "MATERIAL" }, + {CommonAssetType::TECHNIQUE_SET, "TECHNIQUE_SET" }, + {CommonAssetType::IMAGE, "IMAGE" }, + {CommonAssetType::SOUND, "SOUND" }, + {CommonAssetType::SOUND_CURVE, "SOUND_CURVE" }, + {CommonAssetType::LOADED_SOUND, "LOADED_SOUND" }, + {CommonAssetType::CLIP_MAP, "CLIP_MAP" }, + {CommonAssetType::COM_WORLD, "COM_WORLD" }, + {CommonAssetType::GAME_WORLD_SP, "GAME_WORLD_SP" }, + {CommonAssetType::GAME_WORLD_MP, "GAME_WORLD_MP" }, + {CommonAssetType::MAP_ENTS, "MAP_ENTS" }, + {CommonAssetType::GFX_WORLD, "GFX_WORLD" }, + {CommonAssetType::LIGHT_DEF, "LIGHT_DEF" }, + {CommonAssetType::UI_MAP, "UI_MAP" }, + {CommonAssetType::FONT, "FONT" }, + {CommonAssetType::MENU_LIST, "MENU_LIST" }, + {CommonAssetType::MENU, "MENU" }, + {CommonAssetType::LOCALIZE_ENTRY, "LOCALIZE_ENTRY" }, + {CommonAssetType::WEAPON, "WEAPON" }, + {CommonAssetType::SOUND_DRIVER_GLOBALS, "SOUND_DRIVER_GLOBALS"}, + {CommonAssetType::FX, "FX" }, + {CommonAssetType::IMPACT_FX, "IMPACT_FX" }, + {CommonAssetType::AI_TYPE, "AI_TYPE" }, + {CommonAssetType::MP_TYPE, "MP_TYPE" }, + {CommonAssetType::CHARACTER, "CHARACTER" }, + {CommonAssetType::XMODEL_ALIAS, "XMODEL_ALIAS" }, + {CommonAssetType::RAW_FILE, "RAW_FILE" }, + {CommonAssetType::STRING_TABLE, "STRING_TABLE" }, + {CommonAssetType::XMODEL_PIECES, "XMODEL_PIECES" }, + {CommonAssetType::PHYS_COLL_MAP, "PHYS_COLL_MAP" }, + {CommonAssetType::XMODEL_SURFS, "XMODEL_SURFS" }, + {CommonAssetType::PIXEL_SHADER, "PIXEL_SHADER" }, + {CommonAssetType::VERTEX_SHADER, "VERTEX_SHADER" }, + {CommonAssetType::VERTEX_DECL, "VERTEX_DECL" }, + {CommonAssetType::FX_WORLD, "FX_WORLD" }, + {CommonAssetType::LEADERBOARD, "LEADERBOARD" }, + {CommonAssetType::STRUCTURED_DATA_DEF, "STRUCTURED_DATA_DEF" }, + {CommonAssetType::TRACER, "TRACER" }, + {CommonAssetType::VEHICLE, "VEHICLE" }, + {CommonAssetType::ADDON_MAP_ENTS, "ADDON_MAP_ENTS" }, + {CommonAssetType::GLASS_WORLD, "GLASS_WORLD" }, + {CommonAssetType::PATH_DATA, "PATH_DATA" }, + {CommonAssetType::VEHICLE_TRACK, "VEHICLE_TRACK" }, + {CommonAssetType::ATTACHMENT, "ATTACHMENT" }, + {CommonAssetType::SURFACE_FX, "SURFACE_FX" }, + {CommonAssetType::SCRIPT, "SCRIPT" }, + {CommonAssetType::PHYS_CONSTRAINTS, "PHYS_CONSTRAINTS" }, + {CommonAssetType::DESTRUCTIBLE_DEF, "DESTRUCTIBLE_DEF" }, + {CommonAssetType::SOUND_PATCH, "SOUND_PATCH" }, + {CommonAssetType::WEAPON_DEF, "WEAPON_DEF" }, + {CommonAssetType::WEAPON_VARIANT, "WEAPON_VARIANT" }, + {CommonAssetType::MP_BODY, "MP_BODY" }, + {CommonAssetType::MP_HEAD, "MP_HEAD" }, + {CommonAssetType::PACK_INDEX, "PACK_INDEX" }, + {CommonAssetType::XGLOBALS, "XGLOBALS" }, + {CommonAssetType::DDL, "DDL" }, + {CommonAssetType::GLASSES, "GLASSES" }, + {CommonAssetType::EMBLEM_SET, "EMBLEM_SET" }, + {CommonAssetType::FONT_ICON, "FONT_ICON" }, + {CommonAssetType::WEAPON_FULL, "WEAPON_FULL" }, + {CommonAssetType::ATTACHMENT_UNIQUE, "ATTACHMENT_UNIQUE" }, + {CommonAssetType::WEAPON_CAMO, "WEAPON_CAMO" }, + {CommonAssetType::KEY_VALUE_PAIRS, "KEY_VALUE_PAIRS" }, + {CommonAssetType::MEMORY_BLOCK, "MEMORY_BLOCK" }, + {CommonAssetType::SKINNED_VERTS, "SKINNED_VERTS" }, + {CommonAssetType::QDB, "QDB" }, + {CommonAssetType::SLUG, "SLUG" }, + {CommonAssetType::FOOTSTEP_TABLE, "FOOTSTEP_TABLE" }, + {CommonAssetType::FOOTSTEP_FX_TABLE, "FOOTSTEP_FX_TABLE" }, + {CommonAssetType::ZBARRIER, "ZBARRIER" }, +}); + +namespace +{ + class AssetDto + { + public: + CommonAssetType type; + std::string name; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(AssetDto, type, name); + + class ZoneAssetsDto + { + public: + std::vector assets; + std::vector references; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneAssetsDto, assets, references); + + ZoneAssetsDto CreateZoneAssetsDto(const Zone& zone) + { + std::vector assets; + std::vector references; + + // Reserve some entries already. Numbers are arbitrary. + const auto assetCount = zone.m_pools->GetTotalAssetCount(); + assets.reserve(assetCount / 2); + references.reserve(assetCount / 8); + + const auto& assetTypeMapper = *ICommonAssetTypeMapper::GetCommonAssetMapperByGame(zone.m_game_id); + for (const auto& asset : *zone.m_pools) + { + if (asset->IsReference()) + { + references.emplace_back(AssetDto{ + .type = assetTypeMapper.GameToCommonAssetType(asset->m_type), + .name = asset->ReferencedAssetName(), + }); + } + else + { + assets.emplace_back(AssetDto{ + .type = assetTypeMapper.GameToCommonAssetType(asset->m_type), + .name = asset->m_name, + }); + } + } + + return ZoneAssetsDto{ + .assets = std::move(assets), + .references = std::move(references), + }; + } + + std::optional GetZonesForZone(const std::string& zoneName) + { + auto& context = ModManContext::Get().m_fast_file; + + ZoneAssetsDto result; + + { + std::shared_lock lock(context.m_zone_lock); + + for (const auto& loadedZone : context.m_loaded_zones) + { + const auto& zone = *loadedZone->m_zone; + if (zone.m_name == zoneName) + return CreateZoneAssetsDto(zone); + } + } + + return std::nullopt; + } +} // namespace + +namespace ui +{ + void RegisterAssetBinds(webview::webview& wv) + { + Bind>(wv, + "getAssetsForZone", + [](const std::string& zoneName) + { + return GetZonesForZone(zoneName); + }); + } +} // namespace ui diff --git a/src/ModMan/Web/Binds/AssetBinds.h b/src/ModMan/Web/Binds/AssetBinds.h new file mode 100644 index 00000000..282e2a5d --- /dev/null +++ b/src/ModMan/Web/Binds/AssetBinds.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Web/WebViewLib.h" + +namespace ui +{ + void RegisterAssetBinds(webview::webview& wv); +} // namespace ui diff --git a/src/ModMan/Web/Binds/Binds.cpp b/src/ModMan/Web/Binds/Binds.cpp new file mode 100644 index 00000000..e1a13a8c --- /dev/null +++ b/src/ModMan/Web/Binds/Binds.cpp @@ -0,0 +1,17 @@ +#include "Binds.h" + +#include "AssetBinds.h" +#include "DialogBinds.h" +#include "UnlinkingBinds.h" +#include "ZoneBinds.h" + +namespace ui +{ + void RegisterAllBinds(webview::webview& wv) + { + RegisterAssetBinds(wv); + RegisterDialogHandlerBinds(wv); + RegisterUnlinkingBinds(wv); + RegisterZoneBinds(wv); + } +} // namespace ui diff --git a/src/ModMan/Web/Binds/Binds.h b/src/ModMan/Web/Binds/Binds.h new file mode 100644 index 00000000..3dd717ba --- /dev/null +++ b/src/ModMan/Web/Binds/Binds.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Web/WebViewLib.h" + +namespace ui +{ + void RegisterAllBinds(webview::webview& wv); +} diff --git a/src/ModMan/Web/Binds/UnlinkingBinds.cpp b/src/ModMan/Web/Binds/UnlinkingBinds.cpp new file mode 100644 index 00000000..ba3f7a58 --- /dev/null +++ b/src/ModMan/Web/Binds/UnlinkingBinds.cpp @@ -0,0 +1,122 @@ +#include "UnlinkingBinds.h" + +#include "Context/ModManContext.h" +#include "IObjWriter.h" +#include "SearchPath/OutputPathFilesystem.h" +#include "SearchPath/SearchPaths.h" +#include "Utils/PathUtils.h" +#include "Web/UiCommunication.h" + +#include "Json/JsonExtension.h" +#include + +namespace fs = std::filesystem; + +namespace +{ + class ZoneUnlinkProgressDto + { + public: + std::string zoneName; + double percentage; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnlinkProgressDto, zoneName, percentage); + + constexpr double MIN_PROGRESS_TO_REPORT = 0.5; + + class UnlinkingEventProgressReporter : public ProgressCallback + { + public: + explicit UnlinkingEventProgressReporter(std::string zoneName) + : m_zone_name(std::move(zoneName)), + m_last_progress(0) + { + } + + void OnProgress(const size_t current, const size_t total) override + { + const double percentage = static_cast(current) / static_cast(total) * 100.0; + + if (percentage - m_last_progress >= MIN_PROGRESS_TO_REPORT) + { + m_last_progress = percentage; + ui::NotifyZoneUnlinkProgress(m_zone_name, percentage); + } + } + + private: + std::string m_zone_name; + double m_last_progress; + }; + + result::Expected UnlinkZoneInDbThread(const std::string& zoneName) + { + const auto& context = ModManContext::Get().m_fast_file; + const auto existingZone = std::ranges::find_if(context.m_loaded_zones, + [&zoneName](const std::unique_ptr& loadedZone) + { + return loadedZone->m_zone->m_name == zoneName; + }); + + if (existingZone == context.m_loaded_zones.end()) + return result::Unexpected(std::format("No zone with name {} loaded", zoneName)); + + const auto& loadedZone = *existingZone->get(); + + const auto* objWriter = IObjWriter::GetObjWriterForGame(loadedZone.m_zone->m_game_id); + + const auto outputFolderPath = fs::path(utils::GetExecutablePath()).parent_path() / "zone_dump" / zoneName; + const auto outputFolderPathStr = outputFolderPath.string(); + + OutputPathFilesystem outputFolderOutputPath(outputFolderPath); + SearchPaths searchPaths; + AssetDumpingContext dumpingContext( + *loadedZone.m_zone, outputFolderPathStr, outputFolderOutputPath, searchPaths, std::make_unique(zoneName)); + objWriter->DumpZone(dumpingContext); + + return NoResult(); + } + + void UnlinkZone(webview::webview& wv, std::string id, std::string zoneName) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety + { + ModManContext::Get().m_db_thread.Dispatch( + [&wv, id, zoneName] + { + auto result = UnlinkZoneInDbThread(zoneName); + + if (result) + { + con::debug("Unlinked zone \"{}\"", zoneName); + ui::PromiseResolve(wv, id, true); + } + else + { + con::warn("Failed to unlink zone \"{}\": {}", zoneName, result.error()); + ui::PromiseReject(wv, id, std::move(result).error()); + } + }); + } +} // namespace + +namespace ui +{ + void NotifyZoneUnlinkProgress(std::string zoneName, const double percentage) + { + const ZoneUnlinkProgressDto dto{ + .zoneName = std::move(zoneName), + .percentage = percentage, + }; + Notify(*ModManContext::Get().m_main_webview, "zoneUnlinkProgress", dto); + } + + void RegisterUnlinkingBinds(webview::webview& wv) + { + BindAsync(wv, + "unlinkZone", + [&wv](const std::string& id, std::string zoneName) + { + UnlinkZone(wv, id, std::move(zoneName)); + }); + } +} // namespace ui diff --git a/src/ModMan/Web/Binds/UnlinkingBinds.h b/src/ModMan/Web/Binds/UnlinkingBinds.h new file mode 100644 index 00000000..6d2ac0ed --- /dev/null +++ b/src/ModMan/Web/Binds/UnlinkingBinds.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Web/WebViewLib.h" + +namespace ui +{ + void NotifyZoneUnlinkProgress(std::string zoneName, double percentage); + + void RegisterUnlinkingBinds(webview::webview& wv); +} // namespace ui diff --git a/src/ModMan/Web/Binds/ZoneBinds.cpp b/src/ModMan/Web/Binds/ZoneBinds.cpp new file mode 100644 index 00000000..31b93b6e --- /dev/null +++ b/src/ModMan/Web/Binds/ZoneBinds.cpp @@ -0,0 +1,185 @@ +#include "ZoneBinds.h" + +#include "Context/ModManContext.h" +#include "Web/UiCommunication.h" + +#include "Json/JsonExtension.h" + +NLOHMANN_JSON_SERIALIZE_ENUM(GameId, + { + {GameId::IW3, "IW3"}, + {GameId::IW4, "IW4"}, + {GameId::IW5, "IW5"}, + {GameId::T5, "T5" }, + {GameId::T6, "T6" }, +}); + +NLOHMANN_JSON_SERIALIZE_ENUM(GamePlatform, + { + {GamePlatform::PC, "PC" }, + {GamePlatform::XBOX, "XBOX"}, + {GamePlatform::PS3, "PS3" }, +}); + +namespace +{ + class ZoneDto + { + public: + std::string name; + std::string filePath; + GameId game; + GamePlatform platform; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneDto, name, filePath, game, platform); + + class ZoneLoadProgressDto + { + public: + std::string zoneName; + double percentage; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadProgressDto, zoneName, percentage); + + class ZoneLoadedDto + { + public: + ZoneDto zone; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zone); + + class ZoneUnloadedDto + { + public: + std::string zoneName; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnloadedDto, zoneName); + + ZoneDto CreateZoneDto(const LoadedZone& loadedZone) + { + return ZoneDto{ + .name = loadedZone.m_zone->m_name, + .filePath = loadedZone.m_file_path, + .game = loadedZone.m_zone->m_game_id, + .platform = loadedZone.m_zone->m_platform, + }; + } + + std::vector GetLoadedZones() + { + auto& context = ModManContext::Get().m_fast_file; + + std::vector result; + + { + std::shared_lock lock(context.m_zone_lock); + result.reserve(context.m_loaded_zones.size()); + + for (const auto& loadedZone : context.m_loaded_zones) + { + result.emplace_back(CreateZoneDto(*loadedZone)); + } + } + + return result; + } + + void LoadFastFile(webview::webview& wv, std::string id, std::string path) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety + { + ModManContext::Get().m_db_thread.Dispatch( + [&wv, id, path] + { + auto maybeZone = ModManContext::Get().m_fast_file.LoadFastFile(path); + + if (maybeZone) + { + ui::PromiseResolve(wv, + id, + ZoneLoadedDto{ + .zone = CreateZoneDto(*maybeZone.value()), + }); + con::debug("Loaded zone \"{}\"", maybeZone.value()->m_zone->m_name); + } + else + { + con::warn("Failed to load zone \"{}\": {}", path, maybeZone.error()); + ui::PromiseReject(wv, id, std::move(maybeZone).error()); + } + }); + } + + void UnloadZone(webview::webview& wv, std::string id, std::string zoneName) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety + { + ModManContext::Get().m_db_thread.Dispatch( + [&wv, id, zoneName] + { + auto result = ModManContext::Get().m_fast_file.UnloadZone(zoneName); + if (result) + { + con::debug("Unloaded zone \"{}\"", zoneName); + ui::PromiseResolve(wv, id, true); + } + else + { + con::warn("Failed unloading zone {}: {}", zoneName, result.error()); + ui::PromiseReject(wv, id, std::move(result).error()); + } + }); + } +} // namespace + +namespace ui +{ + void NotifyZoneLoadProgress(std::string zoneName, const double percentage) + { + const ZoneLoadProgressDto dto{ + .zoneName = std::move(zoneName), + .percentage = percentage, + }; + Notify(*ModManContext::Get().m_main_webview, "zoneLoadProgress", dto); + } + + void NotifyZoneLoaded(const LoadedZone& loadedZone) + { + const ZoneLoadedDto dto{ + .zone = CreateZoneDto(loadedZone), + }; + Notify(*ModManContext::Get().m_main_webview, "zoneLoaded", dto); + } + + void NotifyZoneUnloaded(std::string zoneName) + { + const ZoneUnloadedDto dto{ + .zoneName = std::move(zoneName), + }; + Notify(*ModManContext::Get().m_main_webview, "zoneUnloaded", dto); + } + + void RegisterZoneBinds(webview::webview& wv) + { + BindRetOnly>(wv, + "getZones", + [] + { + return GetLoadedZones(); + }); + + BindAsync(wv, + "loadFastFile", + [&wv](const std::string& id, std::string path) + { + LoadFastFile(wv, id, std::move(path)); + }); + + BindAsync(wv, + "unloadZone", + [&wv](const std::string& id, std::string zoneName) + { + UnloadZone(wv, id, std::move(zoneName)); + }); + } +} // namespace ui diff --git a/src/ModMan/Web/Binds/ZoneBinds.h b/src/ModMan/Web/Binds/ZoneBinds.h new file mode 100644 index 00000000..ee2efda8 --- /dev/null +++ b/src/ModMan/Web/Binds/ZoneBinds.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Context/FastFileContext.h" +#include "Web/WebViewLib.h" + +namespace ui +{ + void NotifyZoneLoadProgress(std::string zoneName, double percentage); + void NotifyZoneLoaded(const LoadedZone& loadedZone); + void NotifyZoneUnloaded(std::string zoneName); + + void RegisterZoneBinds(webview::webview& wv); +} // namespace ui diff --git a/src/ModMan/Web/Platform/AssetHandler.h b/src/ModMan/Web/Platform/AssetHandler.h index ebdb1ebf..89256ce2 100644 --- a/src/ModMan/Web/Platform/AssetHandler.h +++ b/src/ModMan/Web/Platform/AssetHandler.h @@ -1,7 +1,17 @@ #pragma once -#ifdef _WIN32 -#include "Windows/AssetHandlerWindows.h" -#elif defined(__linux__) -#include "Linux/AssetHandlerLinux.h" +#include "Web/Platform/Platform.h" +#include "Web/WebViewLib.h" + +#include + +namespace ui +{ +#if defined(PLATFORM_WINDOWS) + constexpr auto URL_PREFIX = "http://modman.local/"; +#elif defined(PLATFORM_LINUX) + constexpr auto URL_PREFIX = "modman://localhost/"; #endif + + void InstallAssetHandler(webview::webview& wv); +} // namespace ui diff --git a/src/ModMan/Web/Platform/FaviconHandler.h b/src/ModMan/Web/Platform/FaviconHandler.h new file mode 100644 index 00000000..52d2016e --- /dev/null +++ b/src/ModMan/Web/Platform/FaviconHandler.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Web/WebViewLib.h" + +#include + +namespace ui +{ + void InstallFaviconHandler(webview::webview& wv); +} // namespace ui diff --git a/src/ModMan/Web/Platform/Linux/AssetHandlerLinux.cpp b/src/ModMan/Web/Platform/Linux/AssetHandlerLinux.cpp index bb41be84..ae3826e3 100644 --- a/src/ModMan/Web/Platform/Linux/AssetHandlerLinux.cpp +++ b/src/ModMan/Web/Platform/Linux/AssetHandlerLinux.cpp @@ -1,6 +1,7 @@ -#include "AssetHandlerLinux.h" +#include "Web/Platform/AssetHandler.h" +#include "Web/Platform/Platform.h" -#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) +#ifdef PLATFORM_LINUX #include "Web/UiAssets.h" @@ -8,8 +9,6 @@ #include #include -using namespace PLATFORM_NAMESPACE_LINUX; - namespace { std::unordered_map assetLookup; @@ -37,7 +36,7 @@ namespace } } // namespace -namespace PLATFORM_NAMESPACE_LINUX +namespace ui { void InstallAssetHandler(webview::webview& wv) { @@ -49,6 +48,6 @@ namespace PLATFORM_NAMESPACE_LINUX webkit_web_context_register_uri_scheme(context, "modman", ModManUriSchemeRequestCb, NULL, nullptr); } -} // namespace PLATFORM_NAMESPACE_LINUX +} // namespace ui #endif diff --git a/src/ModMan/Web/Platform/Linux/AssetHandlerLinux.h b/src/ModMan/Web/Platform/Linux/AssetHandlerLinux.h deleted file mode 100644 index e8f49f2f..00000000 --- a/src/ModMan/Web/Platform/Linux/AssetHandlerLinux.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Web/Platform/Platform.h" - -#include - -#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) - -#include "Web/WebViewLib.h" - -namespace PLATFORM_NAMESPACE_LINUX -{ - constexpr auto URL_PREFIX = "modman://localhost/"; - - void InstallAssetHandler(webview::webview& wv); -} // namespace PLATFORM_NAMESPACE_LINUX - -#endif diff --git a/src/ModMan/Web/Platform/Linux/DialogHandlerLinux.cpp b/src/ModMan/Web/Platform/Linux/DialogHandlerLinux.cpp index 16fcdc6e..72963ac5 100644 --- a/src/ModMan/Web/Platform/Linux/DialogHandlerLinux.cpp +++ b/src/ModMan/Web/Platform/Linux/DialogHandlerLinux.cpp @@ -1,6 +1,7 @@ #include "Web/Platform/DialogHandler.h" +#include "Web/Platform/Platform.h" -#ifdef __linux__ +#ifdef PLATFORM_LINUX #include #include @@ -37,8 +38,6 @@ namespace #endif } - void OpenFileDialog() {} - void SetFilters(GtkFileDialog* pDialog, const std::vector& filters) { if (filters.empty()) diff --git a/src/ModMan/Web/Platform/Linux/FaviconHandlerLinux.cpp b/src/ModMan/Web/Platform/Linux/FaviconHandlerLinux.cpp new file mode 100644 index 00000000..c0928934 --- /dev/null +++ b/src/ModMan/Web/Platform/Linux/FaviconHandlerLinux.cpp @@ -0,0 +1,22 @@ +#include "Web/Platform/FaviconHandler.h" +#include "Web/Platform/Platform.h" + +#ifdef PLATFORM_LINUX + +#include "Web/UiAssets.h" + +#include +#include +#include + +namespace ui +{ + void InstallFaviconHandler(webview::webview& wv) + { + // The icon system on Linux works a bit different than on Windows + // and doesn't really support this kind of dynamic icon setting + // we skip it for now + } +} // namespace ui + +#endif diff --git a/src/ModMan/Web/Platform/Linux/TitleHandlerLinux.cpp b/src/ModMan/Web/Platform/Linux/TitleHandlerLinux.cpp new file mode 100644 index 00000000..78eb8e1a --- /dev/null +++ b/src/ModMan/Web/Platform/Linux/TitleHandlerLinux.cpp @@ -0,0 +1,30 @@ +#include "Web/Platform/Platform.h" +#include "Web/Platform/TitleHandler.h" + +#ifdef PLATFORM_LINUX + +#include "Web/UiAssets.h" + +#include +#include +#include + +namespace ui +{ + void InstallTitleHandler(webview::webview& wv) + { + const auto webViewWidget = static_cast(wv.browser_controller().value()); + const auto webView = WEBKIT_WEB_VIEW(webViewWidget); + const auto windowWidget = static_cast(wv.window().value()); + const auto window = GTK_WINDOW(windowWidget); + + auto on_title_changed = +[](GtkWidget* widget, GParamSpec paramSpec, GtkWindow* window) + { + gtk_window_set_title(window, webkit_web_view_get_title(WEBKIT_WEB_VIEW(widget))); + }; + + g_signal_connect(G_OBJECT(webView), "notify::title", G_CALLBACK(on_title_changed), (gpointer)window); + } +} // namespace ui + +#endif diff --git a/src/ModMan/Web/Platform/Platform.h b/src/ModMan/Web/Platform/Platform.h index e531deca..787fe814 100644 --- a/src/ModMan/Web/Platform/Platform.h +++ b/src/ModMan/Web/Platform/Platform.h @@ -1,10 +1,7 @@ #pragma once -#define PLATFORM_NAMESPACE_WINDOWS windows -#define PLATFORM_NAMESPACE_LINUX linux - #ifdef _WIN32 -#define PLATFORM_NAMESPACE PLATFORM_NAMESPACE_WINDOWS +#define PLATFORM_WINDOWS #elif defined(__linux__) -#define PLATFORM_NAMESPACE PLATFORM_NAMESPACE_LINUX +#define PLATFORM_LINUX #endif diff --git a/src/ModMan/Web/Platform/TitleHandler.h b/src/ModMan/Web/Platform/TitleHandler.h new file mode 100644 index 00000000..4ff8ffae --- /dev/null +++ b/src/ModMan/Web/Platform/TitleHandler.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Web/WebViewLib.h" + +#include + +namespace ui +{ + void InstallTitleHandler(webview::webview& wv); +} // namespace ui diff --git a/src/ModMan/Web/Platform/Windows/AssetHandlerWindows.cpp b/src/ModMan/Web/Platform/Windows/AssetHandlerWindows.cpp index ba400bd0..9e2309bb 100644 --- a/src/ModMan/Web/Platform/Windows/AssetHandlerWindows.cpp +++ b/src/ModMan/Web/Platform/Windows/AssetHandlerWindows.cpp @@ -1,6 +1,7 @@ -#include "AssetHandlerWindows.h" +#include "Web/Platform/AssetHandler.h" +#include "Web/Platform/Platform.h" -#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) +#ifdef PLATFORM_WINDOWS #include "PlatformUtilsWindows.h" #include "Web/UiAssets.h" @@ -12,8 +13,6 @@ #include #include -using namespace PLATFORM_NAMESPACE_WINDOWS; - namespace { constexpr auto LOCALHOST_PREFIX = "http://localhost:"; @@ -25,7 +24,7 @@ namespace std::wstringstream wss; wss << std::format(L"Content-Length: {}\n", contentLength); - wss << L"Content-Type: " << StringToWideString(ui::GetMimeTypeForFileName(assetName)); + wss << L"Content-Type: " << utils::StringToWideString(ui::GetMimeTypeForFileName(assetName)); return wss.str(); } @@ -65,7 +64,7 @@ namespace Microsoft::WRL::ComPtr response; - const auto uri = WideStringToString(wUri); + const auto uri = utils::WideStringToString(wUri); bool fileFound = false; #ifdef _DEBUG @@ -74,9 +73,9 @@ namespace return S_OK; #endif - if (uri.starts_with(URL_PREFIX)) + if (uri.starts_with(ui::URL_PREFIX)) { - const auto asset = uri.substr(std::char_traits::length(URL_PREFIX) - 1); + const auto asset = uri.substr(std::char_traits::length(ui::URL_PREFIX) - 1); const auto foundUiFile = assetLookup.find(asset); if (foundUiFile != assetLookup.end()) @@ -117,7 +116,7 @@ namespace } } // namespace -namespace PLATFORM_NAMESPACE_WINDOWS +namespace ui { void InstallAssetHandler(webview::webview& wv) { @@ -157,6 +156,6 @@ namespace PLATFORM_NAMESPACE_WINDOWS std::cerr << "Failed to add resource requested filter\n"; } } -} // namespace PLATFORM_NAMESPACE_WINDOWS +} // namespace ui #endif diff --git a/src/ModMan/Web/Platform/Windows/AssetHandlerWindows.h b/src/ModMan/Web/Platform/Windows/AssetHandlerWindows.h deleted file mode 100644 index b6475fd9..00000000 --- a/src/ModMan/Web/Platform/Windows/AssetHandlerWindows.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Web/Platform/Platform.h" - -#include - -#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) - -#include "Web/WebViewLib.h" - -namespace PLATFORM_NAMESPACE_WINDOWS -{ - constexpr auto URL_PREFIX = "http://modman.local/"; - - void InstallAssetHandler(webview::webview& wv); -} // namespace PLATFORM_NAMESPACE_WINDOWS - -#endif diff --git a/src/ModMan/Web/Platform/Windows/DialogHandlerWindows.cpp b/src/ModMan/Web/Platform/Windows/DialogHandlerWindows.cpp index 68b34f23..396b31ba 100644 --- a/src/ModMan/Web/Platform/Windows/DialogHandlerWindows.cpp +++ b/src/ModMan/Web/Platform/Windows/DialogHandlerWindows.cpp @@ -1,6 +1,7 @@ #include "Web/Platform/DialogHandler.h" +#include "Web/Platform/Platform.h" -#ifdef _WIN32 +#ifdef PLATFORM_WINDOWS #include "PlatformUtilsWindows.h" @@ -8,8 +9,6 @@ #include #include -using namespace PLATFORM_NAMESPACE_WINDOWS; - namespace { bool SetFilters(IFileDialog* pFileOpen, const std::vector& filters) @@ -29,8 +28,8 @@ namespace const auto& filter = filters[i]; COMDLG_FILTERSPEC filterSpec; - const auto& wName = filterStrings.emplace_back(StringToWideString(filter.m_name)); - const auto& wSpec = filterStrings.emplace_back(StringToWideString(filter.m_filter)); + const auto& wName = filterStrings.emplace_back(utils::StringToWideString(filter.m_name)); + const auto& wSpec = filterStrings.emplace_back(utils::StringToWideString(filter.m_filter)); filterSpec.pszName = wName.c_str(); filterSpec.pszSpec = wSpec.c_str(); @@ -65,7 +64,7 @@ namespace // Display the file name to the user. if (SUCCEEDED(hr)) { - result = WideStringToString(pszFilePath); + result = utils::WideStringToString(pszFilePath); CoTaskMemFree(pszFilePath); resultType = ui::DialogCallbackResultType::SUCCESSFUL; diff --git a/src/ModMan/Web/Platform/Windows/FaviconHandlerWindows.cpp b/src/ModMan/Web/Platform/Windows/FaviconHandlerWindows.cpp new file mode 100644 index 00000000..856deb68 --- /dev/null +++ b/src/ModMan/Web/Platform/Windows/FaviconHandlerWindows.cpp @@ -0,0 +1,150 @@ +#include "Web/Platform/FaviconHandler.h" +#include "Web/Platform/Platform.h" + +#ifdef PLATFORM_WINDOWS + +#include "PlatformUtilsWindows.h" +#include "Web/UiAssets.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + class UniqueHIcon + { + public: + UniqueHIcon() + : m_icon(nullptr) + { + } + + ~UniqueHIcon() + { + if (m_icon) + { + DestroyIcon(m_icon); + m_icon = nullptr; + } + } + + UniqueHIcon(const UniqueHIcon& other) = default; + + UniqueHIcon(UniqueHIcon&& other) noexcept + : m_icon(other.m_icon) + { + other.m_icon = nullptr; + } + + UniqueHIcon& operator=(const UniqueHIcon& other) = default; + + UniqueHIcon& operator=(UniqueHIcon&& other) noexcept + { + m_icon = other.m_icon; + other.m_icon = nullptr; + return *this; + } + + HICON& get() + { + return m_icon; + } + + private: + HICON m_icon; + }; + + std::unordered_map icons; + + HRESULT HandleFaviconChanged(ICoreWebView2_15* core15, HWND window) + { + LPWSTR url; + + if (!SUCCEEDED(core15->get_FaviconUri(&url))) + { + std::cerr << "Failed to get favicon uri\n"; + return S_FALSE; + } + + const std::wstring strUrl(url); + CoTaskMemFree(url); + + if (strUrl.empty()) + { + icons.erase(icons.find(window)); + SendMessage(window, WM_SETICON, ICON_SMALL, (LPARAM)NULL); + } + else + { + if (!SUCCEEDED(core15->GetFavicon(COREWEBVIEW2_FAVICON_IMAGE_FORMAT_PNG, + Microsoft::WRL::Callback( + [window](HRESULT errorCode, IStream* iconStream) -> HRESULT + { + Gdiplus::Bitmap iconBitmap(iconStream); + UniqueHIcon icon; + if (iconBitmap.GetHICON(&icon.get()) == Gdiplus::Status::Ok) + { + SendMessage(window, WM_SETICON, ICON_SMALL, reinterpret_cast(icon.get())); + icons.emplace(window, std::move(icon)).first->second.get(); + } + else + { + icons.erase(icons.find(window)); + SendMessage(window, WM_SETICON, ICON_SMALL, NULL); + } + + return S_OK; + }) + .Get()))) + { + std::cerr << "Failed to get favicon\n"; + return S_FALSE; + } + } + return S_OK; + } +} // namespace + +namespace ui +{ + void InstallFaviconHandler(webview::webview& wv) + { + const auto controller = static_cast(wv.browser_controller().value()); + auto window = static_cast(wv.window().value()); + Microsoft::WRL::ComPtr core; + if (!SUCCEEDED(controller->get_CoreWebView2(&core))) + { + std::cerr << "Failed to get webview\n"; + return; + } + + Microsoft::WRL::ComPtr core15; + if (!SUCCEEDED(core->QueryInterface(IID_PPV_ARGS(&core15)))) + { + std::cerr << "Failed to get core15\n"; + return; + } + + const Gdiplus::GdiplusStartupInput gdiPlusStartupInput; + ULONG_PTR gdiPlusToken; + Gdiplus::GdiplusStartup(&gdiPlusToken, &gdiPlusStartupInput, nullptr); + EventRegistrationToken token; + if (!SUCCEEDED(core15->add_FaviconChanged(Microsoft::WRL::Callback( + [core15, window](ICoreWebView2* sender, IUnknown* args) -> HRESULT + { + return HandleFaviconChanged(core15.Get(), window); + }) + .Get(), + &token))) + { + std::cerr << "Failed to add favicon handler\n"; + } + } +} // namespace ui + +#endif diff --git a/src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.cpp b/src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.cpp index b9e419ad..17c43fef 100644 --- a/src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.cpp +++ b/src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.cpp @@ -1,12 +1,12 @@ #include "PlatformUtilsWindows.h" -#ifdef _WIN32 +#ifdef PLATFORM_WINDOWS #include #include #include -namespace PLATFORM_NAMESPACE_WINDOWS +namespace utils { std::string WideStringToString(const std::wstring& wideString) { @@ -35,6 +35,6 @@ namespace PLATFORM_NAMESPACE_WINDOWS MultiByteToWideChar(CP_UTF8, 0, string.data(), static_cast(string.size()), result.data(), sizeNeeded); return result; } -} // namespace PLATFORM_NAMESPACE_WINDOWS +} // namespace utils #endif diff --git a/src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.h b/src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.h index 0edb4b86..5476f3f6 100644 --- a/src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.h +++ b/src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.h @@ -1,16 +1,15 @@ #pragma once -#ifdef _WIN32 - #include "Web/Platform/Platform.h" -#include +#ifdef PLATFORM_WINDOWS + #include -namespace PLATFORM_NAMESPACE_WINDOWS +namespace utils { std::string WideStringToString(const std::wstring& wideString); std::wstring StringToWideString(const std::string& string); -} // namespace PLATFORM_NAMESPACE_WINDOWS +} // namespace utils #endif diff --git a/src/ModMan/Web/Platform/Windows/TitleHandlerWindows.cpp b/src/ModMan/Web/Platform/Windows/TitleHandlerWindows.cpp new file mode 100644 index 00000000..37548484 --- /dev/null +++ b/src/ModMan/Web/Platform/Windows/TitleHandlerWindows.cpp @@ -0,0 +1,62 @@ +#include "Web/Platform/Platform.h" +#include "Web/Platform/TitleHandler.h" + +#ifdef PLATFORM_WINDOWS + +#include "PlatformUtilsWindows.h" +#include "Web/UiAssets.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + HRESULT HandleTitleChanged(ICoreWebView2* core, HWND window) + { + LPWSTR title; + + if (!SUCCEEDED(core->get_DocumentTitle(&title))) + { + std::cerr << "Failed to get title\n"; + return S_FALSE; + } + + SetWindowTextW(window, title); + CoTaskMemFree(title); + + return S_OK; + } +} // namespace + +namespace ui +{ + void InstallTitleHandler(webview::webview& wv) + { + const auto controller = static_cast(wv.browser_controller().value()); + auto window = static_cast(wv.window().value()); + Microsoft::WRL::ComPtr core; + if (!SUCCEEDED(controller->get_CoreWebView2(&core))) + { + std::cerr << "Failed to get webview\n"; + return; + } + + EventRegistrationToken token; + if (!SUCCEEDED(core->add_DocumentTitleChanged(Microsoft::WRL::Callback( + [window](ICoreWebView2* sender, IUnknown* args) -> HRESULT + { + return HandleTitleChanged(sender, window); + }) + .Get(), + &token))) + { + std::cerr << "Failed to add title handler\n"; + } + } +} // namespace ui + +#endif diff --git a/src/ModMan/Web/WebViewLib.h b/src/ModMan/Web/WebViewLib.h index 5a8fd0b0..f120396d 100644 --- a/src/ModMan/Web/WebViewLib.h +++ b/src/ModMan/Web/WebViewLib.h @@ -1,5 +1,7 @@ #pragma once +#define NOMINMAX + #ifdef _MSC_VER #pragma warning(push, 0) #else diff --git a/src/ModMan/main.cpp b/src/ModMan/main.cpp index a6afc7bb..fe8eeba1 100644 --- a/src/ModMan/main.cpp +++ b/src/ModMan/main.cpp @@ -1,32 +1,41 @@ -#include "GitVersion.h" -#include "Web/Binds/DialogBinds.h" +#include "Context/ModManContext.h" +#include "GitVersion.h" +#include "ModManArgs.h" +#include "Web/Binds/Binds.h" #include "Web/Platform/AssetHandler.h" +#include "Web/Platform/FaviconHandler.h" +#include "Web/Platform/TitleHandler.h" #include "Web/UiCommunication.h" #include "Web/ViteAssets.h" #include "Web/WebViewLib.h" -#include #include #include -#include #include #include +#ifdef _WIN32 +#include +#endif + using namespace std::string_literals; -using namespace PLATFORM_NAMESPACE; namespace { #ifdef _DEBUG - std::optional devToolWindow; - - void RunDevToolsWindow() + void SpawnDevToolsWindow() { con::debug("Creating dev tools window"); + auto& context = ModManContext::Get(); + try { - auto& newWindow = devToolWindow.emplace(false, nullptr); + context.m_dev_tools_webview = std::make_unique(false, nullptr); + auto& newWindow = *context.m_dev_tools_webview; + ui::InstallFaviconHandler(newWindow); + ui::InstallTitleHandler(newWindow); + newWindow.set_title("Devtools"); newWindow.set_size(640, 480, WEBVIEW_HINT_NONE); newWindow.set_size(480, 320, WEBVIEW_HINT_MIN); @@ -39,59 +48,46 @@ namespace } #endif - int RunMainWindow() + int SpawnMainWindow() { con::debug("Creating main window"); + auto& context = ModManContext::Get(); try { - webview::webview w( + context.m_main_webview = std::make_unique( #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); + auto& newWindow = *context.m_main_webview; - ui::RegisterDialogHandlerBinds(w); + newWindow.set_title("OpenAssetTools ModMan"); + newWindow.set_size(1280, 640, WEBVIEW_HINT_NONE); + newWindow.set_size(640, 480, 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); - }); - -#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) - InstallAssetHandler(w); - constexpr auto urlPrefix = URL_PREFIX; -#elif defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) - InstallAssetHandler(w); - constexpr auto urlPrefix = URL_PREFIX; -#else -#error Unsupported platform -#endif + ui::InstallAssetHandler(newWindow); + ui::InstallFaviconHandler(newWindow); + ui::InstallTitleHandler(newWindow); + ui::RegisterAllBinds(newWindow); #ifdef _DEBUG - w.navigate(VITE_DEV_SERVER ? std::format("http://localhost:{}", VITE_DEV_SERVER_PORT) : std::format("{}index.html", urlPrefix)); + newWindow.navigate(VITE_DEV_SERVER ? std::format("http://localhost:{}", VITE_DEV_SERVER_PORT) : std::format("{}index.html", ui::URL_PREFIX)); if (VITE_DEV_SERVER) { - w.dispatch( + newWindow.dispatch( [] { - RunDevToolsWindow(); + SpawnDevToolsWindow(); }); } #else - w.navigate(std::format("{}index.html", urlPrefix)); + newWindow.navigate(std::format("{}index.html", ui::URL_PREFIX)); #endif - w.run(); + newWindow.run(); } catch (const webview::exception& e) { @@ -104,16 +100,50 @@ namespace } // namespace #ifdef _WIN32 +#define MODMAN_ARGC __argc +#define MODMAN_ARGV const_cast(__argv) int WINAPI WinMain(HINSTANCE /*hInst*/, HINSTANCE /*hPrevInst*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/) -{ #else -int main() -{ +#define MODMAN_ARGC argc +#define MODMAN_ARGV argv +int main(int argc, const char** argv) #endif +{ +#ifdef _WIN32 + // Attach console if possible on Windows for stdout/stderr in console + if (AttachConsole(-1)) + { + FILE* fDummy; + (void)freopen_s(&fDummy, "CONOUT$", "w", stdout); + (void)freopen_s(&fDummy, "CONOUT$", "w", stderr); + (void)freopen_s(&fDummy, "CONIN$", "r", stdin); + std::cout.clear(); + std::clog.clear(); + std::cerr.clear(); + std::cin.clear(); + } +#endif + +#ifdef __linux__ + g_set_prgname("OpenAssetTools-ModMan"); + g_set_application_name("OpenAssetTools ModMan"); +#endif + + ModManArgs args; + auto shouldContinue = true; + if (!args.ParseArgs(MODMAN_ARGC, MODMAN_ARGV, shouldContinue)) + return false; + + if (!shouldContinue) + return true; con::info("Starting ModMan " GIT_VERSION); - const auto result = RunMainWindow(); + ModManContext::Get().Startup(); + + const auto result = SpawnMainWindow(); + + ModManContext::Get().Destroy(); return result; } diff --git a/src/ModManUi/build/HeaderTransformationPlugin.ts b/src/ModManUi/build/HeaderTransformationPlugin.ts index 4d6fba3c..ca19ed12 100644 --- a/src/ModManUi/build/HeaderTransformationPlugin.ts +++ b/src/ModManUi/build/HeaderTransformationPlugin.ts @@ -1,4 +1,4 @@ -import type { Plugin } from "vite"; +import type { Plugin, UserConfig } from "vite"; import type { OutputAsset, OutputChunk } from "rollup"; import path from "node:path"; import fs from "node:fs"; @@ -7,8 +7,35 @@ type MinimalOutputAsset = Pick; type MinimalOutputChunk = Pick; type MinimalOutputBundle = Record; +interface PublicDirFile { + fullPath: string; + relativePath: string; +} + +function getPublicDirFiles(publicDir?: string): PublicDirFile[] { + if (!publicDir) return []; + + const result: PublicDirFile[] = []; + const files = fs.readdirSync(publicDir, { recursive: true, withFileTypes: true }); + for (const file of files) { + if (!file.isFile()) continue; + const fullPath = path.join(file.parentPath, file.name); + let relativePath = path.relative(publicDir, fullPath).replaceAll(/\\/g, "/"); + if (relativePath.startsWith("./")) { + relativePath = relativePath.substring(2); + } + + result.push({ + fullPath, + relativePath, + }); + } + + return result; +} + function createVarName(fileName: string) { - return fileName.replaceAll(".", "_").toUpperCase(); + return fileName.replaceAll(/[\\/]/g, "__").replaceAll(/[\.-]/g, "_").toUpperCase(); } function transformAsset(asset: MinimalOutputAsset) { @@ -33,10 +60,19 @@ function transformChunk(chunk: MinimalOutputChunk) { `; } +function transformPublicFile(publicFile: PublicDirFile) { + const varName = createVarName(publicFile.relativePath); + const bytes = [...fs.readFileSync(publicFile.fullPath)].map((v) => String(v)).join(","); + + return `constexpr const unsigned char ${varName}[] {${bytes}}; +`; +} + function writeHeader( bundle: MinimalOutputBundle, outputDir?: string, options?: HeaderTransformationPluginOptions, + publicDir?: string, devServerPort?: number, ) { const outputPath = options?.outputPath ?? path.join(outputDir ?? "dist", "ViteAssets.h"); @@ -71,12 +107,18 @@ constexpr auto VITE_DEV_SERVER_PORT = ${devServerPort ? String(devServerPort) : `, ); + const fileNames: string[] = []; for (const curBundle of Object.values(bundle)) { if (curBundle.type === "asset") { fs.writeSync(fd, transformAsset(curBundle)); } else { fs.writeSync(fd, transformChunk(curBundle)); } + fileNames.push(curBundle.fileName); + } + for (const publicDirFile of getPublicDirFiles(publicDir)) { + fs.writeSync(fd, transformPublicFile(publicDirFile)); + fileNames.push(publicDirFile.relativePath); } if (includeFileEnumeration) { @@ -95,8 +137,7 @@ static inline const UiFile MOD_MAN_UI_FILES[] { ); let index = 0; - for (const curBundle of Object.values(bundle)) { - const fileName = curBundle.fileName; + for (const fileName of fileNames) { const varName = createVarName(fileName); let prefix = " "; @@ -133,16 +174,21 @@ export default function headerTransformationPlugin( ): Plugin { let writeServerActive = false; let writeBundleActive = false; + let publicDir: string | undefined = undefined; return { name: "vite-plugin-header-transformation", enforce: "post", - config(_userOptions, env) { + config(userOptions: UserConfig, env) { if (env.command === "serve") { writeServerActive = true; } else { writeBundleActive = true; } + + if (typeof userOptions.publicDir === "string") { + publicDir = userOptions.publicDir; + } }, configureServer(server) { if (!writeServerActive) { @@ -156,6 +202,7 @@ export default function headerTransformationPlugin( }, server.config.build.outDir, options, + publicDir, server.config.server.port, ); }); @@ -165,7 +212,7 @@ export default function headerTransformationPlugin( return; } - writeHeader(bundle, outputOptions.dir, options); + writeHeader(bundle, outputOptions.dir, options, publicDir); }, }; } diff --git a/src/ModManUi/index.html b/src/ModManUi/index.html index 27a13611..945cf0e6 100644 --- a/src/ModManUi/index.html +++ b/src/ModManUi/index.html @@ -2,9 +2,10 @@ - + + - Tauri + Vue + Typescript App + OpenAssetTools ModMan
diff --git a/src/ModManUi/package-lock.json b/src/ModManUi/package-lock.json index 46c3d517..515f6ca6 100644 --- a/src/ModManUi/package-lock.json +++ b/src/ModManUi/package-lock.json @@ -8,33 +8,44 @@ "name": "openassettools", "version": "0.1.0", "dependencies": { + "@fontsource/inter": "^5.2.8", + "@primeuix/themes": "^1.2.5", "pinia": "3.0.3", - "vue": "3.5.22" + "primevue": "^4.4.1", + "vue": "3.5.22", + "vue-router": "4.6.3" }, "devDependencies": { "@tsconfig/node22": "^22.0.2", - "@types/jsdom": "^21.1.7", - "@types/node": "^22.18.6", + "@types/jsdom": "^27.0.0", + "@types/node": "^24.9.2", "@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", + "eslint": "^9.39.0", + "eslint-plugin-vue": "~10.5.1", "jiti": "^2.5.1", - "jsdom": "^27.0.0", + "jsdom": "^27.1.0", "npm-run-all2": "^8.0.4", "prettier": "3.6.2", - "sass": "1.93.2", + "sass": "1.93.3", "typescript": "~5.9.3", - "vite": "7.1.7", - "vite-plugin-vue-devtools": "^8.0.2", + "vite": "7.1.12", + "vite-plugin-vue-devtools": "^8.0.3", "vitest": "^3.2.4", - "vue-tsc": "3.1.0" + "vue-tsc": "3.1.2" } }, + "node_modules/@acemir/cssom": { + "version": "0.9.19", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.19.tgz", + "integrity": "sha512-Pp2gAQXPZ2o7lt4j0IMwNRXqQ3pagxtDj5wctL5U2Lz4oV0ocDNlkgx4DpxfyKav4S/bePuI+SMqcBSUHLy9kg==", + "dev": true, + "license": "MIT" + }, "node_modules/@asamuzakjp/css-color": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.5.tgz", @@ -50,9 +61,9 @@ } }, "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==", + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.4.tgz", + "integrity": "sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==", "dev": true, "license": "MIT", "dependencies": { @@ -101,7 +112,6 @@ "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", @@ -654,7 +664,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -663,9 +672,9 @@ } }, "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==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.15.tgz", + "integrity": "sha512-q0p6zkVq2lJnmzZVPR33doA51G7YOja+FBvRdp5ISIthL0MtFCgYHHhR563z9WFGxcOn0WfjSkPDJ5Qig3H3Sw==", "dev": true, "funding": [ { @@ -680,9 +689,6 @@ "license": "MIT-0", "engines": { "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" } }, "node_modules/@csstools/css-tokenizer": { @@ -701,7 +707,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -1178,13 +1183,13 @@ } }, "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==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -1217,19 +1222,22 @@ } }, "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==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.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==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1288,9 +1296,9 @@ } }, "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==", + "version": "9.39.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.0.tgz", + "integrity": "sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==", "dev": true, "license": "MIT", "engines": { @@ -1301,9 +1309,9 @@ } }, "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==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1311,19 +1319,28 @@ } }, "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==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.2", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fontsource/inter": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.8.tgz", + "integrity": "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1829,6 +1846,74 @@ "dev": true, "license": "MIT" }, + "node_modules/@primeuix/styled": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@primeuix/styled/-/styled-0.7.4.tgz", + "integrity": "sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ==", + "license": "MIT", + "dependencies": { + "@primeuix/utils": "^0.6.1" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@primeuix/styles": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@primeuix/styles/-/styles-1.2.5.tgz", + "integrity": "sha512-nypFRct/oaaBZqP4jinT0puW8ZIfs4u+l/vqUFmJEPU332fl5ePj6DoOpQgTLzo3OfmvSmz5a5/5b4OJJmmi7Q==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.7.3" + } + }, + "node_modules/@primeuix/themes": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@primeuix/themes/-/themes-1.2.5.tgz", + "integrity": "sha512-n3YkwJrHQaEESc/D/A/iD815sxp8cKnmzscA6a8Tm8YvMtYU32eCahwLLe6h5rywghVwxASWuG36XBgISYOIjQ==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.7.3" + } + }, + "node_modules/@primeuix/utils": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@primeuix/utils/-/utils-0.6.1.tgz", + "integrity": "sha512-tQL/ZOPgCdD+NTimlUmhyD0ey8J1XmpZE4hDHM+/fnuBicVVmlKOd5HpS748LcOVRUKbWjmEPdHX4hi5XZoC1Q==", + "license": "MIT", + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@primevue/core": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.4.1.tgz", + "integrity": "sha512-RG56iDKIJT//EtntjQzOiWOHZZJczw/qWWtdL5vFvw8/QDS9DPKn8HLpXK7N5Le6KK1MLXUsxoiGTZK+poUFUg==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.7.4", + "@primeuix/utils": "^0.6.1" + }, + "engines": { + "node": ">=12.11.0" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@primevue/icons": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@primevue/icons/-/icons-4.4.1.tgz", + "integrity": "sha512-UfDimrIjVdY6EziwieyV4zPKzW6mnKHKhy4Dgyjv2oI6pNeuim+onbJo1ce22PEGXW78vfblG/3/JIzVHFweqQ==", + "license": "MIT", + "dependencies": { + "@primeuix/utils": "^0.6.1", + "@primevue/core": "4.4.1" + }, + "engines": { + "node": ">=12.11.0" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.29", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", @@ -2144,26 +2229,6 @@ "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", @@ -2196,9 +2261,9 @@ "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==", + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-27.0.0.tgz", + "integrity": "sha512-NZyFl/PViwKzdEkQg96gtnB8wm+1ljhdDay9ahn4hgb+SfVtPCbm3TlmDUFXTA+MGN3CijicnMhG18SI5H3rFw==", "dev": true, "license": "MIT", "dependencies": { @@ -2215,13 +2280,13 @@ "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==", + "version": "24.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz", + "integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/tough-cookie": { @@ -2277,7 +2342,6 @@ "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", @@ -2785,14 +2849,14 @@ } }, "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==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.0.3.tgz", + "integrity": "sha512-gCEQN7aMmeaigEWJQ2Z2o3g7/CMqGTPvNS1U3n/kzpLoAZ1hkAHNgi4ml/POn/9uqGILBk65GGOUdrraHXRj5Q==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-kit": "^8.0.2", - "@vue/devtools-shared": "^8.0.2", + "@vue/devtools-kit": "^8.0.3", + "@vue/devtools-shared": "^8.0.3", "mitt": "^3.0.1", "nanoid": "^5.1.5", "pathe": "^2.0.3", @@ -2803,14 +2867,14 @@ } }, "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==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.3.tgz", + "integrity": "sha512-UF4YUOVGdfzXLCv5pMg2DxocB8dvXz278fpgEE+nJ/DRALQGAva7sj9ton0VWZ9hmXw+SV8yKMrxP2MpMhq9Wg==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-shared": "^8.0.2", - "birpc": "^2.5.0", + "@vue/devtools-shared": "^8.0.3", + "birpc": "^2.6.1", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^2.0.0", @@ -2819,9 +2883,9 @@ } }, "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==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.3.tgz", + "integrity": "sha512-s/QNll7TlpbADFZrPVsaUNPCOF8NvQgtgmmB7Tip6pLf/HcOvBTly0lfLQ0Eylu9FQ4OqBhFpLyBgwykiSf8zw==", "dev": true, "license": "MIT", "dependencies": { @@ -2920,9 +2984,9 @@ } }, "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==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.1.2.tgz", + "integrity": "sha512-PyFDCqpdfYUT+oMLqcc61oHfJlC6yjhybaefwQjRdkchIihToOEpJ2Wu/Ebq2yrnJdd1EsaAvZaXVAqcxtnDxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3039,7 +3103,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3085,9 +3148,9 @@ } }, "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==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.0.3.tgz", + "integrity": "sha512-2JXjom6R7ZwrISpUphLhf4htUq1aKRCennTJ6u9kFfr3sLmC9+I4CxxVi+McoFnIg+p1HnVrfLT/iCt4Dlz//Q==", "dev": true, "license": "MIT" }, @@ -3233,7 +3296,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -3478,9 +3540,9 @@ } }, "node_modules/cssstyle": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.1.tgz", - "integrity": "sha512-g5PC9Aiph9eiczFpcgUhd9S4UUO3F+LHGRIi5NUMZ+4xtoIYbHNZwZnWA2JsFGe8OU8nl4WyaEFiZuGuxlutJQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.2.tgz", + "integrity": "sha512-zDMqXh8Vs1CdRYZQ2M633m/SFgcjlu8RB8b/1h82i+6vpArF507NSYIWJHGlJaTWoS+imcnctmEz43txhbVkOw==", "dev": true, "license": "MIT", "dependencies": { @@ -3762,26 +3824,24 @@ } }, "node_modules/eslint": { - "version": "9.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", - "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "version": "9.39.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.0.tgz", + "integrity": "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==", "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/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.36.0", - "@eslint/plugin-kit": "^0.3.5", + "@eslint/js": "9.39.0", + "@eslint/plugin-kit": "^0.4.1", "@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", @@ -3829,7 +3889,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -3872,12 +3931,11 @@ } }, "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==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.5.1.tgz", + "integrity": "sha512-SbR9ZBUFKgvWAbq3RrdCtWaW0IKm6wwUiApxf3BVTNfqUIo4IQQmreMg2iHFJJ6C/0wss3LXURBJ1OwS/MhFcQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", @@ -3890,11 +3948,15 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { + "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", "vue-eslint-parser": "^10.0.0" }, "peerDependenciesMeta": { + "@stylistic/eslint-plugin": { + "optional": true + }, "@typescript-eslint/parser": { "optional": true } @@ -4050,33 +4112,6 @@ "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", @@ -4173,22 +4208,6 @@ } } }, - "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", @@ -4295,23 +4314,6 @@ "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", @@ -4423,16 +4425,6 @@ "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", @@ -4575,19 +4567,6 @@ "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", @@ -4595,32 +4574,6 @@ "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", @@ -4678,7 +4631,6 @@ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -4736,22 +4688,22 @@ } }, "node_modules/jsdom": { - "version": "27.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.0.0.tgz", - "integrity": "sha512-lIHeR1qlIRrIN5VMccd8tI2Sgw6ieYXSVktcSHaNe3Z5nE/tcPQYQWOq00wxMvYOsz+73eAkNenVvmPC6bba9A==", + "version": "27.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.1.0.tgz", + "integrity": "sha512-Pcfm3eZ+eO4JdZCXthW9tCDT3nF4K+9dmeZ+5X39n+Kqz0DDIABRP5CAEOHRFZk8RGuC2efksTJxrjp8EXCunQ==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/dom-selector": "^6.5.4", - "cssstyle": "^5.3.0", + "@acemir/cssom": "^0.9.19", + "@asamuzakjp/dom-selector": "^6.7.3", + "cssstyle": "^5.3.2", "data-urls": "^6.0.0", - "decimal.js": "^10.5.0", + "decimal.js": "^10.6.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", + "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", @@ -4759,12 +4711,12 @@ "webidl-conversions": "^8.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^15.0.0", - "ws": "^8.18.2", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=20" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { "canvas": "^3.0.0" @@ -4775,6 +4727,32 @@ } } }, + "node_modules/jsdom/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/jsdom/node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "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", @@ -5163,36 +5141,6 @@ "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", @@ -5302,19 +5250,6 @@ "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", @@ -5487,7 +5422,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5527,7 +5461,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -5551,20 +5484,20 @@ "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, + "node_modules/primevue": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/primevue/-/primevue-4.4.1.tgz", + "integrity": "sha512-JbHBa5k30pZ7mn/z4vYBOnyt5GrR15eM3X0wa3VanonxnFLYkTEx8OMh33aU6ndWeOfi7Ef57dOL3bTH+3f4hQ==", "license": "MIT", "dependencies": { - "parse-ms": "^4.0.0" + "@primeuix/styled": "^0.7.4", + "@primeuix/styles": "^1.2.5", + "@primeuix/utils": "^0.6.1", + "@primevue/core": "4.4.1", + "@primevue/icons": "4.4.1" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12.11.0" } }, "node_modules/proto-list": { @@ -5712,13 +5645,6 @@ "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", @@ -5764,12 +5690,11 @@ "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==", + "version": "1.93.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.3.tgz", + "integrity": "sha512-elOcIZRTM76dvxNAjqYrucTSI0teAF/L2Lv0s6f6b7FOwcwIuA357bIE871580AjHJuSvLIRUosgV+lIWx6Rgg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -6018,19 +5943,6 @@ "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", @@ -6274,7 +6186,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6308,25 +6219,12 @@ } }, "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==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "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", @@ -6393,12 +6291,11 @@ "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==", + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", + "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -6561,16 +6458,15 @@ "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==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.0.3.tgz", + "integrity": "sha512-yIi3u31xUi28HcLlTpV0BvSLQHgZ2dA8Zqa59kWfIeMdHqbsunt6TCjq4wCNfOcGSju+E7qyHyI09EjRRFMbuQ==", "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", + "@vue/devtools-core": "^8.0.3", + "@vue/devtools-kit": "^8.0.3", + "@vue/devtools-shared": "^8.0.3", "sirv": "^3.0.2", "vite-plugin-inspect": "^11.3.3", "vite-plugin-vue-inspector": "^5.3.2" @@ -6583,14 +6479,14 @@ } }, "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==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.3.tgz", + "integrity": "sha512-UF4YUOVGdfzXLCv5pMg2DxocB8dvXz278fpgEE+nJ/DRALQGAva7sj9ton0VWZ9hmXw+SV8yKMrxP2MpMhq9Wg==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-shared": "^8.0.2", - "birpc": "^2.5.0", + "@vue/devtools-shared": "^8.0.3", + "birpc": "^2.6.1", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^2.0.0", @@ -6599,9 +6495,9 @@ } }, "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==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.3.tgz", + "integrity": "sha512-s/QNll7TlpbADFZrPVsaUNPCOF8NvQgtgmmB7Tip6pLf/HcOvBTly0lfLQ0Eylu9FQ4OqBhFpLyBgwykiSf8zw==", "dev": true, "license": "MIT", "dependencies": { @@ -6642,7 +6538,6 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -6722,7 +6617,6 @@ "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", @@ -6783,15 +6677,36 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/vue-router": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz", + "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, "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==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.1.2.tgz", + "integrity": "sha512-3fd4DY0rFczs5f+VB3OhcLU83V6+3Puj2yLBe0Ak65k7ERk+STVNKaOAi0EBo6Lc15UiJB6LzU6Mxy4+h/pKew==", "dev": true, "license": "MIT", "dependencies": { "@volar/typescript": "2.4.23", - "@vue/language-core": "3.1.0" + "@vue/language-core": "3.1.2" }, "bin": { "vue-tsc": "bin/vue-tsc.js" @@ -7082,19 +6997,6 @@ "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 index b182ae49..256162ac 100644 --- a/src/ModManUi/package.json +++ b/src/ModManUi/package.json @@ -13,30 +13,34 @@ "format": "prettier --write **/*.{js,ts,vue,html,json,yml,yaml,md}" }, "dependencies": { + "@fontsource/inter": "^5.2.8", + "@primeuix/themes": "^1.2.5", "pinia": "3.0.3", - "vue": "3.5.22" + "primevue": "^4.4.1", + "vue": "3.5.22", + "vue-router": "4.6.3" }, "devDependencies": { "@tsconfig/node22": "^22.0.2", - "@types/jsdom": "^21.1.7", - "@types/node": "^22.18.6", + "@types/jsdom": "^27.0.0", + "@types/node": "^24.9.2", "@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", + "eslint": "^9.39.0", + "eslint-plugin-vue": "~10.5.1", "jiti": "^2.5.1", - "jsdom": "^27.0.0", + "jsdom": "^27.1.0", "npm-run-all2": "^8.0.4", "prettier": "3.6.2", - "sass": "1.93.2", + "sass": "1.93.3", "typescript": "~5.9.3", - "vite": "7.1.7", - "vite-plugin-vue-devtools": "^8.0.2", + "vite": "7.1.12", + "vite-plugin-vue-devtools": "^8.0.3", "vitest": "^3.2.4", - "vue-tsc": "3.1.0" + "vue-tsc": "3.1.2" } } diff --git a/src/ModManUi/public/favicon.ico b/src/ModManUi/public/favicon.ico index df36fcfb..b2f2e9f9 100644 Binary files a/src/ModManUi/public/favicon.ico and b/src/ModManUi/public/favicon.ico differ diff --git a/src/ModManUi/public/logo_circle.svg b/src/ModManUi/public/logo_circle.svg new file mode 100644 index 00000000..98d88ab3 --- /dev/null +++ b/src/ModManUi/public/logo_circle.svg @@ -0,0 +1,77 @@ + + + + diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index 1ff66ed6..cd4ba26e 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -1,54 +1,56 @@ - diff --git a/src/ModManUi/src/PrimeVue.ts b/src/ModManUi/src/PrimeVue.ts new file mode 100644 index 00000000..0afa4311 --- /dev/null +++ b/src/ModManUi/src/PrimeVue.ts @@ -0,0 +1,51 @@ +import type { App } from "vue"; +import PrimeVue from "primevue/config"; +import Aura from "@primeuix/themes/aura"; +import { definePreset } from "@primeuix/themes"; +import type { ColorScale } from "@primeuix/styled"; + +const ModManTheme = definePreset(Aura, { + primitive: { + brand: { + 50: "var(--color-brand-50)", + 100: "var(--color-brand-100)", + 200: "var(--color-brand-200)", + 300: "var(--color-brand-300)", + 400: "var(--color-brand-400)", + 500: "var(--color-brand-500)", + 600: "var(--color-brand-600)", + 700: "var(--color-brand-700)", + 800: "var(--color-brand-800)", + 900: "var(--color-brand-900)", + 950: "var(--color-brand-950)", + } satisfies ColorScale, + }, + semantic: { + primary: { + 50: "{orange.50}", + 100: "{orange.100}", + 200: "{orange.200}", + 300: "{orange.300}", + 400: "{orange.400}", + 500: "{orange.500}", + 600: "{orange.600}", + 700: "{orange.700}", + 800: "{orange.800}", + 900: "{orange.900}", + 950: "{orange.950}", + }, + }, +}); + +export function configurePrimeVue(app: App) { + app.use(PrimeVue, { + theme: { + preset: ModManTheme, + options: { + darkModeSelector: ".dark", + }, + }, + }); + // Always make dark mode for now + document.documentElement.classList.add("dark"); +} diff --git a/src/ModManUi/src/components/SpinningLoader.vue b/src/ModManUi/src/components/SpinningLoader.vue new file mode 100644 index 00000000..4161cd84 --- /dev/null +++ b/src/ModManUi/src/components/SpinningLoader.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/ModManUi/src/components/header/ModManHeader.vue b/src/ModManUi/src/components/header/ModManHeader.vue new file mode 100644 index 00000000..6aee9f00 --- /dev/null +++ b/src/ModManUi/src/components/header/ModManHeader.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/src/ModManUi/src/components/icons/IconArrowLeft.vue b/src/ModManUi/src/components/icons/IconArrowLeft.vue new file mode 100644 index 00000000..3e37cfb5 --- /dev/null +++ b/src/ModManUi/src/components/icons/IconArrowLeft.vue @@ -0,0 +1,9 @@ + diff --git a/src/ModManUi/src/components/icons/IconGear.vue b/src/ModManUi/src/components/icons/IconGear.vue new file mode 100644 index 00000000..6ba8ca16 --- /dev/null +++ b/src/ModManUi/src/components/icons/IconGear.vue @@ -0,0 +1,9 @@ + diff --git a/src/ModManUi/src/main.scss b/src/ModManUi/src/main.scss deleted file mode 100644 index 20d4d9c6..00000000 --- a/src/ModManUi/src/main.scss +++ /dev/null @@ -1,109 +0,0 @@ -: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 index d1a7b243..fb8b13d7 100644 --- a/src/ModManUi/src/main.ts +++ b/src/ModManUi/src/main.ts @@ -1,13 +1,17 @@ -import "../public/favicon.ico"; -import "./main.scss"; +import "@style/main.scss"; import { createApp } from "vue"; import { createPinia } from "pinia"; +import { configurePrimeVue } from "./PrimeVue.ts"; import App from "./App.vue"; +import { createModManRouter } from "./router/Router.ts"; const app = createApp(App); app.use(createPinia()); +app.use(createModManRouter()); + +configurePrimeVue(app); app.mount("#app"); diff --git a/src/ModManUi/src/native/AssetBinds.ts b/src/ModManUi/src/native/AssetBinds.ts new file mode 100644 index 00000000..ce8a2f6c --- /dev/null +++ b/src/ModManUi/src/native/AssetBinds.ts @@ -0,0 +1,89 @@ +export enum CommonAssetType { + PHYS_PRESET = "PHYS_PRESET", + XANIM = "XANIM", + XMODEL = "XMODEL", + MATERIAL = "MATERIAL", + TECHNIQUE_SET = "TECHNIQUE_SET", + IMAGE = "IMAGE", + SOUND = "SOUND", + SOUND_CURVE = "SOUND_CURVE", + LOADED_SOUND = "LOADED_SOUND", + CLIP_MAP = "CLIP_MAP", + COM_WORLD = "COM_WORLD", + GAME_WORLD_SP = "GAME_WORLD_SP", + GAME_WORLD_MP = "GAME_WORLD_MP", + MAP_ENTS = "MAP_ENTS", + GFX_WORLD = "GFX_WORLD", + LIGHT_DEF = "LIGHT_DEF", + UI_MAP = "UI_MAP", + FONT = "FONT", + MENU_LIST = "MENU_LIST", + MENU = "MENU", + LOCALIZE_ENTRY = "LOCALIZE_ENTRY", + WEAPON = "WEAPON", + SOUND_DRIVER_GLOBALS = "SOUND_DRIVER_GLOBALS", + FX = "FX", + IMPACT_FX = "IMPACT_FX", + AI_TYPE = "AI_TYPE", + MP_TYPE = "MP_TYPE", + CHARACTER = "CHARACTER", + XMODEL_ALIAS = "XMODEL_ALIAS", + RAW_FILE = "RAW_FILE", + STRING_TABLE = "STRING_TABLE", + XMODEL_PIECES = "XMODEL_PIECES", + PHYS_COLL_MAP = "PHYS_COLL_MAP", + XMODEL_SURFS = "XMODEL_SURFS", + PIXEL_SHADER = "PIXEL_SHADER", + VERTEX_SHADER = "VERTEX_SHADER", + VERTEX_DECL = "VERTEX_DECL", + FX_WORLD = "FX_WORLD", + LEADERBOARD = "LEADERBOARD", + STRUCTURED_DATA_DEF = "STRUCTURED_DATA_DEF", + TRACER = "TRACER", + VEHICLE = "VEHICLE", + ADDON_MAP_ENTS = "ADDON_MAP_ENTS", + GLASS_WORLD = "GLASS_WORLD", + PATH_DATA = "PATH_DATA", + VEHICLE_TRACK = "VEHICLE_TRACK", + ATTACHMENT = "ATTACHMENT", + SURFACE_FX = "SURFACE_FX", + SCRIPT = "SCRIPT", + PHYS_CONSTRAINTS = "PHYS_CONSTRAINTS", + DESTRUCTIBLE_DEF = "DESTRUCTIBLE_DEF", + SOUND_PATCH = "SOUND_PATCH", + WEAPON_DEF = "WEAPON_DEF", + WEAPON_VARIANT = "WEAPON_VARIANT", + MP_BODY = "MP_BODY", + MP_HEAD = "MP_HEAD", + PACK_INDEX = "PACK_INDEX", + XGLOBALS = "XGLOBALS", + DDL = "DDL", + GLASSES = "GLASSES", + EMBLEM_SET = "EMBLEM_SET", + FONT_ICON = "FONT_ICON", + WEAPON_FULL = "WEAPON_FULL", + ATTACHMENT_UNIQUE = "ATTACHMENT_UNIQUE", + WEAPON_CAMO = "WEAPON_CAMO", + KEY_VALUE_PAIRS = "KEY_VALUE_PAIRS", + MEMORY_BLOCK = "MEMORY_BLOCK", + SKINNED_VERTS = "SKINNED_VERTS", + QDB = "QDB", + SLUG = "SLUG", + FOOTSTEP_TABLE = "FOOTSTEP_TABLE", + FOOTSTEP_FX_TABLE = "FOOTSTEP_FX_TABLE", + ZBARRIER = "ZBARRIER", +} + +export interface AssetDto { + type: CommonAssetType; + name: string; +} + +export interface ZoneAssetsDto { + assets: AssetDto[]; + references: AssetDto[]; +} + +export interface AssetBinds { + getAssetsForZone(zoneName: string): Promise; +} diff --git a/src/ModManUi/src/native/DialogBinds.ts b/src/ModManUi/src/native/DialogBinds.ts index 8c7e66b7..97ddce0b 100644 --- a/src/ModManUi/src/native/DialogBinds.ts +++ b/src/ModManUi/src/native/DialogBinds.ts @@ -12,7 +12,7 @@ export interface SaveFileDialogDto { } export interface DialogBinds { - openFileDialog(options?: OpenFileDialogDto): string | null; - saveFileDialog(options?: SaveFileDialogDto): string | null; - folderSelectDialog(): string | null; + openFileDialog(options?: OpenFileDialogDto): Promise; + saveFileDialog(options?: SaveFileDialogDto): Promise; + folderSelectDialog(): Promise; } diff --git a/src/ModManUi/src/native/UnlinkingBinds.ts b/src/ModManUi/src/native/UnlinkingBinds.ts new file mode 100644 index 00000000..838282ca --- /dev/null +++ b/src/ModManUi/src/native/UnlinkingBinds.ts @@ -0,0 +1,15 @@ +export interface ZoneUnlinkProgressDto { + zoneName: string; + /** + * Between 0-100 + */ + percentage: number; +} + +export interface UnlinkingBinds { + unlinkZone(zoneName: string): Promise; +} + +export interface UnlinkingEventMap { + zoneUnlinkProgress: ZoneUnlinkProgressDto; +} diff --git a/src/ModManUi/src/native/ZoneBinds.ts b/src/ModManUi/src/native/ZoneBinds.ts new file mode 100644 index 00000000..1be14aad --- /dev/null +++ b/src/ModManUi/src/native/ZoneBinds.ts @@ -0,0 +1,48 @@ +export enum GameId { + IW3 = "IW3", + IW4 = "IW4", + IW5 = "IW5", + T5 = "T5", + T6 = "T6", +} + +export enum GamePlatform { + PC = "PC", + XBOX = "XBOX", + PS3 = "PS3", +} + +export interface ZoneDto { + name: string; + filePath: string; + game: GameId; + platform: GamePlatform; +} + +export interface ZoneLoadProgressDto { + zoneName: string; + /** + * Between 0-100 + */ + percentage: number; +} + +export interface ZoneLoadedDto { + zone: ZoneDto; +} + +export interface ZoneUnloadedDto { + zoneName: string; +} + +export interface ZoneBinds { + getZones(): Promise; + loadFastFile(path: string): Promise; + unloadZone(zoneName: string): Promise; +} + +export interface ZoneEventMap { + zoneLoadProgress: ZoneLoadProgressDto; + zoneLoaded: ZoneLoadedDto; + zoneUnloaded: ZoneUnloadedDto; +} diff --git a/src/ModManUi/src/native/index.ts b/src/ModManUi/src/native/index.ts index ae61f41f..ca0c185f 100644 --- a/src/ModManUi/src/native/index.ts +++ b/src/ModManUi/src/native/index.ts @@ -1,13 +1,11 @@ +import type { AssetBinds } from "./AssetBinds"; import type { DialogBinds } from "./DialogBinds"; +import type { UnlinkingBinds, UnlinkingEventMap } from "./UnlinkingBinds"; +import type { ZoneBinds, ZoneEventMap } from "./ZoneBinds"; +export type NativeMethods = AssetBinds & DialogBinds & UnlinkingBinds & ZoneBinds; -export type NativeMethods = { - greet(name: string): Promise; -} & DialogBinds; - -interface NativeEventMap { - greeting: string; -} +type NativeEventMap = UnlinkingEventMap & ZoneEventMap; type WebViewExtensions = { webviewBinds: NativeMethods; diff --git a/src/ModManUi/src/router/Page.ts b/src/ModManUi/src/router/Page.ts new file mode 100644 index 00000000..0acc9db6 --- /dev/null +++ b/src/ModManUi/src/router/Page.ts @@ -0,0 +1,8 @@ +export const PAGE = { + HOME: "Home", + INSPECT: { + SELECT_ZONE: "Inspect zone", + ZONE_DETAILS: "Zone details", + }, + OPTIONS: "Options", +}; diff --git a/src/ModManUi/src/router/RouteMeta.ts b/src/ModManUi/src/router/RouteMeta.ts new file mode 100644 index 00000000..cf0e924d --- /dev/null +++ b/src/ModManUi/src/router/RouteMeta.ts @@ -0,0 +1,30 @@ +import { type Component, computed } from "vue"; +import { useRoute, type RouteLocationNormalizedGeneric } from "vue-router"; + +type HeaderTitleFunc = (route: RouteLocationNormalizedGeneric) => string; +export interface TypedRouteMeta { + backTo?: string; + headerTitleFunc?: HeaderTitleFunc; + headerTitleComponent?: Component; +} + +export type RouteMeta = TypedRouteMeta & Record; + +export function useRouteMeta() { + const route = useRoute(); + const meta = computed(() => route.meta as RouteMeta); + + const headerTitle = computed(() => { + if (meta.value.headerTitleFunc) { + return meta.value.headerTitleFunc(route); + } else if (meta.value.headerTitleComponent) { + return meta.value.headerTitleComponent; + } + + return String(route.name); + }); + + const routeNavigateBackTo = computed(() => meta.value.backTo ?? null); + + return { headerTitle, routeNavigateBackTo }; +} diff --git a/src/ModManUi/src/router/Router.ts b/src/ModManUi/src/router/Router.ts new file mode 100644 index 00000000..2c8f2121 --- /dev/null +++ b/src/ModManUi/src/router/Router.ts @@ -0,0 +1,36 @@ +import { createRouter, createWebHashHistory, type RouteRecordRaw } from "vue-router"; +import { PAGE } from "./Page"; +import ZoneInspector from "@/view/inspect/ZoneInspector.vue"; +import InspectDetails from "@/view/inspect_details/InspectDetails.vue"; +import type { RouteMeta } from "./RouteMeta"; +import InspectDetailsHeader from "@/view/inspect_details/InspectDetailsHeader.vue"; + +const ROUTES: RouteRecordRaw[] = [ + { + path: "/", + children: [ + { + path: "", + name: PAGE.INSPECT.SELECT_ZONE, + component: ZoneInspector, + }, + { + path: ":zoneName", + name: PAGE.INSPECT.ZONE_DETAILS, + component: InspectDetails, + meta: { + backTo: PAGE.INSPECT.SELECT_ZONE, + headerTitleComponent: InspectDetailsHeader, + } satisfies RouteMeta, + props: true, + }, + ], + }, +]; + +export function createModManRouter() { + return createRouter({ + history: createWebHashHistory(), + routes: ROUTES, + }); +} diff --git a/src/ModManUi/src/stores/AssetStore.ts b/src/ModManUi/src/stores/AssetStore.ts new file mode 100644 index 00000000..80117f97 --- /dev/null +++ b/src/ModManUi/src/stores/AssetStore.ts @@ -0,0 +1,32 @@ +import { computed, ref } from "vue"; +import { defineStore } from "pinia"; +import type { ZoneAssetsDto } from "@/native/AssetBinds"; +import { webviewBinds } from "@/native"; + +export const useAssetStore = defineStore("asset", () => { + const zoneName = ref(null); + const assetsOfZone = ref(null); + + const isLoading = computed(() => Boolean(assetsOfZone.value)); + const assetCount = computed(() => assetsOfZone.value?.assets.length ?? 0); + const referenceCount = computed(() => assetsOfZone.value?.references.length ?? 0); + + function loadAssetsForZone(newZoneName: string | null) { + // Skip if assets are already loaded + if (newZoneName === zoneName.value) return; + + // Reset current state + zoneName.value = newZoneName; + assetsOfZone.value = null; + + // Only load assets when there is a new zone name specified + if (!newZoneName) return; + webviewBinds.getAssetsForZone(newZoneName).then((res) => { + if (zoneName.value === newZoneName) { + assetsOfZone.value = res; + } + }); + } + + return { zoneName, isLoading, assetsOfZone, assetCount, referenceCount, loadAssetsForZone }; +}); diff --git a/src/ModManUi/src/stores/UnlinkingStore.ts b/src/ModManUi/src/stores/UnlinkingStore.ts new file mode 100644 index 00000000..6f10cc27 --- /dev/null +++ b/src/ModManUi/src/stores/UnlinkingStore.ts @@ -0,0 +1,31 @@ +import { ref } from "vue"; +import { defineStore } from "pinia"; +import { webviewAddEventListener, webviewBinds } from "@/native"; + +export const useUnlinkingStore = defineStore("unlinking", () => { + const isUnlinking = ref(false); + const lastPercentage = ref(0); + const failureMessage = ref(null); + + function unlinkZone(zoneName: string) { + isUnlinking.value = true; + lastPercentage.value = 0; + failureMessage.value = null; + return webviewBinds + .unlinkZone(zoneName) + .catch((e: string) => { + console.error("Failed to unlink fastfile:", e); + failureMessage.value = e; + }) + .finally(() => { + isUnlinking.value = false; + lastPercentage.value = 100; + }); + } + + webviewAddEventListener("zoneUnlinkProgress", (dto) => { + lastPercentage.value = dto.percentage; + }); + + return { isUnlinking, lastPercentage, unlinkZone }; +}); diff --git a/src/ModManUi/src/stores/ZoneStore.ts b/src/ModManUi/src/stores/ZoneStore.ts new file mode 100644 index 00000000..b8a5f4b5 --- /dev/null +++ b/src/ModManUi/src/stores/ZoneStore.ts @@ -0,0 +1,77 @@ +import { computed, ref } from "vue"; +import { defineStore } from "pinia"; +import { webviewAddEventListener, webviewBinds } from "@/native"; +import type { ZoneDto, ZoneLoadedDto } from "@/native/ZoneBinds"; + +export const useZoneStore = defineStore("zone", () => { + const loadedZones = ref([]); + const zonesCurrentlyBeingLoaded = ref([]); + const lastPercentageByZoneName = ref>({}); + + const isLoadingZone = computed(() => zonesCurrentlyBeingLoaded.value.length > 0); + + function loadFastFile(fastFilePath: string): Promise { + const lastDirectorySeparator = fastFilePath.replace(/\\/g, "/").lastIndexOf("/"); + const lastDot = fastFilePath.lastIndexOf("."); + const expectedZoneName = fastFilePath.substring( + lastDirectorySeparator >= 0 ? lastDirectorySeparator + 1 : 0, + lastDot > lastDirectorySeparator ? lastDot : fastFilePath.length, + ); + + zonesCurrentlyBeingLoaded.value.push(expectedZoneName); + lastPercentageByZoneName.value[expectedZoneName] = 0; + + return webviewBinds + .loadFastFile(fastFilePath) + .catch((e: string) => { + console.error("Failed to load fastfile:", e); + }) + .finally(() => { + zonesCurrentlyBeingLoaded.value.splice( + zonesCurrentlyBeingLoaded.value.indexOf(expectedZoneName), + 1, + ); + delete lastPercentageByZoneName.value[expectedZoneName]; + }) as Promise; + } + + function getPercentageForZoneBeingLoaded(zoneName: string) { + return lastPercentageByZoneName.value[zoneName] ?? 100; + } + + function getLoadedZoneByName(zoneName: string) { + return loadedZones.value.find((zone) => zone.name === zoneName) ?? null; + } + + // Initially get all loaded zones + webviewBinds.getZones().then((allZones) => { + loadedZones.value = allZones; + }); + + webviewAddEventListener("zoneLoadProgress", (dto) => { + if (lastPercentageByZoneName.value[dto.zoneName] !== undefined) { + lastPercentageByZoneName.value[dto.zoneName] = dto.percentage; + } + }); + + webviewAddEventListener("zoneLoaded", (dto) => { + loadedZones.value.push(dto.zone); + }); + + webviewAddEventListener("zoneUnloaded", (dto) => { + const index = loadedZones.value.findIndex((zone) => zone.name === dto.zoneName); + if (index >= 0) { + loadedZones.value.splice(index, 1); + } + }); + + return { + loadedZones, + zonesCurrentlyBeingLoaded, + isLoadingZone, + lastPercentageByZoneName, + loadFastFile, + getPercentageForZoneBeingLoaded, + getLoadedZoneByName, + }; +}); diff --git a/src/ModManUi/src/stores/counter.ts b/src/ModManUi/src/stores/counter.ts deleted file mode 100644 index 374b4d03..00000000 --- a/src/ModManUi/src/stores/counter.ts +++ /dev/null @@ -1,12 +0,0 @@ -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/src/style/_colors.scss b/src/ModManUi/src/style/_colors.scss new file mode 100644 index 00000000..a8bb3154 --- /dev/null +++ b/src/ModManUi/src/style/_colors.scss @@ -0,0 +1,5 @@ +@use "colors_primitive"; +@forward "colors_primitive"; + +@use "colors_semantic"; +@forward "colors_semantic"; diff --git a/src/ModManUi/src/style/_colors_primitive.scss b/src/ModManUi/src/style/_colors_primitive.scss new file mode 100644 index 00000000..e5ae558b --- /dev/null +++ b/src/ModManUi/src/style/_colors_primitive.scss @@ -0,0 +1,278 @@ +// Primitive colors are colors that have literal values and they do not change depending on the context. +$color-brand-50: #f7ebdf; +$color-brand-100: #f1dfca; +$color-brand-200: #eaceae; +$color-brand-300: #e0b685; +$color-brand-400: #d59d5d; +$color-brand-500: #cc8838; +$color-brand-600: #b9772c; +$color-brand-700: #784818; +$color-brand-800: #573816; +$color-brand-900: #3d2810; +$color-brand-950: #291b0a; +$color-brand: $color-brand-500; + +// These colors are taken from PrimeVue Aura theme +$color-emerald-50: var(--p-emerald-50); +$color-emerald-100: var(--p-emerald-100); +$color-emerald-200: var(--p-emerald-200); +$color-emerald-300: var(--p-emerald-300); +$color-emerald-400: var(--p-emerald-400); +$color-emerald-500: var(--p-emerald-500); +$color-emerald-600: var(--p-emerald-600); +$color-emerald-700: var(--p-emerald-700); +$color-emerald-800: var(--p-emerald-800); +$color-emerald-900: var(--p-emerald-900); +$color-emerald-950: var(--p-emerald-950); + +$color-green-50: var(--p-green-50); +$color-green-100: var(--p-green-100); +$color-green-200: var(--p-green-200); +$color-green-300: var(--p-green-300); +$color-green-400: var(--p-green-400); +$color-green-500: var(--p-green-500); +$color-green-600: var(--p-green-600); +$color-green-700: var(--p-green-700); +$color-green-800: var(--p-green-800); +$color-green-900: var(--p-green-900); +$color-green-950: var(--p-green-950); + +$color-lime-50: var(--p-lime-50); +$color-lime-100: var(--p-lime-100); +$color-lime-200: var(--p-lime-200); +$color-lime-300: var(--p-lime-300); +$color-lime-400: var(--p-lime-400); +$color-lime-500: var(--p-lime-500); +$color-lime-600: var(--p-lime-600); +$color-lime-700: var(--p-lime-700); +$color-lime-800: var(--p-lime-800); +$color-lime-900: var(--p-lime-900); +$color-lime-950: var(--p-lime-950); + +$color-red-50: var(--p-red-50); +$color-red-100: var(--p-red-100); +$color-red-200: var(--p-red-200); +$color-red-300: var(--p-red-300); +$color-red-400: var(--p-red-400); +$color-red-500: var(--p-red-500); +$color-red-600: var(--p-red-600); +$color-red-700: var(--p-red-700); +$color-red-800: var(--p-red-800); +$color-red-900: var(--p-red-900); +$color-red-950: var(--p-red-950); + +$color-orange-50: var(--p-orange-50); +$color-orange-100: var(--p-orange-100); +$color-orange-200: var(--p-orange-200); +$color-orange-300: var(--p-orange-300); +$color-orange-400: var(--p-orange-400); +$color-orange-500: var(--p-orange-500); +$color-orange-600: var(--p-orange-600); +$color-orange-700: var(--p-orange-700); +$color-orange-800: var(--p-orange-800); +$color-orange-900: var(--p-orange-900); +$color-orange-950: var(--p-orange-950); + +$color-amber-50: var(--p-amber-50); +$color-amber-100: var(--p-amber-100); +$color-amber-200: var(--p-amber-200); +$color-amber-300: var(--p-amber-300); +$color-amber-400: var(--p-amber-400); +$color-amber-500: var(--p-amber-500); +$color-amber-600: var(--p-amber-600); +$color-amber-700: var(--p-amber-700); +$color-amber-800: var(--p-amber-800); +$color-amber-900: var(--p-amber-900); +$color-amber-950: var(--p-amber-950); + +$color-yellow-50: var(--p-yellow-50); +$color-yellow-100: var(--p-yellow-100); +$color-yellow-200: var(--p-yellow-200); +$color-yellow-300: var(--p-yellow-300); +$color-yellow-400: var(--p-yellow-400); +$color-yellow-500: var(--p-yellow-500); +$color-yellow-600: var(--p-yellow-600); +$color-yellow-700: var(--p-yellow-700); +$color-yellow-800: var(--p-yellow-800); +$color-yellow-900: var(--p-yellow-900); +$color-yellow-950: var(--p-yellow-950); + +$color-teal-50: var(--p-teal-50); +$color-teal-100: var(--p-teal-100); +$color-teal-200: var(--p-teal-200); +$color-teal-300: var(--p-teal-300); +$color-teal-400: var(--p-teal-400); +$color-teal-500: var(--p-teal-500); +$color-teal-600: var(--p-teal-600); +$color-teal-700: var(--p-teal-700); +$color-teal-800: var(--p-teal-800); +$color-teal-900: var(--p-teal-900); +$color-teal-950: var(--p-teal-950); + +$color-cyan-50: var(--p-cyan-50); +$color-cyan-100: var(--p-cyan-100); +$color-cyan-200: var(--p-cyan-200); +$color-cyan-300: var(--p-cyan-300); +$color-cyan-400: var(--p-cyan-400); +$color-cyan-500: var(--p-cyan-500); +$color-cyan-600: var(--p-cyan-600); +$color-cyan-700: var(--p-cyan-700); +$color-cyan-800: var(--p-cyan-800); +$color-cyan-900: var(--p-cyan-900); +$color-cyan-950: var(--p-cyan-950); + +$color-sky-50: var(--p-sky-50); +$color-sky-100: var(--p-sky-100); +$color-sky-200: var(--p-sky-200); +$color-sky-300: var(--p-sky-300); +$color-sky-400: var(--p-sky-400); +$color-sky-500: var(--p-sky-500); +$color-sky-600: var(--p-sky-600); +$color-sky-700: var(--p-sky-700); +$color-sky-800: var(--p-sky-800); +$color-sky-900: var(--p-sky-900); +$color-sky-950: var(--p-sky-950); + +$color-blue-50: var(--p-blue-50); +$color-blue-100: var(--p-blue-100); +$color-blue-200: var(--p-blue-200); +$color-blue-300: var(--p-blue-300); +$color-blue-400: var(--p-blue-400); +$color-blue-500: var(--p-blue-500); +$color-blue-600: var(--p-blue-600); +$color-blue-700: var(--p-blue-700); +$color-blue-800: var(--p-blue-800); +$color-blue-900: var(--p-blue-900); +$color-blue-950: var(--p-blue-950); + +$color-indigo-50: var(--p-indigo-50); +$color-indigo-100: var(--p-indigo-100); +$color-indigo-200: var(--p-indigo-200); +$color-indigo-300: var(--p-indigo-300); +$color-indigo-400: var(--p-indigo-400); +$color-indigo-500: var(--p-indigo-500); +$color-indigo-600: var(--p-indigo-600); +$color-indigo-700: var(--p-indigo-700); +$color-indigo-800: var(--p-indigo-800); +$color-indigo-900: var(--p-indigo-900); +$color-indigo-950: var(--p-indigo-950); + +$color-violet-50: var(--p-violet-50); +$color-violet-100: var(--p-violet-100); +$color-violet-200: var(--p-violet-200); +$color-violet-300: var(--p-violet-300); +$color-violet-400: var(--p-violet-400); +$color-violet-500: var(--p-violet-500); +$color-violet-600: var(--p-violet-600); +$color-violet-700: var(--p-violet-700); +$color-violet-800: var(--p-violet-800); +$color-violet-900: var(--p-violet-900); +$color-violet-950: var(--p-violet-950); + +$color-purple-50: var(--p-purple-50); +$color-purple-100: var(--p-purple-100); +$color-purple-200: var(--p-purple-200); +$color-purple-300: var(--p-purple-300); +$color-purple-400: var(--p-purple-400); +$color-purple-500: var(--p-purple-500); +$color-purple-600: var(--p-purple-600); +$color-purple-700: var(--p-purple-700); +$color-purple-800: var(--p-purple-800); +$color-purple-900: var(--p-purple-900); +$color-purple-950: var(--p-purple-950); + +$color-fuchsia-50: var(--p-fuchsia-50); +$color-fuchsia-100: var(--p-fuchsia-100); +$color-fuchsia-200: var(--p-fuchsia-200); +$color-fuchsia-300: var(--p-fuchsia-300); +$color-fuchsia-400: var(--p-fuchsia-400); +$color-fuchsia-500: var(--p-fuchsia-500); +$color-fuchsia-600: var(--p-fuchsia-600); +$color-fuchsia-700: var(--p-fuchsia-700); +$color-fuchsia-800: var(--p-fuchsia-800); +$color-fuchsia-900: var(--p-fuchsia-900); +$color-fuchsia-950: var(--p-fuchsia-950); + +$color-pink-50: var(--p-pink-50); +$color-pink-100: var(--p-pink-100); +$color-pink-200: var(--p-pink-200); +$color-pink-300: var(--p-pink-300); +$color-pink-400: var(--p-pink-400); +$color-pink-500: var(--p-pink-500); +$color-pink-600: var(--p-pink-600); +$color-pink-700: var(--p-pink-700); +$color-pink-800: var(--p-pink-800); +$color-pink-900: var(--p-pink-900); +$color-pink-950: var(--p-pink-950); + +$color-rose-50: var(--p-rose-50); +$color-rose-100: var(--p-rose-100); +$color-rose-200: var(--p-rose-200); +$color-rose-300: var(--p-rose-300); +$color-rose-400: var(--p-rose-400); +$color-rose-500: var(--p-rose-500); +$color-rose-600: var(--p-rose-600); +$color-rose-700: var(--p-rose-700); +$color-rose-800: var(--p-rose-800); +$color-rose-900: var(--p-rose-900); +$color-rose-950: var(--p-rose-950); + +$color-slate-50: var(--p-slate-50); +$color-slate-100: var(--p-slate-100); +$color-slate-200: var(--p-slate-200); +$color-slate-300: var(--p-slate-300); +$color-slate-400: var(--p-slate-400); +$color-slate-500: var(--p-slate-500); +$color-slate-600: var(--p-slate-600); +$color-slate-700: var(--p-slate-700); +$color-slate-800: var(--p-slate-800); +$color-slate-900: var(--p-slate-900); +$color-slate-950: var(--p-slate-950); + +$color-gray-50: var(--p-gray-50); +$color-gray-100: var(--p-gray-100); +$color-gray-200: var(--p-gray-200); +$color-gray-300: var(--p-gray-300); +$color-gray-400: var(--p-gray-400); +$color-gray-500: var(--p-gray-500); +$color-gray-600: var(--p-gray-600); +$color-gray-700: var(--p-gray-700); +$color-gray-800: var(--p-gray-800); +$color-gray-900: var(--p-gray-900); +$color-gray-950: var(--p-gray-950); + +$color-zinc-50: var(--p-zinc-50); +$color-zinc-100: var(--p-zinc-100); +$color-zinc-200: var(--p-zinc-200); +$color-zinc-300: var(--p-zinc-300); +$color-zinc-400: var(--p-zinc-400); +$color-zinc-500: var(--p-zinc-500); +$color-zinc-600: var(--p-zinc-600); +$color-zinc-700: var(--p-zinc-700); +$color-zinc-800: var(--p-zinc-800); +$color-zinc-900: var(--p-zinc-900); +$color-zinc-950: var(--p-zinc-950); + +$color-neutral-50: var(--p-neutral-50); +$color-neutral-100: var(--p-neutral-100); +$color-neutral-200: var(--p-neutral-200); +$color-neutral-300: var(--p-neutral-300); +$color-neutral-400: var(--p-neutral-400); +$color-neutral-500: var(--p-neutral-500); +$color-neutral-600: var(--p-neutral-600); +$color-neutral-700: var(--p-neutral-700); +$color-neutral-800: var(--p-neutral-800); +$color-neutral-900: var(--p-neutral-900); +$color-neutral-950: var(--p-neutral-950); + +$color-stone-50: var(--p-stone-50); +$color-stone-100: var(--p-stone-100); +$color-stone-200: var(--p-stone-200); +$color-stone-300: var(--p-stone-300); +$color-stone-400: var(--p-stone-400); +$color-stone-500: var(--p-stone-500); +$color-stone-600: var(--p-stone-600); +$color-stone-700: var(--p-stone-700); +$color-stone-800: var(--p-stone-800); +$color-stone-900: var(--p-stone-900); +$color-stone-950: var(--p-stone-950); diff --git a/src/ModManUi/src/style/_colors_semantic.scss b/src/ModManUi/src/style/_colors_semantic.scss new file mode 100644 index 00000000..d474a295 --- /dev/null +++ b/src/ModManUi/src/style/_colors_semantic.scss @@ -0,0 +1,28 @@ +// Semantic colors are colors that may be different depending on the context. +// They have to be used via CSS variables (which is what the SASS variables access). +$color-surface-0: var(--color-surface-0); +$color-surface-50: var(--color-surface-50); +$color-surface-100: var(--color-surface-100); +$color-surface-200: var(--color-surface-200); +$color-surface-300: var(--color-surface-300); +$color-surface-400: var(--color-surface-400); +$color-surface-500: var(--color-surface-500); +$color-surface-600: var(--color-surface-600); +$color-surface-700: var(--color-surface-700); +$color-surface-800: var(--color-surface-800); +$color-surface-900: var(--color-surface-900); +$color-surface-950: var(--color-surface-950); + +$color-primary: var(--color-primary); +$color-primary-contrast: var(--color-primary-contrast); +$color-primary-hover: var(--color-primary-hover); +$color-primary-active: var(--color-primary-active); + +$color-content-background: var(--color-content-background); +$color-content-hover-background: var(--color-content-hover-background); +$color-content-border: var(--color-content-border); + +$color-text: var(--color-text); +$color-text-hover: var(--color-text-hover); +$color-text-muted: var(--color-text-muted); +$color-text-hover-muted: var(--color-text-hover-muted); diff --git a/src/ModManUi/src/style/_variables.scss b/src/ModManUi/src/style/_variables.scss new file mode 100644 index 00000000..dede5bb8 --- /dev/null +++ b/src/ModManUi/src/style/_variables.scss @@ -0,0 +1,7 @@ +$border-radius-xs: 0.125rem; +$border-radius-sm: 0.25rem; +$border-radius-md: 0.375rem; +$border-radius-lg: 0.5rem; +$border-radius-xl: 0.75rem; + +$transition-duration: 0.2s; diff --git a/src/ModManUi/src/style/css_vars.scss b/src/ModManUi/src/style/css_vars.scss new file mode 100644 index 00000000..83223e26 --- /dev/null +++ b/src/ModManUi/src/style/css_vars.scss @@ -0,0 +1,68 @@ +@use "colors"; +@use "colors_primitive"; +@use "sass:meta"; +@use "sass:string"; + +:root { + @each $name, $value in meta.module-variables(colors_primitive) { + --#{string.to-lower-case($name)}: #{$value}; + } +} + +:root:not(.dark) { + --color-surface-0: #ffffff; + --color-surface-50: #{colors.$color-slate-50}; + --color-surface-100: #{colors.$color-slate-100}; + --color-surface-200: #{colors.$color-slate-200}; + --color-surface-300: #{colors.$color-slate-300}; + --color-surface-400: #{colors.$color-slate-400}; + --color-surface-500: #{colors.$color-slate-500}; + --color-surface-600: #{colors.$color-slate-600}; + --color-surface-700: #{colors.$color-slate-700}; + --color-surface-800: #{colors.$color-slate-800}; + --color-surface-900: #{colors.$color-slate-900}; + --color-surface-950: #{colors.$color-slate-950}; + + --color-primary: #{colors.$color-brand-500}; + --color-primary-contrast: #ffffff; + --color-primary-hover: var(--color-brand-600); + --color-primary-active: var(--color-brand-700); + + --color-content-background: var(--color-surface-0); + --color-content-hover-background: var(--color-surface-100); + --color-content-border: var(--color-surface-200); + + --color-text: var(--color-surface-700); + --color-text-hover: var(--color-surface-800); + --color-text-muted: var(--color-surface-500); + --color-text-hover-muted: var(--color-surface-600); +} + +:root.dark { + --color-surface-0: #ffffff; + --color-surface-50: #{colors.$color-zinc-50}; + --color-surface-100: #{colors.$color-zinc-100}; + --color-surface-200: #{colors.$color-zinc-200}; + --color-surface-300: #{colors.$color-zinc-300}; + --color-surface-400: #{colors.$color-zinc-400}; + --color-surface-500: #{colors.$color-zinc-500}; + --color-surface-600: #{colors.$color-zinc-600}; + --color-surface-700: #{colors.$color-zinc-700}; + --color-surface-800: #{colors.$color-zinc-800}; + --color-surface-900: #{colors.$color-zinc-900}; + --color-surface-950: #{colors.$color-zinc-950}; + + --color-primary: #{colors.$color-brand-400}; + --color-primary-contrast: var(--color-surface-900); + --color-primary-hover: var(--color-brand-300); + --color-primary-active: var(--color-brand-200); + + --color-content-background: var(--color-surface-900); + --color-content-hover-background: var(--color-surface-800); + --color-content-border: var(--color-surface-700); + + --color-text: var(--color-surface-0); + --color-text-hover: var(--color-surface-0); + --color-text-muted: var(--color-surface-400); + --color-text-hover-muted: var(--color-surface-300); +} diff --git a/src/ModManUi/src/style/main.scss b/src/ModManUi/src/style/main.scss new file mode 100644 index 00000000..be5bc42a --- /dev/null +++ b/src/ModManUi/src/style/main.scss @@ -0,0 +1,36 @@ +@use "colors"; +@use "css_vars"; +@use "utils.scss"; + +@import "@fontsource/inter/latin-300.css"; +@import "@fontsource/inter/latin-300-italic.css"; +@import "@fontsource/inter/latin-400.css"; +@import "@fontsource/inter/latin-400-italic.css"; +@import "@fontsource/inter/latin-600.css"; +@import "@fontsource/inter/latin-600-italic.css"; +@import "@fontsource/inter/latin-700.css"; +@import "@fontsource/inter/latin-700-italic.css"; + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color: colors.$color-text; + background-color: colors.$color-content-background; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; +} diff --git a/src/ModManUi/src/style/utils.scss b/src/ModManUi/src/style/utils.scss new file mode 100644 index 00000000..e7d7bfd5 --- /dev/null +++ b/src/ModManUi/src/style/utils.scss @@ -0,0 +1,3 @@ +.icon { + height: 1lh; +} diff --git a/src/ModManUi/src/utils/AssetTypeName.ts b/src/ModManUi/src/utils/AssetTypeName.ts new file mode 100644 index 00000000..d4c3e8d7 --- /dev/null +++ b/src/ModManUi/src/utils/AssetTypeName.ts @@ -0,0 +1,85 @@ +import { CommonAssetType } from "@/native/AssetBinds"; + +const LOOKUP_CAPITALIZED: Record = { + [CommonAssetType.PHYS_PRESET]: "Phys preset", + [CommonAssetType.XANIM]: "XAnim", + [CommonAssetType.XMODEL]: "XModel", + [CommonAssetType.MATERIAL]: "Material", + [CommonAssetType.TECHNIQUE_SET]: "Technique set", + [CommonAssetType.IMAGE]: "Image", + [CommonAssetType.SOUND]: "Sound", + [CommonAssetType.SOUND_CURVE]: "Sound curve", + [CommonAssetType.LOADED_SOUND]: "Loaded sound", + [CommonAssetType.CLIP_MAP]: "Clip map", + [CommonAssetType.COM_WORLD]: "Com world", + [CommonAssetType.GAME_WORLD_SP]: "Game world SP", + [CommonAssetType.GAME_WORLD_MP]: "Game world MP", + [CommonAssetType.MAP_ENTS]: "Map ents", + [CommonAssetType.GFX_WORLD]: "Gfx world", + [CommonAssetType.LIGHT_DEF]: "Light def", + [CommonAssetType.UI_MAP]: "UI map", + [CommonAssetType.FONT]: "Font", + [CommonAssetType.MENU_LIST]: "Menu list", + [CommonAssetType.MENU]: "Menu", + [CommonAssetType.LOCALIZE_ENTRY]: "Localize entry", + [CommonAssetType.WEAPON]: "Weapon", + [CommonAssetType.SOUND_DRIVER_GLOBALS]: "Sound driver globals", + [CommonAssetType.FX]: "FX", + [CommonAssetType.IMPACT_FX]: "Impact FX", + [CommonAssetType.AI_TYPE]: "AI type", + [CommonAssetType.MP_TYPE]: "MP type", + [CommonAssetType.CHARACTER]: "Character", + [CommonAssetType.XMODEL_ALIAS]: "XModel alias", + [CommonAssetType.RAW_FILE]: "Raw file", + [CommonAssetType.STRING_TABLE]: "String table", + [CommonAssetType.XMODEL_PIECES]: "XModel pieces", + [CommonAssetType.PHYS_COLL_MAP]: "Phys coll map", + [CommonAssetType.XMODEL_SURFS]: "XModel surfs", + [CommonAssetType.PIXEL_SHADER]: "Pixel shader", + [CommonAssetType.VERTEX_SHADER]: "Vertex shader", + [CommonAssetType.VERTEX_DECL]: "Vertex decl", + [CommonAssetType.FX_WORLD]: "FX world", + [CommonAssetType.LEADERBOARD]: "Leaderboard", + [CommonAssetType.STRUCTURED_DATA_DEF]: "Structured data def", + [CommonAssetType.TRACER]: "Tracer", + [CommonAssetType.VEHICLE]: "Vehicle", + [CommonAssetType.ADDON_MAP_ENTS]: "Addon map ents", + [CommonAssetType.GLASS_WORLD]: "Glass world", + [CommonAssetType.PATH_DATA]: "Path data", + [CommonAssetType.VEHICLE_TRACK]: "Vehicle track", + [CommonAssetType.ATTACHMENT]: "Attachment", + [CommonAssetType.SURFACE_FX]: "Surface FX", + [CommonAssetType.SCRIPT]: "Script", + [CommonAssetType.PHYS_CONSTRAINTS]: "Phys constraints", + [CommonAssetType.DESTRUCTIBLE_DEF]: "Destructible def", + [CommonAssetType.SOUND_PATCH]: "Sound patch", + [CommonAssetType.WEAPON_DEF]: "Weapon def", + [CommonAssetType.WEAPON_VARIANT]: "Weapon variant", + [CommonAssetType.MP_BODY]: "MP body", + [CommonAssetType.MP_HEAD]: "MP head", + [CommonAssetType.PACK_INDEX]: "Pack index", + [CommonAssetType.XGLOBALS]: "XGlobals", + [CommonAssetType.DDL]: "DDL", + [CommonAssetType.GLASSES]: "Glasses", + [CommonAssetType.EMBLEM_SET]: "Emblem set", + [CommonAssetType.FONT_ICON]: "Font icon", + [CommonAssetType.WEAPON_FULL]: "Weapon full", + [CommonAssetType.ATTACHMENT_UNIQUE]: "Attachment unique", + [CommonAssetType.WEAPON_CAMO]: "Weapon camo", + [CommonAssetType.KEY_VALUE_PAIRS]: "Key value pairs", + [CommonAssetType.MEMORY_BLOCK]: "Memory block", + [CommonAssetType.SKINNED_VERTS]: "Skinned verts", + [CommonAssetType.QDB]: "Qdb", + [CommonAssetType.SLUG]: "Slug", + [CommonAssetType.FOOTSTEP_TABLE]: "Footstep table", + [CommonAssetType.FOOTSTEP_FX_TABLE]: "Footstep FX table", + [CommonAssetType.ZBARRIER]: "ZBarrier", +}; + +export function getAssetTypeNameCapitalized(assetType: CommonAssetType): string { + return LOOKUP_CAPITALIZED[assetType]; +} + +export function getAssetTypeNameLower(assetType: CommonAssetType): string { + return getAssetTypeNameCapitalized(assetType).toLocaleLowerCase(); +} diff --git a/src/ModManUi/src/view/inspect/ZoneInspector.vue b/src/ModManUi/src/view/inspect/ZoneInspector.vue new file mode 100644 index 00000000..ed626058 --- /dev/null +++ b/src/ModManUi/src/view/inspect/ZoneInspector.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/src/ModManUi/src/view/inspect/ZoneInspectorDetails.vue b/src/ModManUi/src/view/inspect/ZoneInspectorDetails.vue new file mode 100644 index 00000000..f5fac169 --- /dev/null +++ b/src/ModManUi/src/view/inspect/ZoneInspectorDetails.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/src/ModManUi/src/view/inspect/ZoneInspectorZoneList.vue b/src/ModManUi/src/view/inspect/ZoneInspectorZoneList.vue new file mode 100644 index 00000000..4a10814d --- /dev/null +++ b/src/ModManUi/src/view/inspect/ZoneInspectorZoneList.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/src/ModManUi/src/view/inspect_details/InspectDetails.vue b/src/ModManUi/src/view/inspect_details/InspectDetails.vue new file mode 100644 index 00000000..aeefee59 --- /dev/null +++ b/src/ModManUi/src/view/inspect_details/InspectDetails.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/src/ModManUi/src/view/inspect_details/InspectDetailsHeader.vue b/src/ModManUi/src/view/inspect_details/InspectDetailsHeader.vue new file mode 100644 index 00000000..5bd5daf9 --- /dev/null +++ b/src/ModManUi/src/view/inspect_details/InspectDetailsHeader.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/src/ModManUi/src/view/inspect_details/components/AssetListOption.vue b/src/ModManUi/src/view/inspect_details/components/AssetListOption.vue new file mode 100644 index 00000000..ceeaed63 --- /dev/null +++ b/src/ModManUi/src/view/inspect_details/components/AssetListOption.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/src/ModManUi/src/view/inspect_details/components/InspectAssetDetails.vue b/src/ModManUi/src/view/inspect_details/components/InspectAssetDetails.vue new file mode 100644 index 00000000..60c4e2af --- /dev/null +++ b/src/ModManUi/src/view/inspect_details/components/InspectAssetDetails.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/src/ModManUi/src/view/inspect_details/components/InspectPreview.vue b/src/ModManUi/src/view/inspect_details/components/InspectPreview.vue new file mode 100644 index 00000000..d76e14da --- /dev/null +++ b/src/ModManUi/src/view/inspect_details/components/InspectPreview.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/src/ModManUi/src/view/inspect_details/components/InspectZoneAssets.vue b/src/ModManUi/src/view/inspect_details/components/InspectZoneAssets.vue new file mode 100644 index 00000000..2d4cf40e --- /dev/null +++ b/src/ModManUi/src/view/inspect_details/components/InspectZoneAssets.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/ModManUi/tsconfig.app.json b/src/ModManUi/tsconfig.app.json index 913b8f27..7f4e553c 100644 --- a/src/ModManUi/tsconfig.app.json +++ b/src/ModManUi/tsconfig.app.json @@ -6,7 +6,8 @@ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@style/*": ["./src/style/*"] } } } diff --git a/src/ModManUi/vite.config.ts b/src/ModManUi/vite.config.ts index f5edcd61..0409fc43 100644 --- a/src/ModManUi/vite.config.ts +++ b/src/ModManUi/vite.config.ts @@ -8,7 +8,6 @@ import headerTransformationPlugin from "./build/HeaderTransformationPlugin"; // https://vite.dev/config/ export default defineConfig({ build: { - copyPublicDir: false, emptyOutDir: true, rollupOptions: { output: { @@ -18,6 +17,7 @@ export default defineConfig({ }, }, }, + publicDir: fileURLToPath(new URL("./public", import.meta.url)), plugins: [ vue(), vueDevTools(), @@ -30,6 +30,7 @@ export default defineConfig({ resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)), + "@style": fileURLToPath(new URL("./src/style", import.meta.url)), }, }, }); diff --git a/src/ObjWriting/Dumping/AbstractAssetDumper.h b/src/ObjWriting/Dumping/AbstractAssetDumper.h index 53fa97dc..c0371bc4 100644 --- a/src/ObjWriting/Dumping/AbstractAssetDumper.h +++ b/src/ObjWriting/Dumping/AbstractAssetDumper.h @@ -1,28 +1,69 @@ #pragma once +#include "Game/IAsset.h" #include "IAssetDumper.h" +#include "Pool/AssetPool.h" -template class AbstractAssetDumper : public IAssetDumper +template class AbstractAssetDumper : public IAssetDumper { + static_assert(std::is_base_of_v); + +public: + using AssetType_t = AssetType; + + [[nodiscard]] size_t GetProgressTotalCount() const override + { + return m_pool.m_asset_lookup.size(); + } + + void Dump(AssetDumpingContext& context) override + { + for (const auto* assetInfo : m_pool) + { + if (assetInfo->m_name[0] == ',' || !ShouldDump(*assetInfo)) + { + context.IncrementProgress(); + continue; + } + + DumpAsset(context, *assetInfo); + context.IncrementProgress(); + } + } + protected: - virtual bool ShouldDump(XAssetInfo* asset) + explicit AbstractAssetDumper(const AssetPool& pool) + : m_pool(pool) + { + } + + virtual bool ShouldDump(const XAssetInfo& asset) { return true; } - virtual void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) = 0; + virtual void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) = 0; + + const AssetPool& m_pool; +}; + +template class AbstractSingleProgressAssetDumper : public IAssetDumper +{ + static_assert(std::is_base_of_v); public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override - { - for (auto assetInfo : *pool) - { - if (assetInfo->m_name[0] == ',' || !ShouldDump(assetInfo)) - { - continue; - } + using AssetType_t = AssetType; - DumpAsset(context, assetInfo); - } + [[nodiscard]] size_t GetProgressTotalCount() const override + { + return m_pool.m_asset_lookup.empty() ? 0uz : 1uz; } + +protected: + explicit AbstractSingleProgressAssetDumper(const AssetPool& pool) + : m_pool(pool) + { + } + + const AssetPool& m_pool; }; diff --git a/src/ObjWriting/Dumping/AssetDumpingContext.cpp b/src/ObjWriting/Dumping/AssetDumpingContext.cpp index ea3041e1..77e59fe0 100644 --- a/src/ObjWriting/Dumping/AssetDumpingContext.cpp +++ b/src/ObjWriting/Dumping/AssetDumpingContext.cpp @@ -1,18 +1,43 @@ #include "AssetDumpingContext.h" #include -#include -#include -AssetDumpingContext::AssetDumpingContext(const Zone& zone, const std::string& basePath, IOutputPath& outputPath, ISearchPath& objSearchPath) +AssetDumpingContext::AssetDumpingContext(const Zone& zone, + const std::string& basePath, + IOutputPath& outputPath, + ISearchPath& objSearchPath, + std::optional> progressCallback) : m_zone(zone), m_base_path(basePath), m_output_path(outputPath), - m_obj_search_path(objSearchPath) + m_obj_search_path(objSearchPath), + m_current_progress(0uz), + m_total_progress(0uz) { + if (progressCallback) + m_progress_callback = *std::move(progressCallback); } std::unique_ptr AssetDumpingContext::OpenAssetFile(const std::string& fileName) const { return m_output_path.Open(fileName); } + +void AssetDumpingContext::IncrementProgress() +{ + if (m_progress_callback) + { + m_current_progress++; + m_progress_callback->OnProgress(m_current_progress, m_total_progress); + } +} + +void AssetDumpingContext::SetTotalProgress(const size_t value) +{ + m_total_progress = value; +} + +bool AssetDumpingContext::ShouldTrackProgress() const +{ + return static_cast(m_progress_callback); +} diff --git a/src/ObjWriting/Dumping/AssetDumpingContext.h b/src/ObjWriting/Dumping/AssetDumpingContext.h index bfc384de..4d6c86b3 100644 --- a/src/ObjWriting/Dumping/AssetDumpingContext.h +++ b/src/ObjWriting/Dumping/AssetDumpingContext.h @@ -4,6 +4,7 @@ #include "Obj/Gdt/GdtStream.h" #include "SearchPath/IOutputPath.h" #include "SearchPath/ISearchPath.h" +#include "Utils/ProgressCallback.h" #include "Zone/Zone.h" #include @@ -14,10 +15,18 @@ class AssetDumpingContext { public: - AssetDumpingContext(const Zone& zone, const std::string& basePath, IOutputPath& outputPath, ISearchPath& objSearchPath); + AssetDumpingContext(const Zone& zone, + const std::string& basePath, + IOutputPath& outputPath, + ISearchPath& objSearchPath, + std::optional> progressCallback); [[nodiscard]] std::unique_ptr OpenAssetFile(const std::string& fileName) const; + void IncrementProgress(); + void SetTotalProgress(size_t value); + [[nodiscard]] bool ShouldTrackProgress() const; + template T* GetZoneAssetDumperState() { static_assert(std::is_base_of_v, "T must inherit IZoneAssetDumperState"); @@ -42,4 +51,7 @@ public: private: std::unordered_map> m_zone_asset_dumper_states; + std::unique_ptr m_progress_callback; + size_t m_current_progress; + size_t m_total_progress; }; diff --git a/src/ObjWriting/Dumping/IAssetDumper.h b/src/ObjWriting/Dumping/IAssetDumper.h index f712f1dd..7c046de3 100644 --- a/src/ObjWriting/Dumping/IAssetDumper.h +++ b/src/ObjWriting/Dumping/IAssetDumper.h @@ -1,9 +1,8 @@ #pragma once #include "AssetDumpingContext.h" -#include "Pool/AssetPool.h" -template class IAssetDumper +class IAssetDumper { public: IAssetDumper() = default; @@ -13,5 +12,6 @@ public: IAssetDumper& operator=(const IAssetDumper& other) = default; IAssetDumper& operator=(IAssetDumper&& other) noexcept = default; - virtual void DumpPool(AssetDumpingContext& context, AssetPool* pool) = 0; + [[nodiscard]] virtual size_t GetProgressTotalCount() const = 0; + virtual void Dump(AssetDumpingContext& context) = 0; }; diff --git a/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.cpp b/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.cpp index fbc44a99..06d0b422 100644 --- a/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.cpp +++ b/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.cpp @@ -62,7 +62,8 @@ namespace namespace image { - DumperIW3::DumperIW3() + DumperIW3::DumperIW3(const AssetPool& pool) + : AbstractAssetDumper(pool) { switch (ObjWriting::Configuration.ImageOutputFormat) { @@ -79,19 +80,14 @@ namespace image } } - bool DumperIW3::ShouldDump(XAssetInfo* asset) + void DumperIW3::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - return true; - } - - void DumperIW3::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) - { - const auto* image = asset->Asset(); + const auto* image = asset.Asset(); const auto texture = LoadImageData(context.m_obj_search_path, *image); if (!texture) return; - const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name, m_writer->GetFileExtension())); + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset.m_name, m_writer->GetFileExtension())); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.h b/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.h index 7a1b181c..32fdf23e 100644 --- a/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.h +++ b/src/ObjWriting/Game/IW3/Image/ImageDumperIW3.h @@ -8,14 +8,13 @@ namespace image { - class DumperIW3 final : public AbstractAssetDumper + class DumperIW3 final : public AbstractAssetDumper { public: - DumperIW3(); + explicit DumperIW3(const AssetPool& pool); protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; private: std::unique_ptr m_writer; diff --git a/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.cpp b/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.cpp index 777c419b..e1e1c9e5 100644 --- a/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.cpp +++ b/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.cpp @@ -11,9 +11,19 @@ using namespace IW3; namespace localize { - void DumperIW3::DumpPool(AssetDumpingContext& context, AssetPool* pool) + DumperIW3::DumperIW3(const AssetPool& pool) + : AbstractSingleProgressAssetDumper(pool) { - if (pool->m_asset_lookup.empty()) + } + + size_t DumperIW3::GetProgressTotalCount() const + { + return m_pool.m_asset_lookup.empty() ? 0uz : 1uz; + } + + void DumperIW3::Dump(AssetDumpingContext& context) + { + if (m_pool.m_asset_lookup.empty()) return; const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); @@ -30,7 +40,7 @@ namespace localize stringFileDumper.SetNotes(""); - for (auto* localizeEntry : *pool) + for (const auto* localizeEntry : m_pool) { stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); } @@ -41,5 +51,7 @@ namespace localize { con::error("Could not create string file for dumping localized strings of zone '{}'", context.m_zone.m_name); } + + context.IncrementProgress(); } } // namespace localize diff --git a/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.h b/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.h index 4cebd7c8..18971f83 100644 --- a/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.h +++ b/src/ObjWriting/Game/IW3/Localize/LocalizeDumperIW3.h @@ -2,12 +2,16 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW3/IW3.h" +#include "Pool/AssetPool.h" namespace localize { - class DumperIW3 final : public IAssetDumper + class DumperIW3 final : public AbstractSingleProgressAssetDumper { public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + explicit DumperIW3(const AssetPool& pool); + + [[nodiscard]] size_t GetProgressTotalCount() const override; + void Dump(AssetDumpingContext& context) override; }; } // namespace localize diff --git a/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.cpp b/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.cpp index 759623e4..e89014a4 100644 --- a/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.cpp +++ b/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.cpp @@ -4,15 +4,15 @@ using namespace IW3; namespace map_ents { - bool DumperIW3::ShouldDump(XAssetInfo* asset) + DumperIW3::DumperIW3(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW3::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW3::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* mapEnts = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name + ".ents"); + const auto* mapEnts = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name + ".ents"); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.h b/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.h index cd41ccc8..7e7786ea 100644 --- a/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.h +++ b/src/ObjWriting/Game/IW3/Maps/MapEntsDumperIW3.h @@ -5,10 +5,12 @@ namespace map_ents { - class DumperIW3 final : public AbstractAssetDumper + class DumperIW3 final : public AbstractAssetDumper { + public: + explicit DumperIW3(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace map_ents diff --git a/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp b/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp index 418e3156..aba69545 100644 --- a/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp +++ b/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp @@ -15,43 +15,55 @@ using namespace IW3; bool ObjWriter::DumpZone(AssetDumpingContext& context) const { -#define DUMP_ASSET_POOL(dumperType, poolName, assetType) \ - if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(assetType)) \ +#define REGISTER_DUMPER(dumperType, poolName) \ + if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(dumperType::AssetType_t::EnumEntry)) \ { \ - dumperType dumper; \ - dumper.DumpPool(context, assetPools->poolName.get()); \ + dumpers.emplace_back(std::make_unique(*assetPools->poolName)); \ } const auto* assetPools = dynamic_cast(context.m_zone.m_pools.get()); + std::vector> dumpers; - // DUMP_ASSET_POOL(AssetDumperPhysPreset, m_phys_preset, ASSET_TYPE_PHYSPRESET) - // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) - 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(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(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(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(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(raw_file::DumperIW3, m_raw_file, ASSET_TYPE_RAWFILE) - DUMP_ASSET_POOL(string_table::DumperIW3, m_string_table, ASSET_TYPE_STRINGTABLE) + // REGISTER_DUMPER(AssetDumperPhysPreset, m_phys_preset) + // REGISTER_DUMPER(AssetDumperXAnimParts, m_xanim_parts) + REGISTER_DUMPER(xmodel::DumperIW3, m_xmodel) + REGISTER_DUMPER(material::JsonDumperIW3, m_material) + // REGISTER_DUMPER(AssetDumperMaterialTechniqueSet, m_technique_set) + REGISTER_DUMPER(image::DumperIW3, m_image) + // REGISTER_DUMPER(AssetDumpersnd_alias_list_t, m_sound) + // REGISTER_DUMPER(AssetDumperSndCurve, m_sound_curve) + REGISTER_DUMPER(sound::LoadedSoundDumperIW3, m_loaded_sound) + // REGISTER_DUMPER(AssetDumperClipMap, m_clip_map) + // REGISTER_DUMPER(AssetDumperComWorld, m_com_world) + // REGISTER_DUMPER(AssetDumperGameWorldSp, m_game_world_sp) + // REGISTER_DUMPER(AssetDumperGameWorldMp, m_game_world_mp) + REGISTER_DUMPER(map_ents::DumperIW3, m_map_ents) + // REGISTER_DUMPER(AssetDumperGfxWorld, m_gfx_world) + // REGISTER_DUMPER(AssetDumperGfxLightDef, m_gfx_light_def) + // REGISTER_DUMPER(AssetDumperFont_s, m_font) + // REGISTER_DUMPER(AssetDumperMenuList, m_menu_list) + // REGISTER_DUMPER(AssetDumpermenuDef_t, m_menu_def) + REGISTER_DUMPER(localize::DumperIW3, m_localize) + // REGISTER_DUMPER(AssetDumperWeapon, m_weapon) + // REGISTER_DUMPER(AssetDumperSndDriverGlobals, m_snd_driver_globals) + // REGISTER_DUMPER(AssetDumperFxEffectDef, m_fx) + // REGISTER_DUMPER(AssetDumperFxImpactTable, m_fx_impact_table) + REGISTER_DUMPER(raw_file::DumperIW3, m_raw_file) + REGISTER_DUMPER(string_table::DumperIW3, m_string_table) + + if (context.ShouldTrackProgress()) + { + size_t totalProgress = 0uz; + for (const auto& dumper : dumpers) + totalProgress += dumper->GetProgressTotalCount(); + + context.SetTotalProgress(totalProgress); + } + + for (const auto& dumper : dumpers) + dumper->Dump(context); return true; -#undef DUMP_ASSET_POOL +#undef REGISTER_DUMPER } diff --git a/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.cpp b/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.cpp index 4b2eb11b..9ec12f1d 100644 --- a/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.cpp +++ b/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.cpp @@ -4,15 +4,15 @@ using namespace IW3; namespace raw_file { - bool DumperIW3::ShouldDump(XAssetInfo* asset) + DumperIW3::DumperIW3(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW3::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW3::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* rawFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* rawFile = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.h b/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.h index 250bea7c..eb6a80b2 100644 --- a/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.h +++ b/src/ObjWriting/Game/IW3/RawFile/RawFileDumperIW3.h @@ -5,10 +5,12 @@ namespace raw_file { - class DumperIW3 final : public AbstractAssetDumper + class DumperIW3 final : public AbstractAssetDumper { + public: + explicit DumperIW3(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace raw_file diff --git a/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.cpp b/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.cpp index 923d3d57..2fe85a25 100644 --- a/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.cpp +++ b/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.cpp @@ -25,15 +25,15 @@ namespace namespace sound { - bool LoadedSoundDumperIW3::ShouldDump(XAssetInfo* asset) + LoadedSoundDumperIW3::LoadedSoundDumperIW3(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void LoadedSoundDumperIW3::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void LoadedSoundDumperIW3::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* loadedSound = asset->Asset(); - const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset->m_name)); + const auto* loadedSound = asset.Asset(); + const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.h b/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.h index 458af961..c639ff07 100644 --- a/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.h +++ b/src/ObjWriting/Game/IW3/Sound/LoadedSoundDumperIW3.h @@ -5,10 +5,12 @@ namespace sound { - class LoadedSoundDumperIW3 final : public AbstractAssetDumper + class LoadedSoundDumperIW3 final : public AbstractAssetDumper { + public: + explicit LoadedSoundDumperIW3(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace sound diff --git a/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.cpp b/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.cpp index b7188a34..cf36f2fe 100644 --- a/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.cpp +++ b/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.cpp @@ -6,15 +6,15 @@ using namespace IW3; namespace string_table { - bool DumperIW3::ShouldDump(XAssetInfo* asset) + DumperIW3::DumperIW3(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW3::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW3::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* stringTable = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* stringTable = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.h b/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.h index 21ef05d5..78c87d6f 100644 --- a/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.h +++ b/src/ObjWriting/Game/IW3/StringTable/StringTableDumperIW3.h @@ -5,10 +5,12 @@ namespace string_table { - class DumperIW3 final : public AbstractAssetDumper + class DumperIW3 final : public AbstractAssetDumper { + public: + explicit DumperIW3(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace string_table diff --git a/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.cpp b/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.cpp index 248c4098..d921581c 100644 --- a/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.cpp @@ -59,7 +59,8 @@ namespace namespace image { - DumperIW4::DumperIW4() + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { switch (ObjWriting::Configuration.ImageOutputFormat) { @@ -76,19 +77,14 @@ namespace image } } - bool DumperIW4::ShouldDump(XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - return true; - } - - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) - { - const auto* image = asset->Asset(); + const auto* image = asset.Asset(); const auto texture = LoadImageData(context.m_obj_search_path, *image); if (!texture) return; - const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name, m_writer->GetFileExtension())); + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset.m_name, m_writer->GetFileExtension())); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.h b/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.h index 5d268ccc..e87e71a9 100644 --- a/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Image/ImageDumperIW4.h @@ -8,14 +8,13 @@ namespace image { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { public: - DumperIW4(); + explicit DumperIW4(const AssetPool& pool); protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; private: std::unique_ptr m_writer; diff --git a/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.cpp b/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.cpp index 80ed3625..41840a68 100644 --- a/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.cpp @@ -77,19 +77,19 @@ namespace namespace leaderboard { - bool JsonDumperIW4::ShouldDump(XAssetInfo* asset) + JsonDumperIW4::JsonDumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void JsonDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void JsonDumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAsset(asset->m_name)); + const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAsset(asset.m_name)); if (!assetFile) return; Dumper dumper(*assetFile); - dumper.Dump(*asset->Asset()); + dumper.Dump(*asset.Asset()); } } // namespace leaderboard diff --git a/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.h b/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.h index a56e5127..e5098b64 100644 --- a/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Leaderboard/LeaderboardJsonDumperIW4.h @@ -5,10 +5,12 @@ namespace leaderboard { - class JsonDumperIW4 final : public AbstractAssetDumper + class JsonDumperIW4 final : public AbstractAssetDumper { + public: + explicit JsonDumperIW4(const AssetPool& pool); + protected: - [[nodiscard]] bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace leaderboard diff --git a/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.cpp b/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.cpp index 205df266..402ed74a 100644 --- a/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.cpp @@ -2,21 +2,19 @@ #include "LightDef/LightDefCommon.h" -#include - using namespace IW4; namespace light_def { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* lightDef = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name)); + 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; diff --git a/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.h b/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.h index 29531abb..9bc28e3a 100644 --- a/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.h +++ b/src/ObjWriting/Game/IW4/LightDef/LightDefDumperIW4.h @@ -5,10 +5,12 @@ namespace light_def { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace light_def diff --git a/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.cpp b/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.cpp index bff7b2ee..67003134 100644 --- a/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.cpp @@ -5,15 +5,19 @@ #include "Utils/Logging/Log.h" #include -#include using namespace IW4; namespace localize { - void DumperIW4::DumpPool(AssetDumpingContext& context, AssetPool* pool) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractSingleProgressAssetDumper(pool) { - if (pool->m_asset_lookup.empty()) + } + + void DumperIW4::Dump(AssetDumpingContext& context) + { + if (m_pool.m_asset_lookup.empty()) return; const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); @@ -30,7 +34,7 @@ namespace localize stringFileDumper.SetNotes(""); - for (auto* localizeEntry : *pool) + for (const auto* localizeEntry : m_pool) { stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); } @@ -41,5 +45,7 @@ namespace localize { con::error("Could not create string file for dumping localized strings of zone '{}'", context.m_zone.m_name); } + + context.IncrementProgress(); } } // namespace localize diff --git a/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.h b/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.h index fd88c200..781e75f0 100644 --- a/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Localize/LocalizeDumperIW4.h @@ -5,9 +5,11 @@ namespace localize { - class DumperIW4 final : public IAssetDumper + class DumperIW4 final : public AbstractSingleProgressAssetDumper { public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + explicit DumperIW4(const AssetPool& pool); + + void Dump(AssetDumpingContext& context) override; }; } // namespace localize diff --git a/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.cpp b/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.cpp index 37b45249..11de5e0e 100644 --- a/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.cpp @@ -7,15 +7,15 @@ using namespace IW4; namespace addon_map_ents { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* addonMapEnts = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* addonMapEnts = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.h b/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.h index 4f1ef6c8..9e3439ea 100644 --- a/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Maps/AddonMapEntsDumperIW4.h @@ -5,10 +5,12 @@ namespace addon_map_ents { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace addon_map_ents diff --git a/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.cpp b/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.cpp index f706ed0e..749c8d52 100644 --- a/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.cpp @@ -1110,17 +1110,17 @@ namespace namespace material { - bool DecompilingGdtDumperIW4::ShouldDump(XAssetInfo* asset) + DecompilingGdtDumperIW4::DecompilingGdtDumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DecompilingGdtDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DecompilingGdtDumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { if (!context.m_gdt) return; - MaterialGdtDumper dumper(*asset->Asset()); + 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 index e6b73e35..f66ad842 100644 --- a/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Material/MaterialDecompilingDumperIW4.h @@ -5,10 +5,12 @@ namespace material { - class DecompilingGdtDumperIW4 final : public AbstractAssetDumper + class DecompilingGdtDumperIW4 final : public AbstractAssetDumper { + public: + explicit DecompilingGdtDumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace material diff --git a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp index d3828080..e25e983a 100644 --- a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp @@ -1,7 +1,6 @@ #include "MenuDumperIW4.h" #include "Game/IW4/GameAssetPoolIW4.h" -#include "Game/IW4/Menu/MenuDumperIW4.h" #include "MenuListDumperIW4.h" #include "MenuWriterIW4.h" #include "ObjWriting.h" @@ -13,12 +12,12 @@ using namespace IW4; namespace { - std::string GetPathForMenu(menu::MenuDumpingZoneState* zoneState, XAssetInfo* asset) + std::string GetPathForMenu(menu::MenuDumpingZoneState* zoneState, const XAssetInfo& asset) { - const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(asset->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 "ui_mp/" + std::string(asset.Asset()->window.name) + ".menu"; return menuDumpingState->second.m_path; } @@ -26,20 +25,20 @@ namespace namespace menu { - bool MenuDumperIW4::ShouldDump(XAssetInfo* asset) + MenuDumperIW4::MenuDumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void MenuDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void MenuDumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* menu = asset->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()); + const auto* gameAssetPool = dynamic_cast(asset.m_zone->m_pools.get()); for (auto* menuListAsset : *gameAssetPool->m_menu_list) CreateDumpingStateForMenuListIW4(zoneState, menuListAsset->Asset()); } diff --git a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h index 43317e26..91e452ab 100644 --- a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" -#include "Menu/MenuDumpingZoneState.h" namespace menu { - class MenuDumperIW4 final : public AbstractAssetDumper + class MenuDumperIW4 final : public AbstractAssetDumper { + public: + explicit MenuDumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace menu diff --git a/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.cpp b/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.cpp index 7946dfe9..217a3c3f 100644 --- a/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.cpp @@ -147,15 +147,15 @@ namespace menu } } - bool MenuListDumperIW4::ShouldDump(XAssetInfo* asset) + MenuListDumperIW4::MenuListDumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void MenuListDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void MenuListDumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* menuList = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* menuList = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; @@ -174,13 +174,13 @@ namespace menu menuWriter->End(); } - void MenuListDumperIW4::DumpPool(AssetDumpingContext& context, AssetPool* pool) + void MenuListDumperIW4::Dump(AssetDumpingContext& context) { auto* zoneState = context.GetZoneAssetDumperState(); - for (auto* asset : *pool) + for (const auto* asset : m_pool) CreateDumpingStateForMenuListIW4(zoneState, asset->Asset()); - AbstractAssetDumper::DumpPool(context, pool); + AbstractAssetDumper::Dump(context); } } // namespace menu diff --git a/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.h b/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.h index b9329966..93610331 100644 --- a/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.h @@ -2,20 +2,20 @@ #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 + 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; + explicit MenuListDumperIW4(const AssetPool& pool); + + void Dump(AssetDumpingContext& context) override; + + protected: + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace menu diff --git a/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp b/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp index b749a985..73d5b2d1 100644 --- a/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp +++ b/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp @@ -30,55 +30,67 @@ using namespace IW4; bool ObjWriter::DumpZone(AssetDumpingContext& context) const { -#define DUMP_ASSET_POOL(dumperType, poolName, assetType) \ - if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(assetType)) \ +#define REGISTER_DUMPER(dumperType, poolName) \ + if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(dumperType::AssetType_t::EnumEntry)) \ { \ - dumperType dumper; \ - dumper.DumpPool(context, assetPools->poolName.get()); \ + dumpers.emplace_back(std::make_unique(*assetPools->poolName)); \ } const auto* assetPools = dynamic_cast(context.m_zone.m_pools.get()); + std::vector> dumpers; - 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(xmodel::DumperIW4, m_xmodel, ASSET_TYPE_XMODEL) - DUMP_ASSET_POOL(material::JsonDumperIW4, m_material, ASSET_TYPE_MATERIAL) + REGISTER_DUMPER(phys_preset::InfoStringDumperIW4, m_phys_preset) + REGISTER_DUMPER(phys_collmap::DumperIW4, m_phys_collmap) + // REGISTER_DUMPER(AssetDumperXAnimParts, m_xanim_parts) + REGISTER_DUMPER(xmodel::DumperIW4, m_xmodel) + REGISTER_DUMPER(material::JsonDumperIW4, m_material) #ifdef EXPERIMENTAL_MATERIAL_COMPILATION - DUMP_ASSET_POOL(material::DecompilingGdtDumperIW4, m_material, ASSET_TYPE_MATERIAL) + DUMP_ASSET_POOL(material::DecompilingGdtDumperIW4, m_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(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) - // 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(AssetDumperFxWorld, m_fx_world, ASSET_TYPE_FXWORLD) - // DUMP_ASSET_POOL(AssetDumperGfxWorld, m_gfx_world, ASSET_TYPE_GFXWORLD) - 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(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(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) + REGISTER_DUMPER(shader::PixelShaderDumperIW4, m_material_pixel_shader) + REGISTER_DUMPER(shader::VertexShaderDumperIW4, m_material_vertex_shader) + REGISTER_DUMPER(techset::DumperIW4, m_technique_set) + REGISTER_DUMPER(image::DumperIW4, m_image) + // REGISTER_DUMPER(AssetDumpersnd_alias_list_t, m_sound) + REGISTER_DUMPER(sound_curve::DumperIW4, m_sound_curve) + REGISTER_DUMPER(sound::LoadedSoundDumperIW4, m_loaded_sound) + // REGISTER_DUMPER(AssetDumperClipMap, m_clip_map) + // REGISTER_DUMPER(AssetDumperComWorld, m_com_world) + // REGISTER_DUMPER(AssetDumperGameWorldSp, m_game_world_sp) + // REGISTER_DUMPER(AssetDumperGameWorldMp, m_game_world_mp) + // REGISTER_DUMPER(AssetDumperMapEnts, m_map_ents) + // REGISTER_DUMPER(AssetDumperFxWorld, m_fx_world) + // REGISTER_DUMPER(AssetDumperGfxWorld, m_gfx_world) + REGISTER_DUMPER(light_def::DumperIW4, m_gfx_light_def) + // REGISTER_DUMPER(AssetDumperFont_s, m_font) + REGISTER_DUMPER(menu::MenuListDumperIW4, m_menu_list) + REGISTER_DUMPER(menu::MenuDumperIW4, m_menu_def) + REGISTER_DUMPER(localize::DumperIW4, m_localize) + REGISTER_DUMPER(weapon::DumperIW4, m_weapon) + // REGISTER_DUMPER(AssetDumperSndDriverGlobals, m_snd_driver_globals) + // REGISTER_DUMPER(AssetDumperFxEffectDef, m_fx) + // REGISTER_DUMPER(AssetDumperFxImpactTable, m_fx_impact_table) + REGISTER_DUMPER(raw_file::DumperIW4, m_raw_file) + REGISTER_DUMPER(string_table::DumperIW4, m_string_table) + REGISTER_DUMPER(leaderboard::JsonDumperIW4, m_leaderboard) + REGISTER_DUMPER(structured_data_def::DumperIW4, m_structed_data_def_set) + REGISTER_DUMPER(tracer::DumperIW4, m_tracer) + REGISTER_DUMPER(vehicle::DumperIW4, m_vehicle) + REGISTER_DUMPER(addon_map_ents::DumperIW4, m_addon_map_ents) + + if (context.ShouldTrackProgress()) + { + size_t totalProgress = 0uz; + for (const auto& dumper : dumpers) + totalProgress += dumper->GetProgressTotalCount(); + + context.SetTotalProgress(totalProgress); + } + + for (const auto& dumper : dumpers) + dumper->Dump(context); return true; -#undef DUMP_ASSET_POOL +#undef REGISTER_DUMPER } diff --git a/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.cpp b/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.cpp index 523c64c8..2cf39ef3 100644 --- a/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.cpp @@ -10,15 +10,15 @@ using namespace IW4; namespace phys_collmap { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* physCollmap = asset->Asset(); - const auto assetFile = context.OpenAssetFile(phys_collmap::GetFileNameForAssetName(asset->m_name)); + const auto* physCollmap = asset.Asset(); + const auto assetFile = context.OpenAssetFile(phys_collmap::GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.h b/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.h index 4dbf68d6..c3cb8061 100644 --- a/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.h +++ b/src/ObjWriting/Game/IW4/PhysCollmap/PhysCollmapDumperIW4.h @@ -5,10 +5,12 @@ namespace phys_collmap { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace phys_collmap diff --git a/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.cpp b/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.cpp index 41ac1f64..c60e38a8 100644 --- a/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.cpp @@ -57,21 +57,21 @@ namespace physPresetInfo->perSurfaceSndAlias = physPreset->perSurfaceSndAlias ? 1 : 0; } - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { auto* physPresetInfo = new PhysPresetInfo; - CopyToPhysPresetInfo(asset->Asset(), 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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); @@ -80,24 +80,24 @@ namespace namespace phys_preset { - bool InfoStringDumperIW4::ShouldDump(XAssetInfo* asset) + InfoStringDumperIW4::InfoStringDumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void InfoStringDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void InfoStringDumperIW4::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.h b/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.h index 4651c1e0..17457ce9 100644 --- a/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.h +++ b/src/ObjWriting/Game/IW4/PhysPreset/PhysPresetInfoStringDumperIW4.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" -#include "InfoString/InfoString.h" namespace phys_preset { - class InfoStringDumperIW4 final : public AbstractAssetDumper + class InfoStringDumperIW4 final : public AbstractAssetDumper { + public: + explicit InfoStringDumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace phys_preset diff --git a/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.cpp b/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.cpp index a1e9a045..39109672 100644 --- a/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.cpp @@ -10,15 +10,15 @@ using namespace IW4; namespace raw_file { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* rawFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* rawFile = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.h b/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.h index 9b8966ea..5a3b7559 100644 --- a/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.h +++ b/src/ObjWriting/Game/IW4/RawFile/RawFileDumperIW4.h @@ -5,10 +5,12 @@ namespace raw_file { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace raw_file diff --git a/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.cpp b/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.cpp index 318c9a68..9ef1571b 100644 --- a/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.cpp @@ -6,15 +6,15 @@ using namespace IW4; namespace shader { - bool PixelShaderDumperIW4::ShouldDump(XAssetInfo* asset) + PixelShaderDumperIW4::PixelShaderDumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void PixelShaderDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void PixelShaderDumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* pixelShader = asset->Asset(); - const auto shaderFile = context.OpenAssetFile(shader::GetFileNameForPixelShaderAssetName(asset->m_name)); + const auto* pixelShader = asset.Asset(); + const auto shaderFile = context.OpenAssetFile(shader::GetFileNameForPixelShaderAssetName(asset.m_name)); if (!shaderFile) return; diff --git a/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.h b/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.h index 01404d0a..0c1e5339 100644 --- a/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Shader/PixelShaderDumperIW4.h @@ -5,10 +5,12 @@ namespace shader { - class PixelShaderDumperIW4 final : public AbstractAssetDumper + class PixelShaderDumperIW4 final : public AbstractAssetDumper { + public: + explicit PixelShaderDumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace shader diff --git a/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.cpp b/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.cpp index c3954562..d5a66fcb 100644 --- a/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.cpp @@ -6,15 +6,15 @@ using namespace IW4; namespace shader { - bool VertexShaderDumperIW4::ShouldDump(XAssetInfo* asset) + VertexShaderDumperIW4::VertexShaderDumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void VertexShaderDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void VertexShaderDumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* vertexShader = asset->Asset(); - const auto shaderFile = context.OpenAssetFile(shader::GetFileNameForVertexShaderAssetName(asset->m_name)); + const auto* vertexShader = asset.Asset(); + const auto shaderFile = context.OpenAssetFile(shader::GetFileNameForVertexShaderAssetName(asset.m_name)); if (!shaderFile) return; diff --git a/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.h b/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.h index 1b722461..5702e94d 100644 --- a/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Shader/VertexShaderDumperIW4.h @@ -5,10 +5,12 @@ namespace shader { - class VertexShaderDumperIW4 final : public AbstractAssetDumper + class VertexShaderDumperIW4 final : public AbstractAssetDumper { + public: + explicit VertexShaderDumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace shader diff --git a/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.cpp b/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.cpp index 20e34703..e0d2ed2d 100644 --- a/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.cpp @@ -25,15 +25,15 @@ namespace namespace sound { - bool LoadedSoundDumperIW4::ShouldDump(XAssetInfo* asset) + LoadedSoundDumperIW4::LoadedSoundDumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void LoadedSoundDumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void LoadedSoundDumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* loadedSound = asset->Asset(); - const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset->m_name)); + const auto* loadedSound = asset.Asset(); + const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.h b/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.h index 7256bc88..fa28defe 100644 --- a/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Sound/LoadedSoundDumperIW4.h @@ -5,10 +5,12 @@ namespace sound { - class LoadedSoundDumperIW4 final : public AbstractAssetDumper + class LoadedSoundDumperIW4 final : public AbstractAssetDumper { + public: + explicit LoadedSoundDumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace sound diff --git a/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.cpp b/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.cpp index b7e00629..8a6ab9a2 100644 --- a/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.cpp @@ -3,20 +3,18 @@ #include "Sound/SndCurveDumper.h" #include "Sound/SoundCurveCommon.h" -#include - using namespace IW4; namespace sound_curve { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* sndCurve = asset->Asset(); + const auto* sndCurve = asset.Asset(); const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(sndCurve->filename)); diff --git a/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.h b/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.h index 241b7a79..0c4358d5 100644 --- a/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Sound/SndCurveDumperIW4.h @@ -5,10 +5,12 @@ namespace sound_curve { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace sound_curve diff --git a/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.cpp b/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.cpp index 784164e2..27a92ecb 100644 --- a/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.cpp @@ -6,15 +6,15 @@ using namespace IW4; namespace string_table { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* stringTable = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* stringTable = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.h b/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.h index 93f1af2b..8abb9057 100644 --- a/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.h +++ b/src/ObjWriting/Game/IW4/StringTable/StringTableDumperIW4.h @@ -5,10 +5,12 @@ namespace string_table { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace string_table diff --git a/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.cpp b/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.cpp index 2e5cf317..10665f83 100644 --- a/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.cpp @@ -1,9 +1,11 @@ #include "StructuredDataDefDumperIW4.h" +#include "StructuredDataDef/CommonStructuredDataDef.h" #include "StructuredDataDef/StructuredDataDefDumper.h" #include #include +#include #include using namespace IW4; @@ -185,15 +187,15 @@ namespace namespace structured_data_def { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* set = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* set = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile || set->defs == nullptr) return; diff --git a/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.h b/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.h index e03d8c61..47b14f89 100644 --- a/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.h +++ b/src/ObjWriting/Game/IW4/StructuredDataDef/StructuredDataDefDumperIW4.h @@ -2,16 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" -#include "StructuredDataDef/CommonStructuredDataDef.h" - -#include namespace structured_data_def { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace structured_data_def diff --git a/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.cpp b/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.cpp index f8eb6e90..8714cd74 100644 --- a/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.cpp @@ -536,14 +536,14 @@ namespace IW4 namespace techset { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* techset = asset->Asset(); + const auto* techset = asset.Asset(); const auto techsetFile = context.OpenAssetFile(GetFileNameForTechsetName(techset->name)); diff --git a/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.h b/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.h index 8115f527..ea8f076f 100644 --- a/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Techset/TechsetDumperIW4.h @@ -5,10 +5,12 @@ namespace techset { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace techset diff --git a/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.cpp b/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.cpp index 2e3635f8..63076021 100644 --- a/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.cpp @@ -32,18 +32,18 @@ namespace } }; - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { - InfoStringFromTracerConverter converter(asset->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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); @@ -52,24 +52,24 @@ namespace namespace tracer { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.h b/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.h index d13a9f60..708d7c76 100644 --- a/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Tracer/TracerDumperIW4.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" -#include "InfoString/InfoString.h" namespace tracer { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace tracer diff --git a/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.cpp b/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.cpp index 9110b559..5830391f 100644 --- a/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.cpp @@ -73,18 +73,18 @@ namespace } }; - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { - InfoStringFromVehicleConverter converter(asset->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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); @@ -93,24 +93,24 @@ namespace namespace vehicle { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.h b/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.h index 5afae348..1af2dcaa 100644 --- a/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Vehicle/VehicleDumperIW4.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" -#include "InfoString/InfoString.h" namespace vehicle { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace vehicle diff --git a/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.cpp b/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.cpp index 1ebabfe7..6ca11889 100644 --- a/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.cpp @@ -353,31 +353,31 @@ namespace } } - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { const auto fullDef = std::make_unique(); memset(fullDef.get(), 0, sizeof(WeaponFullDef)); - CopyToFullDef(asset->Asset(), fullDef.get()); + 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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); } - void DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset) + void DumpAccuracyGraphs(AssetDumpingContext& context, const XAssetInfo& asset) { auto* accuracyGraphWriter = context.GetZoneAssetDumperState(); - const auto weapon = asset->Asset(); + const auto weapon = asset.Asset(); const auto* weapDef = weapon->weapDef; if (!weapDef) @@ -405,24 +405,24 @@ namespace namespace weapon { - bool DumperIW4::ShouldDump(XAssetInfo* asset) + DumperIW4::DumperIW4(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW4::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW4::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.h b/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.h index d6935e77..a97ff280 100644 --- a/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.h +++ b/src/ObjWriting/Game/IW4/Weapon/WeaponDumperIW4.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" -#include "InfoString/InfoString.h" namespace weapon { - class DumperIW4 final : public AbstractAssetDumper + class DumperIW4 final : public AbstractAssetDumper { + public: + explicit DumperIW4(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace weapon diff --git a/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.cpp b/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.cpp index 038f94ea..6a219864 100644 --- a/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.cpp @@ -60,7 +60,8 @@ namespace namespace image { - DumperIW5::DumperIW5() + DumperIW5::DumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { switch (ObjWriting::Configuration.ImageOutputFormat) { @@ -77,19 +78,14 @@ namespace image } } - bool DumperIW5::ShouldDump(XAssetInfo* asset) + void DumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - return true; - } - - void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) - { - const auto* image = asset->Asset(); + const auto* image = asset.Asset(); const auto texture = LoadImageData(context.m_obj_search_path, *image); if (!texture) return; - const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name, m_writer->GetFileExtension())); + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset.m_name, m_writer->GetFileExtension())); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.h b/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.h index 414465aa..7c4f8200 100644 --- a/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Image/ImageDumperIW5.h @@ -8,14 +8,13 @@ namespace image { - class DumperIW5 final : public AbstractAssetDumper + class DumperIW5 final : public AbstractAssetDumper { public: - DumperIW5(); + explicit DumperIW5(const AssetPool& pool); protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; private: std::unique_ptr m_writer; diff --git a/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.cpp b/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.cpp index f79ea849..08586629 100644 --- a/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.cpp @@ -94,19 +94,19 @@ namespace namespace leaderboard { - bool JsonDumperIW5::ShouldDump(XAssetInfo* asset) + JsonDumperIW5::JsonDumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void JsonDumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void JsonDumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAsset(asset->m_name)); + const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAsset(asset.m_name)); if (!assetFile) return; Dumper dumper(*assetFile); - dumper.Dump(*asset->Asset()); + dumper.Dump(*asset.Asset()); } } // namespace leaderboard diff --git a/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.h b/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.h index 3e6ff7f8..04d07ecf 100644 --- a/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Leaderboard/LeaderboardJsonDumperIW5.h @@ -5,10 +5,12 @@ namespace leaderboard { - class JsonDumperIW5 final : public AbstractAssetDumper + class JsonDumperIW5 final : public AbstractAssetDumper { + public: + explicit JsonDumperIW5(const AssetPool& pool); + protected: - [[nodiscard]] bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace leaderboard diff --git a/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.cpp b/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.cpp index a9f979ee..55894aed 100644 --- a/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.cpp @@ -11,9 +11,14 @@ using namespace IW5; namespace localize { - void DumperIW5::DumpPool(AssetDumpingContext& context, AssetPool* pool) + DumperIW5::DumperIW5(const AssetPool& pool) + : AbstractSingleProgressAssetDumper(pool) { - if (pool->m_asset_lookup.empty()) + } + + void DumperIW5::Dump(AssetDumpingContext& context) + { + if (m_pool.m_asset_lookup.empty()) return; const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); @@ -30,7 +35,7 @@ namespace localize stringFileDumper.SetNotes(""); - for (auto* localizeEntry : *pool) + for (const auto* localizeEntry : m_pool) { stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); } @@ -41,5 +46,7 @@ namespace localize { con::error("Could not create string file for dumping localized strings of zone '{}'", context.m_zone.m_name); } + + context.IncrementProgress(); } } // namespace localize diff --git a/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.h b/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.h index 26373073..427ca8dd 100644 --- a/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Localize/LocalizeDumperIW5.h @@ -5,9 +5,11 @@ namespace localize { - class DumperIW5 final : public IAssetDumper + class DumperIW5 final : public AbstractSingleProgressAssetDumper { public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + explicit DumperIW5(const AssetPool& pool); + + void Dump(AssetDumpingContext& context) override; }; } // namespace localize diff --git a/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.cpp b/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.cpp index 3facfa1c..6c2ba3a3 100644 --- a/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.cpp @@ -7,15 +7,15 @@ using namespace IW5; namespace addon_map_ents { - bool DumperIW5::ShouldDump(XAssetInfo* asset) + DumperIW5::DumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* addonMapEnts = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* addonMapEnts = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.h b/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.h index 6c251086..d7a8d226 100644 --- a/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Maps/AddonMapEntsDumperIW5.h @@ -5,10 +5,12 @@ namespace addon_map_ents { - class DumperIW5 final : public AbstractAssetDumper + class DumperIW5 final : public AbstractAssetDumper { + public: + explicit DumperIW5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace addon_map_ents diff --git a/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp index 2abe44a0..986ee1cc 100644 --- a/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp @@ -15,10 +15,10 @@ using namespace IW5; namespace { - const MenuList* GetParentMenuList(XAssetInfo* asset) + const MenuList* GetParentMenuList(const XAssetInfo& asset) { - const auto* menu = asset->Asset(); - const auto* gameAssetPool = dynamic_cast(asset->m_zone->m_pools.get()); + 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(); @@ -33,32 +33,32 @@ namespace return nullptr; } - std::string GetPathForMenu(XAssetInfo* asset) + std::string GetPathForMenu(const XAssetInfo& asset) { const auto* list = GetParentMenuList(asset); if (!list) - return std::format("ui_mp/{}.menu", asset->Asset()->window.name); + return std::format("ui_mp/{}.menu", asset.Asset()->window.name); 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); + return std::format("{}{}.menu", parentPath, asset.Asset()->window.name); } } // namespace namespace menu { - bool MenuDumperIW5::ShouldDump(XAssetInfo* asset) + MenuDumperIW5::MenuDumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void MenuDumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void MenuDumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* menu = asset->Asset(); + const auto* menu = asset.Asset(); const auto menuFilePath = GetPathForMenu(asset); if (ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST)) diff --git a/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h index 03577fcb..e996e47d 100644 --- a/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW5/IW5.h" -#include "Game/IW5/Menu/MenuDumperIW5.h" namespace menu { - class MenuDumperIW5 final : public AbstractAssetDumper + class MenuDumperIW5 final : public AbstractAssetDumper { + public: + explicit MenuDumperIW5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace menu diff --git a/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.cpp b/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.cpp index 9a1d674c..55e61eb1 100644 --- a/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.cpp @@ -106,15 +106,15 @@ namespace namespace menu { - bool MenuListDumperIW5::ShouldDump(XAssetInfo* asset) + MenuListDumperIW5::MenuListDumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void MenuListDumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void MenuListDumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* menuList = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* menuList = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.h b/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.h index d4da415f..7b87deda 100644 --- a/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.h @@ -5,10 +5,12 @@ namespace menu { - class MenuListDumperIW5 final : public AbstractAssetDumper + class MenuListDumperIW5 final : public AbstractAssetDumper { + public: + explicit MenuListDumperIW5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace menu diff --git a/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp b/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp index 4cbd3f31..e7ca7a8f 100644 --- a/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp +++ b/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp @@ -21,56 +21,69 @@ using namespace IW5; bool ObjWriter::DumpZone(AssetDumpingContext& context) const { -#define DUMP_ASSET_POOL(dumperType, poolName, assetType) \ - if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(assetType)) \ +#define REGISTER_DUMPER(dumperType, poolName) \ + if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(dumperType::AssetType_t::EnumEntry)) \ { \ - dumperType dumper; \ - dumper.DumpPool(context, assetPools->poolName.get()); \ + dumpers.emplace_back(std::make_unique(*assetPools->poolName)); \ } 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(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) - // DUMP_ASSET_POOL(AssetDumperXModelSurfs, m_xmodel_surfs, ASSET_TYPE_XMODEL_SURFS) - 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(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(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) - // DUMP_ASSET_POOL(AssetDumperPathData, m_path_data, ASSET_TYPE_PATHDATA) - // DUMP_ASSET_POOL(AssetDumperVehicleTrack, m_vehicle_track, ASSET_TYPE_VEHICLE_TRACK) - // 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(AssetDumperFont_s, m_font, ASSET_TYPE_FONT) - 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(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(addon_map_ents::DumperIW5, m_addon_map_ents, ASSET_TYPE_ADDON_MAP_ENTS) + std::vector> dumpers; + + // REGISTER_DUMPER(AssetDumperPhysPreset, m_phys_preset) + // REGISTER_DUMPER(AssetDumperPhysCollmap, m_phys_collmap) + // REGISTER_DUMPER(AssetDumperXAnimParts, m_xanim_parts) + // REGISTER_DUMPER(AssetDumperXModelSurfs, m_xmodel_surfs) + REGISTER_DUMPER(xmodel::DumperIW5, m_xmodel) + REGISTER_DUMPER(material::JsonDumperIW5, m_material) + // REGISTER_DUMPER(AssetDumperMaterialPixelShader, m_material_pixel_shader) + // REGISTER_DUMPER(AssetDumperMaterialVertexShader, m_material_vertex_shader) + // REGISTER_DUMPER(AssetDumperMaterialVertexDeclaration, m_material_vertex_decl) + // REGISTER_DUMPER(AssetDumperMaterialTechniqueSet, m_technique_set) + REGISTER_DUMPER(image::DumperIW5, m_image) + // REGISTER_DUMPER(AssetDumpersnd_alias_list_t, m_sound) + // REGISTER_DUMPER(AssetDumperSndCurve, m_sound_curve) + REGISTER_DUMPER(sound::LoadedSoundDumperIW5, m_loaded_sound) + // REGISTER_DUMPER(AssetDumperclipMap_t, m_clip_map) + // REGISTER_DUMPER(AssetDumperComWorld, m_com_world) + // REGISTER_DUMPER(AssetDumperGlassWorld, m_glass_world) + // REGISTER_DUMPER(AssetDumperPathData, m_path_data) + // REGISTER_DUMPER(AssetDumperVehicleTrack, m_vehicle_track) + // REGISTER_DUMPER(AssetDumperMapEnts, m_map_ents) + // REGISTER_DUMPER(AssetDumperFxWorld, m_fx_world) + // REGISTER_DUMPER(AssetDumperGfxWorld, m_gfx_world) + // REGISTER_DUMPER(AssetDumperGfxLightDef, m_gfx_light_def) + // REGISTER_DUMPER(AssetDumperFont_s, m_font) + REGISTER_DUMPER(menu::MenuListDumperIW5, m_menu_list) + REGISTER_DUMPER(menu::MenuDumperIW5, m_menu_def) + REGISTER_DUMPER(localize::DumperIW5, m_localize) + REGISTER_DUMPER(attachment::JsonDumperIW5, m_attachment) + REGISTER_DUMPER(weapon::DumperIW5, m_weapon) + // REGISTER_DUMPER(AssetDumperFxEffectDef, m_fx) + // REGISTER_DUMPER(AssetDumperFxImpactTable, m_fx_impact_table) + // REGISTER_DUMPER(AssetDumperSurfaceFxTable, m_surface_fx_table) + REGISTER_DUMPER(raw_file::DumperIW5, m_raw_file) + REGISTER_DUMPER(script::DumperIW5, m_script_file) + REGISTER_DUMPER(string_table::DumperIW5, m_string_table) + REGISTER_DUMPER(leaderboard::JsonDumperIW5, m_leaderboard) + // REGISTER_DUMPER(AssetDumperStructuredDataDefSet, m_structed_data_def_set) + // REGISTER_DUMPER(AssetDumperTracerDef, m_tracer) + // REGISTER_DUMPER(AssetDumperVehicleDef, m_vehicle) + REGISTER_DUMPER(addon_map_ents::DumperIW5, m_addon_map_ents) + + if (context.ShouldTrackProgress()) + { + size_t totalProgress = 0uz; + for (const auto& dumper : dumpers) + totalProgress += dumper->GetProgressTotalCount(); + + context.SetTotalProgress(totalProgress); + } + + for (const auto& dumper : dumpers) + dumper->Dump(context); return true; -#undef DUMP_ASSET_POOL +#undef REGISTER_DUMPER } diff --git a/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.cpp b/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.cpp index 9021e3b3..30b9fcfb 100644 --- a/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.cpp @@ -10,15 +10,15 @@ using namespace IW5; namespace raw_file { - bool DumperIW5::ShouldDump(XAssetInfo* asset) + DumperIW5::DumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* rawFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* rawFile = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.h b/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.h index ed2ceb4f..1da93e45 100644 --- a/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.h +++ b/src/ObjWriting/Game/IW5/RawFile/RawFileDumperIW5.h @@ -5,10 +5,12 @@ namespace raw_file { - class DumperIW5 final : public AbstractAssetDumper + class DumperIW5 final : public AbstractAssetDumper { + public: + explicit DumperIW5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace raw_file diff --git a/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.cpp b/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.cpp index d334cb1c..1fcbf206 100644 --- a/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.cpp @@ -4,16 +4,16 @@ using namespace IW5; namespace script { - bool DumperIW5::ShouldDump(XAssetInfo* asset) + DumperIW5::DumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - 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) + void DumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - auto* scriptFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name + ".gscbin"); + auto* scriptFile = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name + ".gscbin"); if (!assetFile) return; @@ -21,10 +21,10 @@ namespace script 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)); + 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); diff --git a/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.h b/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.h index e1220f5f..920f3733 100644 --- a/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Script/ScriptDumperIW5.h @@ -5,10 +5,12 @@ namespace script { - class DumperIW5 final : public AbstractAssetDumper + class DumperIW5 final : public AbstractAssetDumper { + public: + explicit DumperIW5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace script diff --git a/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.cpp b/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.cpp index 4a50e20b..5a847929 100644 --- a/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.cpp @@ -25,15 +25,15 @@ namespace namespace sound { - bool LoadedSoundDumperIW5::ShouldDump(XAssetInfo* asset) + LoadedSoundDumperIW5::LoadedSoundDumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void LoadedSoundDumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void LoadedSoundDumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* loadedSound = asset->Asset(); - const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset->m_name)); + const auto* loadedSound = asset.Asset(); + const auto assetFile = context.OpenAssetFile(std::format("sound/{}", asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.h b/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.h index 8079f27d..32350c68 100644 --- a/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Sound/LoadedSoundDumperIW5.h @@ -5,10 +5,12 @@ namespace sound { - class LoadedSoundDumperIW5 final : public AbstractAssetDumper + class LoadedSoundDumperIW5 final : public AbstractAssetDumper { + public: + explicit LoadedSoundDumperIW5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace sound diff --git a/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.cpp b/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.cpp index 2bd7fa00..0b3dfae2 100644 --- a/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.cpp @@ -6,15 +6,15 @@ using namespace IW5; namespace string_table { - bool DumperIW5::ShouldDump(XAssetInfo* asset) + DumperIW5::DumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* stringTable = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* stringTable = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.h b/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.h index e826dfd0..071a08ee 100644 --- a/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.h +++ b/src/ObjWriting/Game/IW5/StringTable/StringTableDumperIW5.h @@ -5,10 +5,12 @@ namespace string_table { - class DumperIW5 final : public AbstractAssetDumper + class DumperIW5 final : public AbstractAssetDumper { + public: + explicit DumperIW5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace string_table diff --git a/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.cpp b/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.cpp index c44f99a2..79f842c9 100644 --- a/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.cpp @@ -395,19 +395,19 @@ namespace namespace attachment { - bool JsonDumperIW5::ShouldDump(XAssetInfo* asset) + JsonDumperIW5::JsonDumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void JsonDumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void JsonDumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAssetName(asset->m_name)); + const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAssetName(asset.m_name)); if (!assetFile) return; const JsonDumperImpl dumper(context, *assetFile); - dumper.Dump(asset->Asset()); + dumper.Dump(asset.Asset()); } } // namespace attachment diff --git a/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.h b/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.h index 81088a63..f43204a1 100644 --- a/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Weapon/AttachmentJsonDumperIW5.h @@ -5,10 +5,12 @@ namespace attachment { - class JsonDumperIW5 final : public AbstractAssetDumper + class JsonDumperIW5 final : public AbstractAssetDumper { + public: + explicit JsonDumperIW5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace attachment diff --git a/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.cpp b/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.cpp index 67ab7628..ed3f984d 100644 --- a/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.cpp @@ -681,31 +681,31 @@ namespace } } - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { const auto fullDef = std::make_unique(); memset(fullDef.get(), 0, sizeof(WeaponFullDef)); - CopyToFullDef(asset->Asset(), fullDef.get()); + 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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); } - void DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset) + void DumpAccuracyGraphs(AssetDumpingContext& context, const XAssetInfo& asset) { auto* accuracyGraphWriter = context.GetZoneAssetDumperState(); - const auto weapon = asset->Asset(); + const auto weapon = asset.Asset(); const auto* weapDef = weapon->weapDef; if (!weapDef) @@ -733,12 +733,12 @@ namespace namespace weapon { - bool DumperIW5::ShouldDump(XAssetInfo* asset) + DumperIW5::DumperIW5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperIW5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { // TODO: only dump infostring fields when non-default @@ -746,13 +746,13 @@ namespace weapon if (context.m_gdt) { const auto infoString = CreateInfoString(asset); - GdtEntry gdtEntry(asset->m_name, ObjConstants::GDF_FILENAME_WEAPON); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.h b/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.h index 3e80c669..5772e5fc 100644 --- a/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.h +++ b/src/ObjWriting/Game/IW5/Weapon/WeaponDumperIW5.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW5/IW5.h" -#include "InfoString/InfoString.h" namespace weapon { - class DumperIW5 final : public AbstractAssetDumper + class DumperIW5 final : public AbstractAssetDumper { + public: + explicit DumperIW5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace weapon diff --git a/src/ObjWriting/Game/T5/Image/ImageDumperT5.cpp b/src/ObjWriting/Game/T5/Image/ImageDumperT5.cpp index 6843efa8..27fe8f03 100644 --- a/src/ObjWriting/Game/T5/Image/ImageDumperT5.cpp +++ b/src/ObjWriting/Game/T5/Image/ImageDumperT5.cpp @@ -59,7 +59,8 @@ namespace namespace image { - DumperT5::DumperT5() + DumperT5::DumperT5(const AssetPool& pool) + : AbstractAssetDumper(pool) { switch (ObjWriting::Configuration.ImageOutputFormat) { @@ -76,19 +77,14 @@ namespace image } } - bool DumperT5::ShouldDump(XAssetInfo* asset) + void DumperT5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - return true; - } - - void DumperT5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) - { - const auto* image = asset->Asset(); + const auto* image = asset.Asset(); const auto texture = LoadImageData(context.m_obj_search_path, *image); if (!texture) return; - const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name, m_writer->GetFileExtension())); + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset.m_name, m_writer->GetFileExtension())); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T5/Image/ImageDumperT5.h b/src/ObjWriting/Game/T5/Image/ImageDumperT5.h index 0be6b9bb..109758e5 100644 --- a/src/ObjWriting/Game/T5/Image/ImageDumperT5.h +++ b/src/ObjWriting/Game/T5/Image/ImageDumperT5.h @@ -8,14 +8,13 @@ namespace image { - class DumperT5 final : public AbstractAssetDumper + class DumperT5 final : public AbstractAssetDumper { public: - DumperT5(); + explicit DumperT5(const AssetPool& pool); protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; private: std::unique_ptr m_writer; diff --git a/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.cpp b/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.cpp index 0ed7388b..5edb14a4 100644 --- a/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.cpp +++ b/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.cpp @@ -11,9 +11,14 @@ using namespace T5; namespace localize { - void DumperT5::DumpPool(AssetDumpingContext& context, AssetPool* pool) + DumperT5::DumperT5(const AssetPool& pool) + : AbstractSingleProgressAssetDumper(pool) { - if (pool->m_asset_lookup.empty()) + } + + void DumperT5::Dump(AssetDumpingContext& context) + { + if (m_pool.m_asset_lookup.empty()) return; const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); @@ -30,7 +35,7 @@ namespace localize stringFileDumper.SetNotes(""); - for (auto* localizeEntry : *pool) + for (const auto* localizeEntry : m_pool) { stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); } @@ -41,5 +46,7 @@ namespace localize { con::error("Could not create string file for dumping localized strings of zone '{}'", context.m_zone.m_name); } + + context.IncrementProgress(); } } // namespace localize diff --git a/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.h b/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.h index 321a9aef..5dfac74c 100644 --- a/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.h +++ b/src/ObjWriting/Game/T5/Localize/LocalizeDumperT5.h @@ -5,9 +5,11 @@ namespace localize { - class DumperT5 final : public IAssetDumper + class DumperT5 final : public AbstractSingleProgressAssetDumper { public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + explicit DumperT5(const AssetPool& pool); + + void Dump(AssetDumpingContext& context) override; }; } // namespace localize diff --git a/src/ObjWriting/Game/T5/ObjWriterT5.cpp b/src/ObjWriting/Game/T5/ObjWriterT5.cpp index 208f14ba..c1acccb0 100644 --- a/src/ObjWriting/Game/T5/ObjWriterT5.cpp +++ b/src/ObjWriting/Game/T5/ObjWriterT5.cpp @@ -13,49 +13,61 @@ using namespace T5; bool ObjWriter::DumpZone(AssetDumpingContext& context) const { -#define DUMP_ASSET_POOL(dumperType, poolName, assetType) \ - if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(assetType)) \ +#define REGISTER_DUMPER(dumperType, poolName) \ + if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(dumperType::AssetType_t::EnumEntry)) \ { \ - dumperType dumper; \ - dumper.DumpPool(context, assetPools->poolName.get()); \ + dumpers.emplace_back(std::make_unique(*assetPools->poolName)); \ } const auto* assetPools = dynamic_cast(context.m_zone.m_pools.get()); + std::vector> dumpers; - // DUMP_ASSET_POOL(AssetDumperPhysPreset, m_phys_preset, ASSET_TYPE_PHYSPRESET) - // 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(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(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) - // 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(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(AssetDumperMenuList, m_menu_list, ASSET_TYPE_MENULIST) - // DUMP_ASSET_POOL(AssetDumperMenuDef, m_menu_def, ASSET_TYPE_MENU) - 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(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) - // DUMP_ASSET_POOL(AssetDumperGlasses, m_glasses, ASSET_TYPE_GLASSES) - // DUMP_ASSET_POOL(AssetDumperEmblemSet, m_emblem_set, ASSET_TYPE_EMBLEMSET) + // REGISTER_DUMPER(AssetDumperPhysPreset, m_phys_preset) + // REGISTER_DUMPER(AssetDumperPhysConstraints, m_phys_constraints) + // REGISTER_DUMPER(AssetDumperDestructibleDef, m_destructible_def) + // REGISTER_DUMPER(AssetDumperXAnimParts, m_xanim_parts) + REGISTER_DUMPER(xmodel::DumperT5, m_xmodel) + REGISTER_DUMPER(material::JsonDumperT5, m_material) + // REGISTER_DUMPER(AssetDumperTechniqueSet, m_technique_set) + REGISTER_DUMPER(image::DumperT5, m_image) + // REGISTER_DUMPER(AssetDumperSndBank, m_sound_bank) + // REGISTER_DUMPER(AssetDumperSndPatch, m_sound_patch) + // REGISTER_DUMPER(AssetDumperClipMap, m_clip_map) + // REGISTER_DUMPER(AssetDumperComWorld, m_com_world) + // REGISTER_DUMPER(AssetDumperGameWorldSp, m_game_world_sp) + // REGISTER_DUMPER(AssetDumperGameWorldMp, m_game_world_mp) + // REGISTER_DUMPER(AssetDumperMapEnts, m_map_ents) + // REGISTER_DUMPER(AssetDumperGfxWorld, m_gfx_world) + // REGISTER_DUMPER(AssetDumperGfxLightDef, m_gfx_light_def) + // REGISTER_DUMPER(AssetDumperFont, m_font) + // REGISTER_DUMPER(AssetDumperMenuList, m_menu_list) + // REGISTER_DUMPER(AssetDumperMenuDef, m_menu_def) + REGISTER_DUMPER(localize::DumperT5, m_localize) + // REGISTER_DUMPER(AssetDumperWeapon, m_weapon) + // REGISTER_DUMPER(AssetDumperSndDriverGlobals, m_snd_driver_globals) + // REGISTER_DUMPER(AssetDumperFxEffectDef, m_fx) + // REGISTER_DUMPER(AssetDumperFxImpactTable, m_fx_impact_table) + REGISTER_DUMPER(raw_file::DumperT5, m_raw_file) + REGISTER_DUMPER(string_table::DumperT5, m_string_table) + // REGISTER_DUMPER(AssetDumperPackIndex, m_pack_index) + // REGISTER_DUMPER(AssetDumperXGlobals, m_xglobals) + // REGISTER_DUMPER(AssetDumperDDLRoot, m_ddl) + // REGISTER_DUMPER(AssetDumperGlasses, m_glasses) + // REGISTER_DUMPER(AssetDumperEmblemSet, m_emblem_set) + + if (context.ShouldTrackProgress()) + { + size_t totalProgress = 0uz; + for (const auto& dumper : dumpers) + totalProgress += dumper->GetProgressTotalCount(); + + context.SetTotalProgress(totalProgress); + } + + for (const auto& dumper : dumpers) + dumper->Dump(context); return true; -#undef DUMP_ASSET_POOL +#undef REGISTER_DUMPER } diff --git a/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.cpp b/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.cpp index a3d41751..ca86c231 100644 --- a/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.cpp +++ b/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.cpp @@ -15,9 +15,9 @@ namespace { constexpr static size_t GSC_MAX_SIZE = 0xC000000; - void DumpGsc(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) + void DumpGsc(AssetDumpingContext& context, const XAssetInfo& asset, std::ostream& stream) { - const auto* rawFile = asset->Asset(); + const auto* rawFile = asset.Asset(); if (rawFile->len <= 8) { @@ -96,15 +96,15 @@ namespace namespace raw_file { - bool DumperT5::ShouldDump(XAssetInfo* asset) + DumperT5::DumperT5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* rawFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* rawFile = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.h b/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.h index 056266ae..46ec5302 100644 --- a/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.h +++ b/src/ObjWriting/Game/T5/RawFile/RawFileDumperT5.h @@ -5,10 +5,12 @@ namespace raw_file { - class DumperT5 final : public AbstractAssetDumper + class DumperT5 final : public AbstractAssetDumper { + public: + explicit DumperT5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace raw_file diff --git a/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.cpp b/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.cpp index 0acc3587..ce6135e9 100644 --- a/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.cpp +++ b/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.cpp @@ -6,15 +6,15 @@ using namespace T5; namespace string_table { - bool DumperT5::ShouldDump(XAssetInfo* asset) + DumperT5::DumperT5(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT5::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* stringTable = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* stringTable = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.h b/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.h index 12ee953b..4d388533 100644 --- a/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.h +++ b/src/ObjWriting/Game/T5/StringTable/StringTableDumperT5.h @@ -5,10 +5,12 @@ namespace string_table { - class DumperT5 final : public AbstractAssetDumper + class DumperT5 final : public AbstractAssetDumper { + public: + explicit DumperT5(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace string_table diff --git a/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.cpp b/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.cpp index 95c6f1cc..77282951 100644 --- a/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.cpp +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.cpp @@ -133,19 +133,19 @@ namespace namespace font_icon { - bool CsvDumperT6::ShouldDump(XAssetInfo* asset) + CsvDumperT6::CsvDumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void CsvDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void CsvDumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; Dumper dumper(*assetFile); - dumper.Dump(*asset->Asset()); + 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 index aeaa8877..ee880ff2 100644 --- a/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.h +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconCsvDumperT6.h @@ -5,10 +5,12 @@ namespace font_icon { - class CsvDumperT6 final : public AbstractAssetDumper + class CsvDumperT6 final : public AbstractAssetDumper { + public: + explicit CsvDumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace font_icon diff --git a/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.cpp b/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.cpp index 9f41412b..b042fd13 100644 --- a/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.cpp +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.cpp @@ -9,12 +9,12 @@ using namespace T6; namespace font_icon { - std::unique_ptr> CreateDumperT6() + std::unique_ptr CreateDumperT6(const AssetPool& pool) { #ifdef DUMP_FONT_ICON_AS_CSV - return std::make_unique(); + return std::make_unique(pool); #else - return std::make_unique(); + return std::make_unique(pool); #endif } } // namespace font_icon diff --git a/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.h b/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.h index 55d048f9..e150b9c2 100644 --- a/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.h +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconDumperT6.h @@ -7,5 +7,5 @@ namespace font_icon { - std::unique_ptr> CreateDumperT6(); + std::unique_ptr CreateDumperT6(const AssetPool& pool); } // namespace font_icon diff --git a/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.cpp b/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.cpp index e82b3b36..f82ef8dc 100644 --- a/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.cpp +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.cpp @@ -78,18 +78,18 @@ namespace namespace font_icon { - bool JsonDumperT6::ShouldDump(XAssetInfo* asset) + JsonDumperT6::JsonDumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void JsonDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void JsonDumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAssetName(asset->m_name)); + const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAssetName(asset.m_name)); if (!assetFile) return; - DumpFontIcon(*assetFile, *asset->Asset()); + 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 index aa1c0017..f25c0761 100644 --- a/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.h +++ b/src/ObjWriting/Game/T6/FontIcon/FontIconJsonDumperT6.h @@ -5,10 +5,12 @@ namespace font_icon { - class JsonDumperT6 final : public AbstractAssetDumper + class JsonDumperT6 final : public AbstractAssetDumper { + public: + explicit JsonDumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace font_icon diff --git a/src/ObjWriting/Game/T6/Image/ImageDumperT6.cpp b/src/ObjWriting/Game/T6/Image/ImageDumperT6.cpp index 02317695..3566f780 100644 --- a/src/ObjWriting/Game/T6/Image/ImageDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Image/ImageDumperT6.cpp @@ -77,7 +77,8 @@ namespace namespace image { - DumperT6::DumperT6() + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { switch (ObjWriting::Configuration.ImageOutputFormat) { @@ -94,19 +95,14 @@ namespace image } } - bool DumperT6::ShouldDump(XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - return true; - } - - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) - { - const auto* image = asset->Asset(); + const auto* image = asset.Asset(); const auto texture = LoadImageData(context.m_obj_search_path, *image); if (!texture) return; - const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name, m_writer->GetFileExtension())); + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset.m_name, m_writer->GetFileExtension())); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/Image/ImageDumperT6.h b/src/ObjWriting/Game/T6/Image/ImageDumperT6.h index 5fac0233..81dc2e93 100644 --- a/src/ObjWriting/Game/T6/Image/ImageDumperT6.h +++ b/src/ObjWriting/Game/T6/Image/ImageDumperT6.h @@ -8,14 +8,13 @@ namespace image { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { public: - DumperT6(); + explicit DumperT6(const AssetPool& pool); protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; private: std::unique_ptr m_writer; diff --git a/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.cpp b/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.cpp index 4a90d7b2..0a68fd4a 100644 --- a/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.cpp @@ -98,19 +98,19 @@ namespace namespace leaderboard { - bool JsonDumperT6::ShouldDump(XAssetInfo* asset) + JsonDumperT6::JsonDumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void JsonDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void JsonDumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAsset(asset->m_name)); + const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAsset(asset.m_name)); if (!assetFile) return; Dumper dumper(*assetFile); - dumper.Dump(*asset->Asset()); + dumper.Dump(*asset.Asset()); } } // namespace leaderboard diff --git a/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.h b/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.h index ebbe4c9f..90db9a73 100644 --- a/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.h +++ b/src/ObjWriting/Game/T6/Leaderboard/LeaderboardJsonDumperT6.h @@ -5,10 +5,12 @@ namespace leaderboard { - class JsonDumperT6 final : public AbstractAssetDumper + class JsonDumperT6 final : public AbstractAssetDumper { + public: + explicit JsonDumperT6(const AssetPool& pool); + protected: - [[nodiscard]] bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace leaderboard diff --git a/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.cpp b/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.cpp index c6281264..5928599b 100644 --- a/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.cpp @@ -11,9 +11,14 @@ using namespace T6; namespace localize { - void DumperT6::DumpPool(AssetDumpingContext& context, AssetPool* pool) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractSingleProgressAssetDumper(pool) { - if (pool->m_asset_lookup.empty()) + } + + void DumperT6::Dump(AssetDumpingContext& context) + { + if (m_pool.m_asset_lookup.empty()) return; const auto language = LocalizeCommon::GetNameOfLanguage(context.m_zone.m_language); @@ -30,7 +35,7 @@ namespace localize stringFileDumper.SetNotes(""); - for (auto* localizeEntry : *pool) + for (const auto* localizeEntry : m_pool) { stringFileDumper.WriteLocalizeEntry(localizeEntry->m_name, localizeEntry->Asset()->value); } @@ -41,5 +46,7 @@ namespace localize { con::error("Could not create string file for dumping localized strings of zone '{}'", context.m_zone.m_name); } + + context.IncrementProgress(); } } // namespace localize diff --git a/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.h b/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.h index 1a39defe..184d7a8b 100644 --- a/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.h +++ b/src/ObjWriting/Game/T6/Localize/LocalizeDumperT6.h @@ -5,9 +5,11 @@ namespace localize { - class DumperT6 final : public IAssetDumper + class DumperT6 final : public AbstractSingleProgressAssetDumper { public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + explicit DumperT6(const AssetPool& pool); + + void Dump(AssetDumpingContext& context) override; }; } // namespace localize diff --git a/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.cpp b/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.cpp index caf8abd5..ad6c87aa 100644 --- a/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.cpp @@ -6,14 +6,14 @@ using namespace T6; namespace map_ents { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* mapEnts = asset->Asset(); + const auto* mapEnts = asset.Asset(); const auto mapEntsFile = context.OpenAssetFile(std::format("{}.ents", mapEnts->name)); diff --git a/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.h b/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.h index 9dbf36bb..76270145 100644 --- a/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.h +++ b/src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.h @@ -5,10 +5,12 @@ namespace map_ents { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace map_ents diff --git a/src/ObjWriting/Game/T6/ObjWriterT6.cpp b/src/ObjWriting/Game/T6/ObjWriterT6.cpp index 06c66520..df9c60ab 100644 --- a/src/ObjWriting/Game/T6/ObjWriterT6.cpp +++ b/src/ObjWriting/Game/T6/ObjWriterT6.cpp @@ -31,71 +31,84 @@ using namespace T6; bool ObjWriter::DumpZone(AssetDumpingContext& context) const { -#define DUMP_ASSET_POOL(dumperType, poolName, assetType) \ - if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(assetType)) \ +#define REGISTER_DUMPER(dumperType, poolName) \ + if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(dumperType::AssetType_t::EnumEntry)) \ { \ - dumperType dumper; \ - dumper.DumpPool(context, assetPools->poolName.get()); \ + dumpers.emplace_back(std::make_unique(*assetPools->poolName)); \ } -#define DUMP_ASSET_POOL_WITH_FACTORY(createDumper, poolName, assetType) \ - if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(assetType)) \ +#define REGISTER_DUMPER_WITH_FACTORY(createDumper, poolName, asset) \ + if (assetPools->poolName && ObjWriting::ShouldHandleAssetType(asset::EnumEntry)) \ { \ - const auto dumper = createDumper; \ - dumper->DumpPool(context, assetPools->poolName.get()); \ + auto dumper = createDumper(*assetPools->poolName); \ + if (dumper) \ + dumpers.emplace_back(std::move(dumper)); \ } const auto* assetPools = dynamic_cast(context.m_zone.m_pools.get()); + std::vector> dumpers; - 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(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(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_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(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(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(script::DumperT6, m_script, ASSET_TYPE_SCRIPTPARSETREE) - // DUMP_ASSET_POOL(AssetDumperKeyValuePairs, m_key_value_pairs, ASSET_TYPE_KEYVALUEPAIRS) - 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(tracer::DumperT6, m_tracer, ASSET_TYPE_TRACER) - // DUMP_ASSET_POOL(AssetDumperSkinnedVertsDef, m_skinned_verts, ASSET_TYPE_SKINNEDVERTS) - 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(z_barrier::DumperT6, m_zbarrier, ASSET_TYPE_ZBARRIER) + REGISTER_DUMPER(phys_preset::InfoStringDumperT6, m_phys_preset) + REGISTER_DUMPER(phys_constraints::InfoStringDumperT6, m_phys_constraints) + // REGISTER_DUMPER(AssetDumperDestructibleDef, m_destructible_def) + // REGISTER_DUMPER(AssetDumperXAnimParts, m_xanim_parts) + REGISTER_DUMPER(xmodel::DumperT6, m_xmodel) + REGISTER_DUMPER(material::JsonDumperT6, m_material) + REGISTER_DUMPER(techset::DumperT6, m_technique_set) + REGISTER_DUMPER(image::DumperT6, m_image) + REGISTER_DUMPER(sound::SndBankDumperT6, m_sound_bank) + // REGISTER_DUMPER(AssetDumperSndPatch, m_sound_patch) + // REGISTER_DUMPER(AssetDumperClipMap, m_clip_map) + // REGISTER_DUMPER(AssetDumperComWorld, m_com_world) + // REGISTER_DUMPER(AssetDumperGameWorldSp, m_game_world_sp) + // REGISTER_DUMPER(AssetDumperGameWorldMp, m_game_world_mp) + REGISTER_DUMPER(map_ents::DumperT6, m_map_ents) + // REGISTER_DUMPER(AssetDumperGfxWorld, m_gfx_world) + // REGISTER_DUMPER(AssetDumperGfxLightDef, m_gfx_light_def) + // REGISTER_DUMPER(AssetDumperFont, m_font) + REGISTER_DUMPER_WITH_FACTORY(font_icon::CreateDumperT6, m_font_icon, AssetFontIcon) + // REGISTER_DUMPER(AssetDumperMenuList, m_menu_list) + // REGISTER_DUMPER(AssetDumperMenuDef, m_menu_def) + REGISTER_DUMPER(localize::DumperT6, m_localize) + REGISTER_DUMPER(weapon::DumperT6, m_weapon) + REGISTER_DUMPER(attachment::DumperT6, m_attachment) + REGISTER_DUMPER(attachment_unique::DumperT6, m_attachment_unique) + REGISTER_DUMPER(camo::JsonDumperT6, m_camo) + REGISTER_DUMPER(sound::SndDriverGlobalsDumperT6, m_snd_driver_globals) + // REGISTER_DUMPER(AssetDumperFxEffectDef, m_fx) + // REGISTER_DUMPER(AssetDumperFxImpactTable, m_fx_impact_table) + REGISTER_DUMPER(raw_file::DumperT6, m_raw_file) + REGISTER_DUMPER(string_table::DumperT6, m_string_table) + REGISTER_DUMPER(leaderboard::JsonDumperT6, m_leaderboard) + // REGISTER_DUMPER(AssetDumperXGlobals, m_xglobals) + // REGISTER_DUMPER(AssetDumperDDLRoot, m_ddl) + // REGISTER_DUMPER(AssetDumperGlasses, m_glasses) + // REGISTER_DUMPER(AssetDumperEmblemSet, m_emblem_set) + REGISTER_DUMPER(script::DumperT6, m_script) + // REGISTER_DUMPER(AssetDumperKeyValuePairs, m_key_value_pairs) + REGISTER_DUMPER(vehicle::DumperT6, m_vehicle) + // REGISTER_DUMPER(AssetDumperMemoryBlock, m_memory_block) + // REGISTER_DUMPER(AssetDumperAddonMapEnts, m_addon_map_ents) + REGISTER_DUMPER(tracer::DumperT6, m_tracer) + // REGISTER_DUMPER(AssetDumperSkinnedVertsDef, m_skinned_verts) + REGISTER_DUMPER(qdb::DumperT6, m_qdb) + REGISTER_DUMPER(slug::DumperT6, m_slug) + // REGISTER_DUMPER(AssetDumperFootstepTableDef, m_footstep_table) + // REGISTER_DUMPER(AssetDumperFootstepFXTableDef, m_footstep_fx_table) + REGISTER_DUMPER(z_barrier::DumperT6, m_zbarrier) + + if (context.ShouldTrackProgress()) + { + size_t totalProgress = 0uz; + for (const auto& dumper : dumpers) + totalProgress += dumper->GetProgressTotalCount(); + + context.SetTotalProgress(totalProgress); + } + + for (const auto& dumper : dumpers) + dumper->Dump(context); return true; -#undef DUMP_ASSET_POOL +#undef REGISTER_DUMPER } diff --git a/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.cpp b/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.cpp index c0c2d7ce..4f6dee21 100644 --- a/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.cpp +++ b/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.cpp @@ -39,20 +39,20 @@ namespace } }; - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { - assert(asset->Asset()->count <= 4); + assert(asset.Asset()->count <= 4); - InfoStringFromPhysConstraintsConverter converter(asset->Asset(), + 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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); @@ -61,24 +61,24 @@ namespace namespace phys_constraints { - bool InfoStringDumperT6::ShouldDump(XAssetInfo* asset) + InfoStringDumperT6::InfoStringDumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void InfoStringDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void InfoStringDumperT6::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.h b/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.h index 4d1c5e70..87ae940e 100644 --- a/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.h +++ b/src/ObjWriting/Game/T6/PhysConstraints/PhysConstraintsInfoStringDumperT6.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T6/T6.h" -#include "InfoString/InfoString.h" namespace phys_constraints { - class InfoStringDumperT6 final : public AbstractAssetDumper + class InfoStringDumperT6 final : public AbstractAssetDumper { + public: + explicit InfoStringDumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace phys_constraints diff --git a/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.cpp b/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.cpp index c4f5dd85..b0948f97 100644 --- a/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.cpp +++ b/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.cpp @@ -59,21 +59,21 @@ namespace physPresetInfo->buoyancyBoxMax = physPreset->buoyancyBoxMax; } - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { auto* physPresetInfo = new PhysPresetInfo; - CopyToPhysPresetInfo(asset->Asset(), 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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); @@ -82,24 +82,24 @@ namespace namespace phys_preset { - bool InfoStringDumperT6::ShouldDump(XAssetInfo* asset) + InfoStringDumperT6::InfoStringDumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void InfoStringDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void InfoStringDumperT6::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.h b/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.h index df52d539..90253b78 100644 --- a/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.h +++ b/src/ObjWriting/Game/T6/PhysPreset/PhysPresetInfoStringDumperT6.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T6/T6.h" -#include "InfoString/InfoString.h" namespace phys_preset { - class InfoStringDumperT6 final : public AbstractAssetDumper + class InfoStringDumperT6 final : public AbstractAssetDumper { + public: + explicit InfoStringDumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace phys_preset diff --git a/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.cpp b/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.cpp index f990de0d..0ad923e0 100644 --- a/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.cpp @@ -4,15 +4,15 @@ using namespace T6; namespace qdb { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* qdb = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* qdb = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.h b/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.h index eb452510..6446d1eb 100644 --- a/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.h +++ b/src/ObjWriting/Game/T6/Qdb/QdbDumperT6.h @@ -5,10 +5,12 @@ namespace qdb { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace qdb diff --git a/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.cpp b/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.cpp index 9388c216..beda25b7 100644 --- a/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.cpp +++ b/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.cpp @@ -14,9 +14,9 @@ namespace { constexpr size_t ANIMTREE_MAX_SIZE = 0xC000000; - void DumpAnimtree(AssetDumpingContext& context, XAssetInfo* asset, std::ostream& stream) + void DumpAnimtree(AssetDumpingContext& context, const XAssetInfo& asset, std::ostream& stream) { - const auto* rawFile = asset->Asset(); + const auto* rawFile = asset.Asset(); if (rawFile->len <= 4) { @@ -77,15 +77,15 @@ namespace namespace raw_file { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* rawFile = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* rawFile = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.h b/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.h index c2faa0f9..a45aa9ac 100644 --- a/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.h +++ b/src/ObjWriting/Game/T6/RawFile/RawFileDumperT6.h @@ -5,10 +5,12 @@ namespace raw_file { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace raw_file diff --git a/src/ObjWriting/Game/T6/Script/ScriptDumperT6.cpp b/src/ObjWriting/Game/T6/Script/ScriptDumperT6.cpp index 2b2be090..dfe974a5 100644 --- a/src/ObjWriting/Game/T6/Script/ScriptDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Script/ScriptDumperT6.cpp @@ -4,15 +4,15 @@ using namespace T6; namespace script { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* scriptParseTree = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* scriptParseTree = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/Script/ScriptDumperT6.h b/src/ObjWriting/Game/T6/Script/ScriptDumperT6.h index 895b55b4..bc1a9aee 100644 --- a/src/ObjWriting/Game/T6/Script/ScriptDumperT6.h +++ b/src/ObjWriting/Game/T6/Script/ScriptDumperT6.h @@ -5,10 +5,12 @@ namespace script { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace script diff --git a/src/ObjWriting/Game/T6/Slug/SlugDumperT6.cpp b/src/ObjWriting/Game/T6/Slug/SlugDumperT6.cpp index d02a11d0..7e71e468 100644 --- a/src/ObjWriting/Game/T6/Slug/SlugDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Slug/SlugDumperT6.cpp @@ -4,15 +4,15 @@ using namespace T6; namespace slug { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* slug = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* slug = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/Slug/SlugDumperT6.h b/src/ObjWriting/Game/T6/Slug/SlugDumperT6.h index 3dd202e5..67bba25d 100644 --- a/src/ObjWriting/Game/T6/Slug/SlugDumperT6.h +++ b/src/ObjWriting/Game/T6/Slug/SlugDumperT6.h @@ -5,10 +5,12 @@ namespace slug { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace slug diff --git a/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.cpp b/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.cpp index aca65ad7..29f0485c 100644 --- a/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.cpp @@ -198,7 +198,7 @@ namespace constexpr auto FORMATTING_RETRIES = 5; - class LoadedSoundBankHashes + class LoadedSoundBankHashes : public IZoneAssetDumperState { public: void Initialize() @@ -901,29 +901,30 @@ namespace *duckFile << duckObj.dump(4) << "\n"; } } - - void DumpSndBank(const AssetDumpingContext& context, const LoadedSoundBankHashes& hashes, const XAssetInfo& sndBankInfo) - { - const auto* sndBank = sndBankInfo.Asset(); - - DumpSndBankAliases(context, hashes, *sndBank); - DumpSoundRadverb(context, *sndBank); - DumpSoundDucks(context, *sndBank); - } } // namespace namespace sound { - void SndBankDumperT6::DumpPool(AssetDumpingContext& context, AssetPool* pool) + SndBankDumperT6::SndBankDumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - LoadedSoundBankHashes soundBankHashes; - soundBankHashes.Initialize(); - for (const auto* assetInfo : *pool) - { - if (!assetInfo->m_name.empty() && assetInfo->m_name[0] == ',') - continue; + } - DumpSndBank(context, soundBankHashes, *assetInfo); - } + void SndBankDumperT6::Dump(AssetDumpingContext& context) + { + auto* hashes = context.GetZoneAssetDumperState(); + hashes->Initialize(); + + AbstractAssetDumper::Dump(context); + } + + void SndBankDumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) + { + const auto* sndBank = asset.Asset(); + const auto* hashes = context.GetZoneAssetDumperState(); + + DumpSndBankAliases(context, *hashes, *sndBank); + DumpSoundRadverb(context, *sndBank); + DumpSoundDucks(context, *sndBank); } } // namespace sound diff --git a/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.h b/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.h index f5d4f59d..616729f1 100644 --- a/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.h +++ b/src/ObjWriting/Game/T6/Sound/SndBankDumperT6.h @@ -5,9 +5,13 @@ namespace sound { - class SndBankDumperT6 final : public IAssetDumper + class SndBankDumperT6 final : public AbstractAssetDumper { public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + explicit SndBankDumperT6(const AssetPool& pool); + void Dump(AssetDumpingContext& context) override; + + protected: + void DumpAsset(AssetDumpingContext& context, const XAssetInfo::Type>& asset) override; }; } // namespace sound diff --git a/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.cpp b/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.cpp index 57649091..0c862429 100644 --- a/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.cpp @@ -108,9 +108,9 @@ namespace { } - void DumpPool(AssetPool* pool) + void DumpPool(const AssetPool& pool) { - for (const auto* assetInfo : *pool) + for (const auto* assetInfo : pool) { if (!assetInfo->m_name.empty() && assetInfo->m_name[0] == ',') continue; @@ -385,9 +385,16 @@ namespace namespace sound { - void SndDriverGlobalsDumperT6::DumpPool(AssetDumpingContext& context, AssetPool* pool) + SndDriverGlobalsDumperT6::SndDriverGlobalsDumperT6(const AssetPool& pool) + : AbstractSingleProgressAssetDumper(pool) + { + } + + void SndDriverGlobalsDumperT6::Dump(AssetDumpingContext& context) { Internal internal(context); - internal.DumpPool(pool); + internal.DumpPool(m_pool); + + context.IncrementProgress(); } } // namespace sound diff --git a/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.h b/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.h index 042de43c..9ce2f55f 100644 --- a/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.h +++ b/src/ObjWriting/Game/T6/Sound/SndDriverGlobalsDumperT6.h @@ -5,9 +5,11 @@ namespace sound { - class SndDriverGlobalsDumperT6 final : public IAssetDumper + class SndDriverGlobalsDumperT6 final : public AbstractSingleProgressAssetDumper { public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + explicit SndDriverGlobalsDumperT6(const AssetPool& pool); + + void Dump(AssetDumpingContext& context) override; }; } // namespace sound diff --git a/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.cpp b/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.cpp index 4bd96479..840b0f74 100644 --- a/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.cpp +++ b/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.cpp @@ -6,15 +6,15 @@ using namespace T6; namespace string_table { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* stringTable = asset->Asset(); - const auto assetFile = context.OpenAssetFile(asset->m_name); + const auto* stringTable = asset.Asset(); + const auto assetFile = context.OpenAssetFile(asset.m_name); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.h b/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.h index b61a4380..7e6256cb 100644 --- a/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.h +++ b/src/ObjWriting/Game/T6/StringTable/StringTableDumperT6.h @@ -5,10 +5,12 @@ namespace string_table { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace string_table diff --git a/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.cpp b/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.cpp index 4bb9da3c..8285281e 100644 --- a/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.cpp @@ -79,14 +79,14 @@ namespace namespace techset { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* techniqueSet = asset->Asset(); + const auto* techniqueSet = asset.Asset(); auto* shaderState = context.GetZoneAssetDumperState(); const auto assetFile = context.OpenAssetFile(std::format("techniquesets/{}.json", techniqueSet->name)); diff --git a/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.h b/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.h index 59a48f3f..c4093001 100644 --- a/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.h +++ b/src/ObjWriting/Game/T6/Techset/TechsetDumperT6.h @@ -5,10 +5,12 @@ namespace techset { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace techset diff --git a/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.cpp b/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.cpp index 854a45af..2767db5c 100644 --- a/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.cpp @@ -40,18 +40,18 @@ namespace } }; - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { - InfoStringFromTracerConverter converter(asset->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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); @@ -60,24 +60,24 @@ namespace namespace tracer { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.h b/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.h index 0bfb6d75..08b62c86 100644 --- a/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.h +++ b/src/ObjWriting/Game/T6/Tracer/TracerDumperT6.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T6/T6.h" -#include "InfoString/InfoString.h" namespace tracer { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace tracer diff --git a/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.cpp b/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.cpp index 1178b394..ec71d6e0 100644 --- a/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.cpp @@ -91,18 +91,18 @@ namespace } }; - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { - InfoStringFromVehicleConverter converter(asset->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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); @@ -111,24 +111,24 @@ namespace namespace vehicle { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.h b/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.h index 5d328e87..58b64757 100644 --- a/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.h +++ b/src/ObjWriting/Game/T6/Vehicle/VehicleDumperT6.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T6/T6.h" -#include "InfoString/InfoString.h" namespace vehicle { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace vehicle diff --git a/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.cpp b/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.cpp index d7fcc4c7..d7649885 100644 --- a/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.cpp @@ -47,18 +47,18 @@ namespace } }; - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { - InfoStringFromAttachmentConverter converter(asset->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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); @@ -67,24 +67,24 @@ namespace namespace attachment { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetInfoStringFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.h b/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.h index 0472e2a0..ad9aaee9 100644 --- a/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.h +++ b/src/ObjWriting/Game/T6/Weapon/AttachmentDumperT6.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T6/T6.h" -#include "InfoString/InfoString.h" namespace attachment { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace attachment diff --git a/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.cpp b/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.cpp index c10605ac..e61a2567 100644 --- a/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.cpp @@ -107,22 +107,22 @@ namespace } } - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { const auto fullDef = std::make_unique(); memset(fullDef.get(), 0, sizeof(WeaponAttachmentUniqueFull)); - CopyToFullDef(asset->Asset(), fullDef.get()); + 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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); @@ -131,24 +131,24 @@ namespace namespace attachment_unique { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.h b/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.h index b3e2ae1a..65da39bc 100644 --- a/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.h +++ b/src/ObjWriting/Game/T6/Weapon/AttachmentUniqueDumperT6.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T6/T6.h" -#include "InfoString/InfoString.h" namespace attachment_unique { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace attachment_unique diff --git a/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.cpp b/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.cpp index 4a68492e..e7fda73e 100644 --- a/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.cpp @@ -104,20 +104,20 @@ namespace namespace camo { - bool JsonDumperT6::ShouldDump(XAssetInfo* asset) + JsonDumperT6::JsonDumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void JsonDumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void JsonDumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto fileName = GetJsonFileNameForAssetName(asset->m_name); + const auto fileName = GetJsonFileNameForAssetName(asset.m_name); const auto assetFile = context.OpenAssetFile(fileName); if (!assetFile) return; const JsonDumperImpl dumper(*assetFile); - dumper.Dump(asset->Asset()); + dumper.Dump(asset.Asset()); } } // namespace camo diff --git a/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.h b/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.h index 8bb91f12..7bf2d45b 100644 --- a/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.h +++ b/src/ObjWriting/Game/T6/Weapon/CamoJsonDumperT6.h @@ -5,10 +5,12 @@ namespace camo { - class JsonDumperT6 final : public AbstractAssetDumper + class JsonDumperT6 final : public AbstractAssetDumper { + public: + explicit JsonDumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace camo diff --git a/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp b/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp index 96c5bcdc..4c7c991d 100644 --- a/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp @@ -409,31 +409,31 @@ namespace } } - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { const auto fullDef = std::make_unique(); memset(fullDef.get(), 0, sizeof(WeaponFullDef)); - CopyToFullDef(asset->Asset(), fullDef.get()); + 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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); } - void DumpAccuracyGraphs(AssetDumpingContext& context, XAssetInfo* asset) + void DumpAccuracyGraphs(AssetDumpingContext& context, const XAssetInfo& asset) { auto* accuracyGraphWriter = context.GetZoneAssetDumperState(); - const auto weapon = asset->Asset(); + const auto weapon = asset.Asset(); const auto* weapDef = weapon->weapDef; if (!weapDef) @@ -461,24 +461,24 @@ namespace namespace weapon { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.h b/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.h index 4b3b500f..18fddd9d 100644 --- a/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.h +++ b/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.h @@ -5,10 +5,12 @@ namespace weapon { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace weapon diff --git a/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.cpp b/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.cpp index 1a7edb80..f9e56c21 100644 --- a/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.cpp +++ b/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.cpp @@ -30,18 +30,18 @@ namespace } }; - InfoString CreateInfoString(XAssetInfo* asset) + InfoString CreateInfoString(const XAssetInfo& asset) { - InfoStringFromZBarrierConverter converter(asset->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()) + 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 asset.m_zone->m_script_strings[scrStr]; }); return converter.Convert(); @@ -50,24 +50,24 @@ namespace namespace z_barrier { - bool DumperT6::ShouldDump(XAssetInfo* asset) + DumperT6::DumperT6(const AssetPool& pool) + : AbstractAssetDumper(pool) { - return true; } - void DumperT6::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void DumperT6::DumpAsset(AssetDumpingContext& context, const 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); + 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)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; diff --git a/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.h b/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.h index 9e60f414..a16c3705 100644 --- a/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.h +++ b/src/ObjWriting/Game/T6/ZBarrier/ZBarrierDumperT6.h @@ -2,14 +2,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T6/T6.h" -#include "InfoString/InfoString.h" namespace z_barrier { - class DumperT6 final : public AbstractAssetDumper + class DumperT6 final : public AbstractAssetDumper { + public: + explicit DumperT6(const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace z_barrier diff --git a/src/ObjWriting/Material/MaterialJsonDumper.cpp.template b/src/ObjWriting/Material/MaterialJsonDumper.cpp.template index 7eb8ad51..c62ddc9f 100644 --- a/src/ObjWriting/Material/MaterialJsonDumper.cpp.template +++ b/src/ObjWriting/Material/MaterialJsonDumper.cpp.template @@ -361,27 +361,27 @@ namespace namespace material { - void CLASS_NAME::DumpPool(AssetDumpingContext& context, AssetPool* pool) + CLASS_NAME::CLASS_NAME (const AssetPool& pool) + : AbstractAssetDumper(pool) + { + } + + void CLASS_NAME::Dump(AssetDumpingContext& context) { auto* materialConstantState = context.GetZoneAssetDumperState(); materialConstantState->ExtractNamesFromZone(); - AbstractAssetDumper::DumpPool(context, pool); + AbstractAssetDumper::Dump(context); } - bool CLASS_NAME::ShouldDump(XAssetInfo* asset) + void CLASS_NAME::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { - return true; - } - - void CLASS_NAME::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) - { - const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset->m_name)); + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); if (!assetFile) return; - const auto* material = asset->Asset(); + const auto* material = asset.Asset(); #if defined(FEATURE_T5) assert(material->info.gameFlags < 0x400); assert(material->maxStreamedMips == 0); diff --git a/src/ObjWriting/Material/MaterialJsonDumper.h.template b/src/ObjWriting/Material/MaterialJsonDumper.h.template index f8a41515..c58bd3f4 100644 --- a/src/ObjWriting/Material/MaterialJsonDumper.h.template +++ b/src/ObjWriting/Material/MaterialJsonDumper.h.template @@ -17,13 +17,13 @@ namespace material { - class CLASS_NAME final : public AbstractAssetDumper + class CLASS_NAME final : public AbstractAssetDumper { public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + explicit CLASS_NAME (const AssetPool& pool); + void Dump(AssetDumpingContext& context) override; protected: - [[nodiscard]] bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace material diff --git a/src/ObjWriting/XModel/XModelDumper.cpp.template b/src/ObjWriting/XModel/XModelDumper.cpp.template index 4a569a56..c3bb23c8 100644 --- a/src/ObjWriting/XModel/XModelDumper.cpp.template +++ b/src/ObjWriting/XModel/XModelDumper.cpp.template @@ -560,9 +560,9 @@ namespace } } - void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset) + void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* model = asset->Asset(); + const auto* model = asset.Asset(); const auto mtlFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); if (!mtlFile) @@ -575,9 +575,9 @@ namespace writer->Write(common); } - void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) + void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo& asset, const unsigned lod) { - const auto* model = asset->Asset(); + const auto* model = asset.Asset(); const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".obj")); if (!assetFile) @@ -591,9 +591,9 @@ namespace writer->Write(common); } - void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) + void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo& asset, const unsigned lod) { - const auto* model = asset->Asset(); + const auto* model = asset.Asset(); const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".xmodel_export")); if (!assetFile) @@ -604,9 +604,9 @@ namespace writer->Write(common); } - void DumpXModelBinLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) + void DumpXModelBinLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo& asset, const unsigned lod) { - const auto* model = asset->Asset(); + const auto* model = asset.Asset(); const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".xmodel_bin")); if (!assetFile) @@ -619,9 +619,9 @@ namespace template void DumpGltfLod( - const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod, const std::string& extension) + const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo& asset, const unsigned lod, const std::string& extension) { - const auto* model = asset->Asset(); + const auto* model = asset.Asset(); const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, extension)); if (!assetFile) @@ -634,14 +634,14 @@ namespace writer->Write(common); } - void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) + void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo& asset) { - const auto* model = asset->Asset(); + const auto* model = asset.Asset(); for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) { XModelCommon common; - PopulateXModelWriter(common, context, currentLod, asset->Asset()); + PopulateXModelWriter(common, context, currentLod, asset.Asset()); switch (ObjWriting::Configuration.ModelOutputFormat) { @@ -841,25 +841,25 @@ namespace std::ostream& m_stream; }; - void DumpXModelJson(AssetDumpingContext& context, XAssetInfo* asset) + void DumpXModelJson(AssetDumpingContext& context, const XAssetInfo& asset) { - const auto assetFile = context.OpenAssetFile(xmodel::GetJsonFileNameForAssetName(asset->m_name)); + const auto assetFile = context.OpenAssetFile(xmodel::GetJsonFileNameForAssetName(asset.m_name)); if (!assetFile) return; const JsonDumper dumper(context, *assetFile); - dumper.Dump(asset->Asset()); + dumper.Dump(asset.Asset()); } } // namespace namespace xmodel { - bool CLASS_NAME::ShouldDump(XAssetInfo* asset) + CLASS_NAME::CLASS_NAME (const AssetPool& pool) + : AbstractAssetDumper(pool) { - return !asset->m_name.empty() && asset->m_name[0] != ','; } - void CLASS_NAME::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) + void CLASS_NAME::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) { DumpXModelJson(context, asset); DumpXModelSurfs(context, asset); diff --git a/src/ObjWriting/XModel/XModelDumper.h.template b/src/ObjWriting/XModel/XModelDumper.h.template index 5483e209..8dce3422 100644 --- a/src/ObjWriting/XModel/XModelDumper.h.template +++ b/src/ObjWriting/XModel/XModelDumper.h.template @@ -17,10 +17,12 @@ namespace xmodel { - class CLASS_NAME final : public AbstractAssetDumper + class CLASS_NAME final : public AbstractAssetDumper { + public: + explicit CLASS_NAME (const AssetPool& pool); + protected: - bool ShouldDump(XAssetInfo* asset) override; - void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; }; } // namespace xmodel diff --git a/src/Unlinker/Unlinker.cpp b/src/Unlinker/Unlinker.cpp index 977c1ad3..e1f6ee76 100644 --- a/src/Unlinker/Unlinker.cpp +++ b/src/Unlinker/Unlinker.cpp @@ -1,19 +1,17 @@ #include "Unlinker.h" #include "ContentLister/ContentPrinter.h" -#include "ContentLister/ZoneDefWriter.h" #include "IObjLoader.h" #include "IObjWriter.h" #include "ObjWriting.h" #include "SearchPath/IWD.h" #include "SearchPath/OutputPathFilesystem.h" -#include "SearchPath/SearchPathFilesystem.h" -#include "SearchPath/SearchPaths.h" #include "UnlinkerArgs.h" #include "UnlinkerPaths.h" #include "Utils/ClassUtils.h" #include "Utils/Logging/Log.h" #include "Utils/ObjFileStream.h" +#include "Zone/Definition/ZoneDefWriter.h" #include "ZoneLoading.h" #include @@ -21,7 +19,6 @@ #include #include #include -#include namespace fs = std::filesystem; @@ -54,12 +51,12 @@ public: } private: - _NODISCARD bool ShouldLoadObj() const + [[nodiscard]] bool ShouldLoadObj() const { return m_args.m_task != UnlinkerArgs::ProcessingTask::LIST && !m_args.m_skip_obj; } - bool WriteZoneDefinitionFile(const Zone& zone, const fs::path& zoneDefinitionFileFolder) const + [[nodiscard]] bool WriteZoneDefinitionFile(const Zone& zone, const fs::path& zoneDefinitionFileFolder) const { auto zoneDefinitionFilePath(zoneDefinitionFileFolder); zoneDefinitionFilePath.append(zone.m_name); @@ -73,7 +70,7 @@ private: } const auto* zoneDefWriter = IZoneDefWriter::GetZoneDefWriterForGame(zone.m_game_id); - zoneDefWriter->WriteZoneDef(zoneDefinitionFile, m_args, zone); + zoneDefWriter->WriteZoneDef(zoneDefinitionFile, zone, m_args.m_use_gdt); zoneDefinitionFile.close(); @@ -179,7 +176,7 @@ private: return false; OutputPathFilesystem outputFolderOutputPath(outputFolderPath); - AssetDumpingContext context(zone, outputFolderPathStr, outputFolderOutputPath, searchPath); + AssetDumpingContext context(zone, outputFolderPathStr, outputFolderOutputPath, searchPath, std::nullopt); std::ofstream gdtStream; if (m_args.m_use_gdt) @@ -230,13 +227,15 @@ private: auto absoluteZoneDirectory = absolute(std::filesystem::path(zonePath).remove_filename()).string(); auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory); - auto zone = ZoneLoading::LoadZone(zonePath); - if (zone == nullptr) + auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt); + if (!maybeZone) { - con::error("Failed to load zone \"{}\".", zonePath); + con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error()); return false; } + auto zone = std::move(*maybeZone); + con::debug("Loaded zone \"{}\"", zone->m_name); if (ShouldLoadObj()) @@ -290,16 +289,16 @@ private: auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory); - std::string zoneName; - auto zone = ZoneLoading::LoadZone(zonePath); - if (zone == nullptr) + auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt); + if (!maybeZone) { - con::error("Failed to load zone \"{}\".", zonePath); + con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error()); return false; } - zoneName = zone->m_name; - con::debug("Loaded zone \"{}\"", zoneName); + auto zone = std::move(*maybeZone); + + con::debug("Loaded zone \"{}\"", zone->m_name); const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game_id); if (ShouldLoadObj()) @@ -311,6 +310,8 @@ private: if (ShouldLoadObj()) objLoader->UnloadContainersOfZone(*zone); + // Copy zone name for using it after freeing the zone + std::string zoneName = zone->m_name; zone.reset(); con::debug("Unloaded zone \"{}\"", zoneName); } diff --git a/src/Utils/Utils/Result.h b/src/Utils/Utils/Result.h new file mode 100644 index 00000000..b5d870c0 --- /dev/null +++ b/src/Utils/Utils/Result.h @@ -0,0 +1,185 @@ +#pragma once + +#include +#include + +using NoResult = std::monostate; + +// Can be replaced by std::expected with c++23 +namespace result +{ + template class Unexpected + { + public: + Unexpected(TError result) + : m_data(std::move(result)) + { + } + + constexpr std::add_lvalue_reference_t value() & + { + return m_data; + } + + constexpr std::add_const_t> value() const& + { + return m_data; + } + + constexpr std::add_rvalue_reference_t value() && + { + return std::move(m_data); + } + + constexpr std::add_const_t> value() const&& + { + return std::move(m_data); + } + + constexpr std::add_lvalue_reference_t operator*() & + { + return m_data; + } + + constexpr std::add_const_t> operator*() const& + { + return m_data; + } + + constexpr std::add_rvalue_reference_t operator*() && + { + return std::move(m_data); + } + + constexpr std::add_const_t> operator*() const&& + { + return std::move(m_data); + } + + constexpr std::add_pointer_t operator->() + { + return m_data; + } + + constexpr std::add_const_t> operator->() const + { + return m_data; + } + + private: + TError m_data; + }; + + template class Expected + { + public: + Expected(TResult result) + : m_data(std::variant(std::in_place_index<0>, std::move(result))) + { + } + + Expected(Unexpected unexpected) + : m_data(std::variant(std::in_place_index<1>, std::move(*unexpected))) + { + } + + constexpr operator bool() const noexcept + { + return m_data.index() == 0; + } + + constexpr bool has_value() const noexcept + { + return m_data.index() == 0; + } + + constexpr std::add_lvalue_reference_t value() & + { + return std::get<0>(m_data); + } + + constexpr std::add_const_t> value() const& + { + return std::get<0>(m_data); + } + + constexpr std::add_rvalue_reference_t value() && + { + return std::move(std::get<0>(m_data)); + } + + constexpr std::add_const_t> value() const&& + { + return std::move(std::get<0>(m_data)); + } + + constexpr std::add_lvalue_reference_t operator*() & + { + return std::get<0>(m_data); + } + + constexpr std::add_const_t> operator*() const& + { + return std::get<0>(m_data); + } + + constexpr std::add_rvalue_reference_t operator*() && + { + return std::move(std::get<0>(m_data)); + } + + constexpr std::add_const_t> operator*() const&& + { + return std::move(std::get<0>(m_data)); + } + + constexpr std::add_pointer_t operator->() + { + return std::get<0>(m_data); + } + + constexpr std::add_const_t> operator->() const + { + return std::get<0>(m_data); + } + + constexpr std::add_lvalue_reference_t error() & + { + return std::get<1>(m_data); + } + + constexpr std::add_const_t> error() const& + { + return std::get<1>(m_data); + } + + constexpr std::add_rvalue_reference_t error() && + { + return std::move(std::get<1>(m_data)); + } + + constexpr std::add_const_t> error() const&& + { + return std::move(std::get<1>(m_data)); + } + + private: + explicit Expected(std::variant data) + : m_data(std::move(data)) + { + } + + std::variant m_data; + }; + +#define ENSURE_RESULT_VAR(var) \ + if (!(var)) \ + return (var); +#define ENSURE_RESULT(expr) \ + { \ + const auto result = (expr); \ + if (!result) \ + return result; \ + } + +} // namespace result diff --git a/src/Unlinker/Game/IW3/ZoneDefWriterIW3.cpp b/src/ZoneCommon/Game/IW3/Zone/Definition/ZoneDefWriterIW3.cpp similarity index 88% rename from src/Unlinker/Game/IW3/ZoneDefWriterIW3.cpp rename to src/ZoneCommon/Game/IW3/Zone/Definition/ZoneDefWriterIW3.cpp index 3850cba7..a98caa82 100644 --- a/src/Unlinker/Game/IW3/ZoneDefWriterIW3.cpp +++ b/src/ZoneCommon/Game/IW3/Zone/Definition/ZoneDefWriterIW3.cpp @@ -6,9 +6,9 @@ using namespace IW3; -void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const {} +void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const {} -void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const +void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const { const auto* pools = dynamic_cast(zone.m_pools.get()); diff --git a/src/Unlinker/Game/IW3/ZoneDefWriterIW3.h b/src/ZoneCommon/Game/IW3/Zone/Definition/ZoneDefWriterIW3.h similarity index 62% rename from src/Unlinker/Game/IW3/ZoneDefWriterIW3.h rename to src/ZoneCommon/Game/IW3/Zone/Definition/ZoneDefWriterIW3.h index 3f378d9d..8bfe8f61 100644 --- a/src/Unlinker/Game/IW3/ZoneDefWriterIW3.h +++ b/src/ZoneCommon/Game/IW3/Zone/Definition/ZoneDefWriterIW3.h @@ -1,13 +1,13 @@ #pragma once -#include "ContentLister/ZoneDefWriter.h" +#include "Zone/Definition/ZoneDefWriter.h" namespace IW3 { class ZoneDefWriter final : public AbstractZoneDefWriter { protected: - void WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const override; - void WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const override; + void WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const override; + void WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const override; }; } // namespace IW3 diff --git a/src/Unlinker/Game/IW4/ZoneDefWriterIW4.cpp b/src/ZoneCommon/Game/IW4/Zone/Definition/ZoneDefWriterIW4.cpp similarity index 88% rename from src/Unlinker/Game/IW4/ZoneDefWriterIW4.cpp rename to src/ZoneCommon/Game/IW4/Zone/Definition/ZoneDefWriterIW4.cpp index 7e100945..6bd9cc93 100644 --- a/src/Unlinker/Game/IW4/ZoneDefWriterIW4.cpp +++ b/src/ZoneCommon/Game/IW4/Zone/Definition/ZoneDefWriterIW4.cpp @@ -6,9 +6,9 @@ using namespace IW4; -void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const {} +void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const {} -void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const +void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const { const auto* pools = dynamic_cast(zone.m_pools.get()); diff --git a/src/Unlinker/Game/IW4/ZoneDefWriterIW4.h b/src/ZoneCommon/Game/IW4/Zone/Definition/ZoneDefWriterIW4.h similarity index 62% rename from src/Unlinker/Game/IW4/ZoneDefWriterIW4.h rename to src/ZoneCommon/Game/IW4/Zone/Definition/ZoneDefWriterIW4.h index 83bf1642..e2f3bedc 100644 --- a/src/Unlinker/Game/IW4/ZoneDefWriterIW4.h +++ b/src/ZoneCommon/Game/IW4/Zone/Definition/ZoneDefWriterIW4.h @@ -1,13 +1,13 @@ #pragma once -#include "ContentLister/ZoneDefWriter.h" +#include "Zone/Definition/ZoneDefWriter.h" namespace IW4 { class ZoneDefWriter final : public AbstractZoneDefWriter { protected: - void WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const override; - void WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const override; + void WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const override; + void WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const override; }; } // namespace IW4 diff --git a/src/Unlinker/Game/IW5/ZoneDefWriterIW5.cpp b/src/ZoneCommon/Game/IW5/Zone/Definition/ZoneDefWriterIW5.cpp similarity index 88% rename from src/Unlinker/Game/IW5/ZoneDefWriterIW5.cpp rename to src/ZoneCommon/Game/IW5/Zone/Definition/ZoneDefWriterIW5.cpp index ff54d622..4678dc85 100644 --- a/src/Unlinker/Game/IW5/ZoneDefWriterIW5.cpp +++ b/src/ZoneCommon/Game/IW5/Zone/Definition/ZoneDefWriterIW5.cpp @@ -6,9 +6,9 @@ using namespace IW5; -void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const {} +void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const {} -void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const +void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const { const auto* pools = dynamic_cast(zone.m_pools.get()); diff --git a/src/Unlinker/Game/IW5/ZoneDefWriterIW5.h b/src/ZoneCommon/Game/IW5/Zone/Definition/ZoneDefWriterIW5.h similarity index 62% rename from src/Unlinker/Game/IW5/ZoneDefWriterIW5.h rename to src/ZoneCommon/Game/IW5/Zone/Definition/ZoneDefWriterIW5.h index fac80d00..29f75f75 100644 --- a/src/Unlinker/Game/IW5/ZoneDefWriterIW5.h +++ b/src/ZoneCommon/Game/IW5/Zone/Definition/ZoneDefWriterIW5.h @@ -1,13 +1,13 @@ #pragma once -#include "ContentLister/ZoneDefWriter.h" +#include "Zone/Definition/ZoneDefWriter.h" namespace IW5 { class ZoneDefWriter final : public AbstractZoneDefWriter { protected: - void WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const override; - void WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const override; + void WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const override; + void WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const override; }; } // namespace IW5 diff --git a/src/Unlinker/Game/T5/ZoneDefWriterT5.cpp b/src/ZoneCommon/Game/T5/Zone/Definition/ZoneDefWriterT5.cpp similarity index 88% rename from src/Unlinker/Game/T5/ZoneDefWriterT5.cpp rename to src/ZoneCommon/Game/T5/Zone/Definition/ZoneDefWriterT5.cpp index 5fa7774a..976f8798 100644 --- a/src/Unlinker/Game/T5/ZoneDefWriterT5.cpp +++ b/src/ZoneCommon/Game/T5/Zone/Definition/ZoneDefWriterT5.cpp @@ -6,9 +6,9 @@ using namespace T5; -void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const {} +void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const {} -void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const +void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const { const auto* pools = dynamic_cast(zone.m_pools.get()); diff --git a/src/Unlinker/Game/T5/ZoneDefWriterT5.h b/src/ZoneCommon/Game/T5/Zone/Definition/ZoneDefWriterT5.h similarity index 62% rename from src/Unlinker/Game/T5/ZoneDefWriterT5.h rename to src/ZoneCommon/Game/T5/Zone/Definition/ZoneDefWriterT5.h index 8b206436..7dfb85b4 100644 --- a/src/Unlinker/Game/T5/ZoneDefWriterT5.h +++ b/src/ZoneCommon/Game/T5/Zone/Definition/ZoneDefWriterT5.h @@ -1,13 +1,13 @@ #pragma once -#include "ContentLister/ZoneDefWriter.h" +#include "Zone/Definition/ZoneDefWriter.h" namespace T5 { class ZoneDefWriter final : public AbstractZoneDefWriter { protected: - void WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const override; - void WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const override; + void WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const override; + void WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const override; }; } // namespace T5 diff --git a/src/Unlinker/Game/T6/ZoneDefWriterT6.cpp b/src/ZoneCommon/Game/T6/Zone/Definition/ZoneDefWriterT6.cpp similarity index 96% rename from src/Unlinker/Game/T6/ZoneDefWriterT6.cpp rename to src/ZoneCommon/Game/T6/Zone/Definition/ZoneDefWriterT6.cpp index 5b50ed8d..4b59ebcc 100644 --- a/src/Unlinker/Game/T6/ZoneDefWriterT6.cpp +++ b/src/ZoneCommon/Game/T6/Zone/Definition/ZoneDefWriterT6.cpp @@ -48,7 +48,7 @@ namespace } } // namespace -void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const +void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const { const auto* assetPoolT6 = dynamic_cast(zone.m_pools.get()); if (assetPoolT6 && !assetPoolT6->m_key_value_pairs->m_asset_lookup.empty()) @@ -64,7 +64,7 @@ void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, const Unli } } -void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const +void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const { const auto* pools = dynamic_cast(zone.m_pools.get()); diff --git a/src/Unlinker/Game/T6/ZoneDefWriterT6.h b/src/ZoneCommon/Game/T6/Zone/Definition/ZoneDefWriterT6.h similarity index 62% rename from src/Unlinker/Game/T6/ZoneDefWriterT6.h rename to src/ZoneCommon/Game/T6/Zone/Definition/ZoneDefWriterT6.h index 5af9672c..842a7dd9 100644 --- a/src/Unlinker/Game/T6/ZoneDefWriterT6.h +++ b/src/ZoneCommon/Game/T6/Zone/Definition/ZoneDefWriterT6.h @@ -1,13 +1,13 @@ #pragma once -#include "ContentLister/ZoneDefWriter.h" +#include "Zone/Definition/ZoneDefWriter.h" namespace T6 { class ZoneDefWriter final : public AbstractZoneDefWriter { protected: - void WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const override; - void WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const override; + void WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const override; + void WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const override; }; } // namespace T6 diff --git a/src/ZoneCommon/Pool/AssetPool.h b/src/ZoneCommon/Pool/AssetPool.h index 7b4794a2..f22c71cd 100644 --- a/src/ZoneCommon/Pool/AssetPool.h +++ b/src/ZoneCommon/Pool/AssetPool.h @@ -41,6 +41,32 @@ public: } }; + class CIterator + { + typename std::map*>::const_iterator m_iterator; + + public: + explicit CIterator(typename std::map*>::const_iterator i) + { + m_iterator = i; + } + + bool operator!=(CIterator rhs) + { + return m_iterator != rhs.m_iterator; + } + + const XAssetInfo* operator*() + { + return m_iterator.operator*().second; + } + + void operator++() + { + ++m_iterator; + } + }; + AssetPool() { m_asset_lookup = std::map*>(); @@ -70,4 +96,14 @@ public: { return Iterator(m_asset_lookup.end()); } + + CIterator begin() const + { + return CIterator(m_asset_lookup.cbegin()); + } + + CIterator end() const + { + return CIterator(m_asset_lookup.cend()); + } }; diff --git a/src/ZoneCommon/Pool/XAssetInfo.cpp b/src/ZoneCommon/Pool/XAssetInfo.cpp index c9d94955..7ed5213e 100644 --- a/src/ZoneCommon/Pool/XAssetInfo.cpp +++ b/src/ZoneCommon/Pool/XAssetInfo.cpp @@ -98,6 +98,11 @@ bool XAssetInfoGeneric::IsReference() const return !m_name.empty() && m_name[0] == ','; } +std::string XAssetInfoGeneric::ReferencedAssetName() const +{ + return m_name.substr(1); +} + std::string XAssetInfoGeneric::NormalizeAssetName(std::string input) { utils::MakeStringLowerCase(input); diff --git a/src/ZoneCommon/Pool/XAssetInfo.h b/src/ZoneCommon/Pool/XAssetInfo.h index 16a3edbb..30946927 100644 --- a/src/ZoneCommon/Pool/XAssetInfo.h +++ b/src/ZoneCommon/Pool/XAssetInfo.h @@ -55,6 +55,7 @@ public: XAssetInfoGeneric& operator=(XAssetInfoGeneric&& other) noexcept = default; [[nodiscard]] bool IsReference() const; + [[nodiscard]] std::string ReferencedAssetName() const; static std::string NormalizeAssetName(std::string input); diff --git a/src/ZoneCommon/Pool/ZoneAssetPools.h b/src/ZoneCommon/Pool/ZoneAssetPools.h index 97c94ff9..824498dd 100644 --- a/src/ZoneCommon/Pool/ZoneAssetPools.h +++ b/src/ZoneCommon/Pool/ZoneAssetPools.h @@ -1,5 +1,5 @@ #pragma once -#include "Utils/ClassUtils.h" + #include "XAssetInfo.h" #include "Zone/Zone.h" #include "Zone/ZoneTypes.h" @@ -33,16 +33,16 @@ public: std::vector dependencies, std::vector usedScriptStrings, std::vector indirectAssetReferences); - _NODISCARD virtual XAssetInfoGeneric* GetAsset(asset_type_t type, const std::string& name) const = 0; - _NODISCARD virtual XAssetInfoGeneric* GetAssetOrAssetReference(asset_type_t type, const std::string& name) const; + [[nodiscard]] virtual XAssetInfoGeneric* GetAsset(asset_type_t type, const std::string& name) const = 0; + [[nodiscard]] virtual XAssetInfoGeneric* GetAssetOrAssetReference(asset_type_t type, const std::string& name) const; - _NODISCARD virtual asset_type_t GetAssetTypeCount() const = 0; - _NODISCARD virtual std::optional GetAssetTypeName(asset_type_t assetType) const = 0; + [[nodiscard]] virtual asset_type_t GetAssetTypeCount() const = 0; + [[nodiscard]] virtual std::optional GetAssetTypeName(asset_type_t assetType) const = 0; - _NODISCARD size_t GetTotalAssetCount() const; + [[nodiscard]] size_t GetTotalAssetCount() const; - _NODISCARD iterator begin() const; - _NODISCARD iterator end() const; + [[nodiscard]] iterator begin() const; + [[nodiscard]] iterator end() const; static std::unique_ptr CreateForGame(GameId game, Zone* zone, zone_priority_t priority); diff --git a/src/Unlinker/ContentLister/ZoneDefWriter.cpp b/src/ZoneCommon/Zone/Definition/ZoneDefWriter.cpp similarity index 72% rename from src/Unlinker/ContentLister/ZoneDefWriter.cpp rename to src/ZoneCommon/Zone/Definition/ZoneDefWriter.cpp index 367199e5..2c032afe 100644 --- a/src/Unlinker/ContentLister/ZoneDefWriter.cpp +++ b/src/ZoneCommon/Zone/Definition/ZoneDefWriter.cpp @@ -1,10 +1,10 @@ #include "ZoneDefWriter.h" -#include "Game/IW3/ZoneDefWriterIW3.h" -#include "Game/IW4/ZoneDefWriterIW4.h" -#include "Game/IW5/ZoneDefWriterIW5.h" -#include "Game/T5/ZoneDefWriterT5.h" -#include "Game/T6/ZoneDefWriterT6.h" +#include "Game/IW3/Zone/Definition/ZoneDefWriterIW3.h" +#include "Game/IW4/Zone/Definition/ZoneDefWriterIW4.h" +#include "Game/IW5/Zone/Definition/ZoneDefWriterIW5.h" +#include "Game/T5/Zone/Definition/ZoneDefWriterT5.h" +#include "Game/T6/Zone/Definition/ZoneDefWriterT6.h" #include @@ -25,7 +25,7 @@ const IZoneDefWriter* IZoneDefWriter::GetZoneDefWriterForGame(GameId game) return result; } -void AbstractZoneDefWriter::WriteZoneDef(std::ostream& stream, const UnlinkerArgs& args, const Zone& zone) const +void AbstractZoneDefWriter::WriteZoneDef(std::ostream& stream, const Zone& zone, const bool useGdt) const { ZoneDefinitionOutputStream out(stream); const auto* game = IGame::GetGameById(zone.m_game_id); @@ -34,13 +34,13 @@ void AbstractZoneDefWriter::WriteZoneDef(std::ostream& stream, const UnlinkerArg out.WriteMetaData(META_DATA_KEY_GAME, game->GetShortName()); out.EmptyLine(); - if (args.m_use_gdt) + if (useGdt) { out.WriteComment("Load asset gdt files"); out.WriteMetaData(META_DATA_KEY_GDT, zone.m_name); out.EmptyLine(); } - WriteMetaData(out, args, zone); - WriteContent(out, args, zone); + WriteMetaData(out, zone); + WriteContent(out, zone); } diff --git a/src/Unlinker/ContentLister/ZoneDefWriter.h b/src/ZoneCommon/Zone/Definition/ZoneDefWriter.h similarity index 70% rename from src/Unlinker/ContentLister/ZoneDefWriter.h rename to src/ZoneCommon/Zone/Definition/ZoneDefWriter.h index 4cb7f6a4..1458c1c7 100644 --- a/src/Unlinker/ContentLister/ZoneDefWriter.h +++ b/src/ZoneCommon/Zone/Definition/ZoneDefWriter.h @@ -1,7 +1,7 @@ #pragma once -#include "UnlinkerArgs.h" #include "Zone/Definition/ZoneDefinitionStream.h" +#include "Zone/Zone.h" class IZoneDefWriter { @@ -13,7 +13,7 @@ public: IZoneDefWriter& operator=(const IZoneDefWriter& other) = default; IZoneDefWriter& operator=(IZoneDefWriter&& other) noexcept = default; - virtual void WriteZoneDef(std::ostream& stream, const UnlinkerArgs& args, const Zone& zone) const = 0; + virtual void WriteZoneDef(std::ostream& stream, const Zone& zone, bool useGdt) const = 0; static const IZoneDefWriter* GetZoneDefWriterForGame(GameId game); }; @@ -24,9 +24,9 @@ protected: static constexpr auto META_DATA_KEY_GAME = "game"; static constexpr auto META_DATA_KEY_GDT = "gdt"; - virtual void WriteMetaData(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const = 0; - virtual void WriteContent(ZoneDefinitionOutputStream& stream, const UnlinkerArgs& args, const Zone& zone) const = 0; + virtual void WriteMetaData(ZoneDefinitionOutputStream& stream, const Zone& zone) const = 0; + virtual void WriteContent(ZoneDefinitionOutputStream& stream, const Zone& zone) const = 0; public: - void WriteZoneDef(std::ostream& stream, const UnlinkerArgs& args, const Zone& zone) const override; + void WriteZoneDef(std::ostream& stream, const Zone& zone, bool useGdt) const override; }; diff --git a/src/ZoneCommon/Zone/Zone.cpp b/src/ZoneCommon/Zone/Zone.cpp index deae50d3..4f0fe2b5 100644 --- a/src/ZoneCommon/Zone/Zone.cpp +++ b/src/ZoneCommon/Zone/Zone.cpp @@ -2,13 +2,15 @@ #include "ZoneRegistry.h" -Zone::Zone(std::string name, const zone_priority_t priority, GameId gameId) +Zone::Zone(std::string name, const zone_priority_t priority, const GameId gameId, const GamePlatform platform) : m_name(std::move(name)), m_priority(priority), m_language(GameLanguage::LANGUAGE_NONE), m_game_id(gameId), + m_platform(platform), m_pools(ZoneAssetPools::CreateForGame(gameId, this, priority)), - m_memory(std::make_unique()) + m_memory(std::make_unique()), + m_registered(false) { } diff --git a/src/ZoneCommon/Zone/Zone.h b/src/ZoneCommon/Zone/Zone.h index 658bb412..a4f57018 100644 --- a/src/ZoneCommon/Zone/Zone.h +++ b/src/ZoneCommon/Zone/Zone.h @@ -15,7 +15,7 @@ class ZoneAssetPools; class Zone { public: - Zone(std::string name, zone_priority_t priority, GameId gameId); + Zone(std::string name, zone_priority_t priority, GameId gameId, GamePlatform platform); ~Zone(); Zone(const Zone& other) = delete; Zone(Zone&& other) noexcept = default; @@ -30,6 +30,7 @@ public: zone_priority_t m_priority; GameLanguage m_language; GameId m_game_id; + GamePlatform m_platform; ZoneScriptStrings m_script_strings; std::unique_ptr m_pools; diff --git a/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp b/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp index 3a387efe..0a81643c 100644 --- a/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp +++ b/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp @@ -11,7 +11,6 @@ #include "Loading/Steps/StepAllocXBlocks.h" #include "Loading/Steps/StepLoadZoneContent.h" #include "Loading/Steps/StepLoadZoneSizes.h" -#include "Loading/Steps/StepSkipBytes.h" #include "Utils/ClassUtils.h" #include @@ -22,24 +21,6 @@ using namespace IW3; namespace { - bool CanLoad(const ZoneHeader& header, bool* isSecure, bool* isOfficial) - { - assert(isSecure != nullptr); - assert(isOfficial != nullptr); - - if (header.m_version != ZoneConstants::ZONE_VERSION) - return false; - - if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits::length(ZoneConstants::MAGIC_UNSIGNED))) - { - *isSecure = false; - *isOfficial = true; - return true; - } - - return false; - } - void SetupBlock(ZoneLoader& zoneLoader) { #define XBLOCK_DEF(name, type) std::make_unique(STR(name), name, type) @@ -58,17 +39,37 @@ namespace } } // namespace -std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const +std::optional ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const { - bool isSecure; - bool isOfficial; + if (header.m_version != ZoneConstants::ZONE_VERSION) + return std::nullopt; - // Check if this file is a supported IW4 zone. - if (!CanLoad(header, &isSecure, &isOfficial)) + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits::length(ZoneConstants::MAGIC_UNSIGNED))) + { + return ZoneLoaderInspectionResult{ + .m_game_id = GameId::IW3, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + .m_is_official = true, + .m_is_signed = false, + .m_is_encrypted = false, + }; + } + + return std::nullopt; +} + +std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const +{ + const auto inspectResult = InspectZoneHeader(header); + if (!inspectResult) return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, GameId::IW3); + auto zone = std::make_unique(fileName, 0, GameId::IW3, inspectResult->m_platform); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; @@ -93,7 +94,8 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& 32u, ZoneConstants::OFFSET_BLOCK_BIT_COUNT, ZoneConstants::INSERT_BLOCK, - zonePtr->Memory())); + zonePtr->Memory(), + std::move(progressCallback))); return zoneLoader; } diff --git a/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.h b/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.h index 2e6ba8e6..e5957295 100644 --- a/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.h +++ b/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.h @@ -9,6 +9,9 @@ namespace IW3 class ZoneLoaderFactory final : public IZoneLoaderFactory { public: - std::unique_ptr CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const override; + [[nodiscard]] std::optional InspectZoneHeader(const ZoneHeader& header) const override; + [[nodiscard]] std::unique_ptr CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const override; }; } // namespace IW3 diff --git a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp index 1bfd60d6..78116b19 100644 --- a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp +++ b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp @@ -34,47 +34,75 @@ using namespace IW4; namespace { - bool CanLoad(ZoneHeader& header, bool* isSecure, bool* isOfficial, bool* isIw4x) + struct ZoneLoaderInspectionResultIW4 { - assert(isSecure != nullptr); - assert(isOfficial != nullptr); - assert(isIw4x != nullptr); + ZoneLoaderInspectionResult m_generic_result; + bool m_is_iw4x; + }; + std::optional InspectZoneHeaderIw4(const ZoneHeader& header) + { if (header.m_version != ZoneConstants::ZONE_VERSION) - { - return false; - } + return std::nullopt; if (!memcmp(header.m_magic, ZoneConstants::MAGIC_IW4X, std::char_traits::length(ZoneConstants::MAGIC_IW4X))) { - if (*reinterpret_cast(&header.m_magic[std::char_traits::length(ZoneConstants::MAGIC_IW4X)]) == ZoneConstants::IW4X_ZONE_VERSION) + if (*reinterpret_cast(&header.m_magic[std::char_traits::length(ZoneConstants::MAGIC_IW4X)]) + == ZoneConstants::IW4X_ZONE_VERSION) { - *isSecure = false; - *isOfficial = false; - *isIw4x = true; - return true; + return ZoneLoaderInspectionResultIW4{ + .m_generic_result = + ZoneLoaderInspectionResult{ + .m_game_id = GameId::IW4, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + .m_is_official = false, + .m_is_signed = false, + .m_is_encrypted = false, + }, + .m_is_iw4x = true, + }; } - return false; + return std::nullopt; } if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_INFINITY_WARD, std::char_traits::length(ZoneConstants::MAGIC_SIGNED_INFINITY_WARD))) { - *isSecure = true; - *isOfficial = true; - *isIw4x = false; - return true; + return ZoneLoaderInspectionResultIW4{ + .m_generic_result = + ZoneLoaderInspectionResult{ + .m_game_id = GameId::IW4, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + .m_is_official = true, + .m_is_signed = true, + .m_is_encrypted = false, + }, + .m_is_iw4x = false, + }; } if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits::length(ZoneConstants::MAGIC_UNSIGNED))) { - *isSecure = false; - *isOfficial = true; - *isIw4x = false; - return true; + return ZoneLoaderInspectionResultIW4{ + .m_generic_result = + ZoneLoaderInspectionResult{ + .m_game_id = GameId::IW4, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + .m_is_official = false, + .m_is_signed = false, + .m_is_encrypted = false, + }, + .m_is_iw4x = false, + }; } - return false; + return std::nullopt; } void SetupBlock(ZoneLoader& zoneLoader) @@ -116,14 +144,14 @@ namespace } } - void AddAuthHeaderSteps(const bool isSecure, const bool isOfficial, ZoneLoader& zoneLoader, std::string& fileName) + void AddAuthHeaderSteps(const ZoneLoaderInspectionResultIW4& inspectResult, ZoneLoader& zoneLoader, const std::string& fileName) { // Unsigned zones do not have an auth header - if (!isSecure) + if (!inspectResult.m_generic_result.m_is_signed) return; // If file is signed setup a RSA instance. - auto rsa = SetupRsa(isOfficial); + auto rsa = SetupRsa(inspectResult.m_generic_result.m_is_official); zoneLoader.AddLoadingStep(step::CreateStepVerifyMagic(ZoneConstants::MAGIC_AUTH_HEADER)); zoneLoader.AddLoadingStep(step::CreateStepSkipBytes(4)); // Skip reserved @@ -165,18 +193,25 @@ namespace } } // namespace -std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const +std::optional ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const { - bool isSecure; - bool isOfficial; - bool isIw4x; + auto resultIw4 = InspectZoneHeaderIw4(header); + if (!resultIw4) + return std::nullopt; - // Check if this file is a supported IW4 zone. - if (!CanLoad(header, &isSecure, &isOfficial, &isIw4x)) + return resultIw4->m_generic_result; +} + +std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const +{ + const auto inspectResult = InspectZoneHeaderIw4(header); + if (!inspectResult) return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, GameId::IW4); + auto zone = std::make_unique(fileName, 0, GameId::IW4, inspectResult->m_generic_result.m_platform); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; @@ -193,11 +228,11 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(8)); // Add steps for loading the auth header which also contain the signature of the zone if it is signed. - AddAuthHeaderSteps(isSecure, isOfficial, *zoneLoader, fileName); + AddAuthHeaderSteps(*inspectResult, *zoneLoader, fileName); zoneLoader->AddLoadingStep(step::CreateStepAddProcessor(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE))); - if (isIw4x) // IW4x has one extra byte of padding here for protection purposes + if (inspectResult->m_is_iw4x) // IW4x has one extra byte of padding here for protection purposes { zoneLoader->AddLoadingStep(step::CreateStepAddProcessor(processor::CreateProcessorIW4xDecryption())); zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(1)); @@ -216,7 +251,8 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& 32u, ZoneConstants::OFFSET_BLOCK_BIT_COUNT, ZoneConstants::INSERT_BLOCK, - zonePtr->Memory())); + zonePtr->Memory(), + std::move(progressCallback))); return zoneLoader; } diff --git a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.h b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.h index a840e686..5513c726 100644 --- a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.h +++ b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.h @@ -9,6 +9,9 @@ namespace IW4 class ZoneLoaderFactory final : public IZoneLoaderFactory { public: - std::unique_ptr CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const override; + [[nodiscard]] std::optional InspectZoneHeader(const ZoneHeader& header) const override; + [[nodiscard]] std::unique_ptr CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const override; }; } // namespace IW4 diff --git a/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp b/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp index b7e97184..557d5537 100644 --- a/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp +++ b/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp @@ -33,33 +33,6 @@ using namespace IW5; namespace { - bool CanLoad(const ZoneHeader& header, bool* isSecure, bool* isOfficial) - { - assert(isSecure != nullptr); - assert(isOfficial != nullptr); - - if (header.m_version != ZoneConstants::ZONE_VERSION) - { - return false; - } - - if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_INFINITY_WARD, std::char_traits::length(ZoneConstants::MAGIC_SIGNED_INFINITY_WARD))) - { - *isSecure = true; - *isOfficial = true; - return true; - } - - if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits::length(ZoneConstants::MAGIC_UNSIGNED))) - { - *isSecure = false; - *isOfficial = true; - return true; - } - - return false; - } - void SetupBlock(ZoneLoader& zoneLoader) { #define XBLOCK_DEF(name, type) std::make_unique(STR(name), name, type) @@ -100,14 +73,14 @@ namespace } } - void AddAuthHeaderSteps(const bool isSecure, const bool isOfficial, ZoneLoader& zoneLoader, std::string& fileName) + void AddAuthHeaderSteps(const ZoneLoaderInspectionResult& inspectResult, ZoneLoader& zoneLoader, const std::string& fileName) { // Unsigned zones do not have an auth header - if (!isSecure) + if (!inspectResult.m_is_signed) return; // If file is signed setup a RSA instance. - auto rsa = SetupRsa(isOfficial); + auto rsa = SetupRsa(inspectResult.m_is_official); zoneLoader.AddLoadingStep(step::CreateStepVerifyMagic(ZoneConstants::MAGIC_AUTH_HEADER)); zoneLoader.AddLoadingStep(step::CreateStepSkipBytes(4)); // Skip reserved @@ -149,17 +122,50 @@ namespace } } // namespace -std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const +std::optional ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const { - bool isSecure; - bool isOfficial; + if (header.m_version != ZoneConstants::ZONE_VERSION) + return std::nullopt; - // Check if this file is a supported IW4 zone. - if (!CanLoad(header, &isSecure, &isOfficial)) + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_INFINITY_WARD, std::char_traits::length(ZoneConstants::MAGIC_SIGNED_INFINITY_WARD))) + { + return ZoneLoaderInspectionResult{ + .m_game_id = GameId::IW5, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + .m_is_official = true, + .m_is_signed = true, + .m_is_encrypted = false, + }; + } + + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits::length(ZoneConstants::MAGIC_UNSIGNED))) + { + return ZoneLoaderInspectionResult{ + .m_game_id = GameId::IW5, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + .m_is_official = false, + .m_is_signed = false, + .m_is_encrypted = false, + }; + } + + return std::nullopt; +} + +std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const +{ + const auto inspectResult = InspectZoneHeader(header); + if (!inspectResult) return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, GameId::IW5); + auto zone = std::make_unique(fileName, 0, GameId::IW5, inspectResult->m_platform); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; @@ -176,7 +182,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(8)); // Add steps for loading the auth header which also contain the signature of the zone if it is signed. - AddAuthHeaderSteps(isSecure, isOfficial, *zoneLoader, fileName); + AddAuthHeaderSteps(*inspectResult, *zoneLoader, fileName); zoneLoader->AddLoadingStep(step::CreateStepAddProcessor(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE))); @@ -193,7 +199,8 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& 32u, ZoneConstants::OFFSET_BLOCK_BIT_COUNT, ZoneConstants::INSERT_BLOCK, - zonePtr->Memory())); + zonePtr->Memory(), + std::move(progressCallback))); return zoneLoader; } diff --git a/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.h b/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.h index a3a522a1..847272aa 100644 --- a/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.h +++ b/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.h @@ -9,6 +9,9 @@ namespace IW5 class ZoneLoaderFactory final : public IZoneLoaderFactory { public: - std::unique_ptr CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const override; + [[nodiscard]] std::optional InspectZoneHeader(const ZoneHeader& header) const override; + [[nodiscard]] std::unique_ptr CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const override; }; } // namespace IW5 diff --git a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp index 34ee2025..7d93b5d2 100644 --- a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp +++ b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp @@ -11,7 +11,6 @@ #include "Loading/Steps/StepAllocXBlocks.h" #include "Loading/Steps/StepLoadZoneContent.h" #include "Loading/Steps/StepLoadZoneSizes.h" -#include "Loading/Steps/StepSkipBytes.h" #include "Utils/ClassUtils.h" #include @@ -22,26 +21,6 @@ using namespace T5; namespace { - bool CanLoad(const ZoneHeader& header, bool* isSecure, bool* isOfficial) - { - assert(isSecure != nullptr); - assert(isOfficial != nullptr); - - if (header.m_version != ZoneConstants::ZONE_VERSION) - { - return false; - } - - if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits::length(ZoneConstants::MAGIC_UNSIGNED))) - { - *isSecure = false; - *isOfficial = true; - return true; - } - - return false; - } - void SetupBlock(ZoneLoader& zoneLoader) { #define XBLOCK_DEF(name, type) std::make_unique(STR(name), name, type) @@ -58,17 +37,38 @@ namespace } } // namespace -std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const +std::optional ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const { - bool isSecure; - bool isOfficial; + if (header.m_version != ZoneConstants::ZONE_VERSION) + return std::nullopt; - // Check if this file is a supported IW4 zone. - if (!CanLoad(header, &isSecure, &isOfficial)) + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits::length(ZoneConstants::MAGIC_UNSIGNED))) + { + return ZoneLoaderInspectionResult{ + .m_game_id = GameId::T5, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + // There is no way to know whether unsigned zones are official. + .m_is_official = false, + .m_is_signed = false, + .m_is_encrypted = false, + }; + } + + return std::nullopt; +} + +std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const +{ + const auto inspectResult = InspectZoneHeader(header); + if (!inspectResult) return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, GameId::T5); + auto zone = std::make_unique(fileName, 0, GameId::T5, inspectResult->m_platform); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; @@ -93,7 +93,8 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& 32u, ZoneConstants::OFFSET_BLOCK_BIT_COUNT, ZoneConstants::INSERT_BLOCK, - zonePtr->Memory())); + zonePtr->Memory(), + std::move(progressCallback))); return zoneLoader; } diff --git a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.h b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.h index 12c21ffb..efd298c9 100644 --- a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.h +++ b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.h @@ -9,6 +9,9 @@ namespace T5 class ZoneLoaderFactory final : public IZoneLoaderFactory { public: - std::unique_ptr CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const override; + [[nodiscard]] std::optional InspectZoneHeader(const ZoneHeader& header) const override; + [[nodiscard]] std::unique_ptr CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const override; }; } // namespace T5 diff --git a/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp b/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp index 813d8602..f4a40755 100644 --- a/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp +++ b/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp @@ -24,11 +24,10 @@ #include "Zone/XChunk/XChunkProcessorSalsa20Decryption.h" #include -#include +#include #include #include #include -#include #include using namespace T6; @@ -36,6 +35,130 @@ namespace fs = std::filesystem; namespace { + enum class ZoneCompressionTypeT6 : std::uint8_t + { + DEFLATE, + LZX + }; + + struct ZoneLoaderInspectionResultT6 + { + ZoneLoaderInspectionResult m_generic_result; + ZoneCompressionTypeT6 m_compression_type; + }; + + std::optional InspectZoneHeaderT6(const ZoneHeader& header) + { + if (endianness::FromLittleEndian(header.m_version) == ZoneConstants::ZONE_VERSION_PC) + { + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, 8)) + { + return ZoneLoaderInspectionResultT6{ + .m_generic_result = + ZoneLoaderInspectionResult{ + .m_game_id = GameId::T6, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + .m_is_official = true, + .m_is_signed = true, + .m_is_encrypted = true, + }, + .m_compression_type = ZoneCompressionTypeT6::DEFLATE, + }; + } + + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_OAT, 8)) + { + return ZoneLoaderInspectionResultT6{ + .m_generic_result = + ZoneLoaderInspectionResult{ + .m_game_id = GameId::T6, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + .m_is_official = false, + .m_is_signed = true, + .m_is_encrypted = true, + }, + .m_compression_type = ZoneCompressionTypeT6::DEFLATE, + }; + } + + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, 8)) + { + return ZoneLoaderInspectionResultT6{ + .m_generic_result = + ZoneLoaderInspectionResult{ + .m_game_id = GameId::T6, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + .m_is_official = false, + .m_is_signed = false, + .m_is_encrypted = true, + }, + .m_compression_type = ZoneCompressionTypeT6::DEFLATE, + }; + } + + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED_SERVER, 8)) + { + return ZoneLoaderInspectionResultT6{ + .m_generic_result = + ZoneLoaderInspectionResult{ + .m_game_id = GameId::T6, + .m_endianness = GameEndianness::LE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::PC, + .m_is_official = true, + .m_is_signed = false, + .m_is_encrypted = false, + }, + .m_compression_type = ZoneCompressionTypeT6::DEFLATE, + }; + } + } + else if (endianness::FromBigEndian(header.m_version) == ZoneConstants::ZONE_VERSION_XENON) + { + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, 8)) + { + return ZoneLoaderInspectionResultT6{ + .m_generic_result = + ZoneLoaderInspectionResult{ + .m_game_id = GameId::T6, + .m_endianness = GameEndianness::BE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::XBOX, + .m_is_official = true, + .m_is_signed = true, + .m_is_encrypted = true, + }, + .m_compression_type = ZoneCompressionTypeT6::DEFLATE, + }; + } + + if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_LZX_TREYARCH, 8)) + { + return ZoneLoaderInspectionResultT6{ + .m_generic_result = + ZoneLoaderInspectionResult{ + .m_game_id = GameId::T6, + .m_endianness = GameEndianness::BE, + .m_word_size = GameWordSize::ARCH_32, + .m_platform = GamePlatform::XBOX, + .m_is_official = true, + .m_is_signed = true, + .m_is_encrypted = true, + }, + .m_compression_type = ZoneCompressionTypeT6::LZX, + }; + } + } + + return std::nullopt; + } + GameLanguage GetZoneLanguage(const std::string& zoneName) { const auto& languagePrefixes = IGame::GetGameById(GameId::T6)->GetLanguagePrefixes(); @@ -51,68 +174,6 @@ namespace return GameLanguage::LANGUAGE_NONE; } - bool CanLoad(const ZoneHeader& header, bool& isBigEndian, bool& isSecure, bool& isOfficial, bool& isEncrypted, bool& isLzxCompressed) - { - if (endianness::FromLittleEndian(header.m_version) == ZoneConstants::ZONE_VERSION_PC) - { - 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; - } - } - else if (endianness::FromBigEndian(header.m_version) == ZoneConstants::ZONE_VERSION_XENON) - { - 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; - } - void SetupBlock(ZoneLoader& zoneLoader) { #define XBLOCK_DEF(name, type) std::make_unique(STR(name), name, type) @@ -169,26 +230,16 @@ namespace return signatureLoadStepPtr; } - ICapturedDataProvider* - AddXChunkProcessor(const bool isBigEndian, const bool isEncrypted, const bool isLzxCompressed, ZoneLoader& zoneLoader, std::string& fileName) + ICapturedDataProvider* AddXChunkProcessor(const ZoneLoaderInspectionResultT6& inspectResult, ZoneLoader& zoneLoader, const std::string& fileName) { ICapturedDataProvider* result = nullptr; - std::unique_ptr xChunkProcessor; + auto xChunkProcessor = processor::CreateProcessorXChunks( + ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, inspectResult.m_generic_result.m_endianness, ZoneConstants::VANILLA_BUFFER_SIZE); - 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] = inspectResult.m_generic_result.m_platform == GamePlatform::XBOX ? ZoneConstants::SALSA20_KEY_TREYARCH_XENON + : ZoneConstants::SALSA20_KEY_TREYARCH_PC; - const uint8_t (&salsa20Key)[32] = isBigEndian ? ZoneConstants::SALSA20_KEY_TREYARCH_XENON : ZoneConstants::SALSA20_KEY_TREYARCH_PC; - - if (isEncrypted) + if (inspectResult.m_generic_result.m_is_encrypted) { // If zone is encrypted, the decryption is applied before the decompression. T6 Zones always use Salsa20. auto chunkProcessorSalsa20 = @@ -197,7 +248,7 @@ namespace xChunkProcessor->AddChunkProcessor(std::move(chunkProcessorSalsa20)); } - if (isLzxCompressed) + if (inspectResult.m_compression_type == ZoneCompressionTypeT6::LZX) { // Decompress the chunks using lzx xChunkProcessor->AddChunkProcessor(std::make_unique(ZoneConstants::STREAM_COUNT)); @@ -215,16 +266,25 @@ namespace } } // namespace -std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const +std::optional ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const { - bool isBigEndian, isSecure, isOfficial, isEncrypted, isLzxCompressed; + auto resultT6 = InspectZoneHeaderT6(header); + if (!resultT6) + return std::nullopt; - // Check if this file is a supported T6 zone. - if (!CanLoad(header, isBigEndian, isSecure, isOfficial, isEncrypted, isLzxCompressed)) + return resultT6->m_generic_result; +} + +std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const +{ + const auto inspectResult = InspectZoneHeaderT6(header); + if (!inspectResult) return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, GameId::T6); + auto zone = std::make_unique(fileName, 0, GameId::T6, inspectResult->m_generic_result.m_platform); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GetZoneLanguage(fileName); @@ -235,15 +295,15 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& SetupBlock(*zoneLoader); // If file is signed setup a RSA instance. - auto rsa = isSecure ? SetupRsa(isOfficial) : nullptr; + auto rsa = inspectResult->m_generic_result.m_is_signed ? SetupRsa(inspectResult->m_generic_result.m_is_official) : nullptr; // Add steps for loading the auth header which also contain the signature of the zone if it is signed. - ISignatureProvider* signatureProvider = AddAuthHeaderSteps(isSecure, *zoneLoader, fileName); + ISignatureProvider* signatureProvider = AddAuthHeaderSteps(inspectResult->m_generic_result.m_is_signed, *zoneLoader, fileName); // Setup loading XChunks from the zone from this point on. - ICapturedDataProvider* signatureDataProvider = AddXChunkProcessor(isBigEndian, isEncrypted, isLzxCompressed, *zoneLoader, fileName); + ICapturedDataProvider* signatureDataProvider = AddXChunkProcessor(*inspectResult, *zoneLoader, fileName); - if (!isBigEndian) + if (inspectResult->m_generic_result.m_endianness == GameEndianness::LE) { // Start of the XFile struct zoneLoader->AddLoadingStep(step::CreateStepLoadZoneSizes()); @@ -258,12 +318,11 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& 32u, ZoneConstants::OFFSET_BLOCK_BIT_COUNT, ZoneConstants::INSERT_BLOCK, - zonePtr->Memory())); + zonePtr->Memory(), + std::move(progressCallback))); - if (isSecure) - { + if (inspectResult->m_generic_result.m_is_signed) zoneLoader->AddLoadingStep(step::CreateStepVerifySignature(std::move(rsa), signatureProvider, signatureDataProvider)); - } } else { diff --git a/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.h b/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.h index 0890f555..fd396d61 100644 --- a/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.h +++ b/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.h @@ -9,6 +9,9 @@ namespace T6 class ZoneLoaderFactory final : public IZoneLoaderFactory { public: - std::unique_ptr CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const override; + [[nodiscard]] std::optional InspectZoneHeader(const ZoneHeader& header) const override; + [[nodiscard]] std::unique_ptr CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const override; }; } // namespace T6 diff --git a/src/ZoneLoading/Loading/IZoneLoaderFactory.h b/src/ZoneLoading/Loading/IZoneLoaderFactory.h index 46ab7393..3d9f356c 100644 --- a/src/ZoneLoading/Loading/IZoneLoaderFactory.h +++ b/src/ZoneLoading/Loading/IZoneLoaderFactory.h @@ -1,9 +1,30 @@ #pragma once +#include "Game/IGame.h" +#include "Utils/ProgressCallback.h" #include "Zone/ZoneTypes.h" #include "ZoneLoader.h" #include +#include + +struct ZoneLoaderInspectionResult +{ + // The game this zone is created for. + GameId m_game_id; + // Whether the zone is meant for a little-endian or big-endian loader. + GameEndianness m_endianness; + // Whether the zone is meant for a 32bit or 64bit loader. + GameWordSize m_word_size; + // The platform this zone is for. + GamePlatform m_platform; + // Whether this zone is confirmed official. False if not official or unknown. + bool m_is_official; + // Whether this zone contains a signature confirming the identity of the creator. + bool m_is_signed; + // Whether this zone is encrypted. + bool m_is_encrypted; +}; class IZoneLoaderFactory { @@ -15,7 +36,10 @@ public: IZoneLoaderFactory& operator=(const IZoneLoaderFactory& other) = default; IZoneLoaderFactory& operator=(IZoneLoaderFactory&& other) noexcept = default; - virtual std::unique_ptr CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const = 0; + [[nodiscard]] virtual std::optional InspectZoneHeader(const ZoneHeader& header) const = 0; + [[nodiscard]] virtual std::unique_ptr CreateLoaderForHeader(const ZoneHeader& header, + const std::string& fileName, + std::optional> progressCallback) const = 0; static const IZoneLoaderFactory* GetZoneLoaderFactoryForGame(GameId game); }; diff --git a/src/ZoneLoading/Loading/Steps/StepLoadZoneContent.cpp b/src/ZoneLoading/Loading/Steps/StepLoadZoneContent.cpp index 80cb3591..b4cbfb82 100644 --- a/src/ZoneLoading/Loading/Steps/StepLoadZoneContent.cpp +++ b/src/ZoneLoading/Loading/Steps/StepLoadZoneContent.cpp @@ -11,19 +11,21 @@ namespace const unsigned pointerBitCount, const unsigned offsetBlockBitCount, const block_t insertBlock, - MemoryManager& memory) + MemoryManager& memory, + std::optional> progressCallback) : m_entry_point_factory(std::move(entryPointFactory)), m_pointer_bit_count(pointerBitCount), m_offset_block_bit_count(offsetBlockBitCount), m_insert_block(insertBlock), - m_memory(memory) + m_memory(memory), + m_progress_callback(std::move(progressCallback)) { } void PerformStep(ZoneLoader& zoneLoader, ILoadingStream& stream) override { - const auto inputStream = - ZoneInputStream::Create(m_pointer_bit_count, m_offset_block_bit_count, zoneLoader.m_blocks, m_insert_block, stream, m_memory); + const auto inputStream = ZoneInputStream::Create( + m_pointer_bit_count, m_offset_block_bit_count, zoneLoader.m_blocks, m_insert_block, stream, m_memory, std::move(m_progress_callback)); const auto entryPoint = m_entry_point_factory(*inputStream); assert(entryPoint); @@ -37,6 +39,7 @@ namespace unsigned m_offset_block_bit_count; block_t m_insert_block; MemoryManager& m_memory; + std::optional> m_progress_callback; }; } // namespace @@ -46,8 +49,10 @@ namespace step const unsigned pointerBitCount, const unsigned offsetBlockBitCount, const block_t insertBlock, - MemoryManager& memory) + MemoryManager& memory, + std::optional> progressCallback) { - return std::make_unique(std::move(entryPointFactory), pointerBitCount, offsetBlockBitCount, insertBlock, memory); + return std::make_unique( + std::move(entryPointFactory), pointerBitCount, offsetBlockBitCount, insertBlock, memory, std::move(progressCallback)); } } // namespace step diff --git a/src/ZoneLoading/Loading/Steps/StepLoadZoneContent.h b/src/ZoneLoading/Loading/Steps/StepLoadZoneContent.h index 621fa2a2..f832f1dd 100644 --- a/src/ZoneLoading/Loading/Steps/StepLoadZoneContent.h +++ b/src/ZoneLoading/Loading/Steps/StepLoadZoneContent.h @@ -13,5 +13,6 @@ namespace step unsigned pointerBitCount, unsigned offsetBlockBitCount, block_t insertBlock, - MemoryManager& memory); + MemoryManager& memory, + std::optional> progressCallback); } diff --git a/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp b/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp index 296060fc..f6722962 100644 --- a/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp +++ b/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp @@ -50,7 +50,8 @@ namespace std::vector& blocks, const block_t insertBlock, ILoadingStream& stream, - MemoryManager& memory) + MemoryManager& memory, + std::optional> progressCallback) : m_blocks(blocks), m_stream(stream), m_memory(memory), @@ -58,7 +59,10 @@ namespace m_block_mask((std::numeric_limits::max() >> (sizeof(uintptr_t) * 8 - blockBitCount)) << (pointerBitCount - blockBitCount)), m_block_shift(pointerBitCount - blockBitCount), m_offset_mask(std::numeric_limits::max() >> (sizeof(uintptr_t) * 8 - (pointerBitCount - blockBitCount))), - m_last_fill_size(0) + m_last_fill_size(0), + m_has_progress_callback(false), + m_progress_current_size(0uz), + m_progress_total_size(0uz) { assert(pointerBitCount % 8u == 0u); assert(insertBlock < static_cast(blocks.size())); @@ -68,6 +72,13 @@ namespace std::memset(m_block_offsets.get(), 0, sizeof(size_t) * blockCount); m_insert_block = blocks[insertBlock]; + + if (progressCallback) + { + m_has_progress_callback = true; + m_progress_callback = *std::move(progressCallback); + m_progress_total_size = CalculateTotalSize(); + } } [[nodiscard]] unsigned GetPointerBitCount() const override @@ -444,6 +455,13 @@ namespace void IncBlockPos(const XBlock& block, const size_t size) { m_block_offsets[block.m_index] += size; + + // We cannot know the full size of the temp block + if (m_has_progress_callback && block.m_type != XBlockType::BLOCK_TYPE_TEMP) + { + m_progress_current_size += size; + m_progress_callback->OnProgress(m_progress_current_size, m_progress_total_size); + } } void Align(const XBlock& block, const unsigned align) @@ -455,6 +473,20 @@ namespace } } + [[nodiscard]] size_t CalculateTotalSize() const + { + size_t result = 0uz; + + for (const auto& block : m_blocks) + { + // We cannot know the full size of the temp block + if (block->m_type != XBlockType::BLOCK_TYPE_TEMP) + result += block->m_buffer_size; + } + + return result; + } + std::vector& m_blocks; std::unique_ptr m_block_offsets; @@ -475,6 +507,11 @@ namespace // These lookups map a block offset to a pointer in case of a platform mismatch std::unordered_map m_pointer_redirect_lookup; std::unordered_map m_alias_redirect_lookup; + + bool m_has_progress_callback; + std::unique_ptr m_progress_callback; + size_t m_progress_current_size; + size_t m_progress_total_size; }; } // namespace @@ -483,7 +520,8 @@ std::unique_ptr ZoneInputStream::Create(const unsigned pointerB std::vector& blocks, const block_t insertBlock, ILoadingStream& stream, - MemoryManager& memory) + MemoryManager& memory, + std::optional> progressCallback) { - return std::make_unique(pointerBitCount, blockBitCount, blocks, insertBlock, stream, memory); + return std::make_unique(pointerBitCount, blockBitCount, blocks, insertBlock, stream, memory, std::move(progressCallback)); } diff --git a/src/ZoneLoading/Zone/Stream/ZoneInputStream.h b/src/ZoneLoading/Zone/Stream/ZoneInputStream.h index f86c2e4c..2007d7fd 100644 --- a/src/ZoneLoading/Zone/Stream/ZoneInputStream.h +++ b/src/ZoneLoading/Zone/Stream/ZoneInputStream.h @@ -3,6 +3,7 @@ #include "Loading/Exception/InvalidLookupPositionException.h" #include "Loading/ILoadingStream.h" #include "Utils/MemoryManager.h" +#include "Utils/ProgressCallback.h" #include "Zone/Stream/IZoneStream.h" #include "Zone/XBlock.h" @@ -10,6 +11,7 @@ #include #include #include +#include #include #include @@ -238,6 +240,11 @@ public: virtual void DebugOffsets(size_t assetIndex) const = 0; #endif - static std::unique_ptr Create( - unsigned pointerBitCount, unsigned blockBitCount, std::vector& blocks, block_t insertBlock, ILoadingStream& stream, MemoryManager& memory); + static std::unique_ptr Create(unsigned pointerBitCount, + unsigned blockBitCount, + std::vector& blocks, + block_t insertBlock, + ILoadingStream& stream, + MemoryManager& memory, + std::optional> progressCallback); }; diff --git a/src/ZoneLoading/ZoneLoading.cpp b/src/ZoneLoading/ZoneLoading.cpp index 3d15b80c..e7d4962b 100644 --- a/src/ZoneLoading/ZoneLoading.cpp +++ b/src/ZoneLoading/ZoneLoading.cpp @@ -2,50 +2,41 @@ #include "Loading/IZoneLoaderFactory.h" #include "Loading/ZoneLoader.h" -#include "Utils/Logging/Log.h" #include "Utils/ObjFileStream.h" #include #include #include -#include namespace fs = std::filesystem; -std::unique_ptr ZoneLoading::LoadZone(const std::string& path) +result::Expected, std::string> ZoneLoading::LoadZone(const std::string& path, + std::optional> progressCallback) { auto zoneName = fs::path(path).filename().replace_extension().string(); std::ifstream file(path, std::fstream::in | std::fstream::binary); if (!file.is_open()) - { - con::error("Could not open file '{}'.", path); - return nullptr; - } + return result::Unexpected(std::format("Could not open file '{}'.", path)); ZoneHeader header{}; file.read(reinterpret_cast(&header), sizeof(header)); if (file.gcount() != sizeof(header)) - { - con::error("Failed to read zone header from file '{}'.", path); - return nullptr; - } + return result::Unexpected(std::format("Failed to read zone header from file '{}'.", path)); std::unique_ptr zoneLoader; for (auto game = 0u; game < static_cast(GameId::COUNT); game++) { const auto* factory = IZoneLoaderFactory::GetZoneLoaderFactoryForGame(static_cast(game)); - zoneLoader = factory->CreateLoaderForHeader(header, zoneName); - - if (zoneLoader) + if (factory->InspectZoneHeader(header)) + { + zoneLoader = factory->CreateLoaderForHeader(header, zoneName, std::move(progressCallback)); break; + } } if (!zoneLoader) - { - con::error("Could not create factory for zone '{}'.", zoneName); - return nullptr; - } + return result::Unexpected(std::format("Could not create factory for zone '{}'.", zoneName)); auto loadedZone = zoneLoader->LoadZone(file); diff --git a/src/ZoneLoading/ZoneLoading.h b/src/ZoneLoading/ZoneLoading.h index 749db050..3c86ac8e 100644 --- a/src/ZoneLoading/ZoneLoading.h +++ b/src/ZoneLoading/ZoneLoading.h @@ -1,4 +1,7 @@ #pragma once + +#include "Utils/ProgressCallback.h" +#include "Utils/Result.h" #include "Zone/Zone.h" #include @@ -6,5 +9,6 @@ class ZoneLoading { public: - static std::unique_ptr LoadZone(const std::string& path); + static result::Expected, std::string> LoadZone(const std::string& path, + std::optional> progressCallback); }; diff --git a/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp b/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp index 4d5ab4fa..7e81cf1b 100644 --- a/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp +++ b/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp @@ -20,7 +20,7 @@ namespace public: TestContext() : m_memory(), - m_zone("test", 0, GameId::T6), + m_zone("test", 0, GameId::T6, GamePlatform::PC), m_zone_definition(), m_zone_states(m_zone), m_creators(m_zone), diff --git a/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp b/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp index fc423589..1f2d74df 100644 --- a/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp +++ b/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp @@ -20,7 +20,7 @@ namespace { public: TestContext() - : m_zone("test", 0, GameId::T6), + : m_zone("test", 0, GameId::T6, GamePlatform::PC), m_zone_states(m_zone), m_out_dir() { diff --git a/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp b/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp index c9c6b82c..d92899bf 100644 --- a/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp +++ b/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp @@ -20,7 +20,7 @@ namespace { public: TestContext() - : m_zone("test", 0, GameId::T6), + : m_zone("test", 0, GameId::T6, GamePlatform::PC), m_zone_definition(), m_zone_definition_context(m_zone_definition), m_zone_states(m_zone), diff --git a/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp b/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp index adfef729..c9b1ddc4 100644 --- a/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp +++ b/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp @@ -19,7 +19,7 @@ namespace { public: TestContext() - : m_zone("test", 0, GameId::T6), + : m_zone("test", 0, GameId::T6, GamePlatform::PC), m_zone_definition(), m_zone_definition_context(m_zone_definition), m_zone_states(m_zone), diff --git a/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp b/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp index 33421760..a15cc0c4 100644 --- a/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp +++ b/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp @@ -23,7 +23,7 @@ namespace { public: TestContext() - : m_zone("test", 0, GameId::T6), + : m_zone("test", 0, GameId::T6, GamePlatform::PC), m_zone_states(m_zone), m_out_dir() { diff --git a/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp b/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp index 4c88b469..28806ca1 100644 --- a/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp +++ b/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp @@ -274,7 +274,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::IW3); + Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp b/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp index 4bfd7172..c98e09be 100644 --- a/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp +++ b/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp @@ -20,7 +20,7 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, GameId::IW3); + Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp b/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp index e6fdf9db..1585b4ea 100644 --- a/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp +++ b/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp @@ -20,7 +20,7 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, GameId::IW4); + Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp b/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp index 39fd6075..fcb765de 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, GameId::IW4); + Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp b/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp index dcd495f1..ec4cc5bb 100644 --- a/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp +++ b/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp @@ -30,7 +30,7 @@ namespace test::game::iw4::menu::parsing::it public: MenuParsingItHelper() - : m_zone("MockZone", 0, GameId::IW4), + : m_zone("MockZone", 0, GameId::IW4, GamePlatform::PC), m_creator_collection(m_zone), m_ignored_asset_lookup(), m_context(m_zone, &m_creator_collection, &m_ignored_asset_lookup) diff --git a/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp b/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp index 6aadc816..7b94170c 100644 --- a/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp +++ b/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp @@ -19,7 +19,7 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, GameId::IW5); + Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp b/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp index 8105f4ee..67a12e83 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, GameId::IW5); + Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp b/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp index 7db27a83..86ce69ad 100644 --- a/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp +++ b/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp @@ -19,7 +19,7 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, GameId::T5); + Zone zone("MockZone", 0, GameId::T5, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp b/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp index 4847e225..ce0d8fa5 100644 --- a/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp +++ b/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp @@ -358,7 +358,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::T5); + Zone zone("MockZone", 0, GameId::T5, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp b/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp index e26e8b74..ec8c0500 100644 --- a/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp +++ b/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp @@ -19,7 +19,7 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, GameId::T6); + Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp b/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp index 24311566..a75adfe2 100644 --- a/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp +++ b/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp @@ -79,7 +79,7 @@ namespace ] })FONT_ICON"); - Zone zone("MockZone", 0, GameId::T6); + Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp index 95b3bfa1..24340f67 100644 --- a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp +++ b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp @@ -241,7 +241,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::T6); + Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp b/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp index 66b71dd6..0a1ad83c 100644 --- a/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp +++ b/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp @@ -551,19 +551,19 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::IW3); + Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; MockOutputPath mockOutput; - AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + AssetDumpingContext context(zone, "", mockOutput, mockObjPath, std::nullopt); AssetPoolDynamic materialPool(0); GivenMaterial("wc/ch_plasterwall_long", materialPool, memory); - material::JsonDumperIW3 dumper; - dumper.DumpPool(context, &materialPool); + material::JsonDumperIW3 dumper(materialPool); + dumper.Dump(context); const auto* file = mockOutput.GetMockedFile("materials/wc/ch_plasterwall_long.json"); REQUIRE(file); diff --git a/test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp b/test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp index ea108592..f5397cd0 100644 --- a/test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp +++ b/test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp @@ -532,19 +532,19 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::IW4); + Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; MockOutputPath mockOutput; - AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + AssetDumpingContext context(zone, "", mockOutput, mockObjPath, std::nullopt); AssetPoolDynamic materialPool(0); GivenMaterial("mc/ch_rubble01", materialPool, memory); - material::JsonDumperIW4 dumper; - dumper.DumpPool(context, &materialPool); + material::JsonDumperIW4 dumper(materialPool); + dumper.Dump(context); const auto* file = mockOutput.GetMockedFile("materials/mc/ch_rubble01.json"); REQUIRE(file); diff --git a/test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp b/test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp index 941c961d..3196f28a 100644 --- a/test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp +++ b/test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp @@ -585,19 +585,19 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::IW5); + Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; MockOutputPath mockOutput; - AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + AssetDumpingContext context(zone, "", mockOutput, mockObjPath, std::nullopt); AssetPoolDynamic materialPool(0); GivenMaterial("wc/me_metal_rust_02", materialPool, memory); - material::JsonDumperIW5 dumper; - dumper.DumpPool(context, &materialPool); + material::JsonDumperIW5 dumper(materialPool); + dumper.Dump(context); const auto* file = mockOutput.GetMockedFile("materials/wc/me_metal_rust_02.json"); REQUIRE(file); diff --git a/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp b/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp index 704ea0d5..d2664be6 100644 --- a/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp +++ b/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp @@ -614,19 +614,19 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::T5); + Zone zone("MockZone", 0, GameId::T5, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; MockOutputPath mockOutput; - AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + AssetDumpingContext context(zone, "", mockOutput, mockObjPath, std::nullopt); AssetPoolDynamic materialPool(0); GivenMaterial("mc/ch_rubble01", materialPool, memory); - material::JsonDumperT5 dumper; - dumper.DumpPool(context, &materialPool); + material::JsonDumperT5 dumper(materialPool); + dumper.Dump(context); const auto* file = mockOutput.GetMockedFile("materials/mc/ch_rubble01.json"); REQUIRE(file); diff --git a/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp b/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp index eaeb798d..db107bca 100644 --- a/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp +++ b/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp @@ -141,18 +141,18 @@ namespace ] })FONT_ICON"); - Zone zone("MockZone", 0, GameId::T6); + Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; MockOutputPath mockOutput; - AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + AssetDumpingContext context(zone, "", mockOutput, mockObjPath, std::nullopt); AssetPoolDynamic fontIconPool(0); GivenFontIcon("fonticon/test.csv", fontIconPool, memory); - font_icon::JsonDumperT6 dumper; - dumper.DumpPool(context, &fontIconPool); + font_icon::JsonDumperT6 dumper(fontIconPool); + dumper.Dump(context); const auto* file = mockOutput.GetMockedFile("fonticon/test.json"); REQUIRE(file); diff --git a/test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp b/test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp index f7b70376..49b4a59b 100644 --- a/test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp +++ b/test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp @@ -462,18 +462,18 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::T6); + Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; MockOutputPath mockOutput; - AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + AssetDumpingContext context(zone, "", mockOutput, mockObjPath, std::nullopt); AssetPoolDynamic materialPool(0); GivenMaterial("wpc/metal_ac_duct", materialPool, memory); - material::JsonDumperT6 dumper; - dumper.DumpPool(context, &materialPool); + material::JsonDumperT6 dumper(materialPool); + dumper.Dump(context); const auto* file = mockOutput.GetMockedFile("materials/wpc/metal_ac_duct.json"); REQUIRE(file); diff --git a/thirdparty/catch2 b/thirdparty/catch2 index 33e6fd21..31ee3beb 160000 --- a/thirdparty/catch2 +++ b/thirdparty/catch2 @@ -1 +1 @@ -Subproject commit 33e6fd217aee285eca97bef175df716264088b8a +Subproject commit 31ee3beb0a474463e0101674c22f2fef0311d601 diff --git a/thirdparty/eigen b/thirdparty/eigen index cd4f989f..ce70a507 160000 --- a/thirdparty/eigen +++ b/thirdparty/eigen @@ -1 +1 @@ -Subproject commit cd4f989f8f9288ab5aed1643ecb04c7be021021e +Subproject commit ce70a507c0ae50d7e15e16dd86eac7e4c61042a8 diff --git a/thirdparty/json b/thirdparty/json index 3ed64e50..8deac49f 160000 --- a/thirdparty/json +++ b/thirdparty/json @@ -1 +1 @@ -Subproject commit 3ed64e502a6371311af3c2f309e6525b2f5f6f18 +Subproject commit 8deac49f5039391749606259045acda205fe39ef diff --git a/thirdparty/libtomcrypt b/thirdparty/libtomcrypt index b1fa61d5..a3cc5bf6 160000 --- a/thirdparty/libtomcrypt +++ b/thirdparty/libtomcrypt @@ -1 +1 @@ -Subproject commit b1fa61d594f4a92ebb5e09e665464481a23bfe37 +Subproject commit a3cc5bf62341b7269ace805fa227b701ddae4165 diff --git a/thirdparty/webview.lua b/thirdparty/webview.lua index 5fef3927..20fecd52 100644 --- a/thirdparty/webview.lua +++ b/thirdparty/webview.lua @@ -13,6 +13,7 @@ function webview:link(links) if os.host() == "windows" then links:add("WebView2LoaderStatic") + links:add("gdiplus.lib") filter "platforms:x86" libdirs {