Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 208 additions & 18 deletions src/platform/qt/BattleChipView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "GBAApp.h"
#include "ShortcutController.h"
#include "Window.h"
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/memory.h>

#include <QtAlgorithms>
#include <QFileInfo>
Expand All @@ -20,6 +22,10 @@
#include <QSettings>
#include <QStringList>

#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>

using namespace QGBA;

BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Window* window, QWidget* parent)
Expand All @@ -33,8 +39,21 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
CoreController::Interrupter interrupter(m_controller);
mCore* core = m_controller->thread()->core;
mGameInfo info;
core->getGameInfo(core, &info);
QString qtitle(info.title);
QString qtitle;

if (core->platform(core) == mPLATFORM_GBA) {
struct GBA* gba = (struct GBA*) core->board;
char code[5];
code[0] = (char) GBAView8(gba->cpu, 0x080000AC);
code[1] = (char) GBAView8(gba->cpu, 0x080000AD);
code[2] = (char) GBAView8(gba->cpu, 0x080000AE);
code[3] = (char) GBAView8(gba->cpu, 0x080000AF);
code[4] = '\0';
qtitle = QString::fromLatin1(code);
} else {
core->getGameInfo(core, &info);
qtitle = QString::fromLatin1(info.title).right(4);
}

#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
int size = QFontMetrics(QFont()).height() / ((int) ceil(devicePixelRatioF()) * 12);
Expand All @@ -48,22 +67,23 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
m_ui.chipList->setGridSize(m_ui.chipList->gridSize() * size);
m_model.setScale(size);

connect(m_ui.chipId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_ui.inserted, [this]() {
m_ui.inserted->setChecked(false);
});
connect(m_ui.chipName, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), m_ui.chipId, [this](int id) {
if (id < 0) {
return;
connect(m_ui.chipName, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this](int idx) {
if (idx < 0) return;
const auto keys = m_model.chipNames().keys();
if (idx >= 0 && idx < keys.size()) {
bool blocked = m_ui.chipId->blockSignals(true);
m_ui.chipId->setValue(keys[idx]);
m_ui.chipId->blockSignals(blocked);
}
m_ui.chipId->setValue(m_model.chipNames().keys()[id]);
});

connect(m_ui.inserted, &QAbstractButton::toggled, this, &BattleChipView::insertChip);
connect(m_ui.insert, &QAbstractButton::clicked, this, &BattleChipView::reinsert);
connect(m_ui.add, &QAbstractButton::clicked, this, &BattleChipView::addChip);
connect(m_ui.remove, &QAbstractButton::clicked, this, &BattleChipView::removeChip);
connect(m_ui.save, &QAbstractButton::clicked, this, &BattleChipView::saveDeck);
connect(m_ui.load, &QAbstractButton::clicked, this, &BattleChipView::loadDeck);
connect(m_ui.chipId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &BattleChipView::onChipIdChanged);
connect(m_ui.insert, &QAbstractButton::clicked, this, &BattleChipView::reinsert);
connect(m_ui.add, &QAbstractButton::clicked, this, &BattleChipView::addChip);
connect(m_ui.remove, &QAbstractButton::clicked, this, &BattleChipView::removeChip);
connect(m_ui.save, &QAbstractButton::clicked, this, &BattleChipView::saveDeck);
connect(m_ui.load, &QAbstractButton::clicked, this, &BattleChipView::loadDeck);
connect(m_ui.updateData, &QAbstractButton::clicked, this, &BattleChipView::updateData);
connect(m_ui.buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, &m_model, &BattleChipModel::clear);

Expand Down Expand Up @@ -119,9 +139,23 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
connect(download, &QDialog::accepted, this, &BattleChipView::updateData);
download->show();
}

if (m_ui.netGateEnable && m_ui.netGatePort && m_ui.netGateBind && m_ui.netGateStatus) {
connect(m_ui.netGateEnable, &QCheckBox::toggled, this, &BattleChipView::netGateToggled);
connect(m_ui.netGatePort, &QLineEdit::textChanged, this, &BattleChipView::netGatePortChanged);
connect(m_ui.netGateBind, &QLineEdit::textChanged, this, &BattleChipView::netGateBindChanged);

bool ok = false;
quint32 p = m_ui.netGatePort->text().toUInt(&ok);
if (ok && p > 0 && p <= 65535) {
m_netPort = static_cast<quint16>(p);
}
m_netBindStr = m_ui.netGateBind->text().trimmed();
}
}

BattleChipView::~BattleChipView() {
netGateStop();
m_controller->detachBattleChipGate();
}

Expand All @@ -133,9 +167,6 @@ void BattleChipView::setFlavor(int flavor) {
}

void BattleChipView::insertChip(bool inserted) {
bool blocked = m_ui.inserted->blockSignals(true);
m_ui.inserted->setChecked(inserted);
m_ui.inserted->blockSignals(blocked);
if (inserted) {
m_controller->setBattleChipId(m_ui.chipId->value());
} else {
Expand All @@ -144,12 +175,15 @@ void BattleChipView::insertChip(bool inserted) {
}

void BattleChipView::reinsert() {
if (m_ui.inserted->isChecked()) {
const bool keepInserted = m_ui.inserted->isChecked();
if (keepInserted) {
insertChip(false);
m_next = true;
m_frameCounter = UNINSERTED_TIME;
} else {
insertChip(true);
m_next = false;
m_frameCounter = UNINSERTED_TIME;
}
m_window->setWindowState(m_window->windowState() & ~Qt::WindowActive);
m_window->setWindowState(m_window->windowState() | Qt::WindowActive);
Expand Down Expand Up @@ -261,3 +295,159 @@ void BattleChipView::updateData() {
});
m_updater->downloadUpdate();
}

void BattleChipView::netGateToggled(bool on) {
if (on) {
netGateStart();
} else {
netGateStop();
}
}

void BattleChipView::netGatePortChanged(const QString& s) {
bool ok = false;
quint32 p = s.toUInt(&ok);
if (ok && p > 0 && p <= 65535) {
m_netPort = static_cast<quint16>(p);
if (m_netGateServer && m_netGateServer->isListening()) {
netGateStop();
netGateStart();
}
}
}

void BattleChipView::netGateBindChanged(const QString& s) {
m_netBindStr = s.trimmed();
if (m_netGateServer && m_netGateServer->isListening()) {
netGateStop();
netGateStart();
}
}

void BattleChipView::netGateStart() {
if (!m_ui.netGateEnable || !m_ui.netGatePort || !m_ui.netGateBind || !m_ui.netGateStatus) {
return;
}

if (!m_netPort) {
netGateSetStatus(tr("Failed: invalid port"));
m_ui.netGateEnable->setChecked(false);
return;
}
if (!m_netGateServer) {
m_netGateServer = new QTcpServer(this);
connect(m_netGateServer, &QTcpServer::newConnection, this, &BattleChipView::netGateNewConnection);
}
QHostAddress bindAddr;
if (!bindAddr.setAddress(m_netBindStr)) {
netGateSetStatus(tr("Failed: invalid bind address"));
m_ui.netGateEnable->setChecked(false);
return;
}
if (!m_netGateServer->listen(bindAddr, m_netPort)) {
netGateSetStatus(tr("Failed: %1").arg(m_netGateServer->errorString()));
m_ui.netGateEnable->setChecked(false);
return;
}
netGateDisableFields(true);
netGateSetStatus(tr("Listening on %1:%2").arg(bindAddr.toString()).arg(m_netPort));
}

void BattleChipView::netGateStop() {
for (QTcpSocket* c : m_netClients) {
if (!c) continue;
c->disconnect(this);
c->close();
c->deleteLater();
}
m_netClients.clear();
m_netBufs.clear();

if (m_netGateServer) {
m_netGateServer->close();
}

netGateDisableFields(false);
netGateSetStatus(tr("Stopped"));
}

void BattleChipView::netGateNewConnection() {
if (!m_netGateServer) return;
while (m_netGateServer->hasPendingConnections()) {
QTcpSocket* c = m_netGateServer->nextPendingConnection();
m_netClients.append(c);
m_netBufs.insert(c, QByteArray());
connect(c, &QTcpSocket::readyRead, this, [this, c]() { netGateReadyRead(c); });
connect(c, &QTcpSocket::disconnected, this, [this, c]() {
m_netBufs.remove(c);
m_netClients.removeAll(c);
c->deleteLater();
});
}
}

void BattleChipView::netGateReadyRead(QTcpSocket* sock) {
if (!sock) return;
QByteArray& buf = m_netBufs[sock];
buf += sock->readAll();

for (;;) {
int hdr = buf.indexOf(char(0x80));
if (hdr < 0) { buf.clear(); return; }
if (buf.size() - hdr < 3) {
if (hdr > 0) buf.remove(0, hdr);
return;
}
const unsigned char b0 = (unsigned char)buf.at(hdr + 0);
const unsigned char b1 = (unsigned char)buf.at(hdr + 1);
const unsigned char b2 = (unsigned char)buf.at(hdr + 2);
buf.remove(0, hdr + 3);
if (b0 != 0x80) continue;
const quint16 chipId = (quint16(b1) << 8) | quint16(b2);

netGateApplyChip(chipId);
}
}

void BattleChipView::netGateApplyChip(quint16 chipId) {
bool blocked = m_ui.chipId->blockSignals(true);
m_ui.chipId->setValue(chipId);
m_ui.chipId->blockSignals(blocked);
onChipIdChanged(chipId);

const bool keepInserted = m_ui.inserted->isChecked();
if (keepInserted) {
insertChip(false);
m_next = true;
m_frameCounter = UNINSERTED_TIME;
} else {
insertChip(true);
m_next = false;
m_frameCounter = UNINSERTED_TIME;
}
}

void BattleChipView::netGateSetStatus(const QString& text) {
if (m_ui.netGateStatus) {
m_ui.netGateStatus->setText(text);
}
}

void BattleChipView::netGateDisableFields(bool disable) {
if (!m_ui.netGatePort || !m_ui.netGateBind) return;
m_ui.netGatePort->setEnabled(!disable);
m_ui.netGateBind->setEnabled(!disable);
}

void BattleChipView::onChipIdChanged(int id) {
const auto names = m_model.chipNames();
const QString name = names.value(id);
if (!name.isEmpty()) {
int idx = m_ui.chipName->findText(name);
if (idx >= 0) {
bool blocked = m_ui.chipName->blockSignals(true);
m_ui.chipName->setCurrentIndex(idx);
m_ui.chipName->blockSignals(blocked);
}
}
}
27 changes: 27 additions & 0 deletions src/platform/qt/BattleChipView.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@
#include "BattleChipModel.h"

#include <QDialog>
#include <QByteArray>
#include <QString>
#include <QList>
#include <QHash>

#include <memory>

#include <mgba/core/interface.h>

#include "ui_BattleChipView.h"

class QTcpServer;
class QTcpSocket;

namespace QGBA {

class CoreController;
Expand Down Expand Up @@ -43,6 +50,14 @@ private slots:

void updateData();

void onChipIdChanged(int id);

void netGateToggled(bool on);
void netGatePortChanged(const QString& s);
void netGateBindChanged(const QString& s);
void netGateNewConnection();
void netGateReadyRead(QTcpSocket* sock);

private:
static const int UNINSERTED_TIME = 10;

Expand All @@ -59,6 +74,18 @@ private slots:
Window* m_window;

BattleChipUpdater* m_updater = nullptr;

QTcpServer* m_netGateServer = nullptr;
QList<QTcpSocket*> m_netClients;
QHash<QTcpSocket*, QByteArray> m_netBufs;
quint16 m_netPort;
QString m_netBindStr;

void netGateStart();
void netGateStop();
void netGateSetStatus(const QString& text);
void netGateDisableFields(bool disable);
void netGateApplyChip(quint16 chipId);
};

}
Loading