From d1eb12f7fda49504365d0615ee7453cfb117b45c Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi Date: Sun, 23 Oct 2022 08:13:25 +0300 Subject: [PATCH 1/6] Make RPCS3 not delete savestate on boot by default --- rpcs3/Emu/system_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index a6aee3d1690c..1a062400973f 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -315,7 +315,7 @@ struct cfg_root : cfg::node node_savestate(cfg::node* _this) : cfg::node(_this, "Savestate") {} cfg::_bool start_paused{ this, "Start Paused" }; // Pause on first frame - cfg::_bool suspend_emu{ this, "Suspend Emulation Savestate Mode", true }; // Close emulation when saving, delete save after loading + cfg::_bool suspend_emu{ this, "Suspend Emulation Savestate Mode", false }; // Close emulation when saving, delete save after loading cfg::_bool state_inspection_mode{ this, "Inspection Mode Savestates" }; // Save memory stored in executable files, thus allowing to view state without any files (for debugging) cfg::_bool save_disc_game_data{ this, "Save Disc Game Data", false }; } savestate{this}; From be65750b71428f6fa8178a6c3f8c6c35f03f3369 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 23 Oct 2022 08:49:13 +0300 Subject: [PATCH 2/6] qt: Add "suspend Emulation Mode Savestates" to advanced tab --- rpcs3/rpcs3qt/emu_settings_type.h | 4 ++++ rpcs3/rpcs3qt/settings_dialog.cpp | 3 +++ rpcs3/rpcs3qt/settings_dialog.ui | 7 +++++++ rpcs3/rpcs3qt/tooltips.h | 1 + 4 files changed, 15 insertions(+) diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index 3b654b11be2d..5c2bad36cda4 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -35,6 +35,7 @@ enum class emu_settings_type SPUCache, DebugConsoleMode, SilenceAllLogs, + SuspendEmulationSavestateMode, MaxSPURSThreads, SleepTimersAccuracy, ClocksScale, @@ -354,4 +355,7 @@ inline static const QMap settings_location = { emu_settings_type::LimitCacheSize, { "VFS", "Limit disk cache size"}}, { emu_settings_type::MaximumCacheSize, { "VFS", "Disk cache maximum size (MB)"}}, { emu_settings_type::ConsoleTimeOffset, { "System", "Console time offset (s)"}}, + + // Savestates + { emu_settings_type::SuspendEmulationSavestateMode, { "Savestate", "Suspend Emulation Savestate Mode" }}, }; diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 43812fe2da5d..6e02427479d9 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1325,6 +1325,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->accuratePPUFPCC, emu_settings_type::AccuratePPUFPCC); SubscribeTooltip(ui->accuratePPUFPCC, tooltips.settings.accurate_ppufpcc); + m_emu_settings->EnhanceCheckBox(ui->suspendSavestates, emu_settings_type::SuspendEmulationSavestateMode); + SubscribeTooltip(ui->suspendSavestates, tooltips.settings.suspend_savestates); + m_emu_settings->EnhanceCheckBox(ui->silenceAllLogs, emu_settings_type::SilenceAllLogs); SubscribeTooltip(ui->silenceAllLogs, tooltips.settings.silence_all_logs); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 401d9524a47a..5f9e2484721e 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -2334,6 +2334,13 @@ + + + + Suspend-Emulation Savestates Mode + + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index b42ca1f722cb..00bd4ff8ad63 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -42,6 +42,7 @@ class Tooltips : public QObject const QString vulkan_async_scheduler = tr("Determines how to schedule GPU async compute jobs when using asynchronous streaming.\nUse 'Safe' mode for more spec compliant behavior at the cost of some CPU overhead. This setting works with all devices.\nUse 'Fast' to use a faster but hacky version. This option is internally disabled for NVIDIA GPUs due to causing GPU hangs."); const QString allow_host_labels = tr("Allows the host GPU to synchronize with CELL directly. This incurs a performance penalty, but exposes the true state of GPU objects to the guest CPU. Can help eliminate visual noise and glitching at the cost of performance. Use with caution."); const QString disable_msl_fast_math = tr("Disables Fast Math for MSL shaders, which may violate the IEEE 754 standard.\nDisabling it may fix some artefacts especially on Apple GPUs, at the cost of performance."); + const QString suspend_savestates = tr("When this mode is on, emulation exits when saving and the savestate file is concealed after loading it, preventing reuse by RPCS3.\nThis mode is like hibernation of emulation: if you don't want to be able to cheat using savestates when playing the game, consider using this mode.\nDo note that the savestate file is not gone completely just ignored by RPCS3, you can manually relaunch it if needed."); // audio From 4a0940665460012493ea68bb99abbfd28f09dc97 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 23 Oct 2022 08:52:16 +0300 Subject: [PATCH 3/6] Savestates: Disable HDD1 saving optimization HDD1 is very volatile, it was a bad idea not to save it. --- rpcs3/Emu/System.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 54c5d8cdf6f6..3f571bd7a2da 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2393,20 +2393,13 @@ std::shared_ptr Emulator::Kill(bool allow_autoexit, bool savestat if (!_path.empty()) { - if (!g_cfg.savestate.suspend_emu) - { - save_tar(_path); - } - else - { - ar(usz{}); - } + save_tar(_path); } }; auto save_hdd0 = [&]() { - if (!g_cfg.savestate.suspend_emu && g_cfg.savestate.save_disc_game_data) + if (g_cfg.savestate.save_disc_game_data) { const std::string path = vfs::get("/dev_hdd0/game/"); From f063d0a0441abe909244b714f1abfc28273dac72 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 23 Oct 2022 08:55:03 +0300 Subject: [PATCH 4/6] Savestates: Auto-relaunch savestate when suspen mode is off --- rpcs3/Emu/System.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 3f571bd7a2da..92e1ebb34ad6 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2550,6 +2550,13 @@ std::shared_ptr Emulator::Kill(bool allow_autoexit, bool savestat } ar.set_reading_state(); + + if (!g_cfg.savestate.suspend_emu) + { + to_ar.reset(); + Restart(); + return to_ar; + } } // Boot arg cleanup (preserved in the case restarting) From 06412cc01c1c9d8019c4db5e2ac8751c27b411b8 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 23 Oct 2022 08:58:32 +0300 Subject: [PATCH 5/6] Savestates/Menu: Add "Create Savestate" Button --- rpcs3/rpcs3qt/main_window.cpp | 7 +++++++ rpcs3/rpcs3qt/main_window.ui | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 9bfea13957cf..4b65a32777a8 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -1735,6 +1735,7 @@ void main_window::EnableMenus(bool enabled) const ui->toolsRsxDebuggerAct->setEnabled(enabled); ui->toolsStringSearchAct->setEnabled(enabled); ui->actionCreate_RSX_Capture->setEnabled(enabled); + ui->actionCreate_Savestate->setEnabled(enabled); } void main_window::OnEnableDiscEject(bool enabled) const @@ -2023,6 +2024,12 @@ void main_window::CreateConnects() g_user_asked_for_frame_capture = true; }); + connect(ui->actionCreate_Savestate, &QAction::triggered, this, []() + { + gui_log.notice("User triggered savestate creation from utilities."); + Emu.Kill(false, true); + }); + connect(ui->bootSavestateAct, &QAction::triggered, this, &main_window::BootSavestate); connect(ui->addGamesAct, &QAction::triggered, this, [this]() diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 598e9398a3c2..e816fd934fae 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -1145,6 +1145,14 @@ Create RSX Capture + + + false + + + Create Savestate + + Game Patches From 609fdd4cc1ac86a9a87831234616edfaf1c2dd7e Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 23 Oct 2022 09:40:31 +0300 Subject: [PATCH 6/6] Savestates: Rename savestate when booting the game regularly (suspend mode) --- rpcs3/Emu/System.cpp | 29 +++++++++++++++++++---------- rpcs3/Emu/savestate_utils.cpp | 5 +++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 92e1ebb34ad6..434de3761704 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -81,6 +81,7 @@ extern std::pair, CellError> ppu_load_overlay(const extern bool ppu_load_rel_exec(const ppu_rel_object&); extern bool is_savestate_version_compatible(const std::vector>& data, bool is_boot_check); extern std::vector> read_used_savestate_versions(); +std::string get_savestate_path(std::string_view title_id, std::string_view boot_path); fs::file g_tty; atomic_t g_tty_size{0}; @@ -676,19 +677,27 @@ game_boot_result Emulator::BootGame(const std::string& path, const std::string& auto error = Load(title_id, add_only); - if (g_cfg.savestate.suspend_emu && m_ar) + if (g_cfg.savestate.suspend_emu) { - std::string old_path = path.substr(0, path.find_last_not_of(fs::delim) + 1); - const usz insert_pos = old_path.find_last_of(fs::delim) + 1; - const auto prefix = "used_"sv; - - if (old_path.compare(insert_pos, prefix.size(), prefix) != 0) + for (std::string old_path : std::initializer_list{m_ar ? path : "", m_title_id.empty() ? "" : get_savestate_path(m_title_id, path)}) { - old_path.insert(insert_pos, prefix); + if (old_path.empty()) + { + continue; + } - if (fs::rename(path, old_path, true)) + old_path = old_path.substr(0, old_path.find_last_not_of(fs::delim) + 1); + const usz insert_pos = old_path.find_last_of(fs::delim) + 1; + const auto prefix = "used_"sv; + + if (old_path.compare(insert_pos, prefix.size(), prefix) != 0) { - sys_log.notice("Savestate has been moved to path='%s'", old_path); + old_path.insert(insert_pos, prefix); + + if (fs::rename(path, old_path, true)) + { + sys_log.notice("Savestate has been moved to path='%s'", old_path); + } } } } @@ -2505,7 +2514,7 @@ std::shared_ptr Emulator::Kill(bool allow_autoexit, bool savestat if (savestate) { - const std::string path = fs::get_cache_dir() + "/savestates/" + (m_title_id.empty() ? m_path.substr(m_path.find_last_of(fs::delim) + 1) : m_title_id) + ".SAVESTAT"; + const std::string path = get_savestate_path(m_title_id, m_path); fs::pending_file file(path); diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 1c1d00d5bb23..539a445d4da9 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -146,6 +146,11 @@ bool is_savestate_version_compatible(const std::vector>& dat return ok; } +std::string get_savestate_path(std::string_view title_id, std::string_view boot_path) +{ + return fs::get_cache_dir() + "/savestates/" + std::string{title_id.empty() ? boot_path.substr(boot_path.find_last_of(fs::delim) + 1) : title_id} + ".SAVESTAT"; +} + bool is_savestate_compatible(const fs::file& file) { return is_savestate_version_compatible(get_savestate_versioning_data(file), false);