Add savestate load/saving in Qt frontend

This commit is contained in:
Jeffrey Pfau 2014-10-14 23:15:15 -07:00
parent 642b92fed3
commit fdf2ce299c
7 changed files with 467 additions and 0 deletions

View File

@ -27,11 +27,13 @@ set(SOURCE_FILES
AudioProcessor.cpp
Display.cpp
GameController.cpp
LoadSaveState.cpp
LogView.cpp
Window.cpp
VFileDevice.cpp)
qt5_wrap_ui(UI_FILES
LoadSaveState.ui
LogView.ui)
if(USE_GDB_STUB)

View File

@ -31,6 +31,7 @@ public:
~GameController();
const uint32_t* drawContext() const { return m_drawContext; }
GBAThread* thread() { return &m_threadContext; }
bool isPaused();

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@
#include "GameController.h"
#include "GDBWindow.h"
#include "GDBController.h"
#include "LoadSaveState.h"
#include "LogView.h"
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) {
menubar->clear();
QMenu* fileMenu = menubar->addMenu(tr("&File"));
@ -161,6 +170,19 @@ void Window::setupMenu(QMenuBar* menubar) {
emulationMenu->addAction(shutdown);
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);
pause->setChecked(false);
pause->setCheckable(true);

View File

@ -9,6 +9,7 @@ extern "C" {
}
#include "Display.h"
#include "LoadSaveState.h"
namespace QGBA {
@ -51,6 +52,8 @@ private slots:
private:
void setupMenu(QMenuBar*);
void openStateWindow(LoadSave);
GameController* m_controller;
Display* m_display;
QList<QAction*> m_gameActions;