mirror of https://github.com/mgba-emu/mgba.git
Qt: Add option to download chip data
This commit is contained in:
parent
38c8e4c4e1
commit
c9e1f5d6a6
|
@ -0,0 +1,89 @@
|
||||||
|
/* Copyright (c) 2013-2019 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "AbstractUpdater.h"
|
||||||
|
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
using namespace QGBA;
|
||||||
|
|
||||||
|
AbstractUpdater::AbstractUpdater(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_netman(new QNetworkAccessManager(this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractUpdater::checkUpdate() {
|
||||||
|
QNetworkReply* reply = m_netman->get(QNetworkRequest(manifestLocation()));
|
||||||
|
chaseRedirects(reply, &AbstractUpdater::manifestDownloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractUpdater::downloadUpdate() {
|
||||||
|
if (m_isUpdating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_manifest.isEmpty()) {
|
||||||
|
m_isUpdating = true;
|
||||||
|
checkUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QUrl url = parseManifest(m_manifest);
|
||||||
|
if (!url.isValid()) {
|
||||||
|
emit updateDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_isUpdating = true;
|
||||||
|
QNetworkReply* reply = m_netman->get(QNetworkRequest(url));
|
||||||
|
chaseRedirects(reply, &AbstractUpdater::updateDownloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractUpdater::chaseRedirects(QNetworkReply* reply, void (AbstractUpdater::*cb)(QNetworkReply*)) {
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [this, reply, cb]() {
|
||||||
|
// TODO: check domains, etc
|
||||||
|
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 == 3) {
|
||||||
|
QNetworkReply* newReply = m_netman->get(QNetworkRequest(reply->header(QNetworkRequest::LocationHeader).toString()));
|
||||||
|
chaseRedirects(newReply, cb);
|
||||||
|
} else {
|
||||||
|
(this->*cb)(reply);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractUpdater::manifestDownloaded(QNetworkReply* reply) {
|
||||||
|
m_manifest = reply->readAll();
|
||||||
|
QUrl url = parseManifest(m_manifest);
|
||||||
|
if (m_isUpdating) {
|
||||||
|
if (!url.isValid()) {
|
||||||
|
emit updateDone(false);
|
||||||
|
} else {
|
||||||
|
QNetworkReply* reply = m_netman->get(QNetworkRequest(url));
|
||||||
|
chaseRedirects(reply, &AbstractUpdater::updateDownloaded);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit updateAvailable(url.isValid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractUpdater::updateDownloaded(QNetworkReply* reply) {
|
||||||
|
m_isUpdating = false;
|
||||||
|
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 != 2) {
|
||||||
|
emit updateDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QFile f(destination());
|
||||||
|
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||||
|
emit updateDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
QByteArray bytes = reply->read(4096);
|
||||||
|
if (!bytes.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
f.write(bytes);
|
||||||
|
}
|
||||||
|
emit updateDone(true);
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* Copyright (c) 2013-2019 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QNetworkAccessManager;
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
class AbstractUpdater : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
AbstractUpdater(QObject* parent = nullptr);
|
||||||
|
virtual ~AbstractUpdater() {}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void checkUpdate();
|
||||||
|
void downloadUpdate();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void updateAvailable(bool);
|
||||||
|
void updateDone(bool);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QUrl manifestLocation() const = 0;
|
||||||
|
virtual QUrl parseManifest(const QByteArray&) const = 0;
|
||||||
|
virtual QString destination() const = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void chaseRedirects(QNetworkReply*, void (AbstractUpdater::*cb)(QNetworkReply*));
|
||||||
|
void manifestDownloaded(QNetworkReply*);
|
||||||
|
void updateDownloaded(QNetworkReply*);
|
||||||
|
|
||||||
|
bool m_isUpdating = false;
|
||||||
|
QNetworkAccessManager* m_netman;
|
||||||
|
QByteArray m_manifest;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -155,6 +155,21 @@ void BattleChipModel::setScale(int scale) {
|
||||||
m_scale = scale;
|
m_scale = scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BattleChipModel::reloadAssets() {
|
||||||
|
QResource::unregisterResource(ConfigController::configDir() + "/chips.rcc", "/exe");
|
||||||
|
QResource::unregisterResource(GBAApp::dataDir() + "/chips.rcc", "/exe");
|
||||||
|
|
||||||
|
QResource::registerResource(GBAApp::dataDir() + "/chips.rcc", "/exe");
|
||||||
|
QResource::registerResource(ConfigController::configDir() + "/chips.rcc", "/exe");
|
||||||
|
|
||||||
|
emit layoutAboutToBeChanged();
|
||||||
|
setFlavor(m_flavor);
|
||||||
|
for (int i = 0; i < m_deck.count(); ++i) {
|
||||||
|
m_deck[i] = createChip(m_deck[i].id);
|
||||||
|
}
|
||||||
|
emit layoutChanged();
|
||||||
|
}
|
||||||
|
|
||||||
BattleChipModel::BattleChip BattleChipModel::createChip(int id) const {
|
BattleChipModel::BattleChip BattleChipModel::createChip(int id) const {
|
||||||
QString path = QString(":/exe/exe%1/%2.png").arg(m_flavor).arg(id, 3, 10, QLatin1Char('0'));
|
QString path = QString(":/exe/exe%1/%2.png").arg(m_flavor).arg(id, 3, 10, QLatin1Char('0'));
|
||||||
if (!QFile(path).exists()) {
|
if (!QFile(path).exists()) {
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
|
class BattleChipUpdater;
|
||||||
|
|
||||||
class BattleChipModel : public QAbstractListModel {
|
class BattleChipModel : public QAbstractListModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -35,6 +37,7 @@ public slots:
|
||||||
void setChips(QList<int> ids);
|
void setChips(QList<int> ids);
|
||||||
void clear();
|
void clear();
|
||||||
void setScale(int);
|
void setScale(int);
|
||||||
|
void reloadAssets();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct BattleChip {
|
struct BattleChip {
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* Copyright (c) 2013-2019 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "BattleChipUpdater.h"
|
||||||
|
|
||||||
|
#include "ConfigController.h"
|
||||||
|
#include "GBAApp.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
using namespace QGBA;
|
||||||
|
|
||||||
|
BattleChipUpdater::BattleChipUpdater(QObject* parent)
|
||||||
|
: AbstractUpdater(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl BattleChipUpdater::manifestLocation() const {
|
||||||
|
return {"https://api.github.com/repos/mgba-emu/chip-assets/releases/latest"};
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl BattleChipUpdater::parseManifest(const QByteArray& manifest) const {
|
||||||
|
QJsonDocument manifestDoc(QJsonDocument::fromJson(manifest));
|
||||||
|
if (manifestDoc.isNull()) {
|
||||||
|
return QUrl();
|
||||||
|
}
|
||||||
|
for (const auto& assetv : manifestDoc.object()["assets"].toArray()) {
|
||||||
|
QJsonObject asset = assetv.toObject();
|
||||||
|
if (asset["name"].toString() == "chips.rcc") {
|
||||||
|
return asset["browser_download_url"].toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BattleChipUpdater::destination() const {
|
||||||
|
QFileInfo info(GBAApp::dataDir() + "/chips.rcc");
|
||||||
|
if (info.isWritable()) {
|
||||||
|
return info.filePath();
|
||||||
|
}
|
||||||
|
return ConfigController::configDir() + "/chips.rcc";
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* Copyright (c) 2013-2019 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AbstractUpdater.h"
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
class BattleChipUpdater : public AbstractUpdater {
|
||||||
|
public:
|
||||||
|
BattleChipUpdater(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QUrl manifestLocation() const override;
|
||||||
|
virtual QUrl parseManifest(const QByteArray&) const override;
|
||||||
|
virtual QString destination() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -5,11 +5,15 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "BattleChipView.h"
|
#include "BattleChipView.h"
|
||||||
|
|
||||||
|
#include "BattleChipUpdater.h"
|
||||||
|
#include "ConfigController.h"
|
||||||
#include "CoreController.h"
|
#include "CoreController.h"
|
||||||
|
#include "GBAApp.h"
|
||||||
#include "ShortcutController.h"
|
#include "ShortcutController.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
|
||||||
#include <QtAlgorithms>
|
#include <QtAlgorithms>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QFontMetrics>
|
#include <QFontMetrics>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QMultiMap>
|
#include <QMultiMap>
|
||||||
|
@ -59,6 +63,7 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
|
||||||
connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
|
connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
|
||||||
connect(m_ui.save, &QAbstractButton::clicked, this, &BattleChipView::saveDeck);
|
connect(m_ui.save, &QAbstractButton::clicked, this, &BattleChipView::saveDeck);
|
||||||
connect(m_ui.load, &QAbstractButton::clicked, this, &BattleChipView::loadDeck);
|
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);
|
connect(m_ui.buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, &m_model, &BattleChipModel::clear);
|
||||||
|
|
||||||
connect(m_ui.gateBattleChip, &QAbstractButton::toggled, this, [this](bool on) {
|
connect(m_ui.gateBattleChip, &QAbstractButton::toggled, this, [this](bool on) {
|
||||||
|
@ -101,6 +106,18 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
|
||||||
} else if (qtitle.startsWith("AGB-BR5") || qtitle.startsWith("AGB-BR6")) {
|
} else if (qtitle.startsWith("AGB-BR5") || qtitle.startsWith("AGB-BR6")) {
|
||||||
m_ui.gateBeastLink->setChecked(Qt::Checked);
|
m_ui.gateBeastLink->setChecked(Qt::Checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!QFileInfo(GBAApp::dataDir() + "/chips.rcc").exists() && !QFileInfo(ConfigController::configDir() + "/chips.rcc").exists()) {
|
||||||
|
QMessageBox* download = new QMessageBox(this);
|
||||||
|
download->setIcon(QMessageBox::Information);
|
||||||
|
download->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||||
|
download->setWindowTitle(tr("BattleChip data missing"));
|
||||||
|
download->setText(tr("BattleChip data is missing. BattleChip Gates will still work, but some graphics will be missing. Would you like to download the data now?"));
|
||||||
|
download->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
download->setWindowModality(Qt::NonModal);
|
||||||
|
connect(download, &QDialog::accepted, this, &BattleChipView::updateData);
|
||||||
|
download->show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleChipView::~BattleChipView() {
|
BattleChipView::~BattleChipView() {
|
||||||
|
@ -225,4 +242,21 @@ void BattleChipView::resort() {
|
||||||
chips[x] = m_model.data(index, Qt::UserRole).toInt();
|
chips[x] = m_model.data(index, Qt::UserRole).toInt();
|
||||||
}
|
}
|
||||||
m_model.setChips(chips.values());
|
m_model.setChips(chips.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleChipView::updateData() {
|
||||||
|
if (m_updater) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_updater = new BattleChipUpdater(this);
|
||||||
|
connect(m_updater, &BattleChipUpdater::updateDone, this, [this](bool success) {
|
||||||
|
if (success) {
|
||||||
|
m_model.reloadAssets();
|
||||||
|
m_ui.chipName->clear();
|
||||||
|
m_ui.chipName->addItems(m_model.chipNames().values());
|
||||||
|
}
|
||||||
|
delete m_updater;
|
||||||
|
m_updater = nullptr;
|
||||||
|
});
|
||||||
|
m_updater->downloadUpdate();
|
||||||
}
|
}
|
|
@ -41,6 +41,8 @@ private slots:
|
||||||
void saveDeck();
|
void saveDeck();
|
||||||
void loadDeck();
|
void loadDeck();
|
||||||
|
|
||||||
|
void updateData();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int UNINSERTED_TIME = 10;
|
static const int UNINSERTED_TIME = 10;
|
||||||
|
|
||||||
|
@ -55,6 +57,8 @@ private:
|
||||||
bool m_next = false;
|
bool m_next = false;
|
||||||
|
|
||||||
Window* m_window;
|
Window* m_window;
|
||||||
|
|
||||||
|
BattleChipUpdater* m_updater = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>630</width>
|
<width>658</width>
|
||||||
<height>722</height>
|
<height>722</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -195,6 +195,19 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QPushButton" name="updateData">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Update Chip data</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -66,6 +66,7 @@ endif()
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
AboutScreen.cpp
|
AboutScreen.cpp
|
||||||
|
AbstractUpdater.cpp
|
||||||
AssetTile.cpp
|
AssetTile.cpp
|
||||||
AssetView.cpp
|
AssetView.cpp
|
||||||
AudioProcessor.cpp
|
AudioProcessor.cpp
|
||||||
|
@ -148,6 +149,7 @@ set(UI_FILES
|
||||||
|
|
||||||
set(GBA_SRC
|
set(GBA_SRC
|
||||||
BattleChipModel.cpp
|
BattleChipModel.cpp
|
||||||
|
BattleChipUpdater.cpp
|
||||||
BattleChipView.cpp
|
BattleChipView.cpp
|
||||||
GBAOverride.cpp)
|
GBAOverride.cpp)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue