src/platform/qt/OverrideView.cpp (view raw)
1/* Copyright (c) 2013-2016 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 "OverrideView.h"
7
8#include <QPushButton>
9#include <QStandardItemModel>
10
11#include "ConfigController.h"
12#include "CoreController.h"
13
14#ifdef M_CORE_GBA
15#include "GBAOverride.h"
16#include <mgba/internal/gba/gba.h>
17#endif
18
19#ifdef M_CORE_GB
20#include "GameBoy.h"
21#include "GBOverride.h"
22#include <mgba/internal/gb/gb.h>
23#endif
24
25using namespace QGBA;
26
27OverrideView::OverrideView(ConfigController* config, QWidget* parent)
28 : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
29 , m_config(config)
30{
31 m_ui.setupUi(this);
32
33 connect(m_ui.hwAutodetect, &QAbstractButton::toggled, [this] (bool enabled) {
34 m_ui.hwRTC->setEnabled(!enabled);
35 m_ui.hwGyro->setEnabled(!enabled);
36 m_ui.hwLight->setEnabled(!enabled);
37 m_ui.hwTilt->setEnabled(!enabled);
38 m_ui.hwRumble->setEnabled(!enabled);
39 });
40
41#ifdef M_CORE_GB
42 m_ui.gbModel->setItemData(0, GB_MODEL_AUTODETECT);
43 m_ui.mbc->setItemData(0, GB_MBC_AUTODETECT);
44
45 for (auto model : GameBoy::modelList()) {
46 m_ui.gbModel->addItem(GameBoy::modelName(model), model);
47 }
48
49 QStandardItemModel* model = static_cast<QStandardItemModel*>(m_ui.mbc->model());
50 int bitsSeen = 0;
51 for (auto mbc : GameBoy::mbcList()) {
52 int mbcValue = static_cast<int>(mbc);
53 if ((mbcValue & ~bitsSeen) & 0x001) {
54 m_ui.mbc->addItem(tr("Official MBCs"), -2);
55 model->item(m_ui.mbc->count() - 1)->setFlags(Qt::NoItemFlags);
56 }
57 if ((mbcValue & ~bitsSeen) & 0x010) {
58 m_ui.mbc->addItem(tr("Licensed MBCs"), -3);
59 model->item(m_ui.mbc->count() - 1)->setFlags(Qt::NoItemFlags);
60 }
61 if ((mbcValue & ~bitsSeen) & 0x200) {
62 m_ui.mbc->addItem(tr("Unlicensed MBCs"), -4);
63 model->item(m_ui.mbc->count() - 1)->setFlags(Qt::NoItemFlags);
64 }
65 bitsSeen |= mbcValue;
66 m_ui.mbc->addItem(GameBoy::mbcName(mbc), mbc);
67 }
68
69 m_colorPickers[0] = ColorPicker(m_ui.color0, QColor(0xF8, 0xF8, 0xF8));
70 m_colorPickers[1] = ColorPicker(m_ui.color1, QColor(0xA8, 0xA8, 0xA8));
71 m_colorPickers[2] = ColorPicker(m_ui.color2, QColor(0x50, 0x50, 0x50));
72 m_colorPickers[3] = ColorPicker(m_ui.color3, QColor(0x00, 0x00, 0x00));
73 m_colorPickers[4] = ColorPicker(m_ui.color4, QColor(0xF8, 0xF8, 0xF8));
74 m_colorPickers[5] = ColorPicker(m_ui.color5, QColor(0xA8, 0xA8, 0xA8));
75 m_colorPickers[6] = ColorPicker(m_ui.color6, QColor(0x50, 0x50, 0x50));
76 m_colorPickers[7] = ColorPicker(m_ui.color7, QColor(0x00, 0x00, 0x00));
77 m_colorPickers[8] = ColorPicker(m_ui.color8, QColor(0xF8, 0xF8, 0xF8));
78 m_colorPickers[9] = ColorPicker(m_ui.color9, QColor(0xA8, 0xA8, 0xA8));
79 m_colorPickers[10] = ColorPicker(m_ui.color10, QColor(0x50, 0x50, 0x50));
80 m_colorPickers[11] = ColorPicker(m_ui.color11, QColor(0x00, 0x00, 0x00));
81 for (int colorId = 0; colorId < 12; ++colorId) {
82 connect(&m_colorPickers[colorId], &ColorPicker::colorChanged, this, [this, colorId](const QColor& color) {
83 m_gbColors[colorId] = color.rgb() | 0xFF000000;
84 });
85 }
86#endif
87
88#ifndef M_CORE_GBA
89 m_ui.tabWidget->removeTab(m_ui.tabWidget->indexOf(m_ui.tabGBA));
90#endif
91#ifndef M_CORE_GB
92 m_ui.tabWidget->removeTab(m_ui.tabWidget->indexOf(m_ui.tabGB));
93#endif
94
95 connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &OverrideView::saveOverride);
96 connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close);
97
98 m_recheck.setInterval(200);
99 connect(&m_recheck, &QTimer::timeout, this, &OverrideView::recheck);
100}
101
102void OverrideView::setController(std::shared_ptr<CoreController> controller) {
103 m_controller = controller;
104 connect(controller.get(), &CoreController::started, this, &OverrideView::gameStarted);
105 connect(controller.get(), &CoreController::stopping, this, &OverrideView::gameStopped);
106 recheck();
107}
108
109void OverrideView::saveOverride() {
110 if (!m_controller) {
111 m_savePending = true;
112 return;
113 }
114
115 Override* override = m_controller->override();
116 if (!override) {
117 return;
118 }
119 m_config->saveOverride(*override);
120}
121
122void OverrideView::recheck() {
123 if (!m_controller) {
124 return;
125 }
126 if (m_controller->hasStarted()) {
127 gameStarted();
128 } else {
129 updateOverrides();
130 }
131}
132
133void OverrideView::updateOverrides() {
134 if (!m_controller) {
135 return;
136 }
137 bool hasOverride = false;
138#ifdef M_CORE_GBA
139 if (m_ui.tabWidget->currentWidget() == m_ui.tabGBA) {
140 auto gba = std::make_unique<GBAOverride>();
141 memset(gba->override.id, 0, 4);
142 gba->override.savetype = static_cast<SavedataType>(m_ui.savetype->currentIndex() - 1);
143 gba->override.hardware = HW_NO_OVERRIDE;
144 gba->override.idleLoop = IDLE_LOOP_NONE;
145 gba->override.mirroring = false;
146 gba->override.vbaBugCompat = false;
147
148 if (!m_ui.hwAutodetect->isChecked()) {
149 hasOverride = true;
150 gba->override.hardware = HW_NONE;
151 if (m_ui.hwRTC->isChecked()) {
152 gba->override.hardware |= HW_RTC;
153 }
154 if (m_ui.hwGyro->isChecked()) {
155 gba->override.hardware |= HW_GYRO;
156 }
157 if (m_ui.hwLight->isChecked()) {
158 gba->override.hardware |= HW_LIGHT_SENSOR;
159 }
160 if (m_ui.hwTilt->isChecked()) {
161 gba->override.hardware |= HW_TILT;
162 }
163 if (m_ui.hwRumble->isChecked()) {
164 gba->override.hardware |= HW_RUMBLE;
165 }
166 }
167 if (m_ui.hwGBPlayer->isChecked()) {
168 hasOverride = true;
169 gba->override.hardware |= HW_GB_PLAYER_DETECTION;
170 }
171 if (m_ui.vbaBugCompat->isChecked()) {
172 hasOverride = true;
173 gba->override.vbaBugCompat = true;
174 }
175
176 bool ok;
177 uint32_t parsedIdleLoop = m_ui.idleLoop->text().toInt(&ok, 16);
178 if (ok) {
179 hasOverride = true;
180 gba->override.idleLoop = parsedIdleLoop;
181 }
182
183 if (hasOverride) {
184 m_controller->setOverride(std::move(gba));
185 } else {
186 m_controller->clearOverride();
187 }
188 }
189#endif
190#ifdef M_CORE_GB
191 if (m_ui.tabWidget->currentWidget() == m_ui.tabGB) {
192 auto gb = std::make_unique<GBOverride>();
193 gb->override.mbc = static_cast<GBMemoryBankControllerType>(m_ui.mbc->currentData().toInt());
194 gb->override.model = static_cast<GBModel>(m_ui.gbModel->currentData().toInt());
195 hasOverride = gb->override.mbc != GB_MBC_AUTODETECT || gb->override.model != GB_MODEL_AUTODETECT;
196 for (int i = 0; i < 12; ++i) {
197 gb->override.gbColors[i] = m_gbColors[i];
198 hasOverride = hasOverride || (m_gbColors[i] & 0xFF000000);
199 }
200 if (hasOverride) {
201 m_controller->setOverride(std::move(gb));
202 } else {
203 m_controller->clearOverride();
204 }
205 }
206#endif
207}
208
209void OverrideView::gameStarted() {
210 CoreController::Interrupter interrupter(m_controller);
211 mCoreThread* thread = m_controller->thread();
212
213 m_ui.tabWidget->setEnabled(false);
214 m_recheck.start();
215
216 switch (thread->core->platform(thread->core)) {
217#ifdef M_CORE_GBA
218 case mPLATFORM_GBA: {
219 m_ui.tabWidget->setCurrentWidget(m_ui.tabGBA);
220 GBA* gba = static_cast<GBA*>(thread->core->board);
221 m_ui.savetype->setCurrentIndex(gba->memory.savedata.type + 1);
222 m_ui.hwRTC->setChecked(gba->memory.hw.devices & HW_RTC);
223 m_ui.hwGyro->setChecked(gba->memory.hw.devices & HW_GYRO);
224 m_ui.hwLight->setChecked(gba->memory.hw.devices & HW_LIGHT_SENSOR);
225 m_ui.hwTilt->setChecked(gba->memory.hw.devices & HW_TILT);
226 m_ui.hwRumble->setChecked(gba->memory.hw.devices & HW_RUMBLE);
227 m_ui.hwGBPlayer->setChecked(gba->memory.hw.devices & HW_GB_PLAYER_DETECTION);
228 m_ui.vbaBugCompat->setChecked(gba->vbaBugCompat);
229
230 if (gba->idleLoop != IDLE_LOOP_NONE) {
231 m_ui.idleLoop->setText(QString::number(gba->idleLoop, 16));
232 } else {
233 m_ui.idleLoop->clear();
234 }
235 break;
236 }
237#endif
238#ifdef M_CORE_GB
239 case mPLATFORM_GB: {
240 m_ui.tabWidget->setCurrentWidget(m_ui.tabGB);
241 GB* gb = static_cast<GB*>(thread->core->board);
242 int index = m_ui.mbc->findData(gb->memory.mbcType);
243 if (index >= 0) {
244 m_ui.mbc->setCurrentIndex(index);
245 } else {
246 m_ui.mbc->setCurrentIndex(0);
247 }
248 int model = m_ui.gbModel->findData(gb->model);
249 if (model >= 0) {
250 m_ui.gbModel->setCurrentIndex(model);
251 } else {
252 m_ui.gbModel->setCurrentIndex(0);
253 }
254 break;
255 }
256#endif
257 case mPLATFORM_NONE:
258 break;
259 }
260
261 if (m_savePending) {
262 m_savePending = false;
263 saveOverride();
264 }
265}
266
267void OverrideView::gameStopped() {
268 m_recheck.stop();
269 m_controller.reset();
270 m_ui.tabWidget->setEnabled(true);
271 m_ui.savetype->setCurrentIndex(0);
272 m_ui.idleLoop->clear();
273 m_ui.vbaBugCompat->setChecked(false);
274
275 m_ui.mbc->setCurrentIndex(0);
276 m_ui.gbModel->setCurrentIndex(0);
277}