From 3621705933fdb89d045c811946296a388da9b564 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 13 Jan 2025 13:13:14 +1000 Subject: [PATCH] Achievements: Ensure loading old states behave the same as new --- src/core/achievements.cpp | 11 ++++++++--- src/core/achievements.h | 2 +- src/core/system.cpp | 17 +++-------------- src/util/state_wrapper.cpp | 8 ++++++++ src/util/state_wrapper.h | 1 + 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index 9bbfc993d..4bf873860 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -1642,7 +1642,7 @@ void Achievements::HandleServerReconnectedEvent(const rc_client_event_t* event) }); } -void Achievements::ResetClient() +void Achievements::Reset() { #ifdef ENABLE_RAINTEGRATION if (IsUsingRAIntegration()) @@ -1750,11 +1750,13 @@ void Achievements::SetHardcoreMode(bool enabled, bool force_display_message) bool Achievements::DoState(StateWrapper& sw) { + static constexpr u32 REQUIRED_VERSION = 56; + // if we're inactive, we still need to skip the data (if any) if (!IsActive()) { u32 data_size = 0; - sw.Do(&data_size); + sw.DoEx(&data_size, REQUIRED_VERSION, 0u); if (data_size > 0) sw.SkipBytes(data_size); @@ -1780,8 +1782,11 @@ bool Achievements::DoState(StateWrapper& sw) GPUThread::RunOnThread([]() { FullscreenUI::CloseLoadingScreen(); }); } + // loading an old state without cheevos, so reset the runtime + Achievements::Reset(); + u32 data_size = 0; - sw.Do(&data_size); + sw.DoEx(&data_size, REQUIRED_VERSION, 0u); if (data_size == 0) { // reset runtime, no data (state might've been created without cheevos) diff --git a/src/core/achievements.h b/src/core/achievements.h index 2ac140e99..cbf93de2c 100644 --- a/src/core/achievements.h +++ b/src/core/achievements.h @@ -41,7 +41,7 @@ bool Initialize(); void UpdateSettings(const Settings& old_config); /// Resets the internal state of all achievement tracking. Call on system reset. -void ResetClient(); +void Reset(); /// Called when the system is being reset. If it returns false, the reset should be aborted. bool ConfirmSystemReset(); diff --git a/src/core/system.cpp b/src/core/system.cpp index e8307d892..2cf2ece1a 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -2457,19 +2457,8 @@ bool System::DoState(StateWrapper& sw, bool update_display) UpdateOverclock(); } - if (sw.GetVersion() >= 56) [[unlikely]] - { - if (!sw.DoMarker("Cheevos")) - return false; - - if (!Achievements::DoState(sw)) - return false; - } - else - { - // loading an old state without cheevos, so reset the runtime - Achievements::ResetClient(); - } + if (!sw.DoMarkerEx("Cheevos", 56) || !Achievements::DoState(sw)) + return false; if (sw.HasError()) return false; @@ -2750,7 +2739,7 @@ void System::InternalReset() MDEC::Reset(); SIO::Reset(); PCDrv::Reset(); - Achievements::ResetClient(); + Achievements::Reset(); s_state.frame_number = 1; s_state.internal_frame_number = 0; } diff --git a/src/util/state_wrapper.cpp b/src/util/state_wrapper.cpp index af6634a6d..cc8dc9ff7 100644 --- a/src/util/state_wrapper.cpp +++ b/src/util/state_wrapper.cpp @@ -111,6 +111,14 @@ bool StateWrapper::DoMarker(const char* marker) return false; } +bool StateWrapper::DoMarkerEx(const char* marker, u32 version_introduced) +{ + if (m_version < version_introduced) + return !HasError(); + + return DoMarker(marker); +} + std::span StateWrapper::GetDeferredBytes(size_t size) { if ((m_error = (m_error || (m_pos + size) > m_size))) [[unlikely]] diff --git a/src/util/state_wrapper.h b/src/util/state_wrapper.h index ceb1dd134..9e18f7865 100644 --- a/src/util/state_wrapper.h +++ b/src/util/state_wrapper.h @@ -180,6 +180,7 @@ public: } bool DoMarker(const char* marker); + bool DoMarkerEx(const char* marker, u32 version_introduced); template void DoEx(T* data, u32 version_introduced, T default_value)