mirror of https://github.com/mgba-emu/mgba.git
Add savestate load/saving in Qt frontend
This commit is contained in:
parent
642b92fed3
commit
fdf2ce299c
|
@ -27,11 +27,13 @@ set(SOURCE_FILES
|
||||||
AudioProcessor.cpp
|
AudioProcessor.cpp
|
||||||
Display.cpp
|
Display.cpp
|
||||||
GameController.cpp
|
GameController.cpp
|
||||||
|
LoadSaveState.cpp
|
||||||
LogView.cpp
|
LogView.cpp
|
||||||
Window.cpp
|
Window.cpp
|
||||||
VFileDevice.cpp)
|
VFileDevice.cpp)
|
||||||
|
|
||||||
qt5_wrap_ui(UI_FILES
|
qt5_wrap_ui(UI_FILES
|
||||||
|
LoadSaveState.ui
|
||||||
LogView.ui)
|
LogView.ui)
|
||||||
|
|
||||||
if(USE_GDB_STUB)
|
if(USE_GDB_STUB)
|
||||||
|
|
|
@ -31,6 +31,7 @@ public:
|
||||||
~GameController();
|
~GameController();
|
||||||
|
|
||||||
const uint32_t* drawContext() const { return m_drawContext; }
|
const uint32_t* drawContext() const { return m_drawContext; }
|
||||||
|
GBAThread* thread() { return &m_threadContext; }
|
||||||
|
|
||||||
bool isPaused();
|
bool isPaused();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include "LoadSaveState.h"
|
||||||
|
|
||||||
|
#include "GameController.h"
|
||||||
|
#include "VFileDevice.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "gba-serialize.h"
|
||||||
|
#include "gba-video.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace QGBA;
|
||||||
|
|
||||||
|
LoadSaveState::LoadSaveState(GameController* controller, QWidget* parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, m_controller(controller)
|
||||||
|
{
|
||||||
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
|
QImage currentImage(reinterpret_cast<const uchar*>(controller->drawContext()), VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGB32);
|
||||||
|
m_currentImage.convertFromImage(currentImage.rgbSwapped());
|
||||||
|
|
||||||
|
m_slots[0] = m_ui.state1;
|
||||||
|
m_slots[1] = m_ui.state2;
|
||||||
|
m_slots[2] = m_ui.state3;
|
||||||
|
m_slots[3] = m_ui.state4;
|
||||||
|
m_slots[4] = m_ui.state5;
|
||||||
|
m_slots[5] = m_ui.state6;
|
||||||
|
m_slots[6] = m_ui.state7;
|
||||||
|
m_slots[7] = m_ui.state8;
|
||||||
|
m_slots[8] = m_ui.state9;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < NUM_SLOTS; ++i) {
|
||||||
|
loadState(i);
|
||||||
|
connect(m_slots[i], &QAbstractButton::clicked, this, [this, i]() { triggerState(i); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadSaveState::setMode(LoadSave mode) {
|
||||||
|
m_mode = mode;
|
||||||
|
QString text = mode == LoadSave::LOAD ? tr("Load State") : tr("SaveState");
|
||||||
|
setWindowTitle(text);
|
||||||
|
m_ui.lsLabel->setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadSaveState::loadState(int slot) {
|
||||||
|
GBAThread* thread = m_controller->thread();
|
||||||
|
VFile* vf = GBAGetState(thread->gba, thread->stateDir, slot, false);
|
||||||
|
if (!vf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VFileDevice vdev(vf);
|
||||||
|
QImage stateImage;
|
||||||
|
stateImage.load(&vdev, "PNG");
|
||||||
|
if (!stateImage.isNull()) {
|
||||||
|
QPixmap statePixmap;
|
||||||
|
statePixmap.convertFromImage(stateImage);
|
||||||
|
m_slots[slot]->setIcon(statePixmap);
|
||||||
|
m_slots[slot]->setText(QString());
|
||||||
|
} else {
|
||||||
|
m_slots[slot]->setText(tr("Slot %1").arg(slot + 1));
|
||||||
|
}
|
||||||
|
m_slots[slot]->setShortcut(QString::number(slot + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadSaveState::triggerState(int slot) {
|
||||||
|
GBAThread* thread = m_controller->thread();
|
||||||
|
GBAThreadInterrupt(thread);
|
||||||
|
if (m_mode == LoadSave::SAVE) {
|
||||||
|
GBASaveState(thread->gba, thread->stateDir, slot, true);
|
||||||
|
} else {
|
||||||
|
GBALoadState(thread->gba, thread->stateDir, slot);
|
||||||
|
}
|
||||||
|
GBAThreadContinue(thread);
|
||||||
|
close();
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef QGBA_LOAD_SAVE_STATE
|
||||||
|
#define QGBA_LOAD_SAVE_STATE
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "ui_LoadSaveState.h"
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
class GameController;
|
||||||
|
|
||||||
|
enum class LoadSave {
|
||||||
|
LOAD,
|
||||||
|
SAVE
|
||||||
|
};
|
||||||
|
|
||||||
|
class LoadSaveState : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
const static int NUM_SLOTS = 9;
|
||||||
|
|
||||||
|
LoadSaveState(GameController* controller, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void setMode(LoadSave mode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loadState(int slot);
|
||||||
|
void triggerState(int slot);
|
||||||
|
|
||||||
|
Ui::LoadSaveState m_ui;
|
||||||
|
GameController* m_controller;
|
||||||
|
QPushButton* m_slots[NUM_SLOTS];
|
||||||
|
LoadSave m_mode;
|
||||||
|
|
||||||
|
QPixmap m_currentImage;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,321 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>LoadSaveState</class>
|
||||||
|
<widget class="QWidget" name="LoadSaveState">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>760</width>
|
||||||
|
<height>560</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>%1 State</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2" rowstretch="1,0,0,0" columnstretch="0,0,0">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QPushButton" name="state1">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>242</width>
|
||||||
|
<height>162</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>160</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>1</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QPushButton" name="state2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>242</width>
|
||||||
|
<height>162</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>160</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>2</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="3">
|
||||||
|
<widget class="QLabel" name="lsLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">font-size: 30pt; font-weight: bold;</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>%1 State</string>
|
||||||
|
</property>
|
||||||
|
<property name="scaledContents">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QPushButton" name="state3">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>242</width>
|
||||||
|
<height>162</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>160</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>3</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QPushButton" name="state4">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>242</width>
|
||||||
|
<height>162</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>160</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>4</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QPushButton" name="state5">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>242</width>
|
||||||
|
<height>162</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>160</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>5</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QPushButton" name="state6">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>242</width>
|
||||||
|
<height>162</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>160</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>6</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QPushButton" name="state7">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>242</width>
|
||||||
|
<height>162</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>160</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>7</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QPushButton" name="state8">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>242</width>
|
||||||
|
<height>162</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>160</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>8</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="QPushButton" name="state9">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>242</width>
|
||||||
|
<height>162</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>160</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>9</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>state1</tabstop>
|
||||||
|
<tabstop>state2</tabstop>
|
||||||
|
<tabstop>state3</tabstop>
|
||||||
|
<tabstop>state4</tabstop>
|
||||||
|
<tabstop>state5</tabstop>
|
||||||
|
<tabstop>state6</tabstop>
|
||||||
|
<tabstop>state7</tabstop>
|
||||||
|
<tabstop>state8</tabstop>
|
||||||
|
<tabstop>state9</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -8,6 +8,7 @@
|
||||||
#include "GameController.h"
|
#include "GameController.h"
|
||||||
#include "GDBWindow.h"
|
#include "GDBWindow.h"
|
||||||
#include "GDBController.h"
|
#include "GDBController.h"
|
||||||
|
#include "LoadSaveState.h"
|
||||||
#include "LogView.h"
|
#include "LogView.h"
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
@ -143,6 +144,14 @@ void Window::gameStopped() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Window::openStateWindow(LoadSave ls) {
|
||||||
|
LoadSaveState* window = new LoadSaveState(m_controller);
|
||||||
|
window->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
connect(this, SIGNAL(shutdown()), window, SLOT(hide()));
|
||||||
|
window->setMode(ls);
|
||||||
|
window->show();
|
||||||
|
}
|
||||||
|
|
||||||
void Window::setupMenu(QMenuBar* menubar) {
|
void Window::setupMenu(QMenuBar* menubar) {
|
||||||
menubar->clear();
|
menubar->clear();
|
||||||
QMenu* fileMenu = menubar->addMenu(tr("&File"));
|
QMenu* fileMenu = menubar->addMenu(tr("&File"));
|
||||||
|
@ -161,6 +170,19 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
emulationMenu->addAction(shutdown);
|
emulationMenu->addAction(shutdown);
|
||||||
emulationMenu->addSeparator();
|
emulationMenu->addSeparator();
|
||||||
|
|
||||||
|
QAction* loadState = new QAction(tr("&Load state"), emulationMenu);
|
||||||
|
loadState->setShortcut(tr("Ctrl+L"));
|
||||||
|
connect(loadState, &QAction::triggered, [this]() { this->openStateWindow(LoadSave::LOAD); });
|
||||||
|
m_gameActions.append(loadState);
|
||||||
|
emulationMenu->addAction(loadState);
|
||||||
|
|
||||||
|
QAction* saveState = new QAction(tr("&Save state"), emulationMenu);
|
||||||
|
saveState->setShortcut(tr("Ctrl+S"));
|
||||||
|
connect(saveState, &QAction::triggered, [this]() { this->openStateWindow(LoadSave::SAVE); });
|
||||||
|
m_gameActions.append(saveState);
|
||||||
|
emulationMenu->addAction(saveState);
|
||||||
|
emulationMenu->addSeparator();
|
||||||
|
|
||||||
QAction* pause = new QAction(tr("&Pause"), emulationMenu);
|
QAction* pause = new QAction(tr("&Pause"), emulationMenu);
|
||||||
pause->setChecked(false);
|
pause->setChecked(false);
|
||||||
pause->setCheckable(true);
|
pause->setCheckable(true);
|
||||||
|
|
|
@ -9,6 +9,7 @@ extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "Display.h"
|
#include "Display.h"
|
||||||
|
#include "LoadSaveState.h"
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
|
@ -51,6 +52,8 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupMenu(QMenuBar*);
|
void setupMenu(QMenuBar*);
|
||||||
|
void openStateWindow(LoadSave);
|
||||||
|
|
||||||
GameController* m_controller;
|
GameController* m_controller;
|
||||||
Display* m_display;
|
Display* m_display;
|
||||||
QList<QAction*> m_gameActions;
|
QList<QAction*> m_gameActions;
|
||||||
|
|
Loading…
Reference in New Issue