From a55b5022c7995dba50fe122ba22c68ec4e727188 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 27 Mar 2022 00:52:11 +1000 Subject: [PATCH] System: Include cheevos state in save states --- src/common/state_wrapper.h | 12 +++ src/core/cheevos.cpp | 78 +++++++++++++++++-- src/core/cheevos.h | 3 + src/core/save_state_version.h | 2 +- src/core/system.cpp | 38 ++++++++- src/frontend-common/common_host_interface.cpp | 8 -- 6 files changed, 124 insertions(+), 17 deletions(-) diff --git a/src/common/state_wrapper.h b/src/common/state_wrapper.h index 38ad4a357..9b264615f 100644 --- a/src/common/state_wrapper.h +++ b/src/common/state_wrapper.h @@ -188,6 +188,18 @@ public: Do(data); } + void SkipBytes(size_t count) + { + if (m_mode != Mode::Read) + { + m_error = true; + return; + } + + if (!m_error) + m_error = !m_stream->SeekRelative(static_cast(count)); + } + private: ByteStream* m_stream; Mode m_mode; diff --git a/src/core/cheevos.cpp b/src/core/cheevos.cpp index 7f9b93f5b..d093c853f 100644 --- a/src/core/cheevos.cpp +++ b/src/core/cheevos.cpp @@ -6,6 +6,7 @@ #include "common/log.h" #include "common/md5_digest.h" #include "common/platform.h" +#include "common/state_wrapper.h" #include "common/string_util.h" #include "common/timestamp.h" #include "core/bios.h" @@ -310,6 +311,69 @@ void Update() } } +bool DoState(StateWrapper& sw) +{ + // if we're inactive, we still need to skip the data (if any) + if (!g_active) + { + u32 data_size = 0; + sw.Do(&data_size); + if (data_size > 0) + sw.SkipBytes(data_size); + + return !sw.HasError(); + } + + if (sw.IsReading()) + { + u32 data_size = 0; + sw.Do(&data_size); + if (data_size == 0) + { + // reset runtime, no data (state might've been created without cheevos) + Log_DevPrintf("State is missing cheevos data, resetting runtime"); + rc_runtime_reset(&s_rcheevos_runtime); + return !sw.HasError(); + } + + const std::unique_ptr data(new u8[data_size]); + sw.DoBytes(data.get(), data_size); + if (sw.HasError()) + return false; + + const int result = rc_runtime_deserialize_progress(&s_rcheevos_runtime, data.get(), nullptr); + if (result != RC_OK) + { + Log_WarningPrintf("Failed to deserialize cheevos state (%d), resetting", result); + rc_runtime_reset(&s_rcheevos_runtime); + } + + return true; + } + else + { + // internally this happens twice.. not great. + const int size = rc_runtime_progress_size(&s_rcheevos_runtime, nullptr); + + u32 data_size = (size >= 0) ? static_cast(size) : 0; + std::unique_ptr data(new u8[data_size]); + + const int result = rc_runtime_serialize_progress(data.get(), &s_rcheevos_runtime, nullptr); + if (result != RC_OK) + { + // set data to zero, effectively serializing nothing + Log_WarningPrintf("Failed to serialize cheevos state (%d)", result); + data_size = 0; + } + + sw.Do(&data_size); + if (data_size > 0) + sw.DoBytes(data.get(), data_size); + + return !sw.HasError(); + } +} + bool IsLoggedIn() { return s_logged_in; @@ -410,8 +474,8 @@ bool LoginAsync(const char* username, const char* password) if (ImGuiFullscreen::IsInitialized()) { ImGuiFullscreen::OpenBackgroundProgressDialog( - "cheevos_async_login", g_host_interface->TranslateStdString("Cheevos", "Logging in to RetroAchivements..."), 0, - 1, 0); + "cheevos_async_login", g_host_interface->TranslateStdString("Cheevos", "Logging in to RetroAchivements..."), 0, 1, + 0); } SendLogin(username, password, s_http_downloader.get(), LoginASyncCallback); @@ -545,8 +609,8 @@ static std::string GetBadgeImageFilename(const char* badge_name, bool locked, bo SmallString clean_name(badge_name); FileSystem::SanitizeFileName(clean_name); return g_host_interface->GetUserDirectoryRelativePath("cache" FS_OSPATH_SEPARATOR_STR - "achievement_badge" FS_OSPATH_SEPARATOR_STR "%s%s.png", - clean_name.GetCharArray(), locked ? "_lock" : ""); + "achievement_badge" FS_OSPATH_SEPARATOR_STR "%s%s.png", + clean_name.GetCharArray(), locked ? "_lock" : ""); } } @@ -1019,9 +1083,9 @@ void GameChanged(const std::string& path, CDImage* image) if (s_game_hash.empty()) { - g_host_interface->AddOSDMessage(g_host_interface->TranslateStdString( - "OSDMessage", "Failed to read executable from disc. Achievements disabled."), - 10.0f); + g_host_interface->AddOSDMessage( + g_host_interface->TranslateStdString("OSDMessage", "Failed to read executable from disc. Achievements disabled."), + 10.0f); return; } diff --git a/src/core/cheevos.h b/src/core/cheevos.h index 7e18229e2..6c1d49445 100644 --- a/src/core/cheevos.h +++ b/src/core/cheevos.h @@ -6,6 +6,7 @@ #include class CDImage; +class StateWrapper; namespace Cheevos { @@ -79,7 +80,9 @@ bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_r bool include_unofficial); void Reset(); void Shutdown(); + void Update(); +bool DoState(StateWrapper& sw); bool IsLoggedIn(); bool IsTestModeActive(); diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index 803f853b5..5c1992f73 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -2,7 +2,7 @@ #include "types.h" static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -static constexpr u32 SAVE_STATE_VERSION = 55; +static constexpr u32 SAVE_STATE_VERSION = 56; static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42; static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION); diff --git a/src/core/system.cpp b/src/core/system.cpp index e1a1f08c5..91de0eecb 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -45,6 +45,10 @@ #include Log_SetChannel(System); +#ifdef WITH_CHEEVOS +#include "cheevos.h" +#endif + // #define PROFILE_MEMORY_SAVE_STATES 1 SystemBootParameters::SystemBootParameters() = default; @@ -71,7 +75,8 @@ static bool LoadEXE(const char* filename); /// Opens CD image, preloading if needed. static std::unique_ptr OpenCDImage(const char* path, Common::Error* error, bool force_preload, bool check_for_patches); -static bool ReadExecutableFromImage(ISOReader& iso, std::string* out_executable_name, std::vector* out_executable_data); +static bool ReadExecutableFromImage(ISOReader& iso, std::string* out_executable_name, + std::vector* out_executable_data); static bool ShouldCheckForImagePatches(); static bool DoLoadState(ByteStream* stream, bool force_software_renderer, bool update_display); @@ -1142,6 +1147,33 @@ bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_di UpdateOverclock(); } + if (!is_memory_state) + { + if (sw.GetVersion() >= 56) + { + if (!sw.DoMarker("Cheevos")) + return false; + +#ifdef WITH_CHEEVOS + if (!Cheevos::DoState(sw)) + return false; +#else + // if we compiled without cheevos, we need to toss out the data from states which were + u32 data_size = 0; + sw.Do(&data_size); + if (data_size > 0) + sw.SkipBytes(data_size); +#endif + } + else + { +#ifdef WITH_CHEEVOS + // loading an old state without cheevos, so reset the runtime + Cheevos::Reset(); +#endif + } + } + return !sw.HasError(); } @@ -1172,6 +1204,10 @@ void Reset() TimingEvents::Reset(); ResetPerformanceCounters(); +#ifdef WITH_CHEEVOS + Cheevos::Reset(); +#endif + g_gpu->ResetGraphicsAPIState(); } diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index c02da7852..b9ef00ef6 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -250,10 +250,6 @@ void CommonHostInterface::PowerOffSystem(bool save_resume_state) void CommonHostInterface::ResetSystem() { HostInterface::ResetSystem(); - -#ifdef WITH_CHEEVOS - Cheevos::Reset(); -#endif } static void PrintCommandLineVersion(const char* frontend_name) @@ -768,10 +764,6 @@ bool CommonHostInterface::UndoLoadState() System::ResetPerformanceCounters(); System::ResetThrottler(); -#ifdef WITH_CHEEVOS - Cheevos::Reset(); -#endif - Log_InfoPrintf("Loaded undo save state."); m_undo_load_state.reset(); return true;