Merge pull request #4908 from hamish-milne/feature/savestates-2

Save states
This commit is contained in:
Ben
2020-04-17 21:52:51 +02:00
committed by GitHub
354 changed files with 6100 additions and 604 deletions

View File

@@ -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();

View File

@@ -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}},

View File

@@ -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(

View File

@@ -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 =

View File

@@ -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;

View File

@@ -79,11 +79,28 @@
<property name="title">
<string>&amp;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>&amp;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>