From 062720913136ddf25a61d9239a809115dbbc9050 Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Fri, 16 Feb 2024 14:25:04 -0500 Subject: [PATCH] Add DoState to AchievementManager While state loading is not allowed in the hardcore mode that most players will use, it is allowed in softcore mode; more importantly, if something fails to unlock or unlocks when it shouldn't in either mode the player can create a save that retains the current achievement state. --- Source/Core/Core/AchievementManager.cpp | 40 +++++++++++++++++++++++++ Source/Core/Core/AchievementManager.h | 2 ++ Source/Core/Core/State.cpp | 6 +++- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index 451aa4d49a..4d4700e857 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -396,6 +396,46 @@ std::vector AchievementManager::GetActiveLeaderboards() const return display_values; } +void AchievementManager::DoState(PointerWrap& p) +{ + if (!m_client || !Config::Get(Config::RA_ENABLED)) + return; + size_t size = 0; + if (!p.IsReadMode()) + size = rc_client_progress_size(m_client); + p.Do(size); + auto buffer = std::make_unique(size); + if (!p.IsReadMode()) + { + int result = rc_client_serialize_progress_sized(m_client, buffer.get(), size); + if (result != RC_OK) + { + ERROR_LOG_FMT(ACHIEVEMENTS, "Failed serializing achievement client with error code {}", + result); + return; + } + } + p.DoArray(buffer.get(), (u32)size); + if (p.IsReadMode()) + { + int result = rc_client_deserialize_progress_sized(m_client, buffer.get(), size); + if (result != RC_OK) + { + ERROR_LOG_FMT(ACHIEVEMENTS, "Failed deserializing achievement client with error code {}", + result); + return; + } + size_t new_size = rc_client_progress_size(m_client); + if (size != new_size) + { + ERROR_LOG_FMT(ACHIEVEMENTS, "Loaded client size {} does not match size in state {}", new_size, + size); + return; + } + } + p.DoMarker("AchievementManager"); +} + void AchievementManager::CloseGame() { { diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index cc2dcda34e..eb1cb3d740 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -148,6 +148,8 @@ public: const NamedIconMap& GetChallengeIcons() const; std::vector GetActiveLeaderboards() const; + void DoState(PointerWrap& p); + void CloseGame(); void Logout(); void Shutdown(); diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 90f07d2756..4b3d0edfd0 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -98,7 +98,7 @@ static size_t s_state_writes_in_queue; static std::condition_variable s_state_write_queue_is_empty; // Don't forget to increase this after doing changes on the savestate system -constexpr u32 STATE_VERSION = 167; // Last changed in PR 12494 +constexpr u32 STATE_VERSION = 168; // Last changed in PR 12639 // Increase this if the StateExtendedHeader definition changes constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217 @@ -198,6 +198,10 @@ static void DoState(Core::System& system, PointerWrap& p) p.DoMarker("Wiimote"); Gecko::DoState(p); p.DoMarker("Gecko"); + +#ifdef USE_RETRO_ACHIEVEMENTS + AchievementManager::GetInstance().DoState(p); +#endif // USE_RETRO_ACHIEVEMENTS } void LoadFromBuffer(Core::System& system, std::vector& buffer)