Merge pull request #4908 from hamish-milne/feature/savestates-2
Save states
This commit is contained in:
@@ -132,7 +132,9 @@ void OpenGLWindow::Present() {
|
||||
return;
|
||||
|
||||
context->makeCurrent(this);
|
||||
VideoCore::g_renderer->TryPresent(100);
|
||||
if (VideoCore::g_renderer) {
|
||||
VideoCore::g_renderer->TryPresent(100);
|
||||
}
|
||||
context->swapBuffers(this);
|
||||
auto f = context->versionFunctions<QOpenGLFunctions_3_3_Core>();
|
||||
f->glFinish();
|
||||
|
@@ -57,7 +57,7 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
|
||||
// This must be in alphabetical order according to action name as it must have the same order as
|
||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||
// clang-format off
|
||||
const std::array<UISettings::Shortcut, 21> default_hotkeys{
|
||||
const std::array<UISettings::Shortcut, 23> default_hotkeys{
|
||||
{{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral("\\"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
||||
@@ -73,6 +73,8 @@ const std::array<UISettings::Shortcut, 21> default_hotkeys{
|
||||
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Save to Oldest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Load from Newest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::WindowShortcut}},
|
||||
|
@@ -216,7 +216,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
|
||||
|
||||
const auto& thread = static_cast<const Kernel::Thread&>(object);
|
||||
const auto* process = thread.owner_process;
|
||||
const auto& process = thread.owner_process;
|
||||
|
||||
QString processor;
|
||||
switch (thread.processor_id) {
|
||||
@@ -237,6 +237,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
break;
|
||||
}
|
||||
|
||||
list.push_back(std::make_unique<WaitTreeText>(tr("object id = %1").arg(thread.GetObjectId())));
|
||||
list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor)));
|
||||
list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadId())));
|
||||
list.push_back(
|
||||
|
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <clocale>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <QDesktopWidget>
|
||||
@@ -78,6 +79,7 @@
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/movie.h"
|
||||
#include "core/savestate.h"
|
||||
#include "core/settings.h"
|
||||
#include "game_list_p.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@@ -171,6 +173,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
|
||||
InitializeWidgets();
|
||||
InitializeDebugWidgets();
|
||||
InitializeRecentFileMenuActions();
|
||||
InitializeSaveStateMenuActions();
|
||||
InitializeHotkeys();
|
||||
ShowUpdaterWidgets();
|
||||
|
||||
@@ -396,6 +399,32 @@ void GMainWindow::InitializeRecentFileMenuActions() {
|
||||
UpdateRecentFiles();
|
||||
}
|
||||
|
||||
void GMainWindow::InitializeSaveStateMenuActions() {
|
||||
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
|
||||
actions_load_state[i] = new QAction(this);
|
||||
actions_load_state[i]->setData(i + 1);
|
||||
connect(actions_load_state[i], &QAction::triggered, this, &GMainWindow::OnLoadState);
|
||||
ui.menu_Load_State->addAction(actions_load_state[i]);
|
||||
|
||||
actions_save_state[i] = new QAction(this);
|
||||
actions_save_state[i]->setData(i + 1);
|
||||
connect(actions_save_state[i], &QAction::triggered, this, &GMainWindow::OnSaveState);
|
||||
ui.menu_Save_State->addAction(actions_save_state[i]);
|
||||
}
|
||||
|
||||
connect(ui.action_Load_from_Newest_Slot, &QAction::triggered,
|
||||
[this] { actions_load_state[newest_slot - 1]->trigger(); });
|
||||
connect(ui.action_Save_to_Oldest_Slot, &QAction::triggered,
|
||||
[this] { actions_save_state[oldest_slot - 1]->trigger(); });
|
||||
|
||||
connect(ui.menu_Load_State->menuAction(), &QAction::hovered, this,
|
||||
&GMainWindow::UpdateSaveStates);
|
||||
connect(ui.menu_Save_State->menuAction(), &QAction::hovered, this,
|
||||
&GMainWindow::UpdateSaveStates);
|
||||
|
||||
UpdateSaveStates();
|
||||
}
|
||||
|
||||
void GMainWindow::InitializeHotkeys() {
|
||||
hotkey_registry.LoadHotkeys();
|
||||
|
||||
@@ -509,6 +538,10 @@ void GMainWindow::InitializeHotkeys() {
|
||||
OnCaptureScreenshot();
|
||||
}
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey(main_window, ui.action_Load_from_Newest_Slot->text(), this),
|
||||
&QShortcut::activated, ui.action_Load_from_Newest_Slot, &QAction::trigger);
|
||||
connect(hotkey_registry.GetHotkey(main_window, ui.action_Save_to_Oldest_Slot->text(), this),
|
||||
&QShortcut::activated, ui.action_Save_to_Oldest_Slot, &QAction::trigger);
|
||||
}
|
||||
|
||||
void GMainWindow::ShowUpdaterWidgets() {
|
||||
@@ -687,6 +720,7 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
if (emulation_running) {
|
||||
ui.action_Enable_Frame_Advancing->setChecked(true);
|
||||
ui.action_Advance_Frame->setEnabled(true);
|
||||
Core::System::GetInstance().frame_limiter.SetFrameAdvancing(true);
|
||||
Core::System::GetInstance().frame_limiter.AdvanceFrame();
|
||||
}
|
||||
});
|
||||
@@ -1091,6 +1125,8 @@ void GMainWindow::ShutdownGame() {
|
||||
game_fps_label->setVisible(false);
|
||||
emu_frametime_label->setVisible(false);
|
||||
|
||||
UpdateSaveStates();
|
||||
|
||||
emulation_running = false;
|
||||
|
||||
if (defer_update_prompt) {
|
||||
@@ -1137,6 +1173,62 @@ void GMainWindow::UpdateRecentFiles() {
|
||||
ui.menu_recent_files->setEnabled(num_recent_files != 0);
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateSaveStates() {
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
ui.menu_Load_State->setEnabled(false);
|
||||
ui.menu_Save_State->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ui.menu_Load_State->setEnabled(true);
|
||||
ui.menu_Save_State->setEnabled(true);
|
||||
ui.action_Load_from_Newest_Slot->setEnabled(false);
|
||||
|
||||
oldest_slot = newest_slot = 0;
|
||||
oldest_slot_time = std::numeric_limits<u64>::max();
|
||||
newest_slot_time = 0;
|
||||
|
||||
u64 title_id;
|
||||
if (Core::System::GetInstance().GetAppLoader().ReadProgramId(title_id) !=
|
||||
Loader::ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
auto savestates = Core::ListSaveStates(title_id);
|
||||
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
|
||||
actions_load_state[i]->setEnabled(false);
|
||||
actions_load_state[i]->setText(tr("Slot %1").arg(i + 1));
|
||||
actions_save_state[i]->setText(tr("Slot %1").arg(i + 1));
|
||||
}
|
||||
for (const auto& savestate : savestates) {
|
||||
const auto text = tr("Slot %1 - %2")
|
||||
.arg(savestate.slot)
|
||||
.arg(QDateTime::fromSecsSinceEpoch(savestate.time)
|
||||
.toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")));
|
||||
actions_load_state[savestate.slot - 1]->setEnabled(true);
|
||||
actions_load_state[savestate.slot - 1]->setText(text);
|
||||
actions_save_state[savestate.slot - 1]->setText(text);
|
||||
|
||||
ui.action_Load_from_Newest_Slot->setEnabled(true);
|
||||
|
||||
if (savestate.time > newest_slot_time) {
|
||||
newest_slot = savestate.slot;
|
||||
newest_slot_time = savestate.time;
|
||||
}
|
||||
if (savestate.time < oldest_slot_time) {
|
||||
oldest_slot = savestate.slot;
|
||||
oldest_slot_time = savestate.time;
|
||||
}
|
||||
}
|
||||
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
|
||||
if (!actions_load_state[i]->isEnabled()) {
|
||||
// Prefer empty slot
|
||||
oldest_slot = i + 1;
|
||||
oldest_slot_time = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnGameListLoadFile(QString game_path) {
|
||||
BootGame(game_path);
|
||||
}
|
||||
@@ -1385,7 +1477,7 @@ void GMainWindow::OnCIAInstallFinished() {
|
||||
|
||||
void GMainWindow::OnMenuRecentFile() {
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
assert(action);
|
||||
ASSERT(action);
|
||||
|
||||
const QString filename = action->data().toString();
|
||||
if (QFileInfo::exists(filename)) {
|
||||
@@ -1429,6 +1521,8 @@ void GMainWindow::OnStartGame() {
|
||||
ui.action_Capture_Screenshot->setEnabled(true);
|
||||
|
||||
discord_rpc->Update();
|
||||
|
||||
UpdateSaveStates();
|
||||
}
|
||||
|
||||
void GMainWindow::OnPauseGame() {
|
||||
@@ -1576,6 +1670,23 @@ void GMainWindow::OnCheats() {
|
||||
cheat_dialog.exec();
|
||||
}
|
||||
|
||||
void GMainWindow::OnSaveState() {
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
assert(action);
|
||||
|
||||
Core::System::GetInstance().SendSignal(Core::System::Signal::Save, action->data().toUInt());
|
||||
Core::System::GetInstance().frame_limiter.AdvanceFrame();
|
||||
newest_slot = action->data().toUInt();
|
||||
}
|
||||
|
||||
void GMainWindow::OnLoadState() {
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
assert(action);
|
||||
|
||||
Core::System::GetInstance().SendSignal(Core::System::Signal::Load, action->data().toUInt());
|
||||
Core::System::GetInstance().frame_limiter.AdvanceFrame();
|
||||
}
|
||||
|
||||
void GMainWindow::OnConfigure() {
|
||||
ConfigureDialog configureDialog(this, hotkey_registry,
|
||||
!multiplayer_state->IsHostingPublicRoom());
|
||||
@@ -1968,6 +2079,9 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
|
||||
|
||||
title = tr("System Archive Not Found");
|
||||
status_message = tr("System Archive Missing");
|
||||
} else if (result == Core::System::ResultStatus::ErrorSavestate) {
|
||||
title = tr("Save/load Error");
|
||||
message = QString::fromStdString(details);
|
||||
} else {
|
||||
title = tr("Fatal Error");
|
||||
message =
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <QLabel>
|
||||
#include <QMainWindow>
|
||||
@@ -14,6 +15,7 @@
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/savestate.h"
|
||||
#include "ui_main.h"
|
||||
|
||||
class AboutDialog;
|
||||
@@ -106,6 +108,7 @@ private:
|
||||
void InitializeWidgets();
|
||||
void InitializeDebugWidgets();
|
||||
void InitializeRecentFileMenuActions();
|
||||
void InitializeSaveStateMenuActions();
|
||||
|
||||
void SetDefaultUIGeometry();
|
||||
void SyncMenuUISettings();
|
||||
@@ -149,6 +152,8 @@ private:
|
||||
*/
|
||||
void UpdateRecentFiles();
|
||||
|
||||
void UpdateSaveStates();
|
||||
|
||||
/**
|
||||
* If the emulation is running,
|
||||
* asks the user if he really want to close the emulator
|
||||
@@ -163,6 +168,8 @@ private slots:
|
||||
void OnStartGame();
|
||||
void OnPauseGame();
|
||||
void OnStopGame();
|
||||
void OnSaveState();
|
||||
void OnLoadState();
|
||||
void OnMenuReportCompatibility();
|
||||
/// Called whenever a user selects a game in the game list widget.
|
||||
void OnGameListLoadFile(QString game_path);
|
||||
@@ -282,6 +289,13 @@ private:
|
||||
bool defer_update_prompt = false;
|
||||
|
||||
QAction* actions_recent_files[max_recent_files_item];
|
||||
std::array<QAction*, Core::SaveStateSlotCount> actions_load_state;
|
||||
std::array<QAction*, Core::SaveStateSlotCount> actions_save_state;
|
||||
|
||||
u32 oldest_slot;
|
||||
u64 oldest_slot_time;
|
||||
u32 newest_slot;
|
||||
u64 newest_slot_time;
|
||||
|
||||
QTranslator translator;
|
||||
|
||||
|
@@ -79,11 +79,28 @@
|
||||
<property name="title">
|
||||
<string>&Emulation</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_Save_State">
|
||||
<property name="title">
|
||||
<string>Save State</string>
|
||||
</property>
|
||||
<addaction name="action_Save_to_Oldest_Slot"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Load_State">
|
||||
<property name="title">
|
||||
<string>Load State</string>
|
||||
</property>
|
||||
<addaction name="action_Load_from_Newest_Slot"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<addaction name="action_Start"/>
|
||||
<addaction name="action_Pause"/>
|
||||
<addaction name="action_Stop"/>
|
||||
<addaction name="action_Restart"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menu_Load_State"/>
|
||||
<addaction name="menu_Save_State"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Report_Compatibility"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Configure"/>
|
||||
@@ -217,6 +234,22 @@
|
||||
<string>&Stop</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Save">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_FAQ">
|
||||
<property name="text">
|
||||
<string>FAQ</string>
|
||||
@@ -235,6 +268,16 @@
|
||||
<string>Single Window Mode</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Save_to_Oldest_Slot">
|
||||
<property name="text">
|
||||
<string>Save to Oldest Slot</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_from_Newest_Slot">
|
||||
<property name="text">
|
||||
<string>Load from Newest Slot</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Configure">
|
||||
<property name="text">
|
||||
<string>Configure...</string>
|
||||
|
Reference in New Issue
Block a user