diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp
index 9c01df79e..5afdbb72c 100644
--- a/src/core/host_interface.cpp
+++ b/src/core/host_interface.cpp
@@ -750,8 +750,9 @@ void HostInterface::SetDefaultSettings()
m_settings.emulation_speed = 1.0f;
m_settings.speed_limiter_enabled = true;
- m_settings.start_paused = false;
m_settings.increase_timer_resolution = true;
+ m_settings.start_paused = false;
+ m_settings.save_state_on_exit = true;
m_settings.gpu_renderer = Settings::DEFAULT_GPU_RENDERER;
m_settings.gpu_resolution_scale = 1;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 5822ef651..6c7244cbc 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -13,6 +13,7 @@ void Settings::Load(SettingsInterface& si)
speed_limiter_enabled = si.GetBoolValue("General", "SpeedLimiterEnabled", true);
increase_timer_resolution = si.GetBoolValue("General", "IncreaseTimerResolution", true);
start_paused = si.GetBoolValue("General", "StartPaused", false);
+ save_state_on_exit = si.GetBoolValue("General", "SaveStateOnExit", true);
cpu_execution_mode = ParseCPUExecutionMode(si.GetStringValue("CPU", "ExecutionMode", "Interpreter").c_str())
.value_or(CPUExecutionMode::Interpreter);
@@ -63,6 +64,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("General", "SpeedLimiterEnabled", speed_limiter_enabled);
si.SetBoolValue("General", "IncreaseTimerResolution", increase_timer_resolution);
si.SetBoolValue("General", "StartPaused", start_paused);
+ si.SetBoolValue("General", "SaveStateOnExit", save_state_on_exit);
si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode));
diff --git a/src/core/settings.h b/src/core/settings.h
index 2f0d12616..4e0ac5147 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -36,9 +36,10 @@ struct Settings
CPUExecutionMode cpu_execution_mode = CPUExecutionMode::Interpreter;
float emulation_speed = 1.0f;
- bool start_paused = false;
bool speed_limiter_enabled = true;
bool increase_timer_resolution = true;
+ bool start_paused = false;
+ bool save_state_on_exit = true;
GPURenderer gpu_renderer = GPURenderer::Software;
u32 gpu_resolution_scale = 1;
diff --git a/src/duckstation-qt/consolesettingswidget.cpp b/src/duckstation-qt/consolesettingswidget.cpp
index eb7a2b98b..6810f7216 100644
--- a/src/duckstation-qt/consolesettingswidget.cpp
+++ b/src/duckstation-qt/consolesettingswidget.cpp
@@ -25,6 +25,7 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW
SettingWidgetBinder::BindWidgetToNormalizedSetting(m_host_interface, m_ui.emulationSpeed, "General/EmulationSpeed",
100.0f);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pauseOnStart, "General/StartPaused");
+ SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.saveStateOnExit, "General/SaveStateOnExit");
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.cpuExecutionMode, "CPU/ExecutionMode",
&Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName);
diff --git a/src/duckstation-qt/consolesettingswidget.ui b/src/duckstation-qt/consolesettingswidget.ui
index dd02d7a7e..cf2708cd3 100644
--- a/src/duckstation-qt/consolesettingswidget.ui
+++ b/src/duckstation-qt/consolesettingswidget.ui
@@ -146,6 +146,13 @@
+ -
+
+
+ Save State On Exit
+
+
+
diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp
index ac90a8a83..21deb9253 100644
--- a/src/duckstation-qt/mainwindow.cpp
+++ b/src/duckstation-qt/mainwindow.cpp
@@ -298,7 +298,7 @@ void MainWindow::connectSignals()
&MainWindow::onChangeDiscFromGameListActionTriggered);
connect(m_ui.actionAddGameDirectory, &QAction::triggered,
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
- connect(m_ui.actionPowerOff, &QAction::triggered, [this]() { m_host_interface->destroySystem(true, false); });
+ connect(m_ui.actionPowerOff, &QAction::triggered, m_host_interface, &QtHostInterface::powerOffSystem);
connect(m_ui.actionReset, &QAction::triggered, m_host_interface, &QtHostInterface::resetSystem);
connect(m_ui.actionPause, &QAction::toggled, m_host_interface, &QtHostInterface::pauseSystem);
connect(m_ui.actionLoadState, &QAction::triggered, this, [this]() { m_ui.menuLoadState->exec(QCursor::pos()); });
@@ -340,7 +340,10 @@ void MainWindow::connectSignals()
QString path = QString::fromStdString(entry->path);
if (!m_emulation_running)
{
- m_host_interface->bootSystemFromFile(path);
+ if (m_host_interface->getSettingValue("General/SaveStateOnExit", true).toBool())
+ m_host_interface->resumeSystemFromState(path, true);
+ else
+ m_host_interface->bootSystemFromFile(path);
}
else
{
@@ -433,6 +436,6 @@ void MainWindow::updateDebugMenuGPURenderer()
void MainWindow::closeEvent(QCloseEvent* event)
{
- m_host_interface->destroySystem(true, true);
+ m_host_interface->synchronousPowerOffSystem();
QMainWindow::closeEvent(event);
}
diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp
index 9e2facf97..c7f0702b1 100644
--- a/src/duckstation-qt/qthostinterface.cpp
+++ b/src/duckstation-qt/qthostinterface.cpp
@@ -76,10 +76,10 @@ void QtHostInterface::setDefaultSettings()
updateQSettingsFromCoreSettings();
}
-QVariant QtHostInterface::getSettingValue(const QString& name)
+QVariant QtHostInterface::getSettingValue(const QString& name, const QVariant& default_value)
{
std::lock_guard guard(m_qsettings_mutex);
- return m_qsettings.value(name);
+ return m_qsettings.value(name, default_value);
}
void QtHostInterface::putSettingValue(const QString& name, const QVariant& value)
@@ -178,6 +178,21 @@ void QtHostInterface::bootSystemFromFile(const QString& filename)
HostInterface::BootSystemFromFile(filename.toStdString().c_str());
}
+void QtHostInterface::resumeSystemFromState(const QString& filename, bool boot_on_failure)
+{
+ if (!isOnWorkerThread())
+ {
+ QMetaObject::invokeMethod(this, "resumeSystemFromState", Qt::QueuedConnection, Q_ARG(const QString&, filename),
+ Q_ARG(bool, boot_on_failure));
+ return;
+ }
+
+ if (filename.isEmpty())
+ HostInterface::ResumeSystemFromMostRecentState();
+ else
+ HostInterface::ResumeSystemFromState(filename.toStdString().c_str(), boot_on_failure);
+}
+
void QtHostInterface::bootSystemFromBIOS()
{
if (!isOnWorkerThread())
@@ -485,22 +500,31 @@ void QtHostInterface::addButtonToInputMap(const QString& binding, InputButtonHan
}
}
-void QtHostInterface::destroySystem(bool save_resume_state /* = false */, bool block_until_done /* = false */)
+void QtHostInterface::powerOffSystem()
{
if (!isOnWorkerThread())
{
- QMetaObject::invokeMethod(this, "destroySystem",
- block_until_done ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
- Q_ARG(bool, save_resume_state), Q_ARG(bool, block_until_done));
+ QMetaObject::invokeMethod(this, "powerOffSystem", Qt::QueuedConnection);
return;
}
if (!m_system)
return;
+ if (m_settings.save_state_on_exit)
+ SaveResumeSaveState();
+
DestroySystem();
}
+void QtHostInterface::synchronousPowerOffSystem()
+{
+ if (!isOnWorkerThread())
+ QMetaObject::invokeMethod(this, "powerOffSystem", Qt::BlockingQueuedConnection);
+ else
+ powerOffSystem();
+}
+
void QtHostInterface::resetSystem()
{
if (!isOnWorkerThread())
@@ -555,23 +579,26 @@ void QtHostInterface::populateSaveStateMenus(const char* game_code, QMenu* load_
if (!available_states.empty())
{
bool last_global = available_states.front().global;
+ s32 last_slot = available_states.front().slot;
for (const SaveStateInfo& ssi : available_states)
{
const s32 slot = ssi.slot;
const bool global = ssi.global;
const QDateTime timestamp(QDateTime::fromSecsSinceEpoch(static_cast(ssi.timestamp)));
+ const QString timestamp_str(timestamp.toString(Qt::SystemLocaleShortDate));
const QString path(QString::fromStdString(ssi.path));
- QString title = tr("%1 Save %2 (%3)")
- .arg(global ? tr("Global") : tr("Game"))
- .arg(slot)
- .arg(timestamp.toString(Qt::SystemLocaleShortDate));
+ QString title;
+ if (slot < 0)
+ title = tr("Resume Save (%1)").arg(timestamp_str);
+ else
+ title = tr("%1 Save %2 (%3)").arg(global ? tr("Global") : tr("Game")).arg(slot).arg(timestamp_str);
- if (global != last_global)
- {
+ if (global != last_global || last_slot < 0)
load_menu->addSeparator();
- last_global = global;
- }
+
+ last_global = global;
+ last_slot = slot;
QAction* action = load_menu->addAction(title);
connect(action, &QAction::triggered, [this, path]() { loadState(path); });
diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h
index 2b20e8352..e767a6080 100644
--- a/src/duckstation-qt/qthostinterface.h
+++ b/src/duckstation-qt/qthostinterface.h
@@ -35,7 +35,7 @@ public:
void setDefaultSettings();
/// Thread-safe QSettings access.
- QVariant getSettingValue(const QString& name);
+ QVariant getSettingValue(const QString& name, const QVariant& default_value = QVariant());
void putSettingValue(const QString& name, const QVariant& value);
void removeSettingValue(const QString& name);
@@ -77,8 +77,10 @@ Q_SIGNALS:
public Q_SLOTS:
void applySettings();
void bootSystemFromFile(const QString& filename);
+ void resumeSystemFromState(const QString& filename, bool boot_on_failure);
void bootSystemFromBIOS();
- void destroySystem(bool save_resume_state = false, bool block_until_done = false);
+ void powerOffSystem();
+ void synchronousPowerOffSystem();
void resetSystem();
void pauseSystem(bool paused);
void changeDisc(const QString& new_disc_filename);
diff --git a/src/duckstation-sdl/sdl_host_interface.cpp b/src/duckstation-sdl/sdl_host_interface.cpp
index 8e494757e..3834c2c2b 100644
--- a/src/duckstation-sdl/sdl_host_interface.cpp
+++ b/src/duckstation-sdl/sdl_host_interface.cpp
@@ -1048,7 +1048,11 @@ void SDLHostInterface::DrawPoweredOffWindow()
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, 0xFF575757);
ImGui::SetCursorPosX(button_left);
- ImGui::Button("Resume", button_size);
+ if (ImGui::Button("Resume", button_size))
+ {
+ ResumeSystemFromMostRecentState();
+ ClearImGuiFocus();
+ }
ImGui::NewLine();
ImGui::SetCursorPosX(button_left);
@@ -1163,6 +1167,7 @@ void SDLHostInterface::DrawSettingsWindow()
settings_changed |= ImGui::SliderFloat("##speed", &m_settings.emulation_speed, 0.25f, 5.0f);
settings_changed |= ImGui::Checkbox("Enable Speed Limiter", &m_settings.speed_limiter_enabled);
settings_changed |= ImGui::Checkbox("Pause On Start", &m_settings.start_paused);
+ settings_changed |= ImGui::Checkbox("Save State On Exit", &m_settings.save_state_on_exit);
}
ImGui::NewLine();
@@ -1498,5 +1503,9 @@ void SDLHostInterface::Run()
// Save state on exit so it can be resumed
if (m_system)
+ {
+ if (m_settings.save_state_on_exit)
+ SaveResumeSaveState();
DestroySystem();
+ }
}