From c21d475bbdfec5429cfbcf9e49ce48b70d962915 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 7 May 2022 22:56:44 +1000 Subject: [PATCH] Qt: Implement save-state-on-shutdown --- pcsx2-qt/EmuThread.cpp | 12 ++- pcsx2-qt/EmuThread.h | 3 +- pcsx2-qt/MainWindow.cpp | 98 +++++++++++++++-- pcsx2-qt/MainWindow.h | 1 + pcsx2-qt/MainWindow.ui | 10 ++ pcsx2-qt/Settings/InterfaceSettingsWidget.cpp | 4 +- pcsx2-qt/Settings/InterfaceSettingsWidget.ui | 4 +- pcsx2/Config.h | 1 + pcsx2/Pcsx2Config.cpp | 1 + pcsx2/SaveState.cpp | 8 +- pcsx2/SaveState.h | 1 + pcsx2/VMManager.cpp | 101 ++++++++++-------- pcsx2/VMManager.h | 12 +-- pcsx2/gui/SysState.cpp | 2 +- 14 files changed, 182 insertions(+), 76 deletions(-) diff --git a/pcsx2-qt/EmuThread.cpp b/pcsx2-qt/EmuThread.cpp index 1cd97066fe..f30a30a172 100644 --- a/pcsx2-qt/EmuThread.cpp +++ b/pcsx2-qt/EmuThread.cpp @@ -109,6 +109,7 @@ void EmuThread::startVM(std::shared_ptr boot_params) m_is_fullscreen = boot_params->fullscreen.value_or(QtHost::GetBaseBoolSettingValue("UI", "StartFullscreen", false)); m_is_rendering_to_main = QtHost::GetBaseBoolSettingValue("UI", "RenderToMainWindow", true); m_is_surfaceless = false; + m_save_state_on_shutdown = false; if (!VMManager::Initialize(*boot_params)) return; @@ -142,14 +143,21 @@ void EmuThread::setVMPaused(bool paused) VMManager::SetPaused(paused); } -void EmuThread::shutdownVM(bool allow_save_to_state /* = true */) +void EmuThread::shutdownVM(bool save_state /* = true */) { + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "shutdownVM", Qt::QueuedConnection, Q_ARG(bool, save_state)); + return; + } + const VMState state = VMManager::GetState(); if (state == VMState::Paused) m_event_loop->quit(); else if (state != VMState::Running) return; + m_save_state_on_shutdown = save_state; VMManager::SetState(VMState::Stopping); } @@ -254,7 +262,7 @@ void EmuThread::destroyVM() m_last_video_fps = 0.0f; m_last_internal_width = 0; m_last_internal_height = 0; - VMManager::Shutdown(); + VMManager::Shutdown(m_save_state_on_shutdown); } void EmuThread::executeVM() diff --git a/pcsx2-qt/EmuThread.h b/pcsx2-qt/EmuThread.h index c5b32805a5..b91e97322f 100644 --- a/pcsx2-qt/EmuThread.h +++ b/pcsx2-qt/EmuThread.h @@ -59,7 +59,7 @@ public Q_SLOTS: void startVM(std::shared_ptr boot_params); void resetVM(); void setVMPaused(bool paused); - void shutdownVM(bool allow_save_to_state = true); + void shutdownVM(bool save_state = true); void loadState(const QString& filename); void loadStateFromSlot(qint32 slot); void saveState(const QString& filename); @@ -159,6 +159,7 @@ private: bool m_is_rendering_to_main = false; bool m_is_fullscreen = false; bool m_is_surfaceless = false; + bool m_save_state_on_shutdown = false; float m_last_speed = 0.0f; float m_last_game_fps = 0.0f; diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 26c86b0898..e80e84d333 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -143,7 +143,8 @@ void MainWindow::connectSignals() connect(m_ui.actionChangeDiscFromGameList, &QAction::triggered, this, &MainWindow::onChangeDiscFromGameListActionTriggered); connect(m_ui.menuChangeDisc, &QMenu::aboutToShow, this, &MainWindow::onChangeDiscMenuAboutToShow); connect(m_ui.menuChangeDisc, &QMenu::aboutToHide, this, &MainWindow::onChangeDiscMenuAboutToHide); - connect(m_ui.actionPowerOff, &QAction::triggered, this, [this]() { requestShutdown(); }); + connect(m_ui.actionPowerOff, &QAction::triggered, this, [this]() { requestShutdown(true, true); }); + connect(m_ui.actionPowerOffWithoutSaving, &QAction::triggered, this, [this]() { requestShutdown(false, false); }); connect(m_ui.actionLoadState, &QAction::triggered, this, [this]() { m_ui.menuLoadState->exec(QCursor::pos()); }); connect(m_ui.actionSaveState, &QAction::triggered, this, [this]() { m_ui.menuSaveState->exec(QCursor::pos()); }); connect(m_ui.actionExit, &QAction::triggered, this, &MainWindow::close); @@ -562,6 +563,7 @@ void MainWindow::updateEmulationActions(bool starting, bool running) m_ui.actionStartBios->setDisabled(starting_or_running); m_ui.actionPowerOff->setEnabled(running); + m_ui.actionPowerOffWithoutSaving->setEnabled(running); m_ui.actionReset->setEnabled(running); m_ui.actionPause->setEnabled(running); m_ui.actionChangeDisc->setEnabled(running); @@ -739,18 +741,34 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav if (!VMManager::HasValidVM()) return true; + // if we don't have a crc, we can't save state + allow_save_to_state &= (m_current_game_crc != 0); + bool save_state = allow_save_to_state && EmuConfig.SaveStateOnShutdown; + // only confirm on UI thread because we need to display a msgbox if (allow_confirm && !GSDumpReplayer::IsReplayingDump() && QtHost::GetBaseBoolSettingValue("UI", "ConfirmShutdown", true)) { VMLock lock(pauseAndLockVM()); - if (QMessageBox::question(lock.getDialogParent(), tr("Confirm Shutdown"), - tr("Are you sure you want to shut down the virtual machine?\n\nAll unsaved progress will be lost.")) != QMessageBox::Yes) - { + + QMessageBox msgbox(lock.getDialogParent()); + msgbox.setIcon(QMessageBox::Question); + msgbox.setWindowTitle(tr("Confirm Shutdown")); + msgbox.setText("Are you sure you want to shut down the virtual machine?"); + + QCheckBox* save_cb = new QCheckBox(tr("Save State For Resume"), &msgbox); + save_cb->setChecked(save_state); + save_cb->setEnabled(allow_save_to_state); + msgbox.setCheckBox(save_cb); + msgbox.addButton(QMessageBox::Yes); + msgbox.addButton(QMessageBox::No); + msgbox.setDefaultButton(QMessageBox::Yes); + if (msgbox.exec() != QMessageBox::Yes) return false; - } + + save_state = save_cb->isChecked(); } - g_emu_thread->shutdownVM(allow_save_to_state); + g_emu_thread->shutdownVM(save_state); if (block_until_done || QtHost::InBatchMode()) { @@ -817,9 +835,16 @@ void MainWindow::onGameListEntryActivated() return; } + const std::optional resume = promptForResumeState( + QString::fromStdString(VMManager::GetSaveStateFileName(entry->serial.c_str(), entry->crc, -1))); + if (!resume.has_value()) + { + // cancelled + return; + } + // only resume if the option is enabled, and we have one for this game - const bool resume = (VMManager::ShouldSaveResumeState() && VMManager::HasSaveStateInSlot(entry->serial.c_str(), entry->crc, -1)); - startGameListEntry(entry, resume ? std::optional(-1) : std::optional(), std::nullopt); + startGameListEntry(entry, resume.value() ? std::optional(-1) : std::optional(), std::nullopt); } void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point) @@ -856,7 +881,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point) connect(action, &QAction::triggered, [this, entry]() { startGameListEntry(entry); }); // Make bold to indicate it's the default choice when double-clicking - if (!VMManager::ShouldSaveResumeState() || !VMManager::HasSaveStateInSlot(entry->serial.c_str(), entry->crc, -1)) + if (!VMManager::HasSaveStateInSlot(entry->serial.c_str(), entry->crc, -1)) QtUtils::MarkActionAsDefault(action); action = menu.addAction(tr("Fast Boot")); @@ -902,6 +927,15 @@ void MainWindow::onStartFileActionTriggered() std::shared_ptr params = std::make_shared(); params->filename = filename.toStdString(); + + const std::optional resume( + promptForResumeState( + QString::fromStdString(VMManager::GetSaveStateFileName(params->filename.c_str(), -1)))); + if (!resume.has_value()) + return; + else if (resume.value()) + params->state_index = -1; + g_emu_thread->startVM(std::move(params)); } @@ -1582,6 +1616,49 @@ void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry) m_game_list_widget->refreshGridCovers(); } +std::optional MainWindow::promptForResumeState(const QString& save_state_path) +{ + if (save_state_path.isEmpty()) + return false; + + QFileInfo fi(save_state_path); + if (!fi.exists()) + return false; + + QMessageBox msgbox(this); + msgbox.setIcon(QMessageBox::Question); + msgbox.setWindowTitle(tr("Load Resume State")); + msgbox.setText( + tr("A resume save state was found for this game, saved at:\n\n%1.\n\nDo you want to load this state, or start from a fresh boot?") + .arg(fi.lastModified().toLocalTime().toString())); + + QPushButton* load = msgbox.addButton(tr("Load State"), QMessageBox::AcceptRole); + QPushButton* boot = msgbox.addButton(tr("Fresh Boot"), QMessageBox::RejectRole); + QPushButton* delboot = msgbox.addButton(tr("Delete And Boot"), QMessageBox::RejectRole); + QPushButton* cancel = msgbox.addButton(QMessageBox::Cancel); + msgbox.setDefaultButton(load); + msgbox.exec(); + + QAbstractButton* clicked = msgbox.clickedButton(); + if (load == clicked) + { + return true; + } + else if (boot == clicked) + { + return false; + } + else if (delboot == clicked) + { + if (!QFile::remove(save_state_path)) + QMessageBox::critical(this, tr("Error"), tr("Failed to delete save state file '%1'.").arg(save_state_path)); + + return false; + } + + return std::nullopt; +} + void MainWindow::loadSaveStateSlot(s32 slot) { if (m_vm_valid) @@ -1659,8 +1736,7 @@ void MainWindow::populateLoadStateMenu(QMenu* menu, const QString& filename, con connect(action, &QAction::triggered, [this]() { loadSaveStateSlot(-1); }); // Make bold to indicate it's the default choice when double-clicking - if (VMManager::ShouldSaveResumeState()) - QtUtils::MarkActionAsDefault(action); + QtUtils::MarkActionAsDefault(action); } } diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index 1f10f3bd8c..0e43020ffe 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -185,6 +185,7 @@ private: std::optional fast_boot = std::nullopt); void setGameListEntryCoverImage(const GameList::Entry* entry); + std::optional promptForResumeState(const QString& save_state_path); void loadSaveStateSlot(s32 slot); void loadSaveStateFile(const QString& filename, const QString& state_filename); void populateLoadStateMenu(QMenu* menu, const QString& filename, const QString& serial, quint32 crc); diff --git a/pcsx2-qt/MainWindow.ui b/pcsx2-qt/MainWindow.ui index 601e3318c0..e229a506c4 100644 --- a/pcsx2-qt/MainWindow.ui +++ b/pcsx2-qt/MainWindow.ui @@ -80,6 +80,7 @@ + @@ -285,6 +286,15 @@ Shut &Down + + + + .. + + + Shut Down &Without Saving + + diff --git a/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp b/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp index 5cb45b3d8d..f0bffd3b9c 100644 --- a/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp +++ b/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp @@ -42,7 +42,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.inhibitScreensaver, "UI", "InhibitScreensaver", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.discordPresence, "UI", "DiscordPresence", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmShutdown, "UI", "ConfirmShutdown", true); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.saveStateOnExit, "EmuCore", "AutoStateLoadSave", false); + 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); @@ -87,7 +87,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget m_ui.confirmShutdown, tr("Confirm Shutdown"), tr("Checked"), tr("Determines whether a prompt will be displayed to confirm shutting down the virtual machine " "when the hotkey is pressed.")); - dialog->registerWidgetHelp(m_ui.saveStateOnExit, tr("Save State On Exit"), tr("Checked"), + dialog->registerWidgetHelp(m_ui.saveStateOnShutdown, tr("Save State On Shutdown"), tr("Checked"), tr("Automatically saves the emulator state when powering down or exiting. You can then " "resume directly from where you left off next time.")); dialog->registerWidgetHelp(m_ui.pauseOnStart, tr("Pause On Start"), tr("Unchecked"), diff --git a/pcsx2-qt/Settings/InterfaceSettingsWidget.ui b/pcsx2-qt/Settings/InterfaceSettingsWidget.ui index 7cf727328c..d05189a23d 100644 --- a/pcsx2-qt/Settings/InterfaceSettingsWidget.ui +++ b/pcsx2-qt/Settings/InterfaceSettingsWidget.ui @@ -68,9 +68,9 @@ - + - Save Or Load State On Exit / Resume + Save State On Shutdown diff --git a/pcsx2/Config.h b/pcsx2/Config.h index ba58f88910..90de99c15c 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -939,6 +939,7 @@ struct Pcsx2Config #endif #ifdef PCSX2_CORE EnableGameFixes : 1, // enables automatic game fixes + SaveStateOnShutdown : 1, // default value for saving state on shutdown #endif // when enabled uses BOOT2 injection, skipping sony bios splashes UseBOOT2Injection : 1, diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index ec2a806d5b..8bc9353e2a 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -1062,6 +1062,7 @@ void Pcsx2Config::LoadSave(SettingsWrapper& wrap) #endif #ifdef PCSX2_CORE SettingsWrapBitBool(EnableGameFixes); + SettingsWrapBitBool(SaveStateOnShutdown); #endif SettingsWrapBitBool(ConsoleToStdio); SettingsWrapBitBool(HostFs); diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp index 05f058a6d0..e31e2b111f 100644 --- a/pcsx2/SaveState.cpp +++ b/pcsx2/SaveState.cpp @@ -783,8 +783,7 @@ static bool SaveState_CompressScreenshot(SaveStateScreenshotData* data, zip_t* z // -------------------------------------------------------------------------------------- // CompressThread_VmState // -------------------------------------------------------------------------------------- -static void ZipStateToDiskOnThread(std::unique_ptr srclist, std::unique_ptr screenshot, - std::string filename, s32 slot_for_message) +void SaveState_ZipToDisk(std::unique_ptr srclist, std::unique_ptr screenshot, std::string filename, s32 slot_for_message) { #ifndef PCSX2_CORE wxGetApp().StartPendingSave(); @@ -858,9 +857,10 @@ static void ZipStateToDiskOnThread(std::unique_ptr srclist, st #endif } -void SaveState_ZipToDisk(std::unique_ptr srclist, std::unique_ptr screenshot, std::string filename, s32 slot_for_message) + +void SaveState_ZipToDiskOnThread(std::unique_ptr srclist, std::unique_ptr screenshot, std::string filename, s32 slot_for_message) { - std::thread threaded_save(ZipStateToDiskOnThread, std::move(srclist), std::move(screenshot), std::move(filename), slot_for_message); + std::thread threaded_save(SaveState_ZipToDisk, std::move(srclist), std::move(screenshot), std::move(filename), slot_for_message); threaded_save.detach(); } diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index 6ab851168a..a774c3ffe4 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -59,6 +59,7 @@ class ArchiveEntryList; extern std::unique_ptr SaveState_DownloadState(); extern std::unique_ptr SaveState_SaveScreenshot(); extern void SaveState_ZipToDisk(std::unique_ptr srclist, std::unique_ptr screenshot, std::string filename, s32 slot_for_message); +extern void SaveState_ZipToDiskOnThread(std::unique_ptr srclist, std::unique_ptr screenshot, std::string filename, s32 slot_for_message); extern void SaveState_UnzipFromDisk(const std::string& filename); // -------------------------------------------------------------------------------------- diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 3739fcd1fa..cfad3b11cc 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -84,7 +84,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); + static bool DoSaveState(const char* filename, s32 slot_for_message, bool save_on_thread); static void SetTimerResolutionIncreased(bool enabled); static void SetEmuThreadAffinities(bool force); @@ -534,30 +534,11 @@ bool VMManager::ApplyBootParameters(const VMBootParameters& params, std::string* return false; } - // try the game list first, but this won't work if we're in batch mode + *state_to_load = GetSaveStateFileName(params.filename.c_str(), params.state_index.value()); + if (state_to_load->empty()) { - auto lock = GameList::GetLock(); - if (const GameList::Entry* entry = GameList::GetEntryForPath(params.filename.c_str()); entry) - { - *state_to_load = GetSaveStateFileName(entry->serial.c_str(), entry->crc, params.state_index.value()); - } - else - { - // just scan it.. hopefully it'll come back okay - GameList::Entry temp_entry; - if (!GameList::PopulateEntryFromPath(params.filename.c_str(), &temp_entry)) - { - Host::ReportFormattedErrorAsync("Error", "Could not scan path '%s' for indexed save state load.", params.filename.c_str()); - return false; - } - - *state_to_load = GetSaveStateFileName(temp_entry.serial.c_str(), temp_entry.crc, params.state_index.value()); - } - if (state_to_load->empty()) - { - Host::ReportFormattedErrorAsync("Error", "Could not resolve path indexed save state load."); - return false; - } + Host::ReportFormattedErrorAsync("Error", "Could not resolve path indexed save state load."); + return false; } } @@ -771,7 +752,7 @@ bool VMManager::Initialize(const VMBootParameters& boot_params) { if (!DoLoadState(state_to_load.c_str())) { - Shutdown(); + Shutdown(false); return false; } } @@ -779,7 +760,7 @@ bool VMManager::Initialize(const VMBootParameters& boot_params) return true; } -void VMManager::Shutdown(bool allow_save_resume_state /* = true */) +void VMManager::Shutdown(bool save_resume_state) { SetTimerResolutionIncreased(false); @@ -788,10 +769,10 @@ void VMManager::Shutdown(bool allow_save_resume_state /* = true */) vu1Thread.WaitVU(); GetMTGS().WaitGS(); - if (!GSDumpReplayer::IsReplayingDump() && allow_save_resume_state && ShouldSaveResumeState()) + 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)) + if (!resume_file_name.empty() && !DoSaveState(resume_file_name.c_str(), -1, false)) Console.Error("Failed to save resume state"); } else if (GSDumpReplayer::IsReplayingDump()) @@ -854,23 +835,45 @@ void VMManager::Reset() UpdateRunningGame(true); } -bool VMManager::ShouldSaveResumeState() -{ - return Host::GetBoolSettingValue("EmuCore", "AutoStateLoadSave", false); -} - std::string VMManager::GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot) { - if (!game_serial || game_serial[0] == '\0') - return std::string(); - std::string filename; - if (slot < 0) - filename = StringUtil::StdStringFromFormat("%s (%08X).resume.p2s", game_serial, game_crc); - else - filename = StringUtil::StdStringFromFormat("%s (%08X).%02d.p2s", game_serial, game_crc, slot); + if (game_crc != 0) + { + if (slot < 0) + filename = StringUtil::StdStringFromFormat("%s (%08X).resume.p2s", game_serial, game_crc); + else + filename = StringUtil::StdStringFromFormat("%s (%08X).%02d.p2s", game_serial, game_crc, slot); - return Path::CombineStdString(EmuFolders::Savestates, filename); + filename = Path::CombineStdString(EmuFolders::Savestates, filename); + } + + return filename; +} + +std::string VMManager::GetSaveStateFileName(const char* filename, s32 slot) +{ + pxAssertRel(!HasValidVM(), "Should not have a VM when calling the non-gamelist GetSaveStateFileName()"); + + std::string ret; + + // try the game list first, but this won't work if we're in batch mode + auto lock = GameList::GetLock(); + if (const GameList::Entry* entry = GameList::GetEntryForPath(filename); entry) + { + ret = GetSaveStateFileName(entry->serial.c_str(), entry->crc, slot); + } + else + { + // just scan it.. hopefully it'll come back okay + GameList::Entry temp_entry; + if (GameList::PopulateEntryFromPath(filename, &temp_entry)) + { + ret = GetSaveStateFileName(temp_entry.serial.c_str(), temp_entry.crc, slot); + } + } + + return ret; } bool VMManager::HasSaveStateInSlot(const char* game_serial, u32 game_crc, s32 slot) @@ -906,7 +909,7 @@ bool VMManager::DoLoadState(const char* filename) } } -bool VMManager::DoSaveState(const char* filename, s32 slot_for_message) +bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread) { if (GSDumpReplayer::IsReplayingDump()) return false; @@ -914,7 +917,11 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message) try { std::unique_ptr elist = SaveState_DownloadState(); - SaveState_ZipToDisk(std::move(elist), SaveState_SaveScreenshot(), filename, slot_for_message); + if (zip_on_thread) + SaveState_ZipToDiskOnThread(std::move(elist), SaveState_SaveScreenshot(), filename, slot_for_message); + else + SaveState_ZipToDisk(std::move(elist), SaveState_SaveScreenshot(), filename, slot_for_message); + Host::InvalidateSaveStateCache(); Host::OnSaveStateSaved(filename); return true; @@ -949,12 +956,12 @@ bool VMManager::LoadStateFromSlot(s32 slot) return DoLoadState(filename.c_str()); } -bool VMManager::SaveState(const char* filename) +bool VMManager::SaveState(const char* filename, bool zip_on_thread) { - return DoSaveState(filename, -1); + return DoSaveState(filename, -1, zip_on_thread); } -bool VMManager::SaveStateToSlot(s32 slot) +bool VMManager::SaveStateToSlot(s32 slot, bool zip_on_thread) { const std::string filename(GetCurrentSaveStateFileName(slot)); if (filename.empty()) @@ -962,7 +969,7 @@ bool VMManager::SaveStateToSlot(s32 slot) // if it takes more than a minute.. well.. wtf. Host::AddKeyedFormattedOSDMessage(StringUtil::StdStringFromFormat("SaveStateSlot%d", slot), 60.0f, "Saving state to slot %d...", slot); - return DoSaveState(filename.c_str(), slot); + return DoSaveState(filename.c_str(), slot, zip_on_thread); } LimiterModeType VMManager::GetLimiterMode() diff --git a/pcsx2/VMManager.h b/pcsx2/VMManager.h index 359f415dc1..e66c7f408f 100644 --- a/pcsx2/VMManager.h +++ b/pcsx2/VMManager.h @@ -76,7 +76,7 @@ namespace VMManager bool Initialize(const VMBootParameters& boot_params); /// Destroys all system components. - void Shutdown(bool allow_save_resume_state = true); + void Shutdown(bool save_resume_state); /// Resets all subsystems to a cold boot. void Reset(); @@ -96,12 +96,12 @@ namespace VMManager /// Reloads cheats/patches. If verbose is set, the number of patches loaded will be shown in the OSD. void ReloadPatches(bool verbose); - /// Returns true if a resume save state should be saved/loaded. - bool ShouldSaveResumeState(); - /// Returns the save state filename for the given game serial/crc. std::string GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot); + /// Returns the path to save state for the specified disc/elf. + std::string GetSaveStateFileName(const char* filename, s32 slot); + /// Returns true if there is a save state in the specified slot. bool HasSaveStateInSlot(const char* game_serial, u32 game_crc, s32 slot); @@ -112,10 +112,10 @@ namespace VMManager bool LoadStateFromSlot(s32 slot); /// Saves state to the specified filename. - bool SaveState(const char* filename); + bool SaveState(const char* filename, bool zip_on_thread = true); /// Saves state to the specified slot. - bool SaveStateToSlot(s32 slot); + bool SaveStateToSlot(s32 slot, bool zip_on_thread = true); /// Returns the current limiter mode. LimiterModeType GetLimiterMode(); diff --git a/pcsx2/gui/SysState.cpp b/pcsx2/gui/SysState.cpp index f6806de47e..44b427256a 100644 --- a/pcsx2/gui/SysState.cpp +++ b/pcsx2/gui/SysState.cpp @@ -61,7 +61,7 @@ protected: std::unique_ptr elist = SaveState_DownloadState(); UI_EnableStateActions(); paused_core.AllowResume(); - SaveState_ZipToDisk(std::move(elist), nullptr, StringUtil::wxStringToUTF8String(m_filename), -1); + SaveState_ZipToDiskOnThread(std::move(elist), nullptr, StringUtil::wxStringToUTF8String(m_filename), -1); } };