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}