From bebad5127cdbe876f5aa8e5f6db4f9bade97a728 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 1 Oct 2022 23:46:53 +1000 Subject: [PATCH] Qt: Implement save state backups option --- pcsx2-qt/Settings/InterfaceSettingsWidget.cpp | 3 ++ pcsx2-qt/Settings/InterfaceSettingsWidget.ui | 45 +++++++++++-------- pcsx2/VMManager.cpp | 23 +++++++--- pcsx2/VMManager.h | 2 +- 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp b/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp index a3e110d554..276e4d3f40 100644 --- a/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp +++ b/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp @@ -59,6 +59,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.saveStateOnShutdown, "EmuCore", "SaveStateOnShutdown", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnStart, "UI", "StartPaused", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.backupSaveStates, "EmuCore", "BackupSavestate", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.startFullscreen, "UI", "StartFullscreen", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.doubleClickTogglesFullscreen, "UI", "DoubleClickTogglesFullscreen", @@ -115,6 +116,8 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget dialog->registerWidgetHelp(m_ui.pauseOnFocusLoss, tr("Pause On Focus Loss"), tr("Unchecked"), tr("Pauses the emulator when you minimize the window or switch to another application, " "and unpauses when you switch back.")); + dialog->registerWidgetHelp(m_ui.backupSaveStates, tr("Create Save State Backups"), tr("Unchecked"), + tr("Creates a backup copy of a save state if it already exists when the save is created. The backup copy has a .backup suffix.")); dialog->registerWidgetHelp(m_ui.startFullscreen, tr("Start Fullscreen"), tr("Unchecked"), tr("Automatically switches to fullscreen mode when a game is started.")); dialog->registerWidgetHelp(m_ui.hideMouseCursor, tr("Hide Cursor In Fullscreen"), tr("Checked"), diff --git a/pcsx2-qt/Settings/InterfaceSettingsWidget.ui b/pcsx2-qt/Settings/InterfaceSettingsWidget.ui index 4255c806dd..b91fc0794a 100644 --- a/pcsx2-qt/Settings/InterfaceSettingsWidget.ui +++ b/pcsx2-qt/Settings/InterfaceSettingsWidget.ui @@ -32,31 +32,17 @@ Behaviour - - - - Inhibit Screensaver - - - - - - - Pause On Start - - - - + Pause On Focus Loss - - + + - Confirm Shutdown + Inhibit Screensaver @@ -67,7 +53,28 @@ - + + + + Pause On Start + + + + + + + Confirm Shutdown + + + + + + + Create Save State Backups + + + + Enable Discord Presence diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index d72ec75444..98fc2652d8 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -93,7 +93,7 @@ namespace VMManager static std::string GetCurrentSaveStateFileName(s32 slot); static bool DoLoadState(const char* filename); - static bool DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread); + static bool DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state); static void ZipSaveState(std::unique_ptr elist, std::unique_ptr screenshot, std::string osd_key, const char* filename, s32 slot_for_message); @@ -1028,7 +1028,7 @@ void VMManager::Shutdown(bool save_resume_state) if (!GSDumpReplayer::IsReplayingDump() && save_resume_state) { std::string resume_file_name(GetCurrentSaveStateFileName(-1)); - if (!resume_file_name.empty() && !DoSaveState(resume_file_name.c_str(), -1, true)) + if (!resume_file_name.empty() && !DoSaveState(resume_file_name.c_str(), -1, true, false)) Console.Error("Failed to save resume state"); } else if (GSDumpReplayer::IsReplayingDump()) @@ -1185,7 +1185,7 @@ bool VMManager::DoLoadState(const char* filename) } } -bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread) +bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state) { if (GSDumpReplayer::IsReplayingDump()) return false; @@ -1197,6 +1197,17 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip std::unique_ptr elist(SaveState_DownloadState()); std::unique_ptr screenshot(SaveState_SaveScreenshot()); + if (FileSystem::FileExists(filename) && backup_old_state) + { + const std::string backup_filename(fmt::format("{}.backup", filename)); + Console.WriteLn(fmt::format("Creating save state backup {}...", backup_filename)); + if (!FileSystem::RenamePath(filename, backup_filename.c_str())) + { + Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_EXCLAMATION_TRIANGLE, + fmt::format("Failed to back up old save state {}.", Path::GetFileName(filename))); + } + } + if (zip_on_thread) { // lock order here is important; the thread could exit before we resume here. @@ -1336,9 +1347,9 @@ bool VMManager::LoadStateFromSlot(s32 slot) return DoLoadState(filename.c_str()); } -bool VMManager::SaveState(const char* filename, bool zip_on_thread) +bool VMManager::SaveState(const char* filename, bool zip_on_thread, bool backup_old_state) { - return DoSaveState(filename, -1, zip_on_thread); + return DoSaveState(filename, -1, zip_on_thread, backup_old_state); } bool VMManager::SaveStateToSlot(s32 slot, bool zip_on_thread) @@ -1349,7 +1360,7 @@ bool VMManager::SaveStateToSlot(s32 slot, bool zip_on_thread) // if it takes more than a minute.. well.. wtf. Host::AddIconOSDMessage(fmt::format("SaveStateSlot{}", slot), ICON_FA_SAVE, fmt::format("Saving state to slot {}...", slot), 60.0f); - return DoSaveState(filename.c_str(), slot, zip_on_thread); + return DoSaveState(filename.c_str(), slot, zip_on_thread, EmuConfig.BackupSavestate); } LimiterModeType VMManager::GetLimiterMode() diff --git a/pcsx2/VMManager.h b/pcsx2/VMManager.h index 4e4f180703..e5af10e359 100644 --- a/pcsx2/VMManager.h +++ b/pcsx2/VMManager.h @@ -122,7 +122,7 @@ namespace VMManager bool LoadStateFromSlot(s32 slot); /// Saves state to the specified filename. - bool SaveState(const char* filename, bool zip_on_thread = true); + bool SaveState(const char* filename, bool zip_on_thread = true, bool backup_old_state = false); /// Saves state to the specified slot. bool SaveStateToSlot(s32 slot, bool zip_on_thread = true);