Qt: Initial support for forwarder-kit

This commit is contained in:
Vicki Pfau 2022-11-01 01:59:40 -07:00
parent 6bdb3470e7
commit 7f30bdc850
2 changed files with 94 additions and 15 deletions

View File

@ -11,9 +11,11 @@
#include <QNetworkReply> #include <QNetworkReply>
#include "ConfigController.h" #include "ConfigController.h"
#include "VFileDevice.h"
#include <mgba/core/version.h> #include <mgba/core/version.h>
#include <mgba/feature/updater.h> #include <mgba/feature/updater.h>
#include <mgba-util/vfs.h>
using namespace QGBA; using namespace QGBA;
@ -28,22 +30,17 @@ const char* SUFFIX = "";
ForwarderController::ForwarderController(QObject* parent) ForwarderController::ForwarderController(QObject* parent)
: QObject(parent) : QObject(parent)
, m_netman(new QNetworkAccessManager(this)) , m_netman(new QNetworkAccessManager(this))
, m_originalPath(qgetenv("PATH"))
{ {
m_netman->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); m_netman->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
connect(this, &ForwarderController::buildFailed, this, [this]() { connect(this, &ForwarderController::buildFailed, this, &ForwarderController::cleanup);
m_inProgress = false; connect(this, &ForwarderController::buildComplete, this, &ForwarderController::cleanup);
});
connect(this, &ForwarderController::buildComplete, this, [this]() {
m_inProgress = false;
});
} }
void ForwarderController::setGenerator(std::unique_ptr<ForwarderGenerator>&& generator) { void ForwarderController::setGenerator(std::unique_ptr<ForwarderGenerator>&& generator) {
m_generator = std::move(generator); m_generator = std::move(generator);
connect(m_generator.get(), &ForwarderGenerator::buildFailed, this, &ForwarderController::buildFailed); connect(m_generator.get(), &ForwarderGenerator::buildFailed, this, &ForwarderController::buildFailed);
connect(m_generator.get(), &ForwarderGenerator::buildFailed, this, &ForwarderController::cleanup);
connect(m_generator.get(), &ForwarderGenerator::buildComplete, this, &ForwarderController::buildComplete); connect(m_generator.get(), &ForwarderGenerator::buildComplete, this, &ForwarderController::buildComplete);
connect(m_generator.get(), &ForwarderGenerator::buildComplete, this, &ForwarderController::cleanup);
} }
void ForwarderController::startBuild(const QString& outFilename) { void ForwarderController::startBuild(const QString& outFilename) {
@ -53,6 +50,15 @@ void ForwarderController::startBuild(const QString& outFilename) {
m_inProgress = true; m_inProgress = true;
m_outFilename = outFilename; m_outFilename = outFilename;
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
// Amend the path for downloaded programs forwarder-kit
QByteArray arr = m_originalPath;
QStringList path = QString::fromUtf8(arr).split(LIST_SPLIT);
path << ConfigController::cacheDir();
arr = path.join(LIST_SPLIT).toUtf8();
qputenv("PATH", arr);
#endif
QStringList neededTools = m_generator->externalTools(); QStringList neededTools = m_generator->externalTools();
for (const auto& tool : neededTools) { for (const auto& tool : neededTools) {
if (!toolInstalled(tool)) { if (!toolInstalled(tool)) {
@ -64,8 +70,67 @@ void ForwarderController::startBuild(const QString& outFilename) {
} }
void ForwarderController::downloadForwarderKit() { void ForwarderController::downloadForwarderKit() {
QString fkUrl("https://github.com/mgba-emu/forwarder-kit/releases/latest/download/forwarder-kit-%1.zip");
#ifdef Q_OS_WIN64
fkUrl = fkUrl.arg("win64");
#elif defined(Q_OS_WIN32)
fkUrl = fkUrl.arg("win32");
#elif defined(Q_OS_MAC) && (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
// Modern macOS build
fkUrl = fkUrl.arg("macos");
#else
// TODO // TODO
emit buildFailed(); emit buildFailed();
return;
#endif
QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl(fkUrl)));
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
gotForwarderKit(reply);
});
connectErrorFailure(reply);
}
void ForwarderController::gotForwarderKit(QNetworkReply* reply) {
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
emit buildFailed();
return;
}
QFile fkZip(ConfigController::cacheDir() + "/forwarder-kit.zip");
fkZip.open(QIODevice::WriteOnly | QIODevice::Truncate);
QByteArray arr;
do {
arr = reply->read(0x800);
fkZip.write(arr);
} while (!arr.isEmpty());
fkZip.close();
VDir* fkDir = VFileDevice::openArchive(fkZip.fileName());
// This has to be done in multiple passes to avoid seeking breaking the listing
QStringList files;
for (VDirEntry* entry = fkDir->listNext(fkDir); entry; entry = fkDir->listNext(fkDir)) {
if (entry->type(entry) != VFS_FILE) {
continue;
}
files << entry->name(entry);
}
for (const QString& source : files) {
VFile* sourceVf = fkDir->openFile(fkDir, source.toUtf8().constData(), O_RDONLY);
VFile* targetVf = VFileDevice::open(ConfigController::cacheDir() + "/" + source, O_CREAT | O_TRUNC | O_WRONLY);
VFileDevice::copyFile(sourceVf, targetVf);
sourceVf->close(sourceVf);
targetVf->close(targetVf);
QFile target(ConfigController::cacheDir() + "/" + source);
target.setPermissions(target.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeUser);
}
fkDir->close(fkDir);
fkZip.remove();
downloadManifest();
} }
void ForwarderController::downloadManifest() { void ForwarderController::downloadManifest() {
@ -73,13 +138,7 @@ void ForwarderController::downloadManifest() {
connect(reply, &QNetworkReply::finished, this, [this, reply]() { connect(reply, &QNetworkReply::finished, this, [this, reply]() {
gotManifest(reply); gotManifest(reply);
}); });
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) connectErrorFailure(reply);
connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() {
#else
connect(reply, qOverload<>(&QNetworkReply::error), this, [this, reply]() {
#endif
emit buildFailed();
});
} }
void ForwarderController::gotManifest(QNetworkReply* reply) { void ForwarderController::gotManifest(QNetworkReply* reply) {
@ -128,6 +187,7 @@ void ForwarderController::downloadBuild(const QUrl& url) {
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
m_sourceFile.write(data); m_sourceFile.write(data);
}); });
connectErrorFailure(reply);
} }
void ForwarderController::gotBuild(QNetworkReply* reply) { void ForwarderController::gotBuild(QNetworkReply* reply) {
@ -144,6 +204,11 @@ void ForwarderController::gotBuild(QNetworkReply* reply) {
void ForwarderController::cleanup() { void ForwarderController::cleanup() {
m_sourceFile.remove(); m_sourceFile.remove();
m_inProgress = false;
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
qputenv("PATH", m_originalPath);
#endif
} }
bool ForwarderController::toolInstalled(const QString& tool) { bool ForwarderController::toolInstalled(const QString& tool) {
@ -157,3 +222,13 @@ bool ForwarderController::toolInstalled(const QString& tool) {
} }
return false; return false;
} }
void ForwarderController::connectErrorFailure(QNetworkReply* reply) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() {
#else
connect(reply, qOverload<>(&QNetworkReply::error), this, [this, reply]() {
#endif
emit buildFailed();
});
}

View File

@ -38,6 +38,7 @@ signals:
private slots: private slots:
void gotManifest(QNetworkReply*); void gotManifest(QNetworkReply*);
void gotBuild(QNetworkReply*); void gotBuild(QNetworkReply*);
void gotForwarderKit(QNetworkReply*);
private: private:
void downloadForwarderKit(); void downloadForwarderKit();
@ -46,12 +47,15 @@ private:
bool toolInstalled(const QString& tool); bool toolInstalled(const QString& tool);
void cleanup(); void cleanup();
void connectErrorFailure(QNetworkReply*);
QString m_channel{"dev"}; QString m_channel{"dev"};
QString m_outFilename; QString m_outFilename;
QNetworkAccessManager* m_netman; QNetworkAccessManager* m_netman;
std::unique_ptr<ForwarderGenerator> m_generator; std::unique_ptr<ForwarderGenerator> m_generator;
QFile m_sourceFile; QFile m_sourceFile;
bool m_inProgress = false; bool m_inProgress = false;
QByteArray m_originalPath;
}; };
} }