src/platform/qt/BattleChipView.cpp (view raw)
1/* Copyright (c) 2013-2019 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 "BattleChipView.h"
7
8#include "ConfigController.h"
9#include "CoreController.h"
10#include "GBAApp.h"
11
12#include <QtAlgorithms>
13#include <QFile>
14#include <QFontMetrics>
15#include <QResource>
16#include <QSettings>
17#include <QStringList>
18
19using namespace QGBA;
20
21BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, QWidget* parent)
22 : QDialog(parent)
23 , m_controller(controller)
24{
25 QResource::registerResource(GBAApp::dataDir() + "/chips.rcc");
26 QResource::registerResource(ConfigController::configDir() + "/chips.rcc");
27
28 m_ui.setupUi(this);
29
30 char title[9];
31 CoreController::Interrupter interrupter(m_controller);
32 mCore* core = m_controller->thread()->core;
33 title[8] = '\0';
34 core->getGameCode(core, title);
35 QString qtitle(title);
36
37#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
38 int size = QFontMetrics(QFont()).height() / ((int) ceil(devicePixelRatioF()) * 16);
39#else
40 int size = QFontMetrics(QFont()).height() / (devicePixelRatio() * 16);
41#endif
42 m_ui.chipList->setGridSize(m_ui.chipList->gridSize() * size);
43 m_ui.chipList->setIconSize(m_ui.chipList->iconSize() * size);
44
45 connect(m_ui.chipId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_ui.inserted, [this]() {
46 m_ui.inserted->setChecked(Qt::Unchecked);
47 });
48 connect(m_ui.chipName, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), m_ui.chipId, [this](int id) {
49 m_ui.chipId->setValue(m_chipIndexToId[id]);
50 });
51
52 connect(m_ui.inserted, &QAbstractButton::toggled, this, &BattleChipView::insertChip);
53 connect(m_ui.insert, &QAbstractButton::clicked, this, &BattleChipView::reinsert);
54 connect(m_ui.add, &QAbstractButton::clicked, this, &BattleChipView::addChip);
55 connect(m_ui.remove, &QAbstractButton::clicked, this, &BattleChipView::removeChip);
56 connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
57 connect(m_ui.save, &QAbstractButton::clicked, this, &BattleChipView::saveDeck);
58 connect(m_ui.load, &QAbstractButton::clicked, this, &BattleChipView::loadDeck);
59
60 connect(m_ui.gateBattleChip, &QAbstractButton::toggled, this, [this](bool on) {
61 if (on) {
62 setFlavor(GBA_FLAVOR_BATTLECHIP_GATE);
63 }
64 });
65 connect(m_ui.gateProgress, &QAbstractButton::toggled, this, [this](bool on) {
66 if (on) {
67 setFlavor(GBA_FLAVOR_PROGRESS_GATE);
68 }
69 });
70 connect(m_ui.gateBeastLink, &QAbstractButton::toggled, this, [this, qtitle](bool on) {
71 if (on) {
72 if (qtitle.endsWith('E') || qtitle.endsWith('P')) {
73 setFlavor(GBA_FLAVOR_BEAST_LINK_GATE_US);
74 } else {
75 setFlavor(GBA_FLAVOR_BEAST_LINK_GATE);
76 }
77 }
78 });
79
80 connect(m_controller.get(), &CoreController::frameAvailable, this, &BattleChipView::advanceFrameCounter);
81
82 connect(m_ui.chipList, &QListWidget::itemClicked, this, [this](QListWidgetItem* item) {
83 QVariant chip = item->data(Qt::UserRole);
84 bool blocked = m_ui.chipId->blockSignals(true);
85 m_ui.chipId->setValue(chip.toInt());
86 m_ui.chipId->blockSignals(blocked);
87 reinsert();
88 });
89
90 m_controller->attachBattleChipGate();
91 setFlavor(4);
92 if (qtitle.startsWith("AGB-B4B") || qtitle.startsWith("AGB-B4W") || qtitle.startsWith("AGB-BR4") || qtitle.startsWith("AGB-BZ3")) {
93 m_ui.gateBattleChip->setChecked(Qt::Checked);
94 } else if (qtitle.startsWith("AGB-BRB") || qtitle.startsWith("AGB-BRK")) {
95 m_ui.gateProgress->setChecked(Qt::Checked);
96 } else if (qtitle.startsWith("AGB-BR5") || qtitle.startsWith("AGB-BR6")) {
97 m_ui.gateBeastLink->setChecked(Qt::Checked);
98 }
99}
100
101BattleChipView::~BattleChipView() {
102 m_controller->detachBattleChipGate();
103}
104
105void BattleChipView::setFlavor(int flavor) {
106 m_controller->setBattleChipFlavor(flavor);
107 loadChipNames(flavor);
108}
109
110void BattleChipView::insertChip(bool inserted) {
111 bool blocked = m_ui.inserted->blockSignals(true);
112 m_ui.inserted->setChecked(inserted);
113 m_ui.inserted->blockSignals(blocked);
114 if (inserted) {
115 m_controller->setBattleChipId(m_ui.chipId->value());
116 } else {
117 m_controller->setBattleChipId(0);
118 }
119}
120
121void BattleChipView::reinsert() {
122 if (m_ui.inserted->isChecked()) {
123 insertChip(false);
124 m_next = true;
125 m_frameCounter = UNINSERTED_TIME;
126 } else {
127 insertChip(true);
128 }
129}
130
131void BattleChipView::addChip() {
132 int insertedChip = m_ui.chipId->value();
133 if (insertedChip < 1) {
134 return;
135 }
136 addChipId(insertedChip);
137}
138
139void BattleChipView::addChipId(int id) {
140 QListWidgetItem* add = new QListWidgetItem(m_chipIdToName[id]);
141 add->setData(Qt::UserRole, id);
142 QString path = QString(":/res/exe%1/%2.png").arg(m_flavor).arg(id, 3, 10, QLatin1Char('0'));
143 if (!QFile(path).exists()) {
144 path = QString(":/res/exe%1/placeholder.png").arg(m_flavor);
145 }
146 add->setIcon(QIcon(path));
147 m_ui.chipList->addItem(add);
148}
149
150void BattleChipView::removeChip() {
151 qDeleteAll(m_ui.chipList->selectedItems());
152}
153
154void BattleChipView::loadChipNames(int flavor) {
155 QStringList chipNames;
156 chipNames.append(tr("(None)"));
157
158 m_chipIndexToId.clear();
159 m_chipIdToName.clear();
160 if (flavor == GBA_FLAVOR_BEAST_LINK_GATE_US) {
161 flavor = GBA_FLAVOR_BEAST_LINK_GATE;
162 }
163 m_flavor = flavor;
164
165 QFile file(QString(":/res/exe%1/chip-names.txt").arg(flavor));
166 file.open(QIODevice::ReadOnly | QIODevice::Text);
167 int id = 0;
168 while (true) {
169 QByteArray line = file.readLine();
170 if (line.isEmpty()) {
171 break;
172 }
173 ++id;
174 if (line.trimmed().isEmpty()) {
175 continue;
176 }
177 QString name = QString::fromUtf8(line).trimmed();
178 m_chipIndexToId[chipNames.length()] = id;
179 m_chipIdToName[id] = name;
180 chipNames.append(name);
181 }
182
183 m_ui.chipName->clear();
184 m_ui.chipName->addItems(chipNames);
185}
186
187void BattleChipView::advanceFrameCounter() {
188 if (m_frameCounter == 0) {
189 insertChip(m_next);
190 }
191 if (m_frameCounter >= 0) {
192 --m_frameCounter;
193 }
194}
195
196void BattleChipView::saveDeck() {
197 QString filename = GBAApp::app()->getSaveFileName(this, tr("Select deck file"), tr(("BattleChip deck file (*.deck)")));
198 if (filename.isEmpty()) {
199 return;
200 }
201
202 QStringList deck;
203 for (int i = 0; i < m_ui.chipList->count(); ++i) {
204 deck.append(m_ui.chipList->item(i)->data(Qt::UserRole).toString());
205 }
206
207 QSettings ini(filename, QSettings::IniFormat);
208 ini.clear();
209 ini.beginGroup("BattleChipDeck");
210 ini.setValue("version", m_flavor);
211 ini.setValue("deck", deck.join(','));
212 ini.sync();
213}
214
215void BattleChipView::loadDeck() {
216 QString filename = GBAApp::app()->getOpenFileName(this, tr("Select deck file"), tr(("BattleChip deck file (*.deck)")));
217 if (filename.isEmpty()) {
218 return;
219 }
220
221 QSettings ini(filename, QSettings::IniFormat);
222 ini.beginGroup("BattleChipDeck");
223 int flavor = ini.value("version").toInt();
224 if (flavor != m_flavor) {
225 return;
226 }
227
228 while (m_ui.chipList->count()) {
229 delete m_ui.chipList->takeItem(m_ui.chipList->count() - 1);
230 }
231 QStringList deck = ini.value("deck").toString().split(',');
232 for (const auto& item : deck) {
233 bool ok;
234 int id = item.toInt(&ok);
235 if (ok) {
236 addChipId(id);
237 }
238 }
239}