all repos — mgba @ f7ac90d74eba6bc556b609bb788e5a9d84926968

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