From 0930f3f7611bb881bc618bb118c51b81281d04d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 2 Jan 2022 12:00:16 +0100 Subject: [PATCH] Fix unsafe player access in UpdatePadState We should *not* access the global context or the player actor manually in UpdatePadState because neither are guaranteed to still exist in memory when that function is called. Fixes random crashes if ZR is held during a transition (transitions usually involve destructing the current game state and then reinitialising again -- the global context may be reallocated at a different location). Closes #146. --- source/common/context.h | 1 + source/rst/input.cpp | 9 +++------ source/rst/link.cpp | 6 ++++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/source/common/context.h b/source/common/context.h index 0aff050..bde4462 100644 --- a/source/common/context.h +++ b/source/common/context.h @@ -14,6 +14,7 @@ struct Context { bool going_back_in_time = false; + bool is_swimming = false; bool use_fast_swim = true; u32 a_press_duration = 0; }; diff --git a/source/rst/input.cpp b/source/rst/input.cpp index bc0d3f6..a32a0e4 100644 --- a/source/rst/input.cpp +++ b/source/rst/input.cpp @@ -1,8 +1,6 @@ #include "common/context.h" #include "game/common_data.h" -#include "game/context.h" #include "game/pad.h" -#include "game/player.h" #include "game/ui.h" namespace rst { @@ -34,10 +32,9 @@ RST_HOOK void UpdatePadState() { if (state.input.buttons.IsSet(pad::Button::ZR)) { // XXX: This shouldn't be here... - auto* gctx = rst::GetContext().gctx; - auto* player = gctx ? gctx->GetPlayerActor() : nullptr; - if (player && player->flags1.IsSet(game::act::Player::Flag1::InWater) && - !player->flags_94.IsSet(game::act::Actor::Flag94::Grounded)) { + // Note that we do *not* access the global context or the player actor here manually, + // because neither are guaranteed to still exist in memory when this function is called. + if (GetContext().is_swimming) { // If Link is swimming (as Zora Link most likely but that doesn't matter), // do not unset the A button. set_touch_btn_without_clear(pad::Button::A, pad::TouchscreenButton::Ocarina); diff --git a/source/rst/link.cpp b/source/rst/link.cpp index 288c9cc..a47c5ed 100644 --- a/source/rst/link.cpp +++ b/source/rst/link.cpp @@ -266,6 +266,7 @@ static void HandleFastOcarina(game::GlobalContext* gctx) { } void Calc() { + GetContext().is_swimming = false; game::GlobalContext* gctx = GetContext().gctx; game::act::Player* player = gctx->GetPlayerActor(); if (!player) @@ -279,6 +280,11 @@ void Calc() { GetContext().a_press_duration = 0; } + if (player->flags1.IsSet(game::act::Player::Flag1::InWater) && + !player->flags_94.IsSet(game::act::Actor::Flag94::Grounded)) { + GetContext().is_swimming = true; + } + HandleFastArrowSwitch(player); HandleFastOcarina(gctx); }