all repos — mgba @ fc905657adeba406a2c280bbb94b09533a89e3f8

mGBA Game Boy Advance Emulator

src/platform/qt/LoadSaveState.cpp (view raw)

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "LoadSaveState.h"
  7
  8#include "GameController.h"
  9#include "GamepadAxisEvent.h"
 10#include "GamepadButtonEvent.h"
 11#include "VFileDevice.h"
 12
 13#include <QDateTime>
 14#include <QKeyEvent>
 15#include <QPainter>
 16
 17extern "C" {
 18#include "gba/serialize.h"
 19#include "gba/video.h"
 20}
 21
 22using namespace QGBA;
 23
 24LoadSaveState::LoadSaveState(GameController* controller, QWidget* parent)
 25	: QWidget(parent)
 26	, m_controller(controller)
 27	, m_currentFocus(controller->stateSlot() - 1)
 28	, m_mode(LoadSave::LOAD)
 29{
 30	setAttribute(Qt::WA_TranslucentBackground);
 31	m_ui.setupUi(this);
 32
 33	m_slots[0] = m_ui.state1;
 34	m_slots[1] = m_ui.state2;
 35	m_slots[2] = m_ui.state3;
 36	m_slots[3] = m_ui.state4;
 37	m_slots[4] = m_ui.state5;
 38	m_slots[5] = m_ui.state6;
 39	m_slots[6] = m_ui.state7;
 40	m_slots[7] = m_ui.state8;
 41	m_slots[8] = m_ui.state9;
 42
 43	int i;
 44	for (i = 0; i < NUM_SLOTS; ++i) {
 45		loadState(i + 1);
 46		m_slots[i]->installEventFilter(this);
 47		connect(m_slots[i], &QAbstractButton::clicked, this, [this, i]() { triggerState(i + 1); });
 48	}
 49
 50	if (m_currentFocus >= 9) {
 51		m_currentFocus = 0;
 52	}
 53	if (m_currentFocus < 0) {
 54		m_currentFocus = 0;
 55	}
 56
 57	QAction* escape = new QAction(this);
 58	escape->connect(escape, SIGNAL(triggered()), this, SLOT(close()));
 59	escape->setShortcut(QKeySequence("Esc"));
 60	escape->setShortcutContext(Qt::WidgetWithChildrenShortcut);
 61	addAction(escape);
 62}
 63
 64void LoadSaveState::setMode(LoadSave mode) {
 65	m_mode = mode;
 66	QString text = mode == LoadSave::LOAD ? tr("Load State") : tr("Save State");
 67	setWindowTitle(text);
 68	m_ui.lsLabel->setText(text);
 69}
 70
 71bool LoadSaveState::eventFilter(QObject* object, QEvent* event) {
 72	if (event->type() == QEvent::KeyPress) {
 73		int column = m_currentFocus % 3;
 74		int row = m_currentFocus / 3;
 75		switch (static_cast<QKeyEvent*>(event)->key()) {
 76		case Qt::Key_Up:
 77			row += 2;
 78			break;
 79		case Qt::Key_Down:
 80			row += 1;
 81			break;
 82		case Qt::Key_Left:
 83			column += 2;
 84			break;
 85		case Qt::Key_Right:
 86			column += 1;
 87			break;
 88		case Qt::Key_1:
 89		case Qt::Key_2:
 90		case Qt::Key_3:
 91		case Qt::Key_4:
 92		case Qt::Key_5:
 93		case Qt::Key_6:
 94		case Qt::Key_7:
 95		case Qt::Key_8:
 96		case Qt::Key_9:
 97			triggerState(static_cast<QKeyEvent*>(event)->key() - Qt::Key_1 + 1);
 98			break;
 99		case Qt::Key_Enter:
100		case Qt::Key_Return:
101			triggerState(m_currentFocus + 1);
102			break;
103		default:
104			return false;
105		}
106		column %= 3;
107		row %= 3;
108		m_currentFocus = column + row * 3;
109		m_slots[m_currentFocus]->setFocus();
110		return true;
111	}
112	if (event->type() == QEvent::Enter) {
113		int i;
114		for (i = 0; i < 9; ++i) {
115			if (m_slots[i] == object) {
116				m_currentFocus = i;
117				m_slots[m_currentFocus]->setFocus();
118				return true;
119			}
120		}
121	}
122	if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadAxisEvent::Type()) {
123		int column = m_currentFocus % 3;
124		int row = m_currentFocus - column;
125		GBAKey key = GBA_KEY_NONE;
126		if (event->type() == GamepadButtonEvent::Down()) {
127			key = static_cast<GamepadButtonEvent*>(event)->gbaKey();
128		} else if (event->type() == GamepadAxisEvent::Type()) {
129			GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
130			if (gae->isNew()) {
131				key = gae->gbaKey();
132			} else {
133				return false;
134			}
135		}
136		switch (key) {
137		case GBA_KEY_UP:
138			row += 6;
139			break;
140		case GBA_KEY_DOWN:
141			row += 3;
142			break;
143		case GBA_KEY_LEFT:
144			column += 2;
145			break;
146		case GBA_KEY_RIGHT:
147			column += 1;
148			break;
149		case GBA_KEY_B:
150			event->accept();
151			close();
152			return true;
153		case GBA_KEY_A:
154		case GBA_KEY_START:
155			event->accept();
156			triggerState(m_currentFocus + 1);
157			return true;
158		default:
159			return false;
160		}
161		column %= 3;
162		row %= 9;
163		m_currentFocus = column + row;
164		m_slots[m_currentFocus]->setFocus();
165		event->accept();
166		return true;
167	}
168	return false;
169}
170
171void LoadSaveState::loadState(int slot) {
172	GBAThread* thread = m_controller->thread();
173	VFile* vf = GBAGetState(thread->gba, thread->dirs.state, slot, false);
174	if (!vf) {
175		m_slots[slot - 1]->setText(tr("Empty"));
176		return;
177	}
178
179	GBAExtdata extdata;
180	GBAExtdataInit(&extdata);
181	GBASerializedState* state = GBAExtractState(vf, &extdata);
182	vf->seek(vf, 0, SEEK_SET);
183	if (!state) {
184		m_slots[slot - 1]->setText(tr("Corrupted"));
185		GBAExtdataDeinit(&extdata);
186		return;
187	}
188
189	QDateTime creation(QDateTime::fromMSecsSinceEpoch(state->creationUsec / 1000LL));
190	QImage stateImage;
191
192	GBAExtdataItem item;
193	if (GBAExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item) && item.size >= VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4) {
194		stateImage = QImage((uchar*) item.data, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, QImage::Format_ARGB32).rgbSwapped();
195	}
196
197	if (!stateImage.isNull()) {
198		QPixmap statePixmap;
199		statePixmap.convertFromImage(stateImage);
200		m_slots[slot - 1]->setIcon(statePixmap);
201	}
202	if (creation.toMSecsSinceEpoch()) {
203		m_slots[slot - 1]->setText(creation.toString(Qt::DefaultLocaleShortDate));
204	} else if (stateImage.isNull()) {
205		m_slots[slot - 1]->setText(tr("Slot %1").arg(slot));
206	} else {
207		m_slots[slot - 1]->setText(QString());
208	}
209	vf->close(vf);
210	GBADeallocateState(state);
211}
212
213void LoadSaveState::triggerState(int slot) {
214	if (m_mode == LoadSave::SAVE) {
215		m_controller->saveState(slot);
216	} else {
217		m_controller->loadState(slot);
218	}
219	close();
220}
221
222void LoadSaveState::closeEvent(QCloseEvent* event) {
223	emit closed();
224	QWidget::closeEvent(event);
225}
226
227void LoadSaveState::showEvent(QShowEvent* event) {
228	m_slots[m_currentFocus]->setFocus();
229	QWidget::showEvent(event);
230}
231
232void LoadSaveState::paintEvent(QPaintEvent*) {
233	QPainter painter(this);
234	QRect full(QPoint(), size());
235	painter.fillRect(full, QColor(0, 0, 0, 128));
236}