diff --git a/src/ObjWriting/Dumping/AssetDumpingContext.h b/src/ObjWriting/Dumping/AssetDumpingContext.h index f1218446..9e38bf29 100644 --- a/src/ObjWriting/Dumping/AssetDumpingContext.h +++ b/src/ObjWriting/Dumping/AssetDumpingContext.h @@ -3,13 +3,17 @@ #include #include #include +#include +#include "IZoneAssetDumperState.h" #include "Utils/ClassUtils.h" #include "Obj/Gdt/GdtStream.h" #include "Zone/Zone.h" class AssetDumpingContext { + std::unordered_map> m_zone_asset_dumper_states; + public: Zone* m_zone; std::string m_base_path; @@ -18,4 +22,21 @@ public: AssetDumpingContext(); _NODISCARD std::unique_ptr OpenAssetFile(const std::string& fileName) const; + + template + T* GetZoneAssetDumperState() + { + static_assert(std::is_base_of_v, "T must inherit IZoneAssetDumperState"); + // T must also have a public default constructor + + const auto foundEntry = m_zone_asset_dumper_states.find(typeid(T)); + if (foundEntry != m_zone_asset_dumper_states.end()) + return dynamic_cast(foundEntry->second.get()); + + auto newState = std::make_unique(); + newState->SetZone(m_zone); + auto* newStatePtr = newState.get(); + m_zone_asset_dumper_states.emplace(std::make_pair>(typeid(T), std::move(newState))); + return newStatePtr; + } }; diff --git a/src/ObjWriting/Dumping/IZoneAssetDumperState.h b/src/ObjWriting/Dumping/IZoneAssetDumperState.h new file mode 100644 index 00000000..ad12d5e5 --- /dev/null +++ b/src/ObjWriting/Dumping/IZoneAssetDumperState.h @@ -0,0 +1,20 @@ +#pragma once +#include "Zone/Zone.h" + +class IZoneAssetDumperState +{ +protected: + IZoneAssetDumperState() = default; + +public: + virtual ~IZoneAssetDumperState() = default; + IZoneAssetDumperState(const IZoneAssetDumperState& other) = default; + IZoneAssetDumperState(IZoneAssetDumperState&& other) noexcept = default; + IZoneAssetDumperState& operator=(const IZoneAssetDumperState& other) = default; + IZoneAssetDumperState& operator=(IZoneAssetDumperState&& other) noexcept = default; + + virtual void SetZone(Zone* zone) + { + // Do nothing by default + } +}; diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp index 8c36daba..ce642791 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp @@ -3,46 +3,22 @@ #include #include +#include "AssetDumperMenuList.h" #include "ObjWriting.h" #include "Game/IW4/GameAssetPoolIW4.h" #include "Game/IW4/Menu/MenuDumperIW4.h" #include "Menu/AbstractMenuDumper.h" -namespace fs = std::filesystem; - using namespace IW4; -const MenuList* AssetDumperMenuDef::GetParentMenuList(XAssetInfo* asset) +std::string AssetDumperMenuDef::GetPathForMenu(menu::MenuDumpingZoneState* zoneState, XAssetInfo* asset) { - const auto* menu = asset->Asset(); - const auto* gameAssetPool = dynamic_cast(asset->m_zone->m_pools.get()); - for (const auto* menuList : *gameAssetPool->m_menu_list) - { - const auto* menuListAsset = menuList->Asset(); + const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(asset->Asset()); - for (auto menuIndex = 0; menuIndex < menuListAsset->menuCount; menuIndex++) - { - if (menuListAsset->menus[menuIndex] == menu) - return menuListAsset; - } - } - - return nullptr; -} - -std::string AssetDumperMenuDef::GetPathForMenu(XAssetInfo* asset) -{ - const auto* list = GetParentMenuList(asset); - - if (!list) + if (menuDumpingState == zoneState->m_menu_dumping_state_map.end()) return "ui_mp/" + std::string(asset->Asset()->window.name) + ".menu"; - const fs::path p(list->name); - std::string parentPath; - if (p.has_parent_path()) - parentPath = p.parent_path().string() + "/"; - - return parentPath + std::string(asset->Asset()->window.name) + ".menu"; + return menuDumpingState->second.m_path; } bool AssetDumperMenuDef::ShouldDump(XAssetInfo* asset) @@ -53,16 +29,17 @@ bool AssetDumperMenuDef::ShouldDump(XAssetInfo* asset) void AssetDumperMenuDef::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { const auto* menu = asset->Asset(); - const auto menuFilePath = GetPathForMenu(asset); + auto* zoneState = context.GetZoneAssetDumperState(); - if(ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST)) + if(!ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST)) { - // Don't dump menu file separately if the name matches the menu list - const auto* menuListParent = GetParentMenuList(asset); - if (menuListParent && menuFilePath == menuListParent->name) - return; + // Make sure menu paths based on menu lists are created + const auto* gameAssetPool = dynamic_cast(asset->m_zone->m_pools.get()); + for (auto* menuListAsset : *gameAssetPool->m_menu_list) + AssetDumperMenuList::CreateDumpingStateForMenuList(zoneState, menuListAsset->Asset()); } + const auto menuFilePath = GetPathForMenu(zoneState, asset); const auto assetFile = context.OpenAssetFile(menuFilePath); if (!assetFile) diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.h index 5dc78fda..3fa4a8d2 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.h +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.h @@ -2,13 +2,13 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" +#include "Menu/MenuDumpingZoneState.h" namespace IW4 { class AssetDumperMenuDef final : public AbstractAssetDumper { - static const MenuList* GetParentMenuList(XAssetInfo* asset); - static std::string GetPathForMenu(XAssetInfo* asset); + static std::string GetPathForMenu(menu::MenuDumpingZoneState* zoneState, XAssetInfo* asset); protected: bool ShouldDump(XAssetInfo* asset) override; diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.cpp index 935eac92..86c112d7 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.cpp @@ -69,31 +69,21 @@ void AssetDumperMenuList::DumpFunctions(MenuDumper& menuDumper, const MenuList* } } -void AssetDumperMenuList::DumpMenus(MenuDumper& menuDumper, const MenuList* menuList) +void AssetDumperMenuList::DumpMenus(MenuDumper& menuDumper, menu::MenuDumpingZoneState* zoneState, const MenuList* menuList) { - const fs::path p(menuList->name); - - std::string parentPath; - if (p.has_parent_path()) - parentPath = p.parent_path().string() + "/"; - for (auto menuNum = 0; menuNum < menuList->menuCount; menuNum++) { const auto* menu = menuList->menus[menuNum]; - const auto* menuAssetName = menu->window.name; - if (menuAssetName && menuAssetName[0] == ',') - menuAssetName = &menuAssetName[1]; - std::ostringstream ss; - ss << parentPath << menuAssetName << ".menu"; - - const auto menuName = ss.str(); + const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(menu); + if(menuDumpingState == zoneState->m_menu_dumping_state_map.end()) + continue; // If the menu was embedded directly as menu list write its data in the menu list file - if (menuName == menuList->name) + if (menuDumpingState->second.m_alias_menu_list == menuList) menuDumper.WriteMenu(menu); else - menuDumper.IncludeMenu(ss.str()); + menuDumper.IncludeMenu(menuDumpingState->second.m_path); } } @@ -110,6 +100,8 @@ void AssetDumperMenuList::DumpAsset(AssetDumpingContext& context, XAssetInfo(); + MenuDumper menuDumper(*assetFile); menuDumper.Start(); @@ -117,7 +109,71 @@ void AssetDumperMenuList::DumpAsset(AssetDumpingContext& context, XAssetInfowindow.name; + + if (!menuAssetName) + return ""; + + if (menuAssetName[0] == ',') + menuAssetName = &menuAssetName[1]; + + std::ostringstream ss; + ss << menuListParentPath << menuAssetName << ".menu"; + + return ss.str(); +} + +void AssetDumperMenuList::CreateDumpingStateForMenuList(menu::MenuDumpingZoneState* zoneState, const MenuList* menuList) +{ + if (menuList->menuCount <= 0 || menuList->menus == nullptr || menuList->name == nullptr) + return; + + const std::string menuListName(menuList->name); + const fs::path p(menuListName); + std::string parentPath; + if (p.has_parent_path()) + parentPath = p.parent_path().string() + "/"; + + for(auto i = 0; i < menuList->menuCount; i++) + { + auto* menu = menuList->menus[i]; + + if(menu == nullptr) + continue; + + auto existingState = zoneState->m_menu_dumping_state_map.find(menu); + if(existingState == zoneState->m_menu_dumping_state_map.end()) + { + auto menuPath = PathForMenu(parentPath, menu); + const auto isTheSameAsMenuList = menuPath == menuListName; + zoneState->CreateMenuDumpingState(menu, std::move(menuPath), isTheSameAsMenuList ? menuList : nullptr); + } + else if(existingState->second.m_alias_menu_list == nullptr) + { + auto menuPath = PathForMenu(parentPath, menu); + const auto isTheSameAsMenuList = menuPath == menuListName; + if (isTheSameAsMenuList) + { + existingState->second.m_alias_menu_list = menuList; + existingState->second.m_path = std::move(menuPath); + } + } + } +} + +void AssetDumperMenuList::DumpPool(AssetDumpingContext& context, AssetPool* pool) +{ + auto* zoneState = context.GetZoneAssetDumperState(); + + for(auto* asset : *pool) + CreateDumpingStateForMenuList(zoneState, asset->Asset()); + + AbstractAssetDumper::DumpPool(context, pool); +} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.h index 043a3fd8..eb75e852 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.h +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuList.h @@ -3,6 +3,7 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" #include "Game/IW4/Menu/MenuDumperIW4.h" +#include "Menu/MenuDumpingZoneState.h" namespace IW4 { @@ -11,10 +12,16 @@ namespace IW4 static std::vector GetAllUniqueExpressionSupportingData(const MenuList* menuList); static void DumpFunctions(MenuDumper& menuDumper, const MenuList* menuList); - static void DumpMenus(MenuDumper& menuDumper, const MenuList* menuList); + static void DumpMenus(MenuDumper& menuDumper, menu::MenuDumpingZoneState* zoneState, const MenuList* menuList); + + static std::string PathForMenu(const std::string& menuListParentPath, const menuDef_t* menu); protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + + public: + static void CreateDumpingStateForMenuList(menu::MenuDumpingZoneState* zoneState, const MenuList* menuList); + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; }; } diff --git a/src/ObjWriting/Menu/MenuDumpingZoneState.cpp b/src/ObjWriting/Menu/MenuDumpingZoneState.cpp new file mode 100644 index 00000000..318bc7eb --- /dev/null +++ b/src/ObjWriting/Menu/MenuDumpingZoneState.cpp @@ -0,0 +1,14 @@ +#include "MenuDumpingZoneState.h" + +using namespace menu; + +MenuDumpingZoneState::MenuDumpingState::MenuDumpingState(std::string path, const void* aliasMenuList) + : m_path(std::move(path)), + m_alias_menu_list(aliasMenuList) +{ +} + +void MenuDumpingZoneState::CreateMenuDumpingState(const void* menuDef, std::string path, const void* aliasMenuList) +{ + m_menu_dumping_state_map.emplace(std::make_pair(menuDef, MenuDumpingState(std::move(path), aliasMenuList))); +} diff --git a/src/ObjWriting/Menu/MenuDumpingZoneState.h b/src/ObjWriting/Menu/MenuDumpingZoneState.h new file mode 100644 index 00000000..677891fd --- /dev/null +++ b/src/ObjWriting/Menu/MenuDumpingZoneState.h @@ -0,0 +1,24 @@ +#pragma once +#include + +#include "Dumping/IZoneAssetDumperState.h" + +namespace menu +{ + class MenuDumpingZoneState final : public IZoneAssetDumperState + { + public: + class MenuDumpingState + { + public: + std::string m_path; + const void* m_alias_menu_list; + + MenuDumpingState(std::string path, const void* aliasMenuList); + }; + + std::map m_menu_dumping_state_map; + + void CreateMenuDumpingState(const void* menuDef, std::string path, const void* aliasMenuList); + }; +}