all repos — mgba @ 67905d281bfecbb06f51f2ca5ac939df378734a5

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