mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
b1f9a98937
1
CHANGES
1
CHANGES
|
@ -53,6 +53,7 @@ Other fixes:
|
||||||
- VFS: Fix minizip write returning 0 on success instead of size
|
- VFS: Fix minizip write returning 0 on success instead of size
|
||||||
Misc:
|
Misc:
|
||||||
- GB Serialize: Add missing savestate support for MBC6 and NT (newer)
|
- 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: Add category to plist (closes mgba.io/i/2691)
|
||||||
- macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700)
|
- macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700)
|
||||||
- Qt: Keep track of current pslette preset name (fixes mgba.io/i/2680)
|
- Qt: Keep track of current pslette preset name (fixes mgba.io/i/2680)
|
||||||
|
|
|
@ -154,6 +154,12 @@ void GBAHalt(struct GBA* gba);
|
||||||
void GBAStop(struct GBA* gba);
|
void GBAStop(struct GBA* gba);
|
||||||
void GBADebug(struct GBA* gba, uint16_t value);
|
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
|
#ifdef USE_DEBUGGERS
|
||||||
struct mDebugger;
|
struct mDebugger;
|
||||||
void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger);
|
void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger);
|
||||||
|
|
|
@ -245,7 +245,7 @@ static bool _GBACoreInit(struct mCore* core) {
|
||||||
#ifndef MINIMAL_CORE
|
#ifndef MINIMAL_CORE
|
||||||
core->inputInfo = &GBAInputInfo;
|
core->inputInfo = &GBAInputInfo;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,7 +523,7 @@ static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) {
|
||||||
#ifdef USE_ELF
|
#ifdef USE_ELF
|
||||||
struct ELF* elf = ELFOpen(vf);
|
struct ELF* elf = ELFOpen(vf);
|
||||||
if (elf) {
|
if (elf) {
|
||||||
if (ELFEntry(elf) == BASE_CART0) {
|
if (GBAVerifyELFEntry(elf, BASE_CART0)) {
|
||||||
GBALoadNull(core->board);
|
GBALoadNull(core->board);
|
||||||
}
|
}
|
||||||
bool success = mCoreLoadELF(core, elf);
|
bool success = mCoreLoadELF(core, elf);
|
||||||
|
|
|
@ -594,6 +594,63 @@ void GBADebug(struct GBA* gba, uint16_t flags) {
|
||||||
gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags);
|
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) {
|
bool GBAIsROM(struct VFile* vf) {
|
||||||
if (!vf) {
|
if (!vf) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -605,7 +662,7 @@ bool GBAIsROM(struct VFile* vf) {
|
||||||
uint32_t entry = ELFEntry(elf);
|
uint32_t entry = ELFEntry(elf);
|
||||||
bool isGBA = true;
|
bool isGBA = true;
|
||||||
isGBA = isGBA && ELFMachine(elf) == EM_ARM;
|
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);
|
ELFClose(elf);
|
||||||
return isGBA;
|
return isGBA;
|
||||||
}
|
}
|
||||||
|
@ -661,7 +718,7 @@ bool GBAIsMB(struct VFile* vf) {
|
||||||
#ifdef USE_ELF
|
#ifdef USE_ELF
|
||||||
struct ELF* elf = ELFOpen(vf);
|
struct ELF* elf = ELFOpen(vf);
|
||||||
if (elf) {
|
if (elf) {
|
||||||
bool isMB = ELFEntry(elf) == BASE_WORKING_RAM + 0xC0;
|
bool isMB = GBAVerifyELFEntry(elf, BASE_WORKING_RAM + 0xC0);
|
||||||
ELFClose(elf);
|
ELFClose(elf);
|
||||||
return isMB;
|
return isMB;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,17 @@
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
#include "GBAApp.h"
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
AbstractUpdater::AbstractUpdater(QObject* parent)
|
AbstractUpdater::AbstractUpdater(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_netman(new QNetworkAccessManager(this))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractUpdater::checkUpdate() {
|
void AbstractUpdater::checkUpdate() {
|
||||||
QNetworkReply* reply = m_netman->get(QNetworkRequest(manifestLocation()));
|
QNetworkReply* reply = GBAApp::app()->httpGet(manifestLocation());
|
||||||
chaseRedirects(reply, &AbstractUpdater::manifestDownloaded);
|
chaseRedirects(reply, &AbstractUpdater::manifestDownloaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ void AbstractUpdater::downloadUpdate() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_isUpdating = true;
|
m_isUpdating = true;
|
||||||
QNetworkReply* reply = m_netman->get(QNetworkRequest(url));
|
QNetworkReply* reply = GBAApp::app()->httpGet(url);
|
||||||
chaseRedirects(reply, &AbstractUpdater::updateDownloaded);
|
chaseRedirects(reply, &AbstractUpdater::updateDownloaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ void AbstractUpdater::chaseRedirects(QNetworkReply* reply, void (AbstractUpdater
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply, cb]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply, cb]() {
|
||||||
// TODO: check domains, etc
|
// TODO: check domains, etc
|
||||||
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 == 3) {
|
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);
|
chaseRedirects(newReply, cb);
|
||||||
} else {
|
} else {
|
||||||
(this->*cb)(reply);
|
(this->*cb)(reply);
|
||||||
|
@ -69,7 +70,7 @@ void AbstractUpdater::manifestDownloaded(QNetworkReply* reply) {
|
||||||
if (!url.isValid()) {
|
if (!url.isValid()) {
|
||||||
emit updateDone(false);
|
emit updateDone(false);
|
||||||
} else {
|
} else {
|
||||||
QNetworkReply* reply = m_netman->get(QNetworkRequest(url));
|
QNetworkReply* reply = GBAApp::app()->httpGet(url);
|
||||||
chaseRedirects(reply, &AbstractUpdater::updateDownloaded);
|
chaseRedirects(reply, &AbstractUpdater::updateDownloaded);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
@ -44,7 +43,6 @@ private:
|
||||||
void updateDownloaded(QNetworkReply*);
|
void updateDownloaded(QNetworkReply*);
|
||||||
|
|
||||||
bool m_isUpdating = false;
|
bool m_isUpdating = false;
|
||||||
QNetworkAccessManager* m_netman;
|
|
||||||
QByteArray m_manifest;
|
QByteArray m_manifest;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
|
||||||
#include "ConfigController.h"
|
#include "ConfigController.h"
|
||||||
|
#include "GBAApp.h"
|
||||||
#include "VFileDevice.h"
|
#include "VFileDevice.h"
|
||||||
|
|
||||||
#include <mgba/core/version.h>
|
#include <mgba/core/version.h>
|
||||||
|
@ -29,10 +30,8 @@ const char* SUFFIX = "";
|
||||||
|
|
||||||
ForwarderController::ForwarderController(QObject* parent)
|
ForwarderController::ForwarderController(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_netman(new QNetworkAccessManager(this))
|
|
||||||
, m_originalPath(qgetenv("PATH"))
|
, m_originalPath(qgetenv("PATH"))
|
||||||
{
|
{
|
||||||
m_netman->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
|
|
||||||
connect(this, &ForwarderController::buildFailed, this, &ForwarderController::cleanup);
|
connect(this, &ForwarderController::buildFailed, this, &ForwarderController::cleanup);
|
||||||
connect(this, &ForwarderController::buildComplete, this, &ForwarderController::cleanup);
|
connect(this, &ForwarderController::buildComplete, this, &ForwarderController::cleanup);
|
||||||
}
|
}
|
||||||
|
@ -83,14 +82,12 @@ void ForwarderController::downloadForwarderKit() {
|
||||||
emit buildFailed();
|
emit buildFailed();
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl(fkUrl)));
|
QNetworkReply* reply = GBAApp::app()->httpGet(QUrl(fkUrl));
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connectReply(reply, FORWARDER_KIT, &ForwarderController::gotForwarderKit);
|
||||||
gotForwarderKit(reply);
|
|
||||||
});
|
|
||||||
connectErrorFailure(reply);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwarderController::gotForwarderKit(QNetworkReply* reply) {
|
void ForwarderController::gotForwarderKit(QNetworkReply* reply) {
|
||||||
|
emit downloadComplete(FORWARDER_KIT);
|
||||||
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
|
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
|
||||||
emit buildFailed();
|
emit buildFailed();
|
||||||
return;
|
return;
|
||||||
|
@ -134,14 +131,12 @@ void ForwarderController::gotForwarderKit(QNetworkReply* reply) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwarderController::downloadManifest() {
|
void ForwarderController::downloadManifest() {
|
||||||
QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl("https://mgba.io/latest.ini")));
|
QNetworkReply* reply = GBAApp::app()->httpGet(QUrl("https://mgba.io/latest.ini"));
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connectReply(reply, MANIFEST, &ForwarderController::gotManifest);
|
||||||
gotManifest(reply);
|
|
||||||
});
|
|
||||||
connectErrorFailure(reply);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwarderController::gotManifest(QNetworkReply* reply) {
|
void ForwarderController::gotManifest(QNetworkReply* reply) {
|
||||||
|
emit downloadComplete(MANIFEST);
|
||||||
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
|
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
|
||||||
emit buildFailed();
|
emit buildFailed();
|
||||||
return;
|
return;
|
||||||
|
@ -177,20 +172,17 @@ void ForwarderController::downloadBuild(const QUrl& url) {
|
||||||
emit buildFailed();
|
emit buildFailed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QNetworkReply* reply = m_netman->get(QNetworkRequest(url));
|
QNetworkReply* reply = GBAApp::app()->httpGet(url);
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
|
||||||
gotBuild(reply);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
connectReply(reply, BASE, &ForwarderController::gotBuild);
|
||||||
connect(reply, &QNetworkReply::readyRead, this, [this, reply]() {
|
connect(reply, &QNetworkReply::readyRead, this, [this, reply]() {
|
||||||
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) {
|
||||||
|
emit downloadComplete(BASE);
|
||||||
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
|
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
|
||||||
emit buildFailed();
|
emit buildFailed();
|
||||||
return;
|
return;
|
||||||
|
@ -199,7 +191,13 @@ void ForwarderController::gotBuild(QNetworkReply* reply) {
|
||||||
QByteArray data = reply->readAll();
|
QByteArray data = reply->readAll();
|
||||||
m_sourceFile.write(data);
|
m_sourceFile.write(data);
|
||||||
m_sourceFile.close();
|
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() {
|
void ForwarderController::cleanup() {
|
||||||
|
@ -223,7 +221,7 @@ bool ForwarderController::toolInstalled(const QString& tool) {
|
||||||
return false;
|
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))
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||||
connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() {
|
connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() {
|
||||||
#else
|
#else
|
||||||
|
@ -231,4 +229,12 @@ void ForwarderController::connectErrorFailure(QNetworkReply* reply) {
|
||||||
#endif
|
#endif
|
||||||
emit buildFailed();
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
@ -21,17 +20,27 @@ class ForwarderController : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum Download : int {
|
||||||
|
MANIFEST,
|
||||||
|
BASE,
|
||||||
|
FORWARDER_KIT
|
||||||
|
};
|
||||||
ForwarderController(QObject* parent = nullptr);
|
ForwarderController(QObject* parent = nullptr);
|
||||||
|
|
||||||
void setGenerator(std::unique_ptr<ForwarderGenerator>&& generator);
|
void setGenerator(std::unique_ptr<ForwarderGenerator>&& generator);
|
||||||
ForwarderGenerator* generator() { return m_generator.get(); }
|
ForwarderGenerator* generator() { return m_generator.get(); }
|
||||||
|
|
||||||
QString channel() const { return m_channel; }
|
QString channel() const { return m_channel; }
|
||||||
|
bool inProgress() const { return m_inProgress; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void startBuild(const QString& outFilename);
|
void startBuild(const QString& outFilename);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void buildStarted(bool needsForwarderKit);
|
||||||
|
void downloadStarted(Download which);
|
||||||
|
void downloadComplete(Download which);
|
||||||
|
void downloadProgress(Download which, qint64 bytesGotten, qint64 bytesTotal);
|
||||||
void buildComplete();
|
void buildComplete();
|
||||||
void buildFailed();
|
void buildFailed();
|
||||||
|
|
||||||
|
@ -47,11 +56,10 @@ private:
|
||||||
bool toolInstalled(const QString& tool);
|
bool toolInstalled(const QString& tool);
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
void connectErrorFailure(QNetworkReply*);
|
void connectReply(QNetworkReply*, Download, void (ForwarderController::*next)(QNetworkReply*));
|
||||||
|
|
||||||
QString m_channel{"dev"};
|
QString m_channel{"dev"};
|
||||||
QString m_outFilename;
|
QString m_outFilename;
|
||||||
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;
|
||||||
|
|
|
@ -10,6 +10,10 @@
|
||||||
|
|
||||||
#include "ForwarderGenerator3DS.h"
|
#include "ForwarderGenerator3DS.h"
|
||||||
#include "ForwarderGeneratorVita.h"
|
#include "ForwarderGeneratorVita.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "VFileDevice.h"
|
||||||
|
|
||||||
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
|
@ -73,6 +77,40 @@ QString ForwarderGenerator::systemName(ForwarderGenerator::System system) {
|
||||||
return {};
|
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) {
|
QString ForwarderGenerator::base36(const QByteArray& bytes, int length) {
|
||||||
static const char* alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
static const char* alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
QString buffer(length, 'X');
|
QString buffer(length, 'X');
|
||||||
|
|
|
@ -40,12 +40,15 @@ public:
|
||||||
virtual QList<QPair<QString, QSize>> imageTypes() const = 0;
|
virtual QList<QPair<QString, QSize>> imageTypes() const = 0;
|
||||||
virtual System system() const = 0;
|
virtual System system() const = 0;
|
||||||
QString systemName() const { return systemName(system()); }
|
QString systemName() const { return systemName(system()); }
|
||||||
|
QString systemHumanName() const { return systemHumanName(system()); }
|
||||||
virtual QString extension() const = 0;
|
virtual QString extension() const = 0;
|
||||||
|
|
||||||
virtual QStringList externalTools() const { return {}; }
|
virtual QStringList externalTools() const { return {}; }
|
||||||
|
|
||||||
static QString systemName(System);
|
static QString systemName(System);
|
||||||
|
static QString systemHumanName(System);
|
||||||
|
|
||||||
|
virtual QString extract(const QString& archive);
|
||||||
virtual void rebuild(const QString& source, const QString& target) = 0;
|
virtual void rebuild(const QString& source, const QString& target) = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
#include "ForwarderGenerator3DS.h"
|
#include "ForwarderGenerator3DS.h"
|
||||||
|
|
||||||
#include "ConfigController.h"
|
#include "ConfigController.h"
|
||||||
#include "utils.h"
|
|
||||||
#include "VFileDevice.h"
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
@ -34,39 +32,11 @@ QList<QPair<QString, QSize>> ForwarderGenerator3DS::imageTypes() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwarderGenerator3DS::rebuild(const QString& source, const QString& target) {
|
void ForwarderGenerator3DS::rebuild(const QString& source, const QString& target) {
|
||||||
m_cia = dumpCia(source);
|
m_cia = source;
|
||||||
if (m_cia.isNull()) {
|
|
||||||
emit buildFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_target = target;
|
m_target = target;
|
||||||
extractCia();
|
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() {
|
void ForwarderGenerator3DS::extractCia() {
|
||||||
m_currentProc = std::make_unique<QProcess>();
|
m_currentProc = std::make_unique<QProcess>();
|
||||||
m_currentProc->setProgram("ctrtool");
|
m_currentProc->setProgram("ctrtool");
|
||||||
|
|
|
@ -43,7 +43,6 @@ private slots:
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString dumpCia(const QString& archive);
|
|
||||||
void init3dstoolArgs(QStringList& args, const QString& file, const QString& createType = {});
|
void init3dstoolArgs(QStringList& args, const QString& file, const QString& createType = {});
|
||||||
|
|
||||||
std::unique_ptr<QProcess> m_currentProc;
|
std::unique_ptr<QProcess> m_currentProc;
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
#include "VFileDevice.h"
|
#include "VFileDevice.h"
|
||||||
|
|
||||||
#include <mgba-util/sfo.h>
|
#include <mgba-util/sfo.h>
|
||||||
|
@ -30,15 +29,9 @@ QList<QPair<QString, QSize>> ForwarderGeneratorVita::imageTypes() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwarderGeneratorVita::rebuild(const QString& source, const QString& target) {
|
void ForwarderGeneratorVita::rebuild(const QString& source, const QString& target) {
|
||||||
QString vpk = dumpVpk(source);
|
QFile vpkFile(source);
|
||||||
if (vpk.isNull()) {
|
|
||||||
emit buildFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile vpkFile(vpk);
|
|
||||||
VDir* outdir = VDirOpenZip(target.toLocal8Bit().constData(), O_WRONLY | O_CREAT | O_TRUNC);
|
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->close(outdir);
|
||||||
outdir = nullptr;
|
outdir = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -79,29 +72,6 @@ void ForwarderGeneratorVita::rebuild(const QString& source, const QString& targe
|
||||||
emit buildComplete();
|
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) {
|
bool ForwarderGeneratorVita::copyAssets(const QString& vpk, VDir* outdir) {
|
||||||
VDir* indir = VDirOpenZip(vpk.toLocal8Bit().constData(), O_RDONLY);
|
VDir* indir = VDirOpenZip(vpk.toLocal8Bit().constData(), O_RDONLY);
|
||||||
if (!indir) {
|
if (!indir) {
|
||||||
|
|
|
@ -25,7 +25,6 @@ public:
|
||||||
void rebuild(const QString& source, const QString& target) override;
|
void rebuild(const QString& source, const QString& target) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString dumpVpk(const QString& archive);
|
|
||||||
bool copyAssets(const QString& vpk, VDir* out);
|
bool copyAssets(const QString& vpk, VDir* out);
|
||||||
QString makeSerial() const;
|
QString makeSerial() const;
|
||||||
void writeSfo(VFile* out);
|
void writeSfo(VFile* out);
|
||||||
|
|
|
@ -32,13 +32,49 @@ ForwarderView::ForwarderView(QWidget* parent)
|
||||||
connect(m_ui.imageSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &ForwarderView::setActiveImage);
|
connect(m_ui.imageSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &ForwarderView::setActiveImage);
|
||||||
connect(m_ui.imageBrowse, &QAbstractButton::clicked, this, &ForwarderView::selectImage);
|
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]() {
|
connect(&m_controller, &ForwarderController::buildFailed, this, [this]() {
|
||||||
QMessageBox* error = new QMessageBox(QMessageBox::Critical, tr("Build failed"),
|
QMessageBox* error = new QMessageBox(QMessageBox::Critical, tr("Build failed"),
|
||||||
tr("Failed to build forwarder"),
|
tr("Failed to build forwarder"),
|
||||||
QMessageBox::Ok, this, Qt::Sheet);
|
QMessageBox::Ok, this, Qt::Sheet);
|
||||||
error->setAttribute(Qt::WA_DeleteOnClose);
|
error->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
error->show();
|
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<qreal>(bytesTotal);
|
||||||
|
updateProgress();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_ui.system3DS, &QAbstractButton::clicked, this, [this]() {
|
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()->setTitle(m_ui.title->text());
|
||||||
m_controller.generator()->setRom(m_ui.romFilename->text());
|
m_controller.generator()->setRom(m_ui.romFilename->text());
|
||||||
m_controller.startBuild(m_ui.outputFilename->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() {
|
void ForwarderView::validate() {
|
||||||
|
@ -80,6 +123,9 @@ void ForwarderView::validate() {
|
||||||
if (m_ui.baseType->currentIndex() != 1) {
|
if (m_ui.baseType->currentIndex() != 1) {
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
if (m_controller.inProgress()) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
|
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) {
|
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]() {
|
connect(button, &QAbstractButton::clicked, lineEdit, [this, lineEdit, save, title, filter]() {
|
||||||
QString filename;
|
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) {
|
if (save) {
|
||||||
filename = GBAApp::app()->getSaveFileName(this, title, filter);
|
filename = GBAApp::app()->getSaveFileName(this, title, usedFilter);
|
||||||
} else {
|
} else {
|
||||||
filename = GBAApp::app()->getOpenFileName(this, title, filter);
|
filename = GBAApp::app()->getOpenFileName(this, title, usedFilter);
|
||||||
}
|
}
|
||||||
if (filename.isEmpty()) {
|
if (filename.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -154,3 +208,25 @@ void ForwarderView::setActiveImage(int index) {
|
||||||
m_ui.imagePreview->setMaximumSize(m_activeSize);
|
m_ui.imagePreview->setMaximumSize(m_activeSize);
|
||||||
m_ui.imagePreview->setPixmap(QPixmap::fromImage(m_controller.generator()->image(index)));
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,12 +30,17 @@ private:
|
||||||
void connectBrowseButton(QAbstractButton* button, QLineEdit* lineEdit, const QString& title, bool save = false, const QString& filter = {});
|
void connectBrowseButton(QAbstractButton* button, QLineEdit* lineEdit, const QString& title, bool save = false, const QString& filter = {});
|
||||||
void selectImage();
|
void selectImage();
|
||||||
void setActiveImage(int);
|
void setActiveImage(int);
|
||||||
|
void updateProgress();
|
||||||
|
|
||||||
ForwarderController m_controller;
|
ForwarderController m_controller;
|
||||||
QVector<QImage> m_images;
|
QVector<QImage> m_images;
|
||||||
int m_currentImage;
|
int m_currentImage;
|
||||||
QSize m_activeSize;
|
QSize m_activeSize;
|
||||||
|
|
||||||
|
qreal m_downloadProgress;
|
||||||
|
ForwarderController::Download m_currentDownload;
|
||||||
|
bool m_needsForwarderKit;
|
||||||
|
|
||||||
Ui::ForwarderView m_ui;
|
Ui::ForwarderView m_ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,51 +14,6 @@
|
||||||
<string>Create forwarder</string>
|
<string>Create forwarder</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QGroupBox" name="groupBox">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>System</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item alignment="Qt::AlignHCenter">
|
|
||||||
<widget class="QRadioButton" name="system3DS">
|
|
||||||
<property name="text">
|
|
||||||
<string>3DS</string>
|
|
||||||
</property>
|
|
||||||
<attribute name="buttonGroup">
|
|
||||||
<string notr="true">system</string>
|
|
||||||
</attribute>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item alignment="Qt::AlignHCenter">
|
|
||||||
<widget class="QRadioButton" name="systemVita">
|
|
||||||
<property name="text">
|
|
||||||
<string>Vita</string>
|
|
||||||
</property>
|
|
||||||
<attribute name="buttonGroup">
|
|
||||||
<string notr="true">system</string>
|
|
||||||
</attribute>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0" colspan="2">
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QGroupBox" name="groupBox_3">
|
<widget class="QGroupBox" name="groupBox_3">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
@ -168,6 +123,51 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>System</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item alignment="Qt::AlignHCenter">
|
||||||
|
<widget class="QRadioButton" name="system3DS">
|
||||||
|
<property name="text">
|
||||||
|
<string>3DS</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="buttonGroup">
|
||||||
|
<string notr="true">system</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item alignment="Qt::AlignHCenter">
|
||||||
|
<widget class="QRadioButton" name="systemVita">
|
||||||
|
<property name="text">
|
||||||
|
<string>Vita</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="buttonGroup">
|
||||||
|
<string notr="true">system</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="0" column="1" rowspan="2">
|
<item row="0" column="1" rowspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
@ -399,6 +399,19 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QProgressBar" name="progressBar">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>1000</number>
|
||||||
|
</property>
|
||||||
|
<property name="textVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
|
#include <mgba/core/version.h>
|
||||||
#include <mgba/feature/updater.h>
|
#include <mgba/feature/updater.h>
|
||||||
#include <mgba-util/socket.h>
|
#include <mgba-util/socket.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
@ -81,6 +82,8 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config)
|
||||||
m_configController->updateOption("useDiscordPresence");
|
m_configController->updateOption("useDiscordPresence");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
m_netman.setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
|
|
||||||
cleanupAfterUpdate();
|
cleanupAfterUpdate();
|
||||||
|
|
||||||
connect(this, &GBAApp::aboutToQuit, this, &GBAApp::cleanup);
|
connect(this, &GBAApp::aboutToQuit, this, &GBAApp::cleanup);
|
||||||
|
@ -240,6 +243,19 @@ bool GBAApp::reloadGameDB() {
|
||||||
}
|
}
|
||||||
#endif
|
#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<void ()> job, std::function<void ()> callback) {
|
qint64 GBAApp::submitWorkerJob(std::function<void ()> job, std::function<void ()> callback) {
|
||||||
return submitWorkerJob(job, nullptr, callback);
|
return submitWorkerJob(job, nullptr, callback);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QMultiMap>
|
#include <QMultiMap>
|
||||||
#include <QObject>
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
|
@ -70,6 +71,9 @@ public:
|
||||||
const NoIntroDB* gameDB() const { return m_db; }
|
const NoIntroDB* gameDB() const { return m_db; }
|
||||||
bool reloadGameDB();
|
bool reloadGameDB();
|
||||||
|
|
||||||
|
QNetworkAccessManager* netman();
|
||||||
|
QNetworkReply* httpGet(const QUrl&);
|
||||||
|
|
||||||
qint64 submitWorkerJob(std::function<void ()> job, std::function<void ()> callback = {});
|
qint64 submitWorkerJob(std::function<void ()> job, std::function<void ()> callback = {});
|
||||||
qint64 submitWorkerJob(std::function<void ()> job, QObject* context, std::function<void ()> callback);
|
qint64 submitWorkerJob(std::function<void ()> job, QObject* context, std::function<void ()> callback);
|
||||||
bool removeWorkerJob(qint64 jobId);
|
bool removeWorkerJob(qint64 jobId);
|
||||||
|
@ -128,6 +132,8 @@ private:
|
||||||
QFont m_monospace;
|
QFont m_monospace;
|
||||||
|
|
||||||
NoIntroDB* m_db = nullptr;
|
NoIntroDB* m_db = nullptr;
|
||||||
|
|
||||||
|
QNetworkAccessManager m_netman;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue