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;
|
||||
}
|
||||
|
||||
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 {
|
||||
QString path = QString(":/exe/exe%1/%2.png").arg(m_flavor).arg(id, 3, 10, QLatin1Char('0'));
|
||||
if (!QFile(path).exists()) {
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
namespace QGBA {
|
||||
|
||||
class BattleChipUpdater;
|
||||
|
||||
class BattleChipModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -35,6 +37,7 @@ public slots:
|
|||
void setChips(QList<int> ids);
|
||||
void clear();
|
||||
void setScale(int);
|
||||
void reloadAssets();
|
||||
|
||||
private:
|
||||
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/. */
|
||||
#include "BattleChipView.h"
|
||||
|
||||
#include "BattleChipUpdater.h"
|
||||
#include "ConfigController.h"
|
||||
#include "CoreController.h"
|
||||
#include "GBAApp.h"
|
||||
#include "ShortcutController.h"
|
||||
#include "Window.h"
|
||||
|
||||
#include <QtAlgorithms>
|
||||
#include <QFileInfo>
|
||||
#include <QFontMetrics>
|
||||
#include <QMessageBox>
|
||||
#include <QMultiMap>
|
||||
|
@ -59,6 +63,7 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
|
|||
connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
|
||||
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);
|
||||
|
||||
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")) {
|
||||
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() {
|
||||
|
@ -225,4 +242,21 @@ void BattleChipView::resort() {
|
|||
chips[x] = m_model.data(index, Qt::UserRole).toInt();
|
||||
}
|
||||
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 loadDeck();
|
||||
|
||||
void updateData();
|
||||
|
||||
private:
|
||||
static const int UNINSERTED_TIME = 10;
|
||||
|
||||
|
@ -55,6 +57,8 @@ private:
|
|||
bool m_next = false;
|
||||
|
||||
Window* m_window;
|
||||
|
||||
BattleChipUpdater* m_updater = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>630</width>
|
||||
<width>658</width>
|
||||
<height>722</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -195,6 +195,19 @@
|
|||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -66,6 +66,7 @@ endif()
|
|||
|
||||
set(SOURCE_FILES
|
||||
AboutScreen.cpp
|
||||
AbstractUpdater.cpp
|
||||
AssetTile.cpp
|
||||
AssetView.cpp
|
||||
AudioProcessor.cpp
|
||||
|
@ -148,6 +149,7 @@ set(UI_FILES
|
|||
|
||||
set(GBA_SRC
|
||||
BattleChipModel.cpp
|
||||
BattleChipUpdater.cpp
|
||||
BattleChipView.cpp
|
||||
GBAOverride.cpp)
|
||||
|
||||
|
|
Loading…
Reference in New Issue