mirror of https://github.com/PCSX2/pcsx2.git
Qt: Implement save-state-on-shutdown
This commit is contained in:
parent
935dd046da
commit
c21d475bbd
|
@ -109,6 +109,7 @@ void EmuThread::startVM(std::shared_ptr<VMBootParameters> boot_params)
|
||||||
m_is_fullscreen = boot_params->fullscreen.value_or(QtHost::GetBaseBoolSettingValue("UI", "StartFullscreen", false));
|
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_rendering_to_main = QtHost::GetBaseBoolSettingValue("UI", "RenderToMainWindow", true);
|
||||||
m_is_surfaceless = false;
|
m_is_surfaceless = false;
|
||||||
|
m_save_state_on_shutdown = false;
|
||||||
if (!VMManager::Initialize(*boot_params))
|
if (!VMManager::Initialize(*boot_params))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -142,14 +143,21 @@ void EmuThread::setVMPaused(bool paused)
|
||||||
VMManager::SetPaused(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();
|
const VMState state = VMManager::GetState();
|
||||||
if (state == VMState::Paused)
|
if (state == VMState::Paused)
|
||||||
m_event_loop->quit();
|
m_event_loop->quit();
|
||||||
else if (state != VMState::Running)
|
else if (state != VMState::Running)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
m_save_state_on_shutdown = save_state;
|
||||||
VMManager::SetState(VMState::Stopping);
|
VMManager::SetState(VMState::Stopping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +262,7 @@ void EmuThread::destroyVM()
|
||||||
m_last_video_fps = 0.0f;
|
m_last_video_fps = 0.0f;
|
||||||
m_last_internal_width = 0;
|
m_last_internal_width = 0;
|
||||||
m_last_internal_height = 0;
|
m_last_internal_height = 0;
|
||||||
VMManager::Shutdown();
|
VMManager::Shutdown(m_save_state_on_shutdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::executeVM()
|
void EmuThread::executeVM()
|
||||||
|
|
|
@ -59,7 +59,7 @@ public Q_SLOTS:
|
||||||
void startVM(std::shared_ptr<VMBootParameters> boot_params);
|
void startVM(std::shared_ptr<VMBootParameters> boot_params);
|
||||||
void resetVM();
|
void resetVM();
|
||||||
void setVMPaused(bool paused);
|
void setVMPaused(bool paused);
|
||||||
void shutdownVM(bool allow_save_to_state = true);
|
void shutdownVM(bool save_state = true);
|
||||||
void loadState(const QString& filename);
|
void loadState(const QString& filename);
|
||||||
void loadStateFromSlot(qint32 slot);
|
void loadStateFromSlot(qint32 slot);
|
||||||
void saveState(const QString& filename);
|
void saveState(const QString& filename);
|
||||||
|
@ -159,6 +159,7 @@ private:
|
||||||
bool m_is_rendering_to_main = false;
|
bool m_is_rendering_to_main = false;
|
||||||
bool m_is_fullscreen = false;
|
bool m_is_fullscreen = false;
|
||||||
bool m_is_surfaceless = false;
|
bool m_is_surfaceless = false;
|
||||||
|
bool m_save_state_on_shutdown = false;
|
||||||
|
|
||||||
float m_last_speed = 0.0f;
|
float m_last_speed = 0.0f;
|
||||||
float m_last_game_fps = 0.0f;
|
float m_last_game_fps = 0.0f;
|
||||||
|
|
|
@ -143,7 +143,8 @@ void MainWindow::connectSignals()
|
||||||
connect(m_ui.actionChangeDiscFromGameList, &QAction::triggered, this, &MainWindow::onChangeDiscFromGameListActionTriggered);
|
connect(m_ui.actionChangeDiscFromGameList, &QAction::triggered, this, &MainWindow::onChangeDiscFromGameListActionTriggered);
|
||||||
connect(m_ui.menuChangeDisc, &QMenu::aboutToShow, this, &MainWindow::onChangeDiscMenuAboutToShow);
|
connect(m_ui.menuChangeDisc, &QMenu::aboutToShow, this, &MainWindow::onChangeDiscMenuAboutToShow);
|
||||||
connect(m_ui.menuChangeDisc, &QMenu::aboutToHide, this, &MainWindow::onChangeDiscMenuAboutToHide);
|
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.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.actionSaveState, &QAction::triggered, this, [this]() { m_ui.menuSaveState->exec(QCursor::pos()); });
|
||||||
connect(m_ui.actionExit, &QAction::triggered, this, &MainWindow::close);
|
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.actionStartBios->setDisabled(starting_or_running);
|
||||||
|
|
||||||
m_ui.actionPowerOff->setEnabled(running);
|
m_ui.actionPowerOff->setEnabled(running);
|
||||||
|
m_ui.actionPowerOffWithoutSaving->setEnabled(running);
|
||||||
m_ui.actionReset->setEnabled(running);
|
m_ui.actionReset->setEnabled(running);
|
||||||
m_ui.actionPause->setEnabled(running);
|
m_ui.actionPause->setEnabled(running);
|
||||||
m_ui.actionChangeDisc->setEnabled(running);
|
m_ui.actionChangeDisc->setEnabled(running);
|
||||||
|
@ -739,18 +741,34 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav
|
||||||
if (!VMManager::HasValidVM())
|
if (!VMManager::HasValidVM())
|
||||||
return true;
|
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
|
// only confirm on UI thread because we need to display a msgbox
|
||||||
if (allow_confirm && !GSDumpReplayer::IsReplayingDump() && QtHost::GetBaseBoolSettingValue("UI", "ConfirmShutdown", true))
|
if (allow_confirm && !GSDumpReplayer::IsReplayingDump() && QtHost::GetBaseBoolSettingValue("UI", "ConfirmShutdown", true))
|
||||||
{
|
{
|
||||||
VMLock lock(pauseAndLockVM());
|
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;
|
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())
|
if (block_until_done || QtHost::InBatchMode())
|
||||||
{
|
{
|
||||||
|
@ -817,9 +835,16 @@ void MainWindow::onGameListEntryActivated()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::optional<bool> 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
|
// 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.value() ? std::optional<s32>(-1) : std::optional<s32>(), std::nullopt);
|
||||||
startGameListEntry(entry, resume ? std::optional<s32>(-1) : std::optional<s32>(), std::nullopt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
|
void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
|
||||||
|
@ -856,7 +881,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
|
||||||
connect(action, &QAction::triggered, [this, entry]() { startGameListEntry(entry); });
|
connect(action, &QAction::triggered, [this, entry]() { startGameListEntry(entry); });
|
||||||
|
|
||||||
// Make bold to indicate it's the default choice when double-clicking
|
// 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);
|
QtUtils::MarkActionAsDefault(action);
|
||||||
|
|
||||||
action = menu.addAction(tr("Fast Boot"));
|
action = menu.addAction(tr("Fast Boot"));
|
||||||
|
@ -902,6 +927,15 @@ void MainWindow::onStartFileActionTriggered()
|
||||||
|
|
||||||
std::shared_ptr<VMBootParameters> params = std::make_shared<VMBootParameters>();
|
std::shared_ptr<VMBootParameters> params = std::make_shared<VMBootParameters>();
|
||||||
params->filename = filename.toStdString();
|
params->filename = filename.toStdString();
|
||||||
|
|
||||||
|
const std::optional<bool> 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));
|
g_emu_thread->startVM(std::move(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1582,6 +1616,49 @@ void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry)
|
||||||
m_game_list_widget->refreshGridCovers();
|
m_game_list_widget->refreshGridCovers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<bool> 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)
|
void MainWindow::loadSaveStateSlot(s32 slot)
|
||||||
{
|
{
|
||||||
if (m_vm_valid)
|
if (m_vm_valid)
|
||||||
|
@ -1659,8 +1736,7 @@ void MainWindow::populateLoadStateMenu(QMenu* menu, const QString& filename, con
|
||||||
connect(action, &QAction::triggered, [this]() { loadSaveStateSlot(-1); });
|
connect(action, &QAction::triggered, [this]() { loadSaveStateSlot(-1); });
|
||||||
|
|
||||||
// Make bold to indicate it's the default choice when double-clicking
|
// Make bold to indicate it's the default choice when double-clicking
|
||||||
if (VMManager::ShouldSaveResumeState())
|
QtUtils::MarkActionAsDefault(action);
|
||||||
QtUtils::MarkActionAsDefault(action);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,6 +185,7 @@ private:
|
||||||
std::optional<bool> fast_boot = std::nullopt);
|
std::optional<bool> fast_boot = std::nullopt);
|
||||||
void setGameListEntryCoverImage(const GameList::Entry* entry);
|
void setGameListEntryCoverImage(const GameList::Entry* entry);
|
||||||
|
|
||||||
|
std::optional<bool> promptForResumeState(const QString& save_state_path);
|
||||||
void loadSaveStateSlot(s32 slot);
|
void loadSaveStateSlot(s32 slot);
|
||||||
void loadSaveStateFile(const QString& filename, const QString& state_filename);
|
void loadSaveStateFile(const QString& filename, const QString& state_filename);
|
||||||
void populateLoadStateMenu(QMenu* menu, const QString& filename, const QString& serial, quint32 crc);
|
void populateLoadStateMenu(QMenu* menu, const QString& filename, const QString& serial, quint32 crc);
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
<addaction name="actionStartBios"/>
|
<addaction name="actionStartBios"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionPowerOff"/>
|
<addaction name="actionPowerOff"/>
|
||||||
|
<addaction name="actionPowerOffWithoutSaving"/>
|
||||||
<addaction name="actionReset"/>
|
<addaction name="actionReset"/>
|
||||||
<addaction name="actionPause"/>
|
<addaction name="actionPause"/>
|
||||||
<addaction name="menuChangeDisc"/>
|
<addaction name="menuChangeDisc"/>
|
||||||
|
@ -285,6 +286,15 @@
|
||||||
<string>Shut &Down</string>
|
<string>Shut &Down</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionPowerOffWithoutSaving">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="close-line">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Shut Down &Without Saving</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionReset">
|
<action name="actionReset">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="restart-line">
|
<iconset theme="restart-line">
|
||||||
|
|
|
@ -42,7 +42,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.inhibitScreensaver, "UI", "InhibitScreensaver", true);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.inhibitScreensaver, "UI", "InhibitScreensaver", true);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.discordPresence, "UI", "DiscordPresence", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.discordPresence, "UI", "DiscordPresence", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmShutdown, "UI", "ConfirmShutdown", true);
|
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.pauseOnStart, "UI", "StartPaused", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", 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"),
|
m_ui.confirmShutdown, tr("Confirm Shutdown"), tr("Checked"),
|
||||||
tr("Determines whether a prompt will be displayed to confirm shutting down the virtual machine "
|
tr("Determines whether a prompt will be displayed to confirm shutting down the virtual machine "
|
||||||
"when the hotkey is pressed."));
|
"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 "
|
tr("Automatically saves the emulator state when powering down or exiting. You can then "
|
||||||
"resume directly from where you left off next time."));
|
"resume directly from where you left off next time."));
|
||||||
dialog->registerWidgetHelp(m_ui.pauseOnStart, tr("Pause On Start"), tr("Unchecked"),
|
dialog->registerWidgetHelp(m_ui.pauseOnStart, tr("Pause On Start"), tr("Unchecked"),
|
||||||
|
|
|
@ -68,9 +68,9 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QCheckBox" name="saveStateOnExit">
|
<widget class="QCheckBox" name="saveStateOnShutdown">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Save Or Load State On Exit / Resume</string>
|
<string>Save State On Shutdown</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -939,6 +939,7 @@ struct Pcsx2Config
|
||||||
#endif
|
#endif
|
||||||
#ifdef PCSX2_CORE
|
#ifdef PCSX2_CORE
|
||||||
EnableGameFixes : 1, // enables automatic game fixes
|
EnableGameFixes : 1, // enables automatic game fixes
|
||||||
|
SaveStateOnShutdown : 1, // default value for saving state on shutdown
|
||||||
#endif
|
#endif
|
||||||
// when enabled uses BOOT2 injection, skipping sony bios splashes
|
// when enabled uses BOOT2 injection, skipping sony bios splashes
|
||||||
UseBOOT2Injection : 1,
|
UseBOOT2Injection : 1,
|
||||||
|
|
|
@ -1062,6 +1062,7 @@ void Pcsx2Config::LoadSave(SettingsWrapper& wrap)
|
||||||
#endif
|
#endif
|
||||||
#ifdef PCSX2_CORE
|
#ifdef PCSX2_CORE
|
||||||
SettingsWrapBitBool(EnableGameFixes);
|
SettingsWrapBitBool(EnableGameFixes);
|
||||||
|
SettingsWrapBitBool(SaveStateOnShutdown);
|
||||||
#endif
|
#endif
|
||||||
SettingsWrapBitBool(ConsoleToStdio);
|
SettingsWrapBitBool(ConsoleToStdio);
|
||||||
SettingsWrapBitBool(HostFs);
|
SettingsWrapBitBool(HostFs);
|
||||||
|
|
|
@ -783,8 +783,7 @@ static bool SaveState_CompressScreenshot(SaveStateScreenshotData* data, zip_t* z
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// CompressThread_VmState
|
// CompressThread_VmState
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
static void ZipStateToDiskOnThread(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot,
|
void SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename, s32 slot_for_message)
|
||||||
std::string filename, s32 slot_for_message)
|
|
||||||
{
|
{
|
||||||
#ifndef PCSX2_CORE
|
#ifndef PCSX2_CORE
|
||||||
wxGetApp().StartPendingSave();
|
wxGetApp().StartPendingSave();
|
||||||
|
@ -858,9 +857,10 @@ static void ZipStateToDiskOnThread(std::unique_ptr<ArchiveEntryList> srclist, st
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename, s32 slot_for_message)
|
|
||||||
|
void SaveState_ZipToDiskOnThread(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> 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();
|
threaded_save.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ class ArchiveEntryList;
|
||||||
extern std::unique_ptr<ArchiveEntryList> SaveState_DownloadState();
|
extern std::unique_ptr<ArchiveEntryList> SaveState_DownloadState();
|
||||||
extern std::unique_ptr<SaveStateScreenshotData> SaveState_SaveScreenshot();
|
extern std::unique_ptr<SaveStateScreenshotData> SaveState_SaveScreenshot();
|
||||||
extern void SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename, s32 slot_for_message);
|
extern void SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename, s32 slot_for_message);
|
||||||
|
extern void SaveState_ZipToDiskOnThread(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename, s32 slot_for_message);
|
||||||
extern void SaveState_UnzipFromDisk(const std::string& filename);
|
extern void SaveState_UnzipFromDisk(const std::string& filename);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -84,7 +84,7 @@ namespace VMManager
|
||||||
|
|
||||||
static std::string GetCurrentSaveStateFileName(s32 slot);
|
static std::string GetCurrentSaveStateFileName(s32 slot);
|
||||||
static bool DoLoadState(const char* filename);
|
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 SetTimerResolutionIncreased(bool enabled);
|
||||||
static void SetEmuThreadAffinities(bool force);
|
static void SetEmuThreadAffinities(bool force);
|
||||||
|
@ -534,30 +534,11 @@ bool VMManager::ApplyBootParameters(const VMBootParameters& params, std::string*
|
||||||
return false;
|
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();
|
Host::ReportFormattedErrorAsync("Error", "Could not resolve path indexed save state load.");
|
||||||
if (const GameList::Entry* entry = GameList::GetEntryForPath(params.filename.c_str()); entry)
|
return false;
|
||||||
{
|
|
||||||
*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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -771,7 +752,7 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
|
||||||
{
|
{
|
||||||
if (!DoLoadState(state_to_load.c_str()))
|
if (!DoLoadState(state_to_load.c_str()))
|
||||||
{
|
{
|
||||||
Shutdown();
|
Shutdown(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -779,7 +760,7 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMManager::Shutdown(bool allow_save_resume_state /* = true */)
|
void VMManager::Shutdown(bool save_resume_state)
|
||||||
{
|
{
|
||||||
SetTimerResolutionIncreased(false);
|
SetTimerResolutionIncreased(false);
|
||||||
|
|
||||||
|
@ -788,10 +769,10 @@ void VMManager::Shutdown(bool allow_save_resume_state /* = true */)
|
||||||
vu1Thread.WaitVU();
|
vu1Thread.WaitVU();
|
||||||
GetMTGS().WaitGS();
|
GetMTGS().WaitGS();
|
||||||
|
|
||||||
if (!GSDumpReplayer::IsReplayingDump() && allow_save_resume_state && ShouldSaveResumeState())
|
if (!GSDumpReplayer::IsReplayingDump() && save_resume_state)
|
||||||
{
|
{
|
||||||
std::string resume_file_name(GetCurrentSaveStateFileName(-1));
|
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");
|
Console.Error("Failed to save resume state");
|
||||||
}
|
}
|
||||||
else if (GSDumpReplayer::IsReplayingDump())
|
else if (GSDumpReplayer::IsReplayingDump())
|
||||||
|
@ -854,23 +835,45 @@ void VMManager::Reset()
|
||||||
UpdateRunningGame(true);
|
UpdateRunningGame(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMManager::ShouldSaveResumeState()
|
|
||||||
{
|
|
||||||
return Host::GetBoolSettingValue("EmuCore", "AutoStateLoadSave", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string VMManager::GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot)
|
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;
|
std::string filename;
|
||||||
if (slot < 0)
|
if (game_crc != 0)
|
||||||
filename = StringUtil::StdStringFromFormat("%s (%08X).resume.p2s", game_serial, game_crc);
|
{
|
||||||
else
|
if (slot < 0)
|
||||||
filename = StringUtil::StdStringFromFormat("%s (%08X).%02d.p2s", game_serial, game_crc, slot);
|
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)
|
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())
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
return false;
|
return false;
|
||||||
|
@ -914,7 +917,11 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState();
|
std::unique_ptr<ArchiveEntryList> 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::InvalidateSaveStateCache();
|
||||||
Host::OnSaveStateSaved(filename);
|
Host::OnSaveStateSaved(filename);
|
||||||
return true;
|
return true;
|
||||||
|
@ -949,12 +956,12 @@ bool VMManager::LoadStateFromSlot(s32 slot)
|
||||||
return DoLoadState(filename.c_str());
|
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));
|
const std::string filename(GetCurrentSaveStateFileName(slot));
|
||||||
if (filename.empty())
|
if (filename.empty())
|
||||||
|
@ -962,7 +969,7 @@ bool VMManager::SaveStateToSlot(s32 slot)
|
||||||
|
|
||||||
// if it takes more than a minute.. well.. wtf.
|
// if it takes more than a minute.. well.. wtf.
|
||||||
Host::AddKeyedFormattedOSDMessage(StringUtil::StdStringFromFormat("SaveStateSlot%d", slot), 60.0f, "Saving state to slot %d...", slot);
|
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()
|
LimiterModeType VMManager::GetLimiterMode()
|
||||||
|
|
|
@ -76,7 +76,7 @@ namespace VMManager
|
||||||
bool Initialize(const VMBootParameters& boot_params);
|
bool Initialize(const VMBootParameters& boot_params);
|
||||||
|
|
||||||
/// Destroys all system components.
|
/// 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.
|
/// Resets all subsystems to a cold boot.
|
||||||
void Reset();
|
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.
|
/// Reloads cheats/patches. If verbose is set, the number of patches loaded will be shown in the OSD.
|
||||||
void ReloadPatches(bool verbose);
|
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.
|
/// Returns the save state filename for the given game serial/crc.
|
||||||
std::string GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot);
|
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.
|
/// Returns true if there is a save state in the specified slot.
|
||||||
bool HasSaveStateInSlot(const char* game_serial, u32 game_crc, s32 slot);
|
bool HasSaveStateInSlot(const char* game_serial, u32 game_crc, s32 slot);
|
||||||
|
|
||||||
|
@ -112,10 +112,10 @@ namespace VMManager
|
||||||
bool LoadStateFromSlot(s32 slot);
|
bool LoadStateFromSlot(s32 slot);
|
||||||
|
|
||||||
/// Saves state to the specified filename.
|
/// 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.
|
/// Saves state to the specified slot.
|
||||||
bool SaveStateToSlot(s32 slot);
|
bool SaveStateToSlot(s32 slot, bool zip_on_thread = true);
|
||||||
|
|
||||||
/// Returns the current limiter mode.
|
/// Returns the current limiter mode.
|
||||||
LimiterModeType GetLimiterMode();
|
LimiterModeType GetLimiterMode();
|
||||||
|
|
|
@ -61,7 +61,7 @@ protected:
|
||||||
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState();
|
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState();
|
||||||
UI_EnableStateActions();
|
UI_EnableStateActions();
|
||||||
paused_core.AllowResume();
|
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue