all repos — mgba @ 407f5988aaead60853542c7ec3503f1dcba074fa

mGBA Game Boy Advance Emulator

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