From baa9065d2eb68cdd7d5aa098680bacef50f1e4ae Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 19 Oct 2024 01:11:43 +1000 Subject: [PATCH] System: Add taints to save states This informs the user they are loading a save state that at some point had potentially-game-breaking options enabled, and a reset is necessary to clear them. --- src/core/cheats.cpp | 48 +++++++++++----- src/core/cheats.h | 6 ++ src/core/save_state_version.h | 2 +- src/core/system.cpp | 103 ++++++++++++++++++++++++++++++++++ src/core/system.h | 18 ++++++ 5 files changed, 161 insertions(+), 16 deletions(-) diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index b350c1d24..9a1bf28f8 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -237,6 +237,8 @@ static EnableCodeList s_enabled_patches; static ActiveCodeList s_frame_end_codes; +static u32 s_active_patch_count = 0; +static u32 s_active_cheat_count = 0; static bool s_patches_enabled = false; static bool s_cheats_enabled = false; static bool s_database_cheat_codes_enabled = false; @@ -828,6 +830,8 @@ void Cheats::ReloadCheats(bool reload_files, bool reload_enabled_list, bool verb void Cheats::UnloadAll() { + s_active_cheat_count = 0; + s_active_patch_count = 0; s_frame_end_codes = ActiveCodeList(); s_enabled_patches = EnableCodeList(); s_enabled_cheats = EnableCodeList(); @@ -862,14 +866,15 @@ void Cheats::UpdateActiveCodes(bool reload_enabled_list, bool verbose, bool verb const size_t prev_count = s_frame_end_codes.size(); s_frame_end_codes.clear(); - u32 patch_count = 0; - u32 cheat_count = 0; + s_active_patch_count = 0; + s_active_cheat_count = 0; if (!g_settings.disable_all_enhancements) { const bool hc_mode_active = Achievements::IsHardcoreModeActive(); - patch_count = EnableCheats(s_patch_codes, s_enabled_patches, "Patches", hc_mode_active); - cheat_count = AreCheatsEnabled() ? EnableCheats(s_cheat_codes, s_enabled_cheats, "Cheats", hc_mode_active) : 0; + s_active_patch_count = EnableCheats(s_patch_codes, s_enabled_patches, "Patches", hc_mode_active); + s_active_cheat_count = + AreCheatsEnabled() ? EnableCheats(s_cheat_codes, s_enabled_cheats, "Cheats", hc_mode_active) : 0; } // Display message on first boot when we load patches. @@ -877,20 +882,23 @@ void Cheats::UpdateActiveCodes(bool reload_enabled_list, bool verbose, bool verb const size_t new_count = s_frame_end_codes.size(); if (verbose || (verbose_if_changed && prev_count != new_count)) { - if (patch_count > 0) - { - Host::AddIconOSDMessage("LoadPatches", ICON_FA_BAND_AID, - TRANSLATE_PLURAL_STR("Cheats", "%n game patches are active.", "OSD Message", patch_count), - Host::OSD_INFO_DURATION); - } - if (cheat_count > 0) + if (s_active_patch_count > 0) { + System::SetTaint(System::Taint::Patches); Host::AddIconOSDMessage( - "LoadCheats", ICON_EMOJI_WARNING, - TRANSLATE_PLURAL_STR("Cheats", "%n cheats are enabled. This may crash games.", "OSD Message", cheat_count), - Host::OSD_WARNING_DURATION); + "LoadPatches", ICON_FA_BAND_AID, + TRANSLATE_PLURAL_STR("Cheats", "%n game patches are active.", "OSD Message", s_active_patch_count), + Host::OSD_INFO_DURATION); } - else if (patch_count == 0) + if (s_active_cheat_count > 0) + { + System::SetTaint(System::Taint::Cheats); + Host::AddIconOSDMessage("LoadCheats", ICON_EMOJI_WARNING, + TRANSLATE_PLURAL_STR("Cheats", "%n cheats are enabled. This may crash games.", + "OSD Message", s_active_cheat_count), + Host::OSD_WARNING_DURATION); + } + else if (s_active_patch_count == 0) { Host::RemoveKeyedOSDMessage("LoadPatches"); Host::AddIconOSDMessage("LoadCheats", ICON_FA_BAND_AID, @@ -936,6 +944,16 @@ bool Cheats::ApplyManualCode(const std::string_view name) return false; } +u32 Cheats::GetActivePatchCount() +{ + return s_active_patch_count; +} + +u32 Cheats::GetActiveCheatCount() +{ + return s_active_cheat_count; +} + ////////////////////////////////////////////////////////////////////////// // File Parsing ////////////////////////////////////////////////////////////////////////// diff --git a/src/core/cheats.h b/src/core/cheats.h index 927e68b45..f2c27da2f 100644 --- a/src/core/cheats.h +++ b/src/core/cheats.h @@ -143,6 +143,12 @@ extern bool EnumerateManualCodes(std::function ca /// Invokes/applies the specified manually-activated code. extern bool ApplyManualCode(const std::string_view name); +/// Returns the number of active patches. +extern u32 GetActivePatchCount(); + +/// Returns the number of active cheats. +extern u32 GetActiveCheatCount(); + // Config sections/keys to use to enable patches. extern const char* PATCHES_CONFIG_SECTION; extern const char* CHEATS_CONFIG_SECTION; diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index cb692265f..9c2e098dd 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -6,7 +6,7 @@ #include "common/types.h" static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -static constexpr u32 SAVE_STATE_VERSION = 74; +static constexpr u32 SAVE_STATE_VERSION = 75; 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 6607b837d..aa97c2680 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -151,6 +151,8 @@ static bool ShouldStartPaused(); /// Checks for settings changes, std::move() the old settings away for comparing beforehand. static void CheckForSettingsChanges(const Settings& old_settings); +static void SetTaintsFromSettings(); +static void WarnAboutStateTaints(u32 state_taints); static void WarnAboutUnsafeSettings(); static void LogUnsafeSettingsToConsole(const SmallStringBase& messages); @@ -251,6 +253,7 @@ static u32 s_frame_number = 1; static u32 s_internal_frame_number = 1; static const BIOS::ImageInfo* s_bios_image_info = nullptr; static BIOS::ImageInfo::Hash s_bios_hash = {}; +static u32 s_taints = 0; static std::string s_running_game_path; static std::string s_running_game_serial; @@ -660,6 +663,49 @@ bool System::IsPALRegion() return s_region == ConsoleRegion::PAL; } +const char* System::GetTaintDisplayName(Taint taint) +{ + static constexpr const std::array(Taint::MaxCount)> names = {{ + TRANSLATE_DISAMBIG_NOOP("System", "CPU Overclock", "Taint"), + TRANSLATE_DISAMBIG_NOOP("System", "CD-ROM Read Speedup", "Taint"), + TRANSLATE_DISAMBIG_NOOP("System", "CD-ROM Seek Speedup", "Taint"), + TRANSLATE_DISAMBIG_NOOP("System", "Force Frame Timings", "Taint"), + TRANSLATE_DISAMBIG_NOOP("System", "8MB RAM", "Taint"), + TRANSLATE_DISAMBIG_NOOP("System", "Cheats", "Taint"), + TRANSLATE_DISAMBIG_NOOP("System", "Game Patches", "Taint"), + }}; + + return names[static_cast(taint)]; +} + +const char* System::GetTaintName(Taint taint) +{ + static constexpr const std::array(Taint::MaxCount)> names = {{ + "CPUOverclock", + "CDROMReadSpeedup", + "CDROMSeekSpeedup", + "ForceFrameTimings", + "RAM8MB", + "Cheats", + "Patches", + }}; + + return names[static_cast(taint)]; +} + +bool System::HasTaint(Taint taint) +{ + return (s_taints & (1u << static_cast(taint))) != 0u; +} + +void System::SetTaint(Taint taint) +{ + if (!HasTaint(taint)) + WARNING_LOG("Setting system taint: {}", GetTaintName(taint)); + + s_taints |= (1u << static_cast(taint)); +} + TickCount System::GetMaxSliceTicks() { return s_max_slice_ticks; @@ -2039,6 +2085,7 @@ void System::DestroySystem() Host::ReleaseGPUDevice(); } + s_taints = 0; s_bios_hash = {}; s_bios_image_info = nullptr; s_exe_override = {}; @@ -2464,6 +2511,11 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di sw.Do(&s_region); } + u32 state_taints = s_taints; + sw.DoEx(&state_taints, 75, static_cast(0)); + if (state_taints != s_taints) [[unlikely]] + WarnAboutStateTaints(state_taints); + sw.Do(&s_frame_number); sw.Do(&s_internal_frame_number); @@ -2600,6 +2652,9 @@ void System::InternalReset() if (IsShutdown()) return; + // reset and clear taints + SetTaintsFromSettings(); + TimingEvents::Reset(); CPU::Reset(); CPU::CodeCache::Reset(); @@ -4580,6 +4635,54 @@ void System::CheckForSettingsChanges(const Settings& old_settings) } } +void System::SetTaintsFromSettings() +{ + s_taints = 0; + + if (g_settings.cdrom_read_speedup > 1) + SetTaint(Taint::CDROMReadSpeedup); + if (g_settings.cdrom_seek_speedup > 1) + SetTaint(Taint::CDROMSeekSpeedup); + if (g_settings.cpu_overclock_active) + SetTaint(Taint::CPUOverclock); + if (g_settings.gpu_force_video_timing != ForceVideoTimingMode::Disabled) + SetTaint(Taint::ForceFrameTimings); + if (g_settings.enable_8mb_ram) + SetTaint(Taint::RAM8MB); + if (Cheats::GetActivePatchCount() > 0) + SetTaint(Taint::Patches); + if (Cheats::GetActiveCheatCount() > 0) + SetTaint(Taint::Cheats); +} + +void System::WarnAboutStateTaints(u32 state_taints) +{ + const u32 taints_active_in_file = state_taints & ~s_taints; + if (taints_active_in_file == 0) + return; + + LargeString messages; + for (u32 i = 0; i < static_cast(Taint::MaxCount); i++) + { + if (!(taints_active_in_file & (1u << i))) + continue; + + if (messages.empty()) + { + messages.append_format( + "{} {}\n", ICON_EMOJI_WARNING, + TRANSLATE_SV("System", "This save state was created with the following tainted options, and may\n" + " be unstable. You will need to reset the system to clear any effects.")); + } + + messages.append(" \u2022 "); + messages.append(GetTaintDisplayName(static_cast(i))); + messages.append('\n'); + } + + Host::AddKeyedOSDWarning("SystemTaintsFromState", std::string(messages.view()), Host::OSD_WARNING_DURATION); +} + void System::WarnAboutUnsafeSettings() { LargeString messages; diff --git a/src/core/system.h b/src/core/system.h index 0179c8943..cf2c4d41c 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -104,6 +104,18 @@ enum class BootMode BootPSF, }; +enum class Taint : u8 +{ + CPUOverclock, + CDROMReadSpeedup, + CDROMSeekSpeedup, + ForceFrameTimings, + RAM8MB, + Cheats, + Patches, + MaxCount, +}; + extern TickCount g_ticks_per_second; /// Returns true if the filename is a PlayStation executable we can inject. @@ -156,6 +168,12 @@ ConsoleRegion GetRegion(); DiscRegion GetDiscRegion(); bool IsPALRegion(); +/// Taints - flags that are set on the system and only cleared on reset. +const char* GetTaintDisplayName(Taint taint); +const char* GetTaintName(Taint taint); +bool HasTaint(Taint taint); +void SetTaint(Taint taint); + ALWAYS_INLINE TickCount GetTicksPerSecond() { return g_ticks_per_second;