diff --git a/soh/soh/ResourceManagerHelpers.cpp b/soh/soh/ResourceManagerHelpers.cpp index 4d4f91264..f6ddc2368 100644 --- a/soh/soh/ResourceManagerHelpers.cpp +++ b/soh/soh/ResourceManagerHelpers.cpp @@ -517,7 +517,47 @@ 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; + LinkAnimationHeader* linkAnim = (LinkAnimationHeader*)animHeader; + + // Valid if Normal animation has frameData OR Link animation has segment + if (normalAnim->frameData != NULL || linkAnim->segment != 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/soh/soh/resource/importer/AnimationFactory.cpp b/soh/soh/resource/importer/AnimationFactory.cpp index 99507e4f3..af8dd8ac3 100644 --- a/soh/soh/resource/importer/AnimationFactory.cpp +++ b/soh/soh/resource/importer/AnimationFactory.cpp @@ -78,15 +78,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/soh/src/code/z_skelanime.c b/soh/src/code/z_skelanime.c index 4335df0c8..3449c2ed9 100644 --- a/soh/src/code/z_skelanime.c +++ b/soh/src/code/z_skelanime.c @@ -902,6 +902,10 @@ void AnimationContext_SetLoadFrame(PlayState* play, LinkAnimationHeader* animati if (frame < 0) { frame = 0; } + // SOH [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); } }