From eab40aa844fca48c27235ca83adf9f07b0981a51 Mon Sep 17 00:00:00 2001 From: Jameriquiah Date: Tue, 31 Mar 2026 13:31:12 -0400 Subject: [PATCH 1/2] Add explicit alt prefix checking to animation loading Co-Authored-By: PurpleHato <47987542+PurpleHato@users.noreply.github.com> --- mm/2s2h/BenPort.cpp | 39 +++++++++++++++++++ .../resource/importer/AnimationFactory.cpp | 22 ++++++++++- mm/src/code/z_skelanime.c | 4 ++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/mm/2s2h/BenPort.cpp b/mm/2s2h/BenPort.cpp index 2e410ddf16..0b253cee0a 100644 --- a/mm/2s2h/BenPort.cpp +++ b/mm/2s2h/BenPort.cpp @@ -1501,7 +1501,46 @@ extern "C" int ResourceMgr_OTRSigCheck(char* imgData) { return 0; } +// Load animation with explicit alt asset path checking. +// When Alt Assets is OFF: use original path directly (O2R or vanilla) +// When Alt Assets is ON: try alt/ prefix first, fall back to regular path if not found or invalid extern "C" AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path) { + bool isAlt = ResourceMgr_IsAltAssetsEnabled(); + + if (isAlt) { + std::string pathStr = std::string(path); + static const std::string sOtr = "__OTR__"; + + if (pathStr.starts_with(sOtr)) { + pathStr = pathStr.substr(sOtr.length()); + } + + // Try alt/ first + pathStr = Ship::IResource::gAltAssetPrefix + pathStr; + AnimationHeaderCommon* animHeader = (AnimationHeaderCommon*)ResourceGetDataByName(pathStr.c_str()); + + // If alt loaded successfully, verify it has valid data + if (animHeader != NULL) { + // Check for valid frame count (> 0) + if (animHeader->frameCount > 0) { + // For Normal animations: check frameData (comes after frameCount in AnimationHeader) + // For Link animations: check segment (comes after frameCount in LinkAnimationHeader) + // We check both to be safe - if either is valid, the animation is usable + AnimationHeader* normalAnim = (AnimationHeader*)animHeader; + PlayerAnimationHeader* playerAnim = (PlayerAnimationHeader*)animHeader; + + if (normalAnim->frameData != NULL || playerAnim->segmentVoid != NULL) { + return animHeader; + } + } + // Alt loaded but is invalid (broken), fall through to original path + } + + // Fall back to original path + return (AnimationHeaderCommon*)ResourceGetDataByName(path); + } + + // Alt OFF: use original path directly return (AnimationHeaderCommon*)ResourceGetDataByName(path); } diff --git a/mm/2s2h/resource/importer/AnimationFactory.cpp b/mm/2s2h/resource/importer/AnimationFactory.cpp index 6c07652143..5f6d2e3b4b 100644 --- a/mm/2s2h/resource/importer/AnimationFactory.cpp +++ b/mm/2s2h/resource/importer/AnimationFactory.cpp @@ -80,15 +80,33 @@ ResourceFactoryBinaryAnimationV0::ReadResource(std::shared_ptr file, } animation->animationData.transformUpdateIndex.copyValues = animation->copyValuesArr.data(); } else if (animType == AnimationType::Link) { + // Initialize segment to nullptr (important for alt asset fallback) + animation->animationData.linkAnimationHeader.segment = nullptr; + // Read the frame count animation->animationData.linkAnimationHeader.common.frameCount = reader->ReadInt16(); // Read the segment pointer (always 32 bit, doesn't adjust for system pointer size) std::string path = reader->ReadString(); - const auto animData = std::static_pointer_cast( + auto animData = std::static_pointer_cast( Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(path.c_str())); - animation->animationData.linkAnimationHeader.segment = animData->GetPointer(); + // If direct load failed and alt assets are enabled, try with alt/ prefix + if (animData == nullptr && Ship::Context::GetInstance()->GetResourceManager()->IsAltAssetsEnabled()) { + std::string altPath = path; + if (altPath.find("__OTR__") == 0) { + altPath = altPath.substr(7); // Strip __OTR__ + } + altPath = "alt/" + altPath; + animData = std::static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(altPath.c_str())); + } + + if (animData != nullptr) { + animation->animationData.linkAnimationHeader.segment = animData->GetPointer(); + } else { + SPDLOG_WARN("Animation data segment not found: {}", path); + } } else if (animType == AnimationType::Legacy) { SPDLOG_DEBUG("BEYTAH ANIMATION?!"); } diff --git a/mm/src/code/z_skelanime.c b/mm/src/code/z_skelanime.c index 973b8fe6c9..0f45cb1fda 100644 --- a/mm/src/code/z_skelanime.c +++ b/mm/src/code/z_skelanime.c @@ -1063,6 +1063,10 @@ void AnimTaskQueue_AddLoadPlayerFrame(PlayState* play, PlayerAnimationHeader* an if (frame < 0) { frame = 0; } + // 2S2H [Alt Assets] Check if animData is null (can happen if animation data segment failed to load) + if (animData == NULL) { + return; + } memcpy(ram, (uintptr_t)animData + (((sizeof(Vec3s) * limbCount + 2) * frame)), sizeof(Vec3s) * limbCount + 2); } } From 23c3ed794e65cf72915d8e44c955551256bf7a6a Mon Sep 17 00:00:00 2001 From: Jameriquiah Date: Tue, 31 Mar 2026 14:09:51 -0400 Subject: [PATCH 2/2] forgot comment Co-Authored-By: PurpleHato <47987542+PurpleHato@users.noreply.github.com> --- mm/2s2h/BenPort.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/2s2h/BenPort.cpp b/mm/2s2h/BenPort.cpp index 0b253cee0a..d5899312cf 100644 --- a/mm/2s2h/BenPort.cpp +++ b/mm/2s2h/BenPort.cpp @@ -1529,6 +1529,7 @@ extern "C" AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path) { AnimationHeader* normalAnim = (AnimationHeader*)animHeader; PlayerAnimationHeader* playerAnim = (PlayerAnimationHeader*)animHeader; + // Valid if Normal animation has frameData OR Link animation has segment if (normalAnim->frameData != NULL || playerAnim->segmentVoid != NULL) { return animHeader; }