diff --git a/CHANGES b/CHANGES index 96e2cf71e..7f00b78e8 100644 --- a/CHANGES +++ b/CHANGES @@ -53,6 +53,7 @@ Other fixes: - VFS: Fix minizip write returning 0 on success instead of size Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) + - GBA: Improve detection of valid ELF ROMs - macOS: Add category to plist (closes mgba.io/i/2691) - macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700) - Qt: Keep track of current pslette preset name (fixes mgba.io/i/2680) diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index 206aedf70..020dd2ab5 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -154,6 +154,12 @@ void GBAHalt(struct GBA* gba); void GBAStop(struct GBA* gba); void GBADebug(struct GBA* gba, uint16_t value); +#ifdef USE_ELF +struct ELF; + +bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target); +#endif + #ifdef USE_DEBUGGERS struct mDebugger; void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger); diff --git a/src/gba/core.c b/src/gba/core.c index 18563cae9..40269b8bf 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -245,7 +245,7 @@ static bool _GBACoreInit(struct mCore* core) { #ifndef MINIMAL_CORE core->inputInfo = &GBAInputInfo; #endif - + return true; } @@ -523,7 +523,7 @@ static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { - if (ELFEntry(elf) == BASE_CART0) { + if (GBAVerifyELFEntry(elf, BASE_CART0)) { GBALoadNull(core->board); } bool success = mCoreLoadELF(core, elf); diff --git a/src/gba/gba.c b/src/gba/gba.c index 392012c9c..65b848e80 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -594,6 +594,63 @@ void GBADebug(struct GBA* gba, uint16_t flags) { gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags); } +#ifdef USE_ELF +bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target) { + if (ELFEntry(elf) == target) { + return true; + } + + struct ELFProgramHeaders ph; + ELFProgramHeadersInit(&ph, 0); + ELFGetProgramHeaders(elf, &ph); + size_t i; + for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) { + Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i); + if (!phdr->p_filesz) { + continue; + } + + size_t phdrS = phdr->p_paddr; + size_t phdrE = phdrS + phdr->p_filesz; + + // Does the segment contain our target address? + if (target < phdrS || target + 4 > phdrE) { + continue; + } + + // File offset to what should be the rom entry instruction + size_t off = phdr->p_offset + target - phdrS; + + size_t eSize; + const char* bytes = ELFBytes(elf, &eSize); + + // Bounds and alignment check + if (off >= eSize || off & 3) { + continue; + } + + uint32_t opcode; + LOAD_32(opcode, off, bytes); + struct ARMInstructionInfo info; + ARMDecodeARM(opcode, &info); + + if (info.branchType != ARM_BRANCH && info.branchType != ARM_BRANCH_LINKED) { + continue; + } + + uint32_t bTarget = target + info.op1.immediate + 8; + + if (ELFEntry(elf) == bTarget) { + ELFProgramHeadersDeinit(&ph); + return true; + } + } + + ELFProgramHeadersDeinit(&ph); + return false; +} +#endif + bool GBAIsROM(struct VFile* vf) { if (!vf) { return false; @@ -605,7 +662,7 @@ bool GBAIsROM(struct VFile* vf) { uint32_t entry = ELFEntry(elf); bool isGBA = true; isGBA = isGBA && ELFMachine(elf) == EM_ARM; - isGBA = isGBA && (entry == BASE_CART0 || entry == BASE_WORKING_RAM + 0xC0); + isGBA = isGBA && (GBAVerifyELFEntry(elf, BASE_CART0) || GBAVerifyELFEntry(elf, BASE_WORKING_RAM + 0xC0)); ELFClose(elf); return isGBA; } @@ -661,7 +718,7 @@ bool GBAIsMB(struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { - bool isMB = ELFEntry(elf) == BASE_WORKING_RAM + 0xC0; + bool isMB = GBAVerifyELFEntry(elf, BASE_WORKING_RAM + 0xC0); ELFClose(elf); return isMB; } diff --git a/src/platform/qt/AbstractUpdater.cpp b/src/platform/qt/AbstractUpdater.cpp index b878f8a2f..ae6df137b 100644 --- a/src/platform/qt/AbstractUpdater.cpp +++ b/src/platform/qt/AbstractUpdater.cpp @@ -8,16 +8,17 @@ #include #include +#include "GBAApp.h" + using namespace QGBA; AbstractUpdater::AbstractUpdater(QObject* parent) : QObject(parent) - , m_netman(new QNetworkAccessManager(this)) { } void AbstractUpdater::checkUpdate() { - QNetworkReply* reply = m_netman->get(QNetworkRequest(manifestLocation())); + QNetworkReply* reply = GBAApp::app()->httpGet(manifestLocation()); chaseRedirects(reply, &AbstractUpdater::manifestDownloaded); } @@ -36,7 +37,7 @@ void AbstractUpdater::downloadUpdate() { return; } m_isUpdating = true; - QNetworkReply* reply = m_netman->get(QNetworkRequest(url)); + QNetworkReply* reply = GBAApp::app()->httpGet(url); chaseRedirects(reply, &AbstractUpdater::updateDownloaded); } @@ -54,7 +55,7 @@ void AbstractUpdater::chaseRedirects(QNetworkReply* reply, void (AbstractUpdater 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())); + QNetworkReply* newReply = GBAApp::app()->httpGet(reply->header(QNetworkRequest::LocationHeader).toString()); chaseRedirects(newReply, cb); } else { (this->*cb)(reply); @@ -69,7 +70,7 @@ void AbstractUpdater::manifestDownloaded(QNetworkReply* reply) { if (!url.isValid()) { emit updateDone(false); } else { - QNetworkReply* reply = m_netman->get(QNetworkRequest(url)); + QNetworkReply* reply = GBAApp::app()->httpGet(url); chaseRedirects(reply, &AbstractUpdater::updateDownloaded); } } else { diff --git a/src/platform/qt/AbstractUpdater.h b/src/platform/qt/AbstractUpdater.h index 5fa385175..e12d3eabe 100644 --- a/src/platform/qt/AbstractUpdater.h +++ b/src/platform/qt/AbstractUpdater.h @@ -9,7 +9,6 @@ #include #include -class QNetworkAccessManager; class QNetworkReply; namespace QGBA { @@ -44,7 +43,6 @@ private: void updateDownloaded(QNetworkReply*); bool m_isUpdating = false; - QNetworkAccessManager* m_netman; QByteArray m_manifest; }; diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index ca4676859..6083a1d5e 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -11,6 +11,7 @@ #include #include "ConfigController.h" +#include "GBAApp.h" #include "VFileDevice.h" #include @@ -29,10 +30,8 @@ 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, &ForwarderController::cleanup); connect(this, &ForwarderController::buildComplete, this, &ForwarderController::cleanup); } @@ -83,14 +82,12 @@ void ForwarderController::downloadForwarderKit() { emit buildFailed(); return; #endif - QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl(fkUrl))); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { - gotForwarderKit(reply); - }); - connectErrorFailure(reply); + QNetworkReply* reply = GBAApp::app()->httpGet(QUrl(fkUrl)); + connectReply(reply, FORWARDER_KIT, &ForwarderController::gotForwarderKit); } void ForwarderController::gotForwarderKit(QNetworkReply* reply) { + emit downloadComplete(FORWARDER_KIT); if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { emit buildFailed(); return; @@ -134,14 +131,12 @@ void ForwarderController::gotForwarderKit(QNetworkReply* reply) { } void ForwarderController::downloadManifest() { - QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl("https://mgba.io/latest.ini"))); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { - gotManifest(reply); - }); - connectErrorFailure(reply); + QNetworkReply* reply = GBAApp::app()->httpGet(QUrl("https://mgba.io/latest.ini")); + connectReply(reply, MANIFEST, &ForwarderController::gotManifest); } void ForwarderController::gotManifest(QNetworkReply* reply) { + emit downloadComplete(MANIFEST); if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { emit buildFailed(); return; @@ -177,20 +172,17 @@ void ForwarderController::downloadBuild(const QUrl& url) { emit buildFailed(); return; } - QNetworkReply* reply = m_netman->get(QNetworkRequest(url)); - - connect(reply, &QNetworkReply::finished, this, [this, reply]() { - gotBuild(reply); - }); + QNetworkReply* reply = GBAApp::app()->httpGet(url); + connectReply(reply, BASE, &ForwarderController::gotBuild); connect(reply, &QNetworkReply::readyRead, this, [this, reply]() { QByteArray data = reply->readAll(); m_sourceFile.write(data); }); - connectErrorFailure(reply); } void ForwarderController::gotBuild(QNetworkReply* reply) { + emit downloadComplete(BASE); if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { emit buildFailed(); return; @@ -199,7 +191,13 @@ void ForwarderController::gotBuild(QNetworkReply* reply) { QByteArray data = reply->readAll(); m_sourceFile.write(data); m_sourceFile.close(); - m_generator->rebuild(m_sourceFile.fileName(), m_outFilename); + + QString extracted = m_generator->extract(m_sourceFile.fileName()); + if (extracted.isNull()) { + emit buildFailed(); + return; + } + m_generator->rebuild(extracted, m_outFilename); } void ForwarderController::cleanup() { @@ -223,7 +221,7 @@ bool ForwarderController::toolInstalled(const QString& tool) { return false; } -void ForwarderController::connectErrorFailure(QNetworkReply* reply) { +void ForwarderController::connectReply(QNetworkReply* reply, Download download, void (ForwarderController::*next)(QNetworkReply*)) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() { #else @@ -231,4 +229,12 @@ void ForwarderController::connectErrorFailure(QNetworkReply* reply) { #endif emit buildFailed(); }); + + connect(reply, &QNetworkReply::finished, this, [this, reply, next]() { + (this->*next)(reply); + }); + connect(reply, &QNetworkReply::downloadProgress, this, [this, download](qint64 bytesReceived, qint64 bytesTotal) { + emit downloadProgress(download, bytesReceived, bytesTotal); + }); + emit downloadStarted(download); } diff --git a/src/platform/qt/ForwarderController.h b/src/platform/qt/ForwarderController.h index 09902a397..7129b63d0 100644 --- a/src/platform/qt/ForwarderController.h +++ b/src/platform/qt/ForwarderController.h @@ -12,7 +12,6 @@ #include -class QNetworkAccessManager; class QNetworkReply; namespace QGBA { @@ -21,17 +20,27 @@ class ForwarderController : public QObject { Q_OBJECT public: + enum Download : int { + MANIFEST, + BASE, + FORWARDER_KIT + }; ForwarderController(QObject* parent = nullptr); void setGenerator(std::unique_ptr&& generator); ForwarderGenerator* generator() { return m_generator.get(); } QString channel() const { return m_channel; } + bool inProgress() const { return m_inProgress; } public slots: void startBuild(const QString& outFilename); signals: + void buildStarted(bool needsForwarderKit); + void downloadStarted(Download which); + void downloadComplete(Download which); + void downloadProgress(Download which, qint64 bytesGotten, qint64 bytesTotal); void buildComplete(); void buildFailed(); @@ -47,11 +56,10 @@ private: bool toolInstalled(const QString& tool); void cleanup(); - void connectErrorFailure(QNetworkReply*); + void connectReply(QNetworkReply*, Download, void (ForwarderController::*next)(QNetworkReply*)); QString m_channel{"dev"}; QString m_outFilename; - QNetworkAccessManager* m_netman; std::unique_ptr m_generator; QFile m_sourceFile; bool m_inProgress = false; diff --git a/src/platform/qt/ForwarderGenerator.cpp b/src/platform/qt/ForwarderGenerator.cpp index 691c9cf92..7230be1e0 100644 --- a/src/platform/qt/ForwarderGenerator.cpp +++ b/src/platform/qt/ForwarderGenerator.cpp @@ -10,6 +10,10 @@ #include "ForwarderGenerator3DS.h" #include "ForwarderGeneratorVita.h" +#include "utils.h" +#include "VFileDevice.h" + +#include using namespace QGBA; @@ -73,6 +77,40 @@ QString ForwarderGenerator::systemName(ForwarderGenerator::System system) { return {}; } +QString ForwarderGenerator::systemHumanName(ForwarderGenerator::System system) { + switch (system) { + case ForwarderGenerator::System::N3DS: + return tr("3DS"); + case ForwarderGenerator::System::VITA: + return tr("Vita"); + } + + return {}; +} + +QString ForwarderGenerator::extract(const QString& archive) { + VDir* inArchive = VFileDevice::openArchive(archive); + if (!inArchive) { + return {}; + } + bool gotFile = extractMatchingFile(inArchive, [this](VDirEntry* dirent) -> QString { + if (dirent->type(dirent) != VFS_FILE) { + return {}; + } + QString filename(dirent->name(dirent)); + if (!filename.endsWith("." + extension())) { + return {}; + } + return "tmp." + extension(); + }); + inArchive->close(inArchive); + + if (gotFile) { + return QLatin1String("tmp.") + extension(); + } + return {}; +} + QString ForwarderGenerator::base36(const QByteArray& bytes, int length) { static const char* alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; QString buffer(length, 'X'); diff --git a/src/platform/qt/ForwarderGenerator.h b/src/platform/qt/ForwarderGenerator.h index c471910f2..0db97f148 100644 --- a/src/platform/qt/ForwarderGenerator.h +++ b/src/platform/qt/ForwarderGenerator.h @@ -40,12 +40,15 @@ public: virtual QList> imageTypes() const = 0; virtual System system() const = 0; QString systemName() const { return systemName(system()); } + QString systemHumanName() const { return systemHumanName(system()); } virtual QString extension() const = 0; virtual QStringList externalTools() const { return {}; } static QString systemName(System); + static QString systemHumanName(System); + virtual QString extract(const QString& archive); virtual void rebuild(const QString& source, const QString& target) = 0; signals: diff --git a/src/platform/qt/ForwarderGenerator3DS.cpp b/src/platform/qt/ForwarderGenerator3DS.cpp index 8b52d734a..51edc6aa7 100644 --- a/src/platform/qt/ForwarderGenerator3DS.cpp +++ b/src/platform/qt/ForwarderGenerator3DS.cpp @@ -6,8 +6,6 @@ #include "ForwarderGenerator3DS.h" #include "ConfigController.h" -#include "utils.h" -#include "VFileDevice.h" #include #include @@ -34,39 +32,11 @@ QList> ForwarderGenerator3DS::imageTypes() const { } void ForwarderGenerator3DS::rebuild(const QString& source, const QString& target) { - m_cia = dumpCia(source); - if (m_cia.isNull()) { - emit buildFailed(); - return; - } - + m_cia = source; m_target = target; extractCia(); } -QString ForwarderGenerator3DS::dumpCia(const QString& archive) { - VDir* inArchive = VFileDevice::openArchive(archive); - if (!inArchive) { - return {}; - } - bool gotFile = extractMatchingFile(inArchive, [](VDirEntry* dirent) -> QString { - if (dirent->type(dirent) != VFS_FILE) { - return {}; - } - QString filename(dirent->name(dirent)); - if (!filename.endsWith(".cia")) { - return {}; - } - return "tmp.cia"; - }); - inArchive->close(inArchive); - - if (gotFile) { - return QLatin1String("tmp.cia"); - } - return {}; -} - void ForwarderGenerator3DS::extractCia() { m_currentProc = std::make_unique(); m_currentProc->setProgram("ctrtool"); diff --git a/src/platform/qt/ForwarderGenerator3DS.h b/src/platform/qt/ForwarderGenerator3DS.h index d50d87e42..b22585063 100644 --- a/src/platform/qt/ForwarderGenerator3DS.h +++ b/src/platform/qt/ForwarderGenerator3DS.h @@ -43,7 +43,6 @@ private slots: void cleanup(); private: - QString dumpCia(const QString& archive); void init3dstoolArgs(QStringList& args, const QString& file, const QString& createType = {}); std::unique_ptr m_currentProc; diff --git a/src/platform/qt/ForwarderGeneratorVita.cpp b/src/platform/qt/ForwarderGeneratorVita.cpp index 97b3ef802..f6e97e5d3 100644 --- a/src/platform/qt/ForwarderGeneratorVita.cpp +++ b/src/platform/qt/ForwarderGeneratorVita.cpp @@ -8,7 +8,6 @@ #include #include -#include "utils.h" #include "VFileDevice.h" #include @@ -30,15 +29,9 @@ QList> ForwarderGeneratorVita::imageTypes() const { } void ForwarderGeneratorVita::rebuild(const QString& source, const QString& target) { - QString vpk = dumpVpk(source); - if (vpk.isNull()) { - emit buildFailed(); - return; - } - - QFile vpkFile(vpk); + QFile vpkFile(source); VDir* outdir = VDirOpenZip(target.toLocal8Bit().constData(), O_WRONLY | O_CREAT | O_TRUNC); - if (outdir && !copyAssets(vpk, outdir)) { + if (outdir && !copyAssets(source, outdir)) { outdir->close(outdir); outdir = nullptr; } @@ -79,29 +72,6 @@ void ForwarderGeneratorVita::rebuild(const QString& source, const QString& targe emit buildComplete(); } -QString ForwarderGeneratorVita::dumpVpk(const QString& archive) { - VDir* inArchive = VFileDevice::openArchive(archive); - if (!inArchive) { - return {}; - } - bool gotFile = extractMatchingFile(inArchive, [](VDirEntry* dirent) -> QString { - if (dirent->type(dirent) != VFS_FILE) { - return {}; - } - QString filename(dirent->name(dirent)); - if (!filename.endsWith(".vpk")) { - return {}; - } - return "tmp.vpk"; - }); - inArchive->close(inArchive); - - if (gotFile) { - return QLatin1String("tmp.vpk"); - } - return {}; -} - bool ForwarderGeneratorVita::copyAssets(const QString& vpk, VDir* outdir) { VDir* indir = VDirOpenZip(vpk.toLocal8Bit().constData(), O_RDONLY); if (!indir) { diff --git a/src/platform/qt/ForwarderGeneratorVita.h b/src/platform/qt/ForwarderGeneratorVita.h index ee2a5b17e..e3a083e6e 100644 --- a/src/platform/qt/ForwarderGeneratorVita.h +++ b/src/platform/qt/ForwarderGeneratorVita.h @@ -25,7 +25,6 @@ public: void rebuild(const QString& source, const QString& target) override; private: - QString dumpVpk(const QString& archive); bool copyAssets(const QString& vpk, VDir* out); QString makeSerial() const; void writeSfo(VFile* out); diff --git a/src/platform/qt/ForwarderView.cpp b/src/platform/qt/ForwarderView.cpp index da730cf86..2e8929f1d 100644 --- a/src/platform/qt/ForwarderView.cpp +++ b/src/platform/qt/ForwarderView.cpp @@ -32,13 +32,49 @@ ForwarderView::ForwarderView(QWidget* parent) connect(m_ui.imageSelect, qOverload(&QComboBox::currentIndexChanged), this, &ForwarderView::setActiveImage); connect(m_ui.imageBrowse, &QAbstractButton::clicked, this, &ForwarderView::selectImage); - connect(&m_controller, &ForwarderController::buildComplete, this, &QDialog::accept); + connect(&m_controller, &ForwarderController::buildComplete, this, [this]() { + QMessageBox* message = new QMessageBox(QMessageBox::Information, tr("Build finished"), + tr("Forwarder finished building"), + QMessageBox::Ok, parentWidget(), Qt::Sheet); + message->setAttribute(Qt::WA_DeleteOnClose); + message->show(); + accept(); + }); connect(&m_controller, &ForwarderController::buildFailed, this, [this]() { QMessageBox* error = new QMessageBox(QMessageBox::Critical, tr("Build failed"), tr("Failed to build forwarder"), QMessageBox::Ok, this, Qt::Sheet); error->setAttribute(Qt::WA_DeleteOnClose); error->show(); + + m_ui.progressBar->setValue(0); + m_ui.progressBar->setEnabled(false); + validate(); + }); + connect(&m_controller, &ForwarderController::downloadStarted, this, [this](ForwarderController::Download download) { + m_currentDownload = download; + m_downloadProgress = 0; + if (download == ForwarderController::FORWARDER_KIT) { + m_needsForwarderKit = true; + } + updateProgress(); + }); + connect(&m_controller, &ForwarderController::downloadComplete, this, [this](ForwarderController::Download download) { + if (m_currentDownload != download) { + return; + } + m_downloadProgress = 1; + updateProgress(); + }); + connect(&m_controller, &ForwarderController::downloadProgress, this, [this](ForwarderController::Download download, qint64 bytesReceived, qint64 bytesTotal) { + if (m_currentDownload != download) { + return; + } + if (bytesTotal <= 0 || bytesTotal < bytesReceived) { + return; + } + m_downloadProgress = bytesReceived / static_cast(bytesTotal); + updateProgress(); }); connect(m_ui.system3DS, &QAbstractButton::clicked, this, [this]() { @@ -59,6 +95,13 @@ void ForwarderView::build() { m_controller.generator()->setTitle(m_ui.title->text()); m_controller.generator()->setRom(m_ui.romFilename->text()); m_controller.startBuild(m_ui.outputFilename->text()); + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + m_ui.progressBar->setEnabled(true); + + m_currentDownload = ForwarderController::FORWARDER_KIT; + m_downloadProgress = 0; + m_needsForwarderKit = false; + updateProgress(); } void ForwarderView::validate() { @@ -80,6 +123,9 @@ void ForwarderView::validate() { if (m_ui.baseType->currentIndex() != 1) { valid = false; } + if (m_controller.inProgress()) { + valid = false; + } m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); } @@ -105,10 +151,18 @@ void ForwarderView::setSystem(ForwarderGenerator::System system) { void ForwarderView::connectBrowseButton(QAbstractButton* button, QLineEdit* lineEdit, const QString& title, bool save, const QString& filter) { connect(button, &QAbstractButton::clicked, lineEdit, [this, lineEdit, save, title, filter]() { QString filename; + QString usedFilter = filter; + if (filter.isEmpty()) { + // Use the forwarder type, if selected + ForwarderGenerator* generator = m_controller.generator(); + if (generator) { + usedFilter = tr("%1 installable package (*.%2)").arg(generator->systemHumanName()).arg(generator->extension()); + } + } if (save) { - filename = GBAApp::app()->getSaveFileName(this, title, filter); + filename = GBAApp::app()->getSaveFileName(this, title, usedFilter); } else { - filename = GBAApp::app()->getOpenFileName(this, title, filter); + filename = GBAApp::app()->getOpenFileName(this, title, usedFilter); } if (filename.isEmpty()) { return; @@ -154,3 +208,25 @@ void ForwarderView::setActiveImage(int index) { m_ui.imagePreview->setMaximumSize(m_activeSize); m_ui.imagePreview->setPixmap(QPixmap::fromImage(m_controller.generator()->image(index))); } + +void ForwarderView::updateProgress() { + switch (m_currentDownload) { + case ForwarderController::FORWARDER_KIT: + m_ui.progressBar->setValue(m_downloadProgress * 450); + break; + case ForwarderController::MANIFEST: + if (m_needsForwarderKit) { + m_ui.progressBar->setValue(450 + m_downloadProgress * 50); + } else { + m_ui.progressBar->setValue(m_downloadProgress * 100); + } + break; + case ForwarderController::BASE: + if (m_needsForwarderKit) { + m_ui.progressBar->setValue(500 + m_downloadProgress * 500); + } else { + m_ui.progressBar->setValue(100 + m_downloadProgress * 900); + } + break; + } +} diff --git a/src/platform/qt/ForwarderView.h b/src/platform/qt/ForwarderView.h index cd80d65b5..51ca5356c 100644 --- a/src/platform/qt/ForwarderView.h +++ b/src/platform/qt/ForwarderView.h @@ -30,12 +30,17 @@ private: void connectBrowseButton(QAbstractButton* button, QLineEdit* lineEdit, const QString& title, bool save = false, const QString& filter = {}); void selectImage(); void setActiveImage(int); + void updateProgress(); ForwarderController m_controller; QVector m_images; int m_currentImage; QSize m_activeSize; + qreal m_downloadProgress; + ForwarderController::Download m_currentDownload; + bool m_needsForwarderKit; + Ui::ForwarderView m_ui; }; diff --git a/src/platform/qt/ForwarderView.ui b/src/platform/qt/ForwarderView.ui index cac27af70..d5a7de842 100644 --- a/src/platform/qt/ForwarderView.ui +++ b/src/platform/qt/ForwarderView.ui @@ -14,51 +14,6 @@ Create forwarder - - - - - 0 - 0 - - - - System - - - - - - 3DS - - - system - - - - - - - Vita - - - system - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - @@ -168,6 +123,51 @@ + + + + + 0 + 0 + + + + System + + + + + + 3DS + + + system + + + + + + + Vita + + + system + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + @@ -399,6 +399,19 @@ + + + + false + + + 1000 + + + false + + + diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index d53985313..1e3c6a894 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -81,6 +82,8 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config) m_configController->updateOption("useDiscordPresence"); #endif + m_netman.setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); + cleanupAfterUpdate(); connect(this, &GBAApp::aboutToQuit, this, &GBAApp::cleanup); @@ -240,6 +243,19 @@ bool GBAApp::reloadGameDB() { } #endif +QNetworkAccessManager* GBAApp::netman() { + return &m_netman; +} + +QNetworkReply* GBAApp::httpGet(const QUrl& url) { + QNetworkRequest req(url); + req.setHeader(QNetworkRequest::UserAgentHeader, + QString("%1/%2 (+https://mgba.io) is definitely not Mozilla/5.0") + .arg(projectName) + .arg(projectVersion)); + return m_netman.get(req); +} + qint64 GBAApp::submitWorkerJob(std::function job, std::function callback) { return submitWorkerJob(job, nullptr, callback); } diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index d39f3a4a8..6245dba3f 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -11,7 +11,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -70,6 +71,9 @@ public: const NoIntroDB* gameDB() const { return m_db; } bool reloadGameDB(); + QNetworkAccessManager* netman(); + QNetworkReply* httpGet(const QUrl&); + qint64 submitWorkerJob(std::function job, std::function callback = {}); qint64 submitWorkerJob(std::function job, QObject* context, std::function callback); bool removeWorkerJob(qint64 jobId); @@ -128,6 +132,8 @@ private: QFont m_monospace; NoIntroDB* m_db = nullptr; + + QNetworkAccessManager m_netman; }; }