all repos — mgba @ f4dc546da68a757b6e8c04a379583909902f3628

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