From c3528b3911d2a5dc0d80879dc3ab92c31fae2b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 27 Mar 2021 12:35:06 +0100 Subject: [PATCH] Implement most of aocManager And fix FileDevicePrefix's userdata param type --- data/uking_functions.csv | 74 +-- lib/NintendoSDK | 2 +- lib/sead | 2 +- src/Game/DLC/aocManager.cpp | 489 +++++++++++++++++- src/Game/DLC/aocManager.h | 66 ++- .../Resource/resResourceMgrTask.cpp | 7 +- src/KingSystem/Resource/resResourceMgrTask.h | 23 +- 7 files changed, 605 insertions(+), 58 deletions(-) diff --git a/data/uking_functions.csv b/data/uking_functions.csv index be737134..a402f9f6 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -437,7 +437,7 @@ 0x000000710000ded8,sub_710000DED8,1412, 0x000000710000e45c,Dragon::m78,164, 0x000000710000e500,sub_710000E500,756, -0x000000710000e7f4,_ZN4sead15FixedSafeStringILi128EEaSERKNS_14SafeStringBaseIcEE,240, +0x000000710000e7f4,_ZN4sead15FixedSafeStringILi128EEaSERKNS_14SafeStringBaseIcEE,240,_ZN4sead15FixedSafeStringILi16EEaSERKNS_14SafeStringBaseIcEE 0x000000710000e8e4,sub_710000E8E4,420, 0x000000710000ea88,sub_710000EA88,456, 0x000000710000ec50,sub_710000EC50,84, @@ -501,7 +501,7 @@ 0x0000007100012538,sub_7100012538,68, 0x000000710001257c,nullsub_45,4, 0x0000007100012580,_ZNK4sead15RuntimeTypeInfo6DeriveINS_13TaskParameterEE9isDerivedEPKNS0_9InterfaceE,140, -0x000000710001260c,j__ZdlPv_6,4, +0x000000710001260c,j__ZdlPv_6,4,_ZN4sead15FixedSafeStringILi16EED0Ev 0x0000007100012610,_ZNK4sead14SafeStringBaseIcE9findIndexERKS1_,468,_ZNK4sead14SafeStringBaseIcE9findIndexERKS1_ 0x00000071000127e4,_ZN4sead18Matrix34CalcCommonIfE7inverseERNS_9BaseMtx34IfEERKS3_,336, 0x0000007100012934,j__ZdlPv_7,4, @@ -53187,8 +53187,8 @@ 0x00000071008af674,sub_71008AF674,204, 0x00000071008af740,sub_71008AF740,92, 0x00000071008af79c,nullsub_2626,4, -0x00000071008af7a0,j__ZdlPv_394,4, -0x00000071008af7a4,_ZN4sead19FixedSafeStringBaseIcLi93EEaSERKNS_14SafeStringBaseIcEE,240, +0x00000071008af7a0,j__ZdlPv_394,4,_ZN4sead15FixedSafeStringILi73EED0Ev +0x00000071008af7a4,_ZN4sead15FixedSafeStringILi73EEaSERKNS_14SafeStringBaseIcEE,240,_ZN4sead15FixedSafeStringILi73EEaSERKNS_14SafeStringBaseIcEE 0x00000071008af894,sub_71008AF894,140, 0x00000071008af920,evt::S7EventFlow::ctor,536, 0x00000071008afb38,evt::S7EventFlow::m4,244, @@ -74403,48 +74403,48 @@ 0x0000007100d68d94,printLifeRecoverInfo,192, 0x0000007100d68e54,sub_7100D68E54,188, 0x0000007100d68f10,nullsub_5545,4, -0x0000007100d68f14,sub_7100D68F14,412, +0x0000007100d68f14,sub_7100D68F14,412,_GLOBAL__sub_I_aocManager.cpp 0x0000007100d690b0,aocManager::disposer::dtor,92,_ZN5uking3aoc7Manager18SingletonDisposer_D2Ev 0x0000007100d6910c,aocManager::disposer::dtorDelete,100,_ZN5uking3aoc7Manager18SingletonDisposer_D0Ev 0x0000007100d69170,aocManager::createInstance,128,_ZN5uking3aoc7Manager14createInstanceEPN4sead4HeapE 0x0000007100d691f0,aocManager::ctor,1560,_ZN5uking3aoc7ManagerC1Ev -0x0000007100d69808,aocManagerInitFlagsFuncPtr,4, +0x0000007100d69808,aocManagerInitFlagsFuncPtr,4,_ZN5uking3aoc7Manager11onGdtReinitEPN4ksys3gdt7Manager11ReinitEventE 0x0000007100d6980c,aocManager::dtor,380,_ZN5uking3aoc7ManagerD1Ev -0x0000007100d69988,aocManager::init,256, -0x0000007100d69a88,aocManager::loadVersionFile,204, -0x0000007100d69b54,sub_7100D69B54,180, -0x0000007100d69c08,aocManager::insertPrefixMap,152, -0x0000007100d69ca0,aocManager::unloadAocMainFieldPack,68, -0x0000007100d69ce4,aocManager::getFileDevForAocMap,324, -0x0000007100d69e28,aocManager::archiveHasFile,292, -0x0000007100d69f4c,isAoCMap,1064, -0x0000007100d6a374,aocManager::getFileDevAndPackResForMubin,376, -0x0000007100d6a4ec,aocManager::getFileDevForPhysicsStaticCompound,168, -0x0000007100d6a594,aocManager::getFileDevForAocTeraMesh,104, -0x0000007100d6a5fc,aocManager::getFileDevForAocNavMesh,168, -0x0000007100d6a6a4,aocManager::getFileDevIfTerrain,104, -0x0000007100d6a70c,aocManager::getFileDevIfGameAocField,104, -0x0000007100d6a774,aocManager::getAocDevIfPathContainsUIStaffRollDLC,104, -0x0000007100d6a7dc,aoc::isAocDungeon,516, -0x0000007100d6a9e0,aoc::isAocMap,748, -0x0000007100d6accc,stringIsAocField,140, -0x0000007100d6ad58,aocManager::getAocFileDeviceIfIsNotNormalDungeon,448, -0x0000007100d6af18,aocManager::addPrefixForArchiveFiles,172, -0x0000007100d6afc4,aocManager::movieDemoPathStuff,660, +0x0000007100d69988,aocManager::init,256,_ZN5uking3aoc7Manager4initEPN4sead4HeapE +0x0000007100d69a88,aocManager::loadVersionFile,204,_ZN5uking3aoc7Manager15loadVersionFileEv +0x0000007100d69b54,sub_7100D69B54,180,_ZN5uking3aoc7Manager20loadAocMainFieldPackEPN4ksys12OverlayArenaE +0x0000007100d69c08,aocManager::insertPrefixMap,152,_ZN5uking3aoc7Manager24registerAocMainFieldPackEv +0x0000007100d69ca0,aocManager::unloadAocMainFieldPack,68,_ZN5uking3aoc7Manager22unloadAocMainFieldPackEv +0x0000007100d69ce4,aocManager::getFileDevForAocMap,324,_ZNK5uking3aoc7Manager23getFileDeviceForMapFileERKN4sead14SafeStringBaseIcEE +0x0000007100d69e28,aocManager::archiveHasFile,292,_ZNK5uking3aoc7Manager14aocPackHasFileERKN4sead14SafeStringBaseIcEE +0x0000007100d69f4c,isAoCMap,1064,_ZNK5uking3aoc7Manager9isAocFileERKN4sead14SafeStringBaseIcEES6_S6_S6_S6_ +0x0000007100d6a374,aocManager::getFileDevAndPackResForMubin,376,_ZN5uking3aoc7Manager19getFileDeviceForMapEPPN4sead10FileDeviceEPPN4ksys3res6HandleERKNS2_14SafeStringBaseIcEE +0x0000007100d6a4ec,aocManager::getFileDevForPhysicsStaticCompound,168,_ZNK5uking3aoc7Manager30getFileDeviceForStaticCompoundERKN4sead14SafeStringBaseIcEE +0x0000007100d6a594,aocManager::getFileDevForAocTeraMesh,104,_ZNK5uking3aoc7Manager24getFileDeviceForTeraMeshERKN4sead14SafeStringBaseIcEE +0x0000007100d6a5fc,aocManager::getFileDevForAocNavMesh,168,_ZNK5uking3aoc7Manager23getFileDeviceForNavMeshERKN4sead14SafeStringBaseIcEE +0x0000007100d6a6a4,aocManager::getFileDevIfTerrain,104,_ZNK5uking3aoc7Manager23getFileDeviceForTerrainERKN4sead14SafeStringBaseIcEE +0x0000007100d6a70c,aocManager::getFileDevIfGameAocField,104,_ZNK5uking3aoc7Manager20getFileDeviceForGameERKN4sead14SafeStringBaseIcEE +0x0000007100d6a774,aocManager::getAocDevIfPathContainsUIStaffRollDLC,104,_ZNK5uking3aoc7Manager18getFileDeviceForUIERKN4sead14SafeStringBaseIcEE +0x0000007100d6a7dc,aoc::isAocDungeon,516,_ZN5uking3aoc7Manager12isAocDungeonERKN4sead14SafeStringBaseIcEE +0x0000007100d6a9e0,aoc::isAocMap,748,_ZN5uking3aoc7Manager8isAocMapERKN4sead14SafeStringBaseIcEES6_ +0x0000007100d6accc,stringIsAocField,140,_ZN5uking3aoc7Manager10isAocFieldERKN4sead14SafeStringBaseIcEE +0x0000007100d6ad58,aocManager::getAocFileDeviceIfIsNotNormalDungeon,448,_ZNK5uking3aoc7Manager27getFileDeviceForDungeonPackERKN4sead14SafeStringBaseIcEE +0x0000007100d6af18,aocManager::addPrefixForArchiveFiles,172,_ZN5uking3aoc7Manager15registerAocPackEPN4ksys3res6HandleE +0x0000007100d6afc4,aocManager::movieDemoPathStuff,660,_ZNK5uking3aoc7Manager15changeMoviePathERN4sead22BufferedSafeStringBaseIcEE 0x0000007100d6b258,aocManager::parseMainFieldStaticForDlc,2488, 0x0000007100d6bc10,aocManager::x,1224, -0x0000007100d6c0d8,aocManager::parseVersionString,916, -0x0000007100d6c46c,aocManager::Version::ready,384, -0x0000007100d6c5ec,aocManager::initApp,48, -0x0000007100d6c61c,aocManager::initFlags,460, +0x0000007100d6c0d8,aocManager::parseVersionString,916,_ZN5uking3aoc7Manager12parseVersionEv? +0x0000007100d6c46c,aocManager::Version::ready,384,_ZN5uking3aoc7Manager11VersionFile11readVersionEv +0x0000007100d6c5ec,aocManager::initApp,48,_ZN5uking3aoc7Manager12initGameDataEv +0x0000007100d6c61c,aocManager::initFlags,460,_ZN5uking3aoc7Manager11reinitFlagsEv 0x0000007100d6c7e8,aocManager::setLatestPlayedGameDataFlags,220, -0x0000007100d6c8c4,getAocFlagString,428, -0x0000007100d6ca70,j__ZdlPv_858,4, -0x0000007100d6ca74,_ZN4sead19FixedSafeStringBaseIcLi107EEaSERKNS_14SafeStringBaseIcEE,240, +0x0000007100d6c8c4,getAocFlagString,428,_ZN5uking3aoc7Manager12GameDataFlag5text_Ei +0x0000007100d6ca70,j__ZdlPv_858,4,_ZN4sead19FixedSafeStringBaseIcLi73EED0Ev +0x0000007100d6ca74,_ZN4sead19FixedSafeStringBaseIcLi73EEaSERKNS_14SafeStringBaseIcEE,240,_ZN4sead19FixedSafeStringBaseIcLi73EEaSERKNS_14SafeStringBaseIcEE 0x0000007100d6cb64,sub_7100D6CB64,48,_ZN4sead9Delegate1IN5uking3aoc7ManagerEPN4ksys3gdt7Manager11ReinitEventEE6invokeES8_ 0x0000007100d6cb94,sub_7100D6CB94,92,_ZNK4sead9Delegate1IN5uking3aoc7ManagerEPN4ksys3gdt7Manager11ReinitEventEE5cloneEPNS_4HeapE -0x0000007100d6cbf0,j__ZdlPv_859,4, -0x0000007100d6cbf4,_ZN4sead15FixedSafeStringILi153EEaSERKNS_14SafeStringBaseIcEE,240, +0x0000007100d6cbf0,j__ZdlPv_859,4,_ZN4sead15FixedSafeStringILi4EED0Ev +0x0000007100d6cbf4,_ZN4sead15FixedSafeStringILi153EEaSERKNS_14SafeStringBaseIcEE,240,_ZN4sead15FixedSafeStringILi4EEaSERKNS_14SafeStringBaseIcEE 0x0000007100d6cce4,aoc2::Instance::dtor,92,_ZN5uking4aoc218SingletonDisposer_D2Ev 0x0000007100d6cd40,aoc2::Instance::dtorDelete,100,_ZN5uking4aoc218SingletonDisposer_D0Ev 0x0000007100d6cda4,aoc2::createInstance,136,_ZN5uking4aoc214createInstanceEPN4sead4HeapE @@ -93189,7 +93189,7 @@ 0x000000710120a9ec,return_0_0,8,_ZNK4ksys3res15ResourceMgrTask10isHostPathERKN4sead14SafeStringBaseIcEE 0x000000710120a9f4,res::ResourceMgrTask::dropSFromExtensionForBfevflBcamanimAndBarslist,756,_ZNK4ksys3res15ResourceMgrTask26dropSFromExtensionIfNeededERKN4sead14SafeStringBaseIcEERNS2_22BufferedSafeStringBaseIcEEiS6_ 0x000000710120ace8,res::ResourceMgrTask::unloadSeadResource,84,_ZN4ksys3res15ResourceMgrTask18unloadSeadResourceEPN4sead8ResourceE -0x000000710120ad3c,res::ResourceMgrTask::getResourceSize,328,_ZNK4ksys3res15ResourceMgrTask15getResourceSizeERKN4sead14SafeStringBaseIcEEPNS2_10FileDeviceE +0x000000710120ad3c,res::ResourceMgrTask::getResourceSize,328,_ZNK4ksys3res15ResourceMgrTask15getResourceSizeERKN4sead14SafeStringBaseIcEEPv 0x000000710120ae84,res::ResourceMgrTask::insertIntoFileDeviceToPrefixMap,116,_ZN4ksys3res15ResourceMgrTask24registerFileDevicePrefixERNS0_16FileDevicePrefixE 0x000000710120aef8,res::ResourceMgrTask::eraseFromFileDeviceToPrefixMap,104,_ZN4ksys3res15ResourceMgrTask26deregisterFileDevicePrefixERNS0_16FileDevicePrefixE 0x000000710120af60,res::ResourceMgrTask::__auto3,164,_ZN4ksys3res15ResourceMgrTask27callStubbedFunctionOnArenasEv diff --git a/lib/NintendoSDK b/lib/NintendoSDK index 9d3e4be6..7ea03832 160000 --- a/lib/NintendoSDK +++ b/lib/NintendoSDK @@ -1 +1 @@ -Subproject commit 9d3e4be6c4404efa6cf039bd8b93ad98609e3480 +Subproject commit 7ea03832ecf296f4ad2331094398ca07fa92b76a diff --git a/lib/sead b/lib/sead index 77a3f152..dfbcf903 160000 --- a/lib/sead +++ b/lib/sead @@ -1 +1 @@ -Subproject commit 77a3f1521986cdbe957ee26339c2659400f9e304 +Subproject commit dfbcf903b8543c5f85fe166eaf8d4bc92c9c93ba diff --git a/src/Game/DLC/aocManager.cpp b/src/Game/DLC/aocManager.cpp index 947bbba4..62e09255 100644 --- a/src/Game/DLC/aocManager.cpp +++ b/src/Game/DLC/aocManager.cpp @@ -1,13 +1,53 @@ #include "Game/DLC/aocManager.h" +#include #include +#include +#include +#include "KingSystem/Resource/resLoadRequest.h" +#include "KingSystem/Resource/resResourceMgrTask.h" +#include "KingSystem/Utils/InitTimeInfo.h" #include "KingSystem/Utils/SafeDelete.h" #ifdef NNSDK +#include +#include #include +#include #endif namespace uking::aoc { +namespace { + +struct DungeonInfo { + int number; + sead::SafeString flag; +}; + +ksys::util::InitConstants sInitConstants; +DungeonInfo sDungeonInfoData[] = { + {120, "Defeat_OneHitDungeon001"}, + {121, "BalladOfHeroGerudo_AppearDungeon03"}, + {122, "Defeat_OneHitDungeon002"}, + {123, "Defeat_OneHitDungeon003"}, + {124, "BalladOfHeroZora_AppearDungeon01"}, + {125, "BalladOfHeroZora_AppearDungeon02"}, + {126, "BalladOfHeroZora_AppearDungeon03"}, + {127, "BalladOfHeroRito_TargetHittingSuccess"}, + {128, "BalladOfHeroRito_AppearDungeon03"}, + {129, "BalladOfHeroRito_AppearDungeon02"}, + {130, "BalladOfHeroGoron_FirstKillGolemR"}, + {131, "BalladOfHeroGoron_AppearDungeon01"}, + {132, "BalladOfHeroGoron_AppearDungeon03"}, + {133, "BalladOfHeroGerudo_AppearDungeon02"}, + {134, "BalladOfHeroGerudo_AppearDungeon01"}, + {135, "Defeat_OneHitDungeon004"}, +}; +sead::Buffer sDungeonInfo{sDungeonInfoData}; +constexpr int NumDungeons = 16; + +} // namespace + SEAD_SINGLETON_DISPOSER_IMPL(Manager) Manager::Manager() : mGdtReinitSlot{this, &Manager::onGdtReinit} { @@ -18,8 +58,8 @@ Manager::~Manager() { if (auto* gdm = ksys::gdt::Manager::instance()) gdm->removeReinitCallback(mGdtReinitSlot); - mFileDevicePrefix.deregister(); - mFileDevicePrefix2.deregister(); + mAocMainFieldPackPrefix.deregister(); + mAocPackPrefix.deregister(); mVersionFileDevPrefix.deregister(); if (mFileDevice) { @@ -43,4 +83,449 @@ void Manager::resetFlags() { mFlagHasAocVer3 = ksys::gdt::InvalidHandle; } +void Manager::init(sead::Heap* heap) { +#ifdef NNSDK + std::array dlc_info; + if (nn::aoc::ListAddOnContent(dlc_info.data(), 0, dlc_info.size()) == dlc_info.size() && + (dlc_info[0] == 1 || dlc_info[1] == 1)) { + size_t cache_size = 0; + nn::fs::QueryMountAddOnContentCacheSize(&cache_size, 1); + const size_t cache_size_ = cache_size; + mAocFsCache = static_cast(heap->tryAlloc(cache_size_, sizeof(void*))); + + nn::fs::MountAddOnContent("aoc", 1, mAocFsCache, cache_size); + + mFileDevice = new (heap) sead::NinAocFileDevice("aoc"); + sead::FileDeviceMgr::instance()->mount(mFileDevice, "aoc"); + loadVersionFile(); + } +#endif +} + +void Manager::loadVersionFile() { + if (!mFileDevice) + return; + + mVersionFileDevPrefix.registerPrefix("Aoc/0010/", mFileDevice, false); + + ksys::res::LoadRequest req; + req.mRequester = "aocManager"; + req._26 = false; + req.mAocFileDevice = mFileDevice; + const sead::SafeString path = "System/AocVersion.txt"; + mVersionFile->file_handle.requestLoad(path, &req); +} + +void Manager::loadAocMainFieldPack(ksys::OverlayArena* arena) { + if (!hasAoc3()) + return; + + auto* device = mFileDevice; + if (!device) + return; + + ksys::res::LoadRequest req; + req.mRequester = "aocManager"; + req._8 = true; + req._26 = false; + req.mAocFileDevice = device; + req.mArena = arena; + mAocMainFieldPack.requestLoad("Pack/AocMainField.pack", &req); +} + +void Manager::registerAocMainFieldPack() { + mAocMainFieldPack.waitForReady(); + mAocMainFieldPack.parseResource(nullptr); + if (mAocMainFieldPack.isSuccess()) + mAocMainFieldPackPrefix.registerPrefix("Aoc/0010/", mAocMainFieldPack.getResource(), false); +} + +void Manager::unloadAocMainFieldPack() { + mAocMainFieldPackPrefix.deregister(); + mAocMainFieldPack.requestUnload(); +} + +sead::FileDevice* Manager::getFileDeviceForMapFile(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + auto* device = mFileDevice; + + if (aocPackHasFile(path)) + return nullptr; + + if (path.startsWith("Map/MainField/")) + return device; + + if (hasAoc3() && path.startsWith("Map/MainFieldDungeon/")) + return device; + + if (isAocFile(path, "Map/", "Map/AocField/", "Map/MainFieldDungeon/", "Map/CDungeon/")) + return device; + + return nullptr; +} + +bool Manager::getFileDeviceForMap(sead::FileDevice** p_file_device, ksys::res::Handle** p_handle, + const sead::SafeString& path) { + if (!hasAoc2()) + return false; + + auto* device = mFileDevice; + + if (path.startsWith("Map/MainField/")) { + if (mAocMainFieldPack.isSuccess()) + *p_handle = &mAocMainFieldPack; + else + *p_file_device = device; + return true; + } + + if (aocPackHasFile(path)) + return false; + + if (hasAoc3() && path.startsWith("Map/MainFieldDungeon/")) { + *p_file_device = device; + return true; + } + + if (isAocFile(path, "Map/", "Map/AocField/", "Map/MainFieldDungeon/", "Map/CDungeon/")) { + *p_file_device = device; + return true; + } + + return false; +} + +sead::FileDevice* Manager::getFileDeviceForStaticCompound(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + auto* device = mFileDevice; + + if (aocPackHasFile(path)) + return nullptr; + + if (isAocFile(path, "Physics/StaticCompound/", "Physics/StaticCompound/AocField/", + "Physics/StaticCompound/MainFieldDungeon/", "Physics/StaticCompound/CDungeon/")) { + return device; + } + + return nullptr; +} + +sead::FileDevice* Manager::getFileDeviceForTeraMesh(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + if (path.startsWith("Physics/TeraMeshRigidBody/AocField/")) + return mFileDevice; + + return nullptr; +} + +sead::FileDevice* Manager::getFileDeviceForNavMesh(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + auto* device = mFileDevice; + + if (aocPackHasFile(path)) + return nullptr; + + if (isAocFile(path, "NavMesh/", "NavMesh/AocField/", "NavMesh/MainFieldDungeon/", + "NavMesh/CDungeon/")) { + return device; + } + + return nullptr; +} + +sead::FileDevice* Manager::getFileDeviceForTerrain(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + if (path.startsWith("Terrain/A/AocField")) + return mFileDevice; + + return nullptr; +} + +sead::FileDevice* Manager::getFileDeviceForGame(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + if (path.startsWith("Game/AocField/")) + return mFileDevice; + + return nullptr; +} + +sead::FileDevice* Manager::getFileDeviceForUI(const sead::SafeString& path) const { + if (!hasAoc3()) + return nullptr; + + if (path.startsWith("UI/StaffRollDLC/")) + return mFileDevice; + + return nullptr; +} + +bool Manager::isAocFile(const sead::SafeString& path, const sead::SafeString& dir, + const sead::SafeString& dir_aoc_field, + const sead::SafeString& dir_main_field_dungeon, + const sead::SafeString& dir_cdungeon) const { + if (!path.startsWith(dir)) + return false; + + if (path.startsWith(dir_aoc_field)) + return true; + + if (path.startsWith(dir_main_field_dungeon)) { + const auto rel_path = path.getPart(dir_main_field_dungeon.calcLength()); + if (rel_path.startsWith("RemainsElectric")) + return false; + if (rel_path.startsWith("RemainsFire")) + return false; + if (rel_path.startsWith("RemainsWater")) + return false; + if (rel_path.startsWith("RemainsWind")) + return false; + return true; + } + + if (path.startsWith(dir_cdungeon)) { + const auto rel_path = path.getPart(dir_cdungeon.calcLength()); + return isAocDungeon(rel_path); + } + + return false; +} + +static bool isAocDungeonNumber(const sead::SafeString& number) { + int num; + sead::FixedSafeString<4> buffer; + buffer.copy(number, 3); + return sead::StringUtil::tryParseS32(&num, buffer, sead::StringUtil::CardinalNumber::Base10) && + num >= 120; +} + +bool Manager::isAocDungeon(const sead::SafeString& map_name) { + if (!map_name.startsWith("Dungeon")) + return true; + const auto number = map_name.getPart(sead::SafeString("Dungeon").calcLength()); + return isAocDungeonNumber(number); +} + +bool Manager::isAocMap(const sead::SafeString& map_type, const sead::SafeString& map_name) { + if (isAocField(map_type)) + return true; + + if (map_type == "MainFieldDungeon") { + if (map_name == "RemainsElectric") + return false; + if (map_name == "RemainsFire") + return false; + if (map_name == "RemainsWater") + return false; + if (map_name == "RemainsWind") + return false; + return true; + } + + if (map_type == "CDungeon") + return isAocDungeon(map_name); + + return false; +} + +bool Manager::isAocField(const sead::SafeString& map_type) { + return map_type == "AocField"; +} + +sead::FileDevice* Manager::getFileDeviceForDungeonPack(const sead::SafeString& path) const { + if (!hasAoc3()) + return nullptr; + + auto* device = mFileDevice; + + const sead::SafeString prefix = "Pack/"; + const auto prefix_len = prefix.calcLength(); + + if (!path.startsWith(prefix)) + return nullptr; + + const auto rel_path = path.getPart(prefix_len); + + if (rel_path.startsWith("Dungeon") && !isAocDungeon(rel_path)) + device = nullptr; + + return device; +} + +void Manager::registerAocPack(ksys::res::Handle* pack) { + mAocPack = pack; + if (pack) { + mAocPackPrefix.registerPrefix("Aoc/0010/", pack->getResource(), true); + } else { + mAocPackPrefix.deregister(); + } +} + +bool Manager::aocPackHasFile(const sead::SafeString& path) const { + if (!mAocPack) + return false; + + const auto* res = sead::DynamicCast(mAocPack->getResource()); + if (!res) + return false; + + sead::FixedStringBuilder<0x81> builder; + builder.copy(path.cstr()); + ksys::res::ResourceMgrTask::instance()->addSExtensionPrefix(builder); + return res->isExistFile(builder); +} + +bool Manager::changeMoviePath(sead::BufferedSafeString& path) const { + if (!hasAoc3()) + return false; + + const sead::SafeString prefix = "Movie/"; + const auto prefix_len = prefix.calcLength(); + if (!path.startsWith(prefix)) + return false; + + const auto rel_path = path.getPart(prefix_len); + if (!rel_path.startsWith("Demo6")) + return false; + + path.prepend("aoc://"); + return true; +} + +bool Manager::parseVersion() { + if (!mVersionFile->readVersion()) + return false; + + if (mVersionFile->string.isEmpty()) { + mVersion = 0; + return true; + } + + { + const int dot_index = mVersionFile->string.findIndex("."); + const int minor_index = dot_index + 1; + if (dot_index <= 0 || minor_index >= mVersionFile->string.calcLength()) { + mVersionFile->string.clear(); + mVersion = 0; + return true; + } + + u32 major, minor; + const auto parse = [&] { + sead::FixedSafeString<16> major_str; + major_str.copy(mVersionFile->string, dot_index); + + constexpr auto base = sead::StringUtil::CardinalNumber::Base10; + if (!sead::StringUtil::tryParseU32(&major, major_str, base)) + return false; + + const auto minor_str = mVersionFile->string.getPart(minor_index); + if (!sead::StringUtil::tryParseU32(&minor, minor_str, base)) + return false; + + return true; + }; + + if (!parse()) { + mVersionFile->string.clear(); + mVersion = 0; + return true; + } + + mVersion = (major << 8) + minor; + } + + checkVersion(); + return true; +} + +// NON_MATCHING: stack and duplicated branch -- volatile variables are painful +void Manager::checkVersion() { + if (mVersion == 0) + return; + + const auto fail = [this](VersionError error) { + mVersionFlags.setBit(error); + mVersion = 0; + }; + + if (mVersion < 0x300) { + fail(VersionError::TooOld); + } else if (mVersion >= 0x400) { + fail(VersionError::TooNew); + } + + mVersionFlags.isOnBit(VersionError(VersionError::TooOld)); + mVersionFlags.isOnBit(VersionError(VersionError::TooNew)); +} + +bool Manager::VersionFile::readVersion() { + if (!file_handle.isFlag2Set()) + return true; + + if (file_handle.isSuccess()) { + auto* res = sead::DynamicCast(file_handle.getResource()); + string.copy(reinterpret_cast(res->getRawData()), res->getRawSize()); + } else { + if (!file_handle.checkLoadStatus()) + return false; + string.clear(); + } + + file_handle.requestUnload(); + return true; +} + +void Manager::initGameData() { + reinitFlags(); + ksys::gdt::Manager::instance()->addReinitCallback(mGdtReinitSlot); +} + +static const sead::SafeString& getDungeonFlag(int number) { + for (auto it = sDungeonInfo.begin(); it != sDungeonInfo.end(); ++it) { + if (it->number == number) + return it->flag; + } + return sead::SafeString::cEmptyString; +} + +void Manager::reinitFlags() { + mFlagAocVerAtLastPlay = ksys::gdt::Manager::instance()->getS32Handle("AoCVerAtLastPlay"); + mFlagLatestAocVerPlayed = ksys::gdt::Manager::instance()->getS32Handle("LatestAoCVerPlayed"); + mFlagHasAocVer1 = + ksys::gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer1)); + mFlagHasAocVer2 = + ksys::gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer2)); + mFlagHasAocVer3 = + ksys::gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer3)); + + for (int i = 0; i < mDLCPositions.size(); ++i) { + ksys::gdt::FlagHandle handle = ksys::gdt::InvalidHandle; + + if (i < NumDungeons) { + if (mDLCPositions[i].flag_handle == ksys::gdt::InvalidHandle) + continue; + + handle = ksys::gdt::Manager::instance()->getBoolHandle(getDungeonFlag(i + 120)); + } + + mDLCPositions[i].flag_handle = handle; + } +} + +void Manager::onGdtReinit(ksys::gdt::Manager::ReinitEvent* event) { + reinitFlags(); +} + } // namespace uking::aoc diff --git a/src/Game/DLC/aocManager.h b/src/Game/DLC/aocManager.h index bb201944..aed6f9fa 100644 --- a/src/Game/DLC/aocManager.h +++ b/src/Game/DLC/aocManager.h @@ -3,14 +3,19 @@ #include #include #include +#include #include #include -#include +#include #include "KingSystem/GameData/gdtManager.h" #include "KingSystem/Resource/resHandle.h" #include "KingSystem/Resource/resResourceMgrTask.h" #include "KingSystem/Utils/Types.h" +namespace ksys { +class OverlayArena; +} + namespace sead { class FileDevice; } @@ -24,6 +29,8 @@ class Manager { ~Manager(); public: + void init(sead::Heap* heap); + u32 getVersion() const { return mVersion; } /// @return Whether the Master Trials DLC is supported. @@ -32,10 +39,44 @@ public: /// @return Whether the Champion's Ballad DLC is supported. bool hasAoc3() const { return mVersion >= 0x300; } -private: - enum class Flag : u8 { + void loadAocMainFieldPack(ksys::OverlayArena* arena); + void registerAocMainFieldPack(); + void unloadAocMainFieldPack(); - }; + sead::FileDevice* getFileDeviceForMapFile(const sead::SafeString& path) const; + bool getFileDeviceForMap(sead::FileDevice** p_file_device, ksys::res::Handle** p_handle, + const sead::SafeString& path); + sead::FileDevice* getFileDeviceForStaticCompound(const sead::SafeString& path) const; + sead::FileDevice* getFileDeviceForTeraMesh(const sead::SafeString& path) const; + sead::FileDevice* getFileDeviceForNavMesh(const sead::SafeString& path) const; + sead::FileDevice* getFileDeviceForTerrain(const sead::SafeString& path) const; + sead::FileDevice* getFileDeviceForGame(const sead::SafeString& path) const; + sead::FileDevice* getFileDeviceForUI(const sead::SafeString& path) const; + + bool isAocFile(const sead::SafeString& path, const sead::SafeString& dir, + const sead::SafeString& dir_aoc_field, + const sead::SafeString& dir_main_field_dungeon, + const sead::SafeString& dir_cdungeon) const; + static bool isAocDungeon(const sead::SafeString& map_name); + static bool isAocMap(const sead::SafeString& map_type, const sead::SafeString& map_name); + static bool isAocField(const sead::SafeString& map_type); + + sead::FileDevice* getFileDeviceForDungeonPack(const sead::SafeString& path) const; + void registerAocPack(ksys::res::Handle* pack); + bool aocPackHasFile(const sead::SafeString& path) const; + + bool changeMoviePath(sead::BufferedSafeString & path) const; + + void parseAocMainFieldStaticInfo(const al::ByamlIter& iter); + // TODO: figure out what this does + bool getSomePos(sead::Vector3f* translate, u32* p_mask, u32* mask); + + bool parseVersion(); + void setGameDataFlags() const; + +private: + SEAD_ENUM(GameDataFlag, AoCVerAtLastPlay ,LatestAoCVerPlayed ,HasAoCVer1 ,HasAoCVer2 ,HasAoCVer3) + SEAD_ENUM(VersionError, TooNew, TooOld) struct DLCPosition { sead::Vector3f translate = sead::Vector3f::zero; @@ -47,12 +88,19 @@ private: KSYS_CHECK_SIZE_NX150(DLCPosition, 0x2c); struct VersionFile { + bool readVersion(); + ksys::res::Handle file_handle; sead::FixedSafeString<16> string; }; KSYS_CHECK_SIZE_NX150(VersionFile, 0x78); + void loadVersionFile(); + void checkVersion(); + void resetFlags(); + void initGameData(); + void reinitFlags(); void onGdtReinit(ksys::gdt::Manager::ReinitEvent* event); sead::FileDevice* mFileDevice{}; @@ -61,10 +109,10 @@ private: sead::StorageFor mVersionFile{sead::ZeroInitializeTag{}}; u32 mVersion{}; - ksys::res::Handle mAocMapMainFieldPack; - ksys::res::FileDevicePrefix mFileDevicePrefix; - ksys::res::Handle* mAocArchive{}; - ksys::res::FileDevicePrefix mFileDevicePrefix2; + ksys::res::Handle mAocMainFieldPack; + ksys::res::FileDevicePrefix mAocMainFieldPackPrefix; + ksys::res::Handle* mAocPack{}; + ksys::res::FileDevicePrefix mAocPackPrefix; sead::SafeArray mDLCPositions; @@ -75,7 +123,7 @@ private: ksys::gdt::FlagHandle mFlagHasAocVer3{}; ksys::gdt::Manager::ReinitSignal::Slot mGdtReinitSlot; - sead::TypedBitFlag mFlags{}; + sead::BitFlag8 mVersionFlags{}; #ifdef NNSDK u8* mAocFsCache{}; #endif diff --git a/src/KingSystem/Resource/resResourceMgrTask.cpp b/src/KingSystem/Resource/resResourceMgrTask.cpp index b883c84a..33bb0a49 100644 --- a/src/KingSystem/Resource/resResourceMgrTask.cpp +++ b/src/KingSystem/Resource/resResourceMgrTask.cpp @@ -421,15 +421,14 @@ void ResourceMgrTask::unloadSeadResource(sead::Resource* resource) { stubbedLogFunction(); } -u32 ResourceMgrTask::getResourceSize(const sead::SafeString& name, - sead::FileDevice* file_device) const { - if (!file_device) +u32 ResourceMgrTask::getResourceSize(const sead::SafeString& name, void* userdata) const { + if (!userdata) return mResourceInfoContainer.getResourceSize(name); mFileDevicePrefixesLock.readLock(); for (const auto& entry : mFileDevicePrefixes) { - if (entry.getFileDevice() == file_device) { + if (entry.getUserData() == userdata) { const u32 size = mResourceInfoContainer.getResourceSize(entry.getPrefix(), name); if (size == 0 && entry.getField28()) break; diff --git a/src/KingSystem/Resource/resResourceMgrTask.h b/src/KingSystem/Resource/resResourceMgrTask.h index 774ab55c..187e4552 100644 --- a/src/KingSystem/Resource/resResourceMgrTask.h +++ b/src/KingSystem/Resource/resResourceMgrTask.h @@ -57,8 +57,8 @@ class FileDevicePrefix { public: FileDevicePrefix() = default; - sead::FileDevice* getFileDevice() const { return mFileDevice; } - void setFileDevice(sead::FileDevice* device) { mFileDevice = device; } + void* getUserData() const { return mUserData; } + void setUserData(void* userdata) { mUserData = userdata; } const sead::SafeString& getPrefix() const { return mPrefix; } void setPrefix(const sead::SafeString& prefix) { mPrefix = prefix; } @@ -66,13 +66,15 @@ public: bool getField28() const { return _28; } void setField28(bool value) { _28 = value; } + void registerPrefix(const sead::SafeString& prefix, void* userdata, bool set28); + void registerPrefix(const char* prefix, void* userdata, bool set28); void deregister(); static constexpr size_t getListNodeOffset() { return offsetof(FileDevicePrefix, mListNode); } private: sead::ListNode mListNode; - sead::FileDevice* mFileDevice = nullptr; + void* mUserData = nullptr; sead::SafeString mPrefix; bool _28 = false; }; @@ -173,7 +175,7 @@ public: void unloadSeadResource(sead::Resource* resource); - u32 getResourceSize(const sead::SafeString& name, sead::FileDevice* file_device) const; + u32 getResourceSize(const sead::SafeString& name, void* userdata) const; void registerFileDevicePrefix(FileDevicePrefix& prefix); void deregisterFileDevicePrefix(FileDevicePrefix& prefix); @@ -406,6 +408,19 @@ KSYS_CHECK_SIZE_NX150(sead::TaskBase, 0xd0); KSYS_CHECK_SIZE_NX150(sead::MethodTreeNode, 0x98); KSYS_CHECK_SIZE_NX150(ResourceMgrTask, 0x9c0eb8); +inline void FileDevicePrefix::registerPrefix(const sead::SafeString& prefix, void* userdata, + bool set28) { + setUserData(userdata); + setPrefix(prefix); + if (set28) + setField28(true); + ResourceMgrTask::instance()->registerFileDevicePrefix(*this); +} + +inline void FileDevicePrefix::registerPrefix(const char* prefix, void* userdata, bool set28) { + registerPrefix(sead::SafeString(prefix), userdata, set28); +} + inline void FileDevicePrefix::deregister() { if (mListNode.isLinked()) ResourceMgrTask::instance()->deregisterFileDevicePrefix(*this);