diff --git a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h index 090ac2901fa..f4a89d8a4ca 100644 --- a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h +++ b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h @@ -24,8 +24,10 @@ typedef enum { NAVI_PREPARE = 600, NAVI_ACTIVE = 3000, NAVI_COOLDOWN = 25800, - DAY_BEGINS = 17759, - NIGHT_BEGINS = 49155 + SUNRISE_BEGINS = 0x2AAC, + DAY_BEGINS = 0x4555, + SUNSET_BEGINS = 0xAAAB, + NIGHT_BEGINS = 0xC001 }; typedef struct { diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 9189da7e581..bcf6dd1203c 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1138,9 +1138,12 @@ extern "C" void InitOTR() { DetectOTRVersion("oot-mq.otr", true); OTRGlobals::Instance = new OTRGlobals(); + + ResourceMgr_LoadPersistentAltAssets(); CustomMessageManager::Instance = new CustomMessageManager(); ItemTableManager::Instance = new ItemTableManager(); GameInteractor::Instance = new GameInteractor(); + ResourceMgr_RegisterHooks(); SaveManager::Instance = new SaveManager(); std::shared_ptr conf = OTRGlobals::Instance->context->GetConfig(); @@ -1202,6 +1205,8 @@ extern "C" void InitOTR() { Sail::Instance->Enable(); } #endif + + ResourceMgr_Init(); } extern "C" void SaveManager_ThreadPoolWait() { diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 854ef5b8fc2..01972743b2e 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -62,7 +62,6 @@ class OTRGlobals { bool HasMasterQuest(); bool HasOriginal(); uint32_t GetInterpolationFPS(); - std::shared_ptr> ListFiles(std::string path); private: void CheckSaveFile(size_t sramSize) const; diff --git a/soh/soh/ResourceManagerHelpers.cpp b/soh/soh/ResourceManagerHelpers.cpp index 2ab34586148..5ddc83cfe54 100644 --- a/soh/soh/ResourceManagerHelpers.cpp +++ b/soh/soh/ResourceManagerHelpers.cpp @@ -11,8 +11,18 @@ #include "resource/type/Array.h" #include "resource/type/Skeleton.h" #include "resource/type/PlayerAnimation.h" +#include "soh/ActorDB.h" +#include "soh/Enhancements/TimeDisplay/TimeDisplay.h" +#include "soh/resource/type/Scene.h" +#include "soh/resource/type/scenecommand/SetRoomList.h" +#include "soh/resource/type/scenecommand/SetActorList.h" +#include "soh/resource/type/scenecommand/SetObjectList.h" #include #include +#include "resource/ResourceManager.h" + +s16 unloadScene = -1; +std::shared_ptr helperThreads; extern "C" PlayState* gPlayState; @@ -116,8 +126,8 @@ extern "C" uint32_t ResourceMgr_IsGameMasterQuest() { return gPlayState != NULL ? IsSceneMasterQuest(gPlayState->sceneNum) : 0; } -extern "C" void ResourceMgr_LoadDirectory(const char* resName) { - Ship::Context::GetInstance()->GetResourceManager()->LoadResources(resName); +extern "C" void ResourceMgr_LoadDirectory(const char* filter) { + Ship::Context::GetInstance()->GetResourceManager()->LoadResources(filter); } extern "C" void ResourceMgr_DirtyDirectory(const char* resName) { @@ -135,7 +145,7 @@ extern "C" void ResourceMgr_UnloadResource(const char* resName) { // OTRTODO: There is probably a more elegant way to go about this... // Kenix: This is definitely leaking memory when it's called. extern "C" char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize) { - auto lst = Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager()->ListFiles(searchMask); + auto lst = Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager()->ListFiles({searchMask}, {}); char** result = (char**)malloc(lst->size() * sizeof(char*)); for (size_t i = 0; i < lst->size(); i++) { @@ -151,7 +161,7 @@ extern "C" char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize) extern "C" uint8_t ResourceMgr_FileExists(const char* filePath) { std::string path = filePath; - if(path.substr(0, 7) == "__OTR__"){ + if (path.substr(0, 7) == "__OTR__") { path = path.substr(7); } @@ -179,7 +189,7 @@ extern "C" bool ResourceMgr_IsAltAssetsEnabled() { // The resource is only removed from the internal cache to prevent it from used in the next resource lookup extern "C" void ResourceMgr_UnloadOriginalWhenAltExists(const char* resName) { if (ResourceMgr_IsAltAssetsEnabled() && ResourceMgr_FileAltExists((char*)resName)) { - ResourceMgr_UnloadResource((char*) resName); + ResourceMgr_UnloadResource((char*)resName); } } @@ -391,53 +401,46 @@ extern "C" char* ResourceMgr_LoadArrayByName(const char* path) { extern "C" char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path) { auto res = std::static_pointer_cast(ResourceMgr_GetResourceByNameHandlingMQ(path)); - // if (res->CachedGameAsset != nullptr) - // return (char*)res->CachedGameAsset; - // else - // { - Vec3s* data = (Vec3s*)malloc(sizeof(Vec3s) * res->Scalars.size()); - - for (size_t i = 0; i < res->Scalars.size(); i += 3) { - data[(i / 3)].x = res->Scalars[i + 0].s16; - data[(i / 3)].y = res->Scalars[i + 1].s16; - data[(i / 3)].z = res->Scalars[i + 2].s16; - } + Vec3s* data = (Vec3s*)malloc(sizeof(Vec3s) * res->Scalars.size()); - // res->CachedGameAsset = data; + for (size_t i = 0; i < res->Scalars.size(); i += 3) { + data[(i / 3)].x = res->Scalars[i + 0].s16; + data[(i / 3)].y = res->Scalars[i + 1].s16; + data[(i / 3)].z = res->Scalars[i + 2].s16; + } - return (char*)data; - // } + return (char*)data; } extern "C" CollisionHeader* ResourceMgr_LoadColByName(const char* path) { - return (CollisionHeader*) ResourceGetDataByName(path); + return (CollisionHeader*)ResourceGetDataByName(path); } extern "C" Vtx* ResourceMgr_LoadVtxByName(char* path) { - return (Vtx*) ResourceGetDataByName(path); + return (Vtx*)ResourceGetDataByName(path); } extern "C" SequenceData ResourceMgr_LoadSeqByName(const char* path) { - SequenceData* sequence = (SequenceData*) ResourceGetDataByName(path); + SequenceData* sequence = (SequenceData*)ResourceGetDataByName(path); return *sequence; } extern "C" SoundFontSample* ResourceMgr_LoadAudioSample(const char* path) { - return (SoundFontSample*) ResourceGetDataByName(path); + return (SoundFontSample*)ResourceGetDataByName(path); } extern "C" SoundFont* ResourceMgr_LoadAudioSoundFont(const char* path) { - return (SoundFont*) ResourceGetDataByName(path); + return (SoundFont*)ResourceGetDataByName(path); } extern "C" int ResourceMgr_OTRSigCheck(char* imgData) { - uintptr_t i = (uintptr_t)(imgData); + uintptr_t i = (uintptr_t)(imgData); -// if (i == 0xD9000000 || i == 0xE7000000 || (i & 1) == 1) + // if (i == 0xD9000000 || i == 0xE7000000 || (i & 1) == 1) if ((i & 1) == 1) return 0; -// if ((i & 0xFF000000) != 0xAB000000 && (i & 0xFF000000) != 0xCD000000 && i != 0) { + // if ((i & 0xFF000000) != 0xAB000000 && (i & 0xFF000000) != 0xCD000000 && i != 0) { if (i != 0) { if ( imgData[0] == '_' && @@ -447,7 +450,7 @@ extern "C" int ResourceMgr_OTRSigCheck(char* imgData) { imgData[4] == 'R' && imgData[5] == '_' && imgData[6] == '_' - ) { + ) { return 1; } } @@ -456,7 +459,7 @@ extern "C" int ResourceMgr_OTRSigCheck(char* imgData) { } extern "C" AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path) { - return (AnimationHeaderCommon*) ResourceGetDataByName(path); + return (AnimationHeaderCommon*)ResourceGetDataByName(path); } extern "C" SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path, SkelAnime* skelAnime) { @@ -473,11 +476,11 @@ extern "C" SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path, Skel pathStr = Ship::IResource::gAltAssetPrefix + pathStr; } - SkeletonHeader* skelHeader = (SkeletonHeader*) ResourceGetDataByName(pathStr.c_str()); + SkeletonHeader* skelHeader = (SkeletonHeader*)ResourceGetDataByName(pathStr.c_str()); // If there isn't an alternate model, load the regular one if (isAlt && skelHeader == NULL) { - skelHeader = (SkeletonHeader*) ResourceGetDataByName(path); + skelHeader = (SkeletonHeader*)ResourceGetDataByName(path); } // This function is only called when a skeleton is initialized. @@ -503,3 +506,267 @@ extern "C" void ResourceMgr_ClearSkeletons() { extern "C" s32* ResourceMgr_LoadCSByName(const char* path) { return (s32*)ResourceMgr_GetResourceDataByNameHandlingMQ(path); } + +bool IsSharedScene(int16_t sceneNum) { + return sceneNum != SCENE_DEKU_TREE && + sceneNum != SCENE_DODONGOS_CAVERN && + sceneNum != SCENE_JABU_JABU && + sceneNum != SCENE_FOREST_TEMPLE && + sceneNum != SCENE_FIRE_TEMPLE && + sceneNum != SCENE_WATER_TEMPLE && + sceneNum != SCENE_SPIRIT_TEMPLE && + sceneNum != SCENE_SHADOW_TEMPLE && + sceneNum != SCENE_BOTTOM_OF_THE_WELL && + sceneNum != SCENE_ICE_CAVERN && + sceneNum != SCENE_GERUDO_TRAINING_GROUND && + sceneNum != SCENE_INSIDE_GANONS_CASTLE; +} + +std::string GetSceneRootPath(int16_t sceneNum, bool alt = true) { + std::string sceneName = gSceneTable[sceneNum].sceneFile.fileName; + std::string path = fmt::format("{}scenes/shared/{}/", (alt ? "alt/" : ""), sceneName); + if (!IsSharedScene(sceneNum)) { + size_t pos = path.find("/shared/", 0); + if (IS_MASTER_QUEST || (IS_RANDO && OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(sceneNum)->IsMQ())) { + path.replace(pos, 8, "/mq/"); + } else { + path.replace(pos, 8, "/nonmq/"); + } + } + return path; +} + +// Return full path mask for scene assets +std::string GetScenePathMask(int16_t sceneNum, bool alt = true) { + return GetSceneRootPath(sceneNum, alt) + "*"; +} + +std::string GetSceneFilePath(int16_t sceneNum, bool alt = true) { + return GetSceneRootPath(sceneNum, alt) + gSceneTable[sceneNum].sceneFile.fileName; +} + +std::array, SCENE_TESTROOM + 1> sceneObjects; + +void LoadSceneResourcesProcess(int16_t sceneNum) { + auto play = gPlayState; + for (auto objectName : sceneObjects[sceneNum]) { + if (!sceneObjects[play->sceneNum].contains(objectName)) { + OTRGlobals::Instance->context->GetResourceManager()->LoadResources("alt/objects/" + objectName + "/*"); + } + } + OTRGlobals::Instance->context->GetResourceManager()->LoadResources(GetScenePathMask(sceneNum)); +} + +// Iterate over scene object/actor commands if not already done so, and load the scene and object assets +extern "C" void ResourceMgr_LoadAllSceneResources(int16_t sceneNum, bool now) { + helperThreads->submit_task(std::bind(LoadSceneResourcesProcess, sceneNum)); +} + +extern "C" void ResourceMgr_RegisterUnloadSceneAssets(s16 prevScene) { + unloadScene = prevScene; +} + +// Unload previously determined scene assets +void UnloadSceneAssetsProcess() { + for (auto objectName : sceneObjects[unloadScene]) { + if (!sceneObjects[gPlayState->sceneNum].contains(objectName)) { + std::string objectPath = fmt::format("alt/objects/{}/*", objectName); + OTRGlobals::Instance->context->GetResourceManager()->UnloadResources(objectPath); + } + } + OTRGlobals::Instance->context->GetResourceManager()->UnloadResources(GetScenePathMask(unloadScene)); + OTRGlobals::Instance->context->GetResourceManager()->UnloadResources("alt/textures/vr_holy*"); + unloadScene = -1; +} + +// Start unload loops on a thread for performance purposes +extern "C" void ResourceMgr_UnloadSceneAssets() { + if (unloadScene != -1) { + helperThreads->submit_task(UnloadSceneAssetsProcess); + } +} + +// Persisted assets never unload, generally because they're used in multiple places. These include things like +// audio assets, icons, items, font, gameplay*keep objects, title cards, and interior assets (for now) +void ResourceMgr_LoadDelayedPersistentAltAssets() { + // Load sound effects first for title screen "Press Start" and pause sounds. These are loaded + // before the alt assets to prevent load lock for the audio itself + static std::list textureIncludes = {"audio/fonts/00_Sound_Effects_1", "audio/fonts/00_Sound_Effects_2", + "audio/*", "alt/textures/parameter*", "alt/textures/icon*", "alt/textures/item*", "alt/textures/font*", + "alt/objects/gameplay_*", "alt/overlays/*", "alt/code/*", "alt/textures/*" }; + static std::list textureExcludes = { "alt/textures/vr_holy*", "alt/textures/vr_cloud*", "alt/textures/vr_fine*" }; + Ship::Context::GetInstance()->GetResourceManager()->LoadResourcesAsync({textureIncludes, textureExcludes, 0, nullptr}); +} + +static int lastSkyboxLoad = -1; + +void LoadSkyBoxProcess(TimeOfDay timeIndex, bool fileSelect) { + std::string mask = fmt::format("alt/textures/vr_fine{}*", static_cast(timeIndex)); + Ship::Context::GetInstance()->GetResourceManager()->LoadResources(mask); + if (!fileSelect) { + std::string mask = fmt::format("alt/textures/vr_cloud{}*", static_cast(timeIndex)); + Ship::Context::GetInstance()->GetResourceManager()->LoadResources(mask); + } + lastSkyboxLoad = timeIndex; +} +// Load regular and cloudy skyboxes for specified TimeOfDay +extern "C" void ResourceMgr_LoadSkyBox(TimeOfDay timeIndex, bool fileSelect) { + helperThreads->submit_task(std::bind(LoadSkyBoxProcess, timeIndex, fileSelect)); +} + +static int lastSkyboxUnload = -1; +// Unload skyboxes for specified TimeOfDay +extern "C" void ResourceMgr_UnloadSkyBox(TimeOfDay timeIndex) { + helperThreads->submit_task([timeIndex]() -> void { + std::string mask = fmt::format("alt/textures/vr_cloud{}*", static_cast(timeIndex)); + ResourceUnloadDirectory(mask.c_str()); + mask = fmt::format("alt/textures/vr_fine{}*", static_cast(timeIndex)); + ResourceUnloadDirectory(mask.c_str()); + }); +} + +// Setup initial preload based on Fast File Select and Save Index options +extern "C" void ResourceMgr_LoadPersistentAltAssets() { + bool skipTitle = CVarGetInteger(CVAR_DEVELOPER_TOOLS("SkipLogoTitle"), 0); + int fastFile = CVarGetInteger(CVAR_DEVELOPER_TOOLS("SaveFileID"), 0); + + if (!skipTitle) { + ResourceLoadDirectoryAsync("alt/textures/nintendo_rogo_static/*"); + // Title screen/hyrule field + ResourceLoadDirectoryAsync("alt/scenes/*/spot00*"); + // Title logos + ResourceLoadDirectoryAsync("alt/objects/object_mag/*"); + // Title screen music + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceAsync("audio/sequences/030_Title_Theme"); + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceAsync("audio/fonts/06_Title_Theme"); + } else if (skipTitle && fastFile == 4) { + Ship::Context::GetInstance()->GetResourceManager()->LoadResourcesAsync({ + {"alt/overlays/ovl_file_choose/*", "alt/textures/title_static/*", "alt/objects/gameplay_keep/*", "alt/textures/vr_fine3*", "alt/textures/vr_fine0*"}, + {}, 0, nullptr}); + // File Select music + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceAsync("audio/sequences/087_File_Select"); + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceAsync("audio/fonts/09_Fairy_Fountain"); + } else if (skipTitle && fastFile < 3) { + ResourceLoadDirectoryAsync("alt/textures/icon*"); + ResourceLoadDirectoryAsync("alt/textures/do_action_static/*"); + ResourceLoadDirectoryAsync("alt/textures/map*"); + ResourceLoadDirectoryAsync("alt/textures/parameter_static/*"); + } + ResourceMgr_LoadDelayedPersistentAltAssets(); +} + +// Just to make sure the cloudy skyboxes are loaded across transitions for things like Song of Storms +extern "C" void ResourceMgr_SceneInitSkybox() { + if (gSaveContext.dayTime > SUNRISE_BEGINS && gSaveContext.dayTime < DAY_BEGINS) { + ResourceMgr_LoadSkyBox(TOD_Sunrise, false); + ResourceMgr_LoadSkyBox(TOD_Night, false); + ResourceMgr_LoadSkyBox(TOD_Day, false); + } else if (gSaveContext.dayTime > DAY_BEGINS && gSaveContext.dayTime < SUNSET_BEGINS) { + ResourceMgr_LoadSkyBox(TOD_Day, false); + } else if (gSaveContext.dayTime > SUNSET_BEGINS && gSaveContext.dayTime < NIGHT_BEGINS) { + ResourceMgr_LoadSkyBox(TOD_Day, false); + ResourceMgr_LoadSkyBox(TOD_Sunset, false); + ResourceMgr_LoadSkyBox(TOD_Night, false); + } else if (gSaveContext.dayTime > NIGHT_BEGINS || gSaveContext.dayTime < SUNRISE_BEGINS) { + ResourceMgr_LoadSkyBox(TOD_Night, false); + } +} + +// These are absolutely arbitrary numbers, and are my best guess for lead time needed to load skyboxes for the given time of day +uint16_t skyboxLoadTimes[2][4] = { { SUNRISE_BEGINS - 3000, DAY_BEGINS - 3000, SUNSET_BEGINS - 3000, NIGHT_BEGINS - 3000 }, { 63000, 16000, 32000, 49000 } }; +// These are what were needed to be able to unload the skyboxes without hiccups, since they're held onto for a bit after transitions are done +uint16_t skyboxUnloadTimes[2][4] = { { DAY_BEGINS + 4000, SUNSET_BEGINS + 4000, NIGHT_BEGINS + 4000, SUNRISE_BEGINS + 4000 }, { 32000, 49000, 63000, 16000 } }; + +// Used to preload and unload skyboxes for time-based skybox changes +extern "C" void ResourceMgr_CheckLoadSkybox(bool fileSelect) { + if (gSaveContext.skyboxTime > skyboxLoadTimes[fileSelect][TOD_Sunrise] && gSaveContext.skyboxTime < (skyboxLoadTimes[fileSelect][TOD_Sunrise] + 800)) { + if (lastSkyboxLoad != 0) { + lastSkyboxLoad = 0; + ResourceMgr_LoadSkyBox(TOD_Sunrise, fileSelect); + } + } else if (gSaveContext.skyboxTime > skyboxLoadTimes[fileSelect][TOD_Day] && gSaveContext.skyboxTime < (skyboxLoadTimes[fileSelect][TOD_Day] + 800)) { + if (lastSkyboxLoad != 1) { + lastSkyboxLoad = 1; + ResourceMgr_LoadSkyBox(TOD_Day, fileSelect); + } + } else if (gSaveContext.skyboxTime > skyboxLoadTimes[fileSelect][TOD_Sunset] && gSaveContext.skyboxTime < (skyboxLoadTimes[fileSelect][TOD_Sunset] + 800)) { + if (lastSkyboxLoad != 2) { + lastSkyboxLoad = 2; + ResourceMgr_LoadSkyBox(TOD_Sunset, fileSelect); + } + } else if (gSaveContext.skyboxTime > skyboxLoadTimes[fileSelect][TOD_Night] && gSaveContext.skyboxTime < (skyboxLoadTimes[fileSelect][TOD_Night] + 800)) { + if (lastSkyboxLoad != 3) { + lastSkyboxLoad = 3; + ResourceMgr_LoadSkyBox(TOD_Night, fileSelect); + } + } + + if (gSaveContext.skyboxTime > skyboxUnloadTimes[fileSelect][TOD_Sunrise] && gSaveContext.skyboxTime < (skyboxUnloadTimes[fileSelect][TOD_Sunrise] + 800)) { + if (lastSkyboxUnload != 0) { + lastSkyboxUnload = 0; + ResourceMgr_UnloadSkyBox(TOD_Sunrise); + } + } else if (gSaveContext.skyboxTime > skyboxUnloadTimes[fileSelect][TOD_Day] && gSaveContext.skyboxTime < (skyboxUnloadTimes[fileSelect][TOD_Day] + 800)) { + if (lastSkyboxUnload != 1) { + lastSkyboxUnload = 1; + ResourceMgr_UnloadSkyBox(TOD_Day); + } + } else if (gSaveContext.skyboxTime > skyboxUnloadTimes[fileSelect][TOD_Sunset] && gSaveContext.skyboxTime < (skyboxUnloadTimes[fileSelect][TOD_Sunset] + 800)) { + if (lastSkyboxUnload != 2) { + lastSkyboxUnload = 2; + ResourceMgr_UnloadSkyBox(TOD_Sunset); + } + } else if (gSaveContext.skyboxTime > skyboxUnloadTimes[fileSelect][TOD_Night] && gSaveContext.skyboxTime < (skyboxUnloadTimes[fileSelect][TOD_Night] + 800)) { + if (lastSkyboxUnload != 3) { + lastSkyboxUnload = 3; + ResourceMgr_UnloadSkyBox(TOD_Night); + } + } +} + +extern "C" void ResourceMgr_Init() { + helperThreads = std::make_shared(1); + for (int16_t sceneNum = 0; sceneNum <= SCENE_OUTSIDE_GANONS_CASTLE; sceneNum++) { + if (sceneObjects[sceneNum].empty()) { + SOH::SceneCommandID cmdCode; + std::string scenePath = GetSceneFilePath(sceneNum, false); + auto scene = (SOH::Scene*)Ship::Context::GetInstance()->GetResourceManager()->LoadResource(scenePath.c_str()).get(); + for (auto sceneCmd : scene->commands) { + if (sceneCmd->cmdId == SOH::SceneCommandID::SetRoomList) { + auto setRoomListCmd = std::dynamic_pointer_cast(sceneCmd); + for (auto room : setRoomListCmd->rooms) { + auto roomScene = (SOH::Scene*)Ship::Context::GetInstance()->GetResourceManager()->LoadResource(room.fileName).get(); + for (auto roomSceneCmd : roomScene->commands) { + if (roomSceneCmd->cmdId == SOH::SceneCommandID::SetObjectList) { + auto setObjectCmd = std::dynamic_pointer_cast(roomSceneCmd); + for (auto objectId : setObjectCmd->objects) { + std::string objectName = gObjectTable[objectId].fileName; + sceneObjects[sceneNum].insert(objectName); + } + } else if (roomSceneCmd->cmdId == SOH::SceneCommandID::SetActorList) { + auto setActorCmd = std::dynamic_pointer_cast(roomSceneCmd); + if (setActorCmd->numActors > 0) { + for (uint16_t i = 0; i < setActorCmd->numActors; i++) { + auto actorEntry = (ActorEntry*)setActorCmd->GetRawPointer(); + std::string objectName = gObjectTable[ActorDB::Instance->RetrieveEntry(actorEntry->id).entry.objectId].fileName; + sceneObjects[sceneNum].insert(objectName); + } + } + } + } + } + } + } + } + } +} + +extern "C" void ResourceMgr_RegisterHooks() { + GameInteractor::Instance->RegisterGameHook([]() { + ResourceMgr_CheckLoadSkybox(false); + }); + GameInteractor::Instance->RegisterGameHook([](uint32_t sceneNum) { + ResourceMgr_SceneInitSkybox(); + ResourceMgr_UnloadSceneAssets(); + }); +} diff --git a/soh/soh/ResourceManagerHelpers.h b/soh/soh/ResourceManagerHelpers.h index 3b14188b759..2d1dbad08f6 100644 --- a/soh/soh/ResourceManagerHelpers.h +++ b/soh/soh/ResourceManagerHelpers.h @@ -19,6 +19,12 @@ extern "C" { #include "z64animation.h" #include "z64audio.h" #include "z64bgcheck.h" + typedef enum { + TOD_Sunrise, + TOD_Day, + TOD_Sunset, + TOD_Night + } TimeOfDay; uint32_t ResourceMgr_IsGameMasterQuest(); uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum); uint32_t ResourceMgr_GameHasMasterQuest(); @@ -61,6 +67,17 @@ extern "C" { s32* ResourceMgr_LoadCSByName(const char* path); int ResourceMgr_OTRSigCheck(char* imgData); char* ResourceMgr_GetResourceDataByNameHandlingMQ(const char* path); + void ResourceMgr_LoadAllSceneResources(int16_t sceneNum, bool now); + void ResourceMgr_UnloadSceneAssets(); + void ResourceMgr_LoadDelayedPersistentAltAssets(); + void ResourceMgr_LoadPersistentAltAssets(); + void ResourceMgr_RegisterUnloadSceneAssets(s16 prevScene); + void ResourceMgr_CheckLoadSkybox(bool fileSelect); + void ResourceMgr_SceneInitSkybox(); + void ResourceMgr_RegisterHooks(); + void ResourceMgr_LoadSkyBox(TimeOfDay timeIndex, bool fileSelect); + void ResourceMgr_UnloadSkyBox(TimeOfDay timeIndex); + void ResourceMgr_Init(); #ifdef __cplusplus } #endif // __cplusplus \ No newline at end of file diff --git a/soh/soh/z_play_otr.cpp b/soh/soh/z_play_otr.cpp index 279b4e696cb..e30b93dac7b 100644 --- a/soh/soh/z_play_otr.cpp +++ b/soh/soh/z_play_otr.cpp @@ -1,6 +1,7 @@ #include "OTRGlobals.h" #include "ResourceManagerHelpers.h" #include +#include "soh/ResourceManagerHelpers.h" #include "soh/resource/type/Scene.h" #include #include "soh/Enhancements/game-interactor/GameInteractor.h" @@ -14,7 +15,7 @@ void OTRPlay_InitScene(PlayState* play, s32 spawn); s32 OTRScene_ExecuteCommands(PlayState* play, SOH::Scene* scene); //LUS::OTRResource* OTRPlay_LoadFile(PlayState* play, RomFile* file) { -Ship::IResource* OTRPlay_LoadFile(PlayState* play, const char* fileName) +Ship::IResource* OTRPlay_LoadFile(const char* fileName) { auto res = Ship::Context::GetInstance()->GetResourceManager()->LoadResource(fileName); return res.get(); @@ -40,7 +41,7 @@ extern "C" void OTRPlay_SpawnScene(PlayState* play, s32 sceneId, s32 spawn) { } std::string scenePath = StringHelper::Sprintf("scenes/%s/%s/%s", sceneVersion.c_str(), scene->sceneFile.fileName, scene->sceneFile.fileName); - play->sceneSegment = OTRPlay_LoadFile(play, scenePath.c_str()); + play->sceneSegment = OTRPlay_LoadFile(scenePath.c_str()); // Failed to load scene... default to doodongs cavern if (play->sceneSegment == nullptr) @@ -76,6 +77,7 @@ void OTRPlay_InitScene(PlayState* play, s32 spawn) { Object_InitBank(play, &play->objectCtx); LightContext_Init(play, &play->lightCtx); TransitionActor_InitContext(&play->state, &play->transiActorCtx); + func_80096FD4(play, &play->roomCtx.curRoom); YREG(15) = 0; gSaveContext.worldMapArea = 0; diff --git a/soh/soh/z_scene_otr.cpp b/soh/soh/z_scene_otr.cpp index 022aeee372b..3dac098664c 100644 --- a/soh/soh/z_scene_otr.cpp +++ b/soh/soh/z_scene_otr.cpp @@ -35,7 +35,7 @@ #include "soh/resource/type/scenecommand/SetEchoSettings.h" #include "soh/resource/type/scenecommand/SetAlternateHeaders.h" -extern Ship::IResource* OTRPlay_LoadFile(PlayState* play, const char* fileName); +extern Ship::IResource* OTRPlay_LoadFile(const char* fileName); extern "C" s32 Object_Spawn(ObjectContext* objectCtx, s16 objectId); extern "C" RomFile sNaviMsgFiles[]; s32 OTRScene_ExecuteCommands(PlayState* play, SOH::Scene* scene); @@ -107,7 +107,7 @@ bool Scene_CommandSpecialFiles(PlayState* play, SOH::ISceneCommand* cmd) { if (specialCmd->specialObjects.elfMessage != 0) { auto res = - (Ship::Blob*)OTRPlay_LoadFile(play, sNaviMsgFiles[specialCmd->specialObjects.elfMessage - 1].fileName); + (Ship::Blob*)OTRPlay_LoadFile(sNaviMsgFiles[specialCmd->specialObjects.elfMessage - 1].fileName); play->cUpElfMsgs = (ElfMessage*)res->Data.data(); } diff --git a/soh/src/code/game.c b/soh/src/code/game.c index a1426671993..8f5324c9a2c 100644 --- a/soh/src/code/game.c +++ b/soh/src/code/game.c @@ -466,11 +466,20 @@ void GameState_Destroy(GameState* gameState) { osSyncPrintf("game デストラクタ終了\n"); // "game destructor end" + PlayState* play = (PlayState*)gameState; + static s16 sceneNum = -1; + // Performing clear skeletons before unload resources fixes an actor heap corruption crash due to the skeleton patching system. ResourceMgr_ClearSkeletons(); + if (play->sceneNum != sceneNum) { + if (play->sceneNum >= SCENE_DEKU_TREE && play->sceneNum <= SCENE_TESTROOM) { + ResourceMgr_RegisterUnloadSceneAssets(play->sceneNum); + } + ResourceUnloadDirectory("alt/textures/vs*"); + sceneNum = play->sceneNum; + } if (ResourceMgr_IsAltAssetsEnabled()) { - ResourceUnloadDirectory("alt/*"); gfx_texture_cache_clear(); } } diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index cda6356f5ba..e3d0b3e6e6c 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -5155,6 +5155,7 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol gSaveContext.retainWeatherMode = 1; Scene_SetTransitionForNextEntrance(play); } + ResourceMgr_LoadAllSceneResources(gEntranceTable[((void)0, play->nextEntranceIndex)].scene, false); play->transitionTrigger = TRANS_TRIGGER_START; } diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 53901821bad..e5c7c744e51 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -3300,6 +3300,7 @@ void FileChoose_Main(GameState* thisx) { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeFlowFileSelect"), 0) != 0) { gSaveContext.skyboxTime += 0x10; + ResourceMgr_CheckLoadSkybox(true); } if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("SkipLogoTitle"), 0) && CVarGetInteger(CVAR_DEVELOPER_TOOLS("SaveFileID"), FASTFILE_1) <= FASTFILE_3 && !isFastFileIdIncompatible) {