all repos — mgba @ fdf2ce299c8666486ac539d5750351f2ce62a670

mGBA Game Boy Advance Emulator

Add savestate load/saving in Qt frontend
Jeffrey Pfau jeffrey@endrift.com
Tue, 14 Oct 2014 23:15:15 -0700
commit

fdf2ce299c8666486ac539d5750351f2ce62a670

parent

642b92fed3377ee94678f73e02f92d9a68fe3557

M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -27,11 +27,13 @@ AudioDevice.cpp

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)
M src/platform/qt/GameController.hsrc/platform/qt/GameController.h

@@ -31,6 +31,7 @@ GameController(QObject* parent = nullptr);

~GameController(); const uint32_t* drawContext() const { return m_drawContext; } + GBAThread* thread() { return &m_threadContext; } bool isPaused();
A src/platform/qt/LoadSaveState.cpp

@@ -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(); +}
A src/platform/qt/LoadSaveState.h

@@ -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
A src/platform/qt/LoadSaveState.ui

@@ -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>
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

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

} } +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"));

@@ -159,6 +168,19 @@ QAction* shutdown = new QAction(tr("Sh&utdown"), emulationMenu);

connect(shutdown, SIGNAL(triggered()), m_controller, SLOT(closeGame())); m_gameActions.append(shutdown); 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);
M src/platform/qt/Window.hsrc/platform/qt/Window.h

@@ -9,6 +9,7 @@ #include "gba.h"

} #include "Display.h" +#include "LoadSaveState.h" namespace QGBA {

@@ -51,6 +52,8 @@ void gameStopped();

private: void setupMenu(QMenuBar*); + void openStateWindow(LoadSave); + GameController* m_controller; Display* m_display; QList<QAction*> m_gameActions;