summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSirius902 <10891979+Sirius902@users.noreply.github.com>2025-05-18 15:17:14 -0700
committerGitHub <noreply@github.com>2025-05-18 17:17:14 -0500
commit4e228e0a69da03589204b54c7b02d5813619f5ec (patch)
tree9b3b047583740048bd7c92c97113e34082278414
parent3142df4517f9cc21641399b3e9e2b2bedc819cfe (diff)
[Enhancement] Add N64 Weird Frames restoration (#1077)
* [Enhancement] Add N64 Weird Frames restoration * Tweaks to N64WeirdFrames --------- Co-authored-by: Garrett <garrettjcox@gmail.com>
-rw-r--r--mm/2s2h/Enhancements/Restorations/N64WeirdFrames/N64WeirdFrames.cpp96
-rw-r--r--mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.cpp65
-rw-r--r--mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.h43
-rw-r--r--mm/2s2h/GameInteractor/GameInteractor.h1
-rw-r--r--mm/src/code/z_skelanime.c5
5 files changed, 209 insertions, 1 deletions
diff --git a/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/N64WeirdFrames.cpp b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/N64WeirdFrames.cpp
new file mode 100644
index 000000000..fb7ffa221
--- /dev/null
+++ b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/N64WeirdFrames.cpp
@@ -0,0 +1,96 @@
+#include "public/bridge/consolevariablebridge.h"
+#include "2s2h/GameInteractor/GameInteractor.h"
+#include "2s2h/ShipInit.hpp"
+
+#include <vector>
+#include <spdlog/spdlog.h>
+
+#include "WeirdAnimation.h"
+
+extern "C" {
+#include "functions.h"
+#include "z64player.h"
+
+#include "objects/gameplay_keep/gameplay_keep.h"
+
+int ResourceMgr_OTRSigCheck(char* imgData);
+AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path);
+}
+
+#define CVAR_NAME "gEnhancements.Restorations.N64WeirdFrames"
+// This CVAR is defaulted to on, because it is consistent with hardware, but able to be disabled if desired.
+#define CVAR CVarGetInteger(CVAR_NAME, 1)
+
+// A list of weird animations to contruct. These can either be an index out of bounds before
+// the start of the animation or past the end of it. In either case you add a list of animations'
+// data that are neighboring before or after the target animation. If more weird frame data is
+// required then add more of the neighboring animations in ROM.
+static std::vector<WeirdAnimation> weirdAnimations{
+ // For weirdshots.
+ { gPlayerAnim_link_bow_side_walk,
+ PLAYER_LIMB_MAX,
+ IndexDirection::FORWARD,
+ {
+ "__OTR__misc/link_animetion/gPlayerAnim_alink_ozigi_Data",
+ "__OTR__misc/link_animetion/gPlayerAnim_pz_attackBendR_Data",
+ "__OTR__misc/link_animetion/gPlayerAnim_pz_attackCendR_Data",
+ "__OTR__misc/link_animetion/gPlayerAnim_pz_attackAendR_Data",
+ "__OTR__misc/link_animetion/gPlayerAnim_pz_cutterattack_Data",
+ "__OTR__misc/link_animetion/gPlayerAnim_kf_hanare_loop_Data",
+ "__OTR__misc/link_animetion/gPlayerAnimData_2C6350",
+ } },
+};
+
+void RegisterN64WeirdFrames() {
+ COND_VB_SHOULD(VB_LOAD_PLAYER_ANIMATION_FRAME, CVAR, {
+ const auto entry = va_arg(args, AnimationEntry*);
+ if (entry == nullptr) {
+ return;
+ }
+
+ auto animation = va_arg(args, LinkAnimationHeader*);
+ auto frame = va_arg(args, s32);
+ const auto limbCount = va_arg(args, s32);
+ const auto frameTable = va_arg(args, Vec3s*);
+
+ std::optional<const char*> animationName;
+
+ if (ResourceMgr_OTRSigCheck(reinterpret_cast<char*>(animation)) != 0) {
+ animationName = reinterpret_cast<const char*>(animation);
+ animation = reinterpret_cast<LinkAnimationHeader*>(ResourceMgr_LoadAnimByName(*animationName));
+ }
+
+ const auto playerAnimHeader =
+ static_cast<LinkAnimationHeader*>(Lib_SegmentedToVirtual(static_cast<void*>(animation)));
+
+ if (frame < 0 || frame >= playerAnimHeader->common.frameCount) {
+ const auto direction = frame < 0 ? IndexDirection::BACKWARD : IndexDirection::FORWARD;
+
+ if (animationName.has_value()) {
+ for (auto& weirdAnimation : weirdAnimations) {
+ if (weirdAnimation.GetDirection() == direction &&
+ weirdAnimation.GetTargetAnimation() == *animationName) {
+ if (const auto frameData = weirdAnimation.GetFrame(frame, playerAnimHeader->common.frameCount);
+ frameData != nullptr) {
+ *should = false;
+
+ SPDLOG_DEBUG("Weird animation for \"{}\": frame {}", weirdAnimation.GetTargetAnimation(),
+ frame);
+ std::memcpy(frameTable, frameData, sizeof(Vec3s) * limbCount + sizeof(s16));
+ } else {
+ SPDLOG_WARN("Weird Frame {} not included in weird animation for \"{}\"", frame,
+ weirdAnimation.GetTargetAnimation());
+ }
+
+ return;
+ }
+ }
+ }
+
+ SPDLOG_WARN("Weird Animation not present for \"{}\" but frame {} is out of bounds",
+ animationName.has_value() ? *animationName : "<null>", frame);
+ }
+ });
+}
+
+static RegisterShipInitFunc initFunc(RegisterN64WeirdFrames, { CVAR_NAME });
diff --git a/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.cpp b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.cpp
new file mode 100644
index 000000000..7f55e0663
--- /dev/null
+++ b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.cpp
@@ -0,0 +1,65 @@
+#include "WeirdAnimation.h"
+
+#include "ResourceManager.h"
+#include "Context.h"
+
+#include <cassert>
+#include <cstring>
+#include <spdlog/spdlog.h>
+
+extern "C" {
+#include "z64math.h"
+}
+
+const void* WeirdAnimation::GetFrame(s32 frame, s32 targetAnimationFrameCount) {
+ if (!animationData.has_value()) {
+ Build();
+ }
+
+ if (direction == IndexDirection::BACKWARD) {
+ const auto frameData = std::end(*animationData) + (((sizeof(Vec3s) * limbCount + sizeof(s16)) * frame));
+
+ return frameData >= std::begin(*animationData) ? &*frameData : nullptr;
+ } else if (direction == IndexDirection::FORWARD) {
+ const auto frameData = std::begin(*animationData) +
+ (((sizeof(Vec3s) * limbCount + sizeof(s16)) * (frame - targetAnimationFrameCount)));
+
+ return frameData < std::end(*animationData) ? &*frameData : nullptr;
+ } else {
+ SPDLOG_ERROR("Expected valid IndexDirection, got: {}", static_cast<int>(direction));
+ assert(false);
+ return nullptr;
+ }
+}
+
+void WeirdAnimation::Build() {
+ auto& animation = animationData.emplace();
+
+ for (const auto& neighborName : neighborAnimations) {
+ const auto neighbor = Ship::Context::GetInstance()->GetResourceManager()->LoadResource(neighborName);
+
+ const auto prevSize = animation.size();
+ animation.resize(prevSize + neighbor->GetPointerSize());
+ std::memcpy(animation.data() + prevSize, neighbor->GetRawPointer(), neighbor->GetPointerSize());
+
+ // Animation data in ROM is padded to 0x10 bytes. Align the buffer the same way.
+ animation.resize((animation.size() + 0xF) & ~0xF);
+ }
+
+ const auto frameCount = animation.size() / (sizeof(Vec3s) * limbCount + sizeof(s16));
+ const auto directionStr = [this] {
+ switch (direction) {
+ case IndexDirection::BACKWARD:
+ return "backward";
+ case IndexDirection::FORWARD:
+ return "forward";
+ default:
+ SPDLOG_ERROR("Expected valid IndexDirection, got: {}", static_cast<int>(direction));
+ assert(false);
+ return "???";
+ }
+ }();
+
+ SPDLOG_DEBUG("Weird animation built for \"{}\": frameCount = {}, direction = {}", targetAnimation, frameCount,
+ directionStr);
+}
diff --git a/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.h b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.h
new file mode 100644
index 000000000..e8c353720
--- /dev/null
+++ b/mm/2s2h/Enhancements/Restorations/N64WeirdFrames/WeirdAnimation.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <libultraship/libultra/types.h>
+
+#include <cstddef>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+enum class IndexDirection {
+ BACKWARD,
+ FORWARD,
+};
+
+class WeirdAnimation {
+ public:
+ WeirdAnimation(std::string targetAnimation, s32 limbCount, IndexDirection direction,
+ std::vector<std::string> neighborAnimations)
+ : targetAnimation(std::move(targetAnimation)), limbCount(limbCount), direction(direction),
+ neighborAnimations(std::move(neighborAnimations)) {
+ }
+
+ const std::string& GetTargetAnimation() {
+ return targetAnimation;
+ }
+
+ IndexDirection GetDirection() {
+ return direction;
+ }
+
+ const void* GetFrame(s32 frame, s32 targetAnimationFrameCount);
+
+ private:
+ std::string targetAnimation;
+ s32 limbCount;
+ IndexDirection direction;
+ std::vector<std::string> neighborAnimations;
+
+ std::optional<std::vector<std::byte>> animationData;
+
+ void Build();
+};
diff --git a/mm/2s2h/GameInteractor/GameInteractor.h b/mm/2s2h/GameInteractor/GameInteractor.h
index bcc84f4bc..9828f7aeb 100644
--- a/mm/2s2h/GameInteractor/GameInteractor.h
+++ b/mm/2s2h/GameInteractor/GameInteractor.h
@@ -239,6 +239,7 @@ typedef enum {
VB_START_JUMPSLASH,
VB_SETUP_TRANSITION,
VB_BE_NEAR_DOOR,
+ VB_LOAD_PLAYER_ANIMATION_FRAME,
} GIVanillaBehavior;
typedef enum {
diff --git a/mm/src/code/z_skelanime.c b/mm/src/code/z_skelanime.c
index 924d368f9..33a6365bc 100644
--- a/mm/src/code/z_skelanime.c
+++ b/mm/src/code/z_skelanime.c
@@ -3,6 +3,8 @@
#include <string.h>
#include <stdio.h>
+#include "2s2h/GameInteractor/GameInteractor.h"
+
#define ANIM_INTERP 1
s32 PlayerAnimation_Loop(PlayState* play, SkelAnime* skelAnime);
@@ -1017,7 +1019,8 @@ void AnimationContext_SetLoadFrame(PlayState* play, PlayerAnimationHeader* anima
Vec3s* frameTable) {
AnimationEntry* entry = AnimationContext_AddEntry(&play->animationCtx, ANIMATION_LINKANIMETION);
- if (entry != NULL) {
+ if (GameInteractor_Should(VB_LOAD_PLAYER_ANIMATION_FRAME, entry != NULL, entry, animation, frame, limbCount,
+ frameTable)) {
if (ResourceMgr_OTRSigCheck(animation) != 0)
animation = ResourceMgr_LoadAnimByName(animation);