diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index 108169cd4..ca4676859 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -11,9 +11,11 @@ #include #include "ConfigController.h" +#include "VFileDevice.h" #include #include +#include using namespace QGBA; @@ -28,22 +30,17 @@ const char* SUFFIX = ""; ForwarderController::ForwarderController(QObject* parent) : QObject(parent) , m_netman(new QNetworkAccessManager(this)) + , m_originalPath(qgetenv("PATH")) { m_netman->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); - connect(this, &ForwarderController::buildFailed, this, [this]() { - m_inProgress = false; - }); - connect(this, &ForwarderController::buildComplete, this, [this]() { - m_inProgress = false; - }); + connect(this, &ForwarderController::buildFailed, this, &ForwarderController::cleanup); + connect(this, &ForwarderController::buildComplete, this, &ForwarderController::cleanup); } void ForwarderController::setGenerator(std::unique_ptr&& generator) { m_generator = std::move(generator); 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::cleanup); } void ForwarderController::startBuild(const QString& outFilename) { @@ -53,6 +50,15 @@ void ForwarderController::startBuild(const QString& outFilename) { m_inProgress = true; 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(); for (const auto& tool : neededTools) { if (!toolInstalled(tool)) { @@ -64,8 +70,67 @@ void ForwarderController::startBuild(const QString& outFilename) { } 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 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() { @@ -73,13 +138,7 @@ void ForwarderController::downloadManifest() { connect(reply, &QNetworkReply::finished, this, [this, reply]() { gotManifest(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(); - }); + connectErrorFailure(reply); } void ForwarderController::gotManifest(QNetworkReply* reply) { @@ -128,6 +187,7 @@ void ForwarderController::downloadBuild(const QUrl& url) { QByteArray data = reply->readAll(); m_sourceFile.write(data); }); + connectErrorFailure(reply); } void ForwarderController::gotBuild(QNetworkReply* reply) { @@ -144,6 +204,11 @@ void ForwarderController::gotBuild(QNetworkReply* reply) { void ForwarderController::cleanup() { 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) { @@ -157,3 +222,13 @@ bool ForwarderController::toolInstalled(const QString& tool) { } 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(); + }); +} diff --git a/src/platform/qt/ForwarderController.h b/src/platform/qt/ForwarderController.h index 391fe0e76..09902a397 100644 --- a/src/platform/qt/ForwarderController.h +++ b/src/platform/qt/ForwarderController.h @@ -38,6 +38,7 @@ signals: private slots: void gotManifest(QNetworkReply*); void gotBuild(QNetworkReply*); + void gotForwarderKit(QNetworkReply*); private: void downloadForwarderKit(); @@ -46,12 +47,15 @@ private: bool toolInstalled(const QString& tool); void cleanup(); + void connectErrorFailure(QNetworkReply*); + QString m_channel{"dev"}; QString m_outFilename; QNetworkAccessManager* m_netman; std::unique_ptr m_generator; QFile m_sourceFile; bool m_inProgress = false; + QByteArray m_originalPath; }; }