Merge branch 'master' (early part) into medusa
jump to
@@ -27,33 +27,40 @@ Bugfixes:
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208) - GBA: Reset now reloads multiboot ROMs - GBA BIOS: Fix multiboot entry point (fixes Magic Floor) - - Switch: Fix final cleanup (fixes mgba.io/i/1283) - - Qt: Fix tile and sprite views not always displaying at first + - Qt: More app metadata fixes +Misc: + - GBA Savedata: EEPROM performance fixes + - GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash + - GB Memory: Support running from blocked memory + - Qt: Don't unload ROM immediately if it crashes + - Debugger: Add breakpoint and watchpoint listing + +0.7.1: (2019-02-24) +Bugfixes: + - 3DS: Work around menu freezing (fixes mgba.io/i/1294) + - GB: Fix crash when accessing SRAM if no save loaded and cartridge has no SRAM + - GB Serialize: Fix loading states with negative pixel x (fixes mgba.io/i/1293) + - GB, GBA Serialize: Fix loading two states in a row + - GBA: Fix video timing when skipping BIOS (fixes mgba.io/i/1318) + - GBA DMA: Fix Display Start DMAs + - GBA DMA: Fix DMA start/end timing + - GBA DMA: Fix invalid DMA handling (fixes mgba.io/i/1301) - GBA Memory: Fix a few AGBPrint crashes - GBA Memory: Fix OOB ROM reads showing up as AGBPrint memory - - GB Serialize: Fix loading states with negative pixel x (fixes mgba.io/i/1293) - - Qt: Fix audio context holding onto closed game controller - - Switch: Fix gyroscope orientation (fixes mgba.io/i/1300) - GBA SIO: Prevent writing read-only multiplayer bits - - Qt: Fix color picking in sprite view (fixes mgba.io/i/1307) - - GB: Fix crash when accessing SRAM if no save loaded and cartridge has no SRAM + - GBA Video: Fix enabling layers in non-tile modes (fixes mgba.io/i/1317) - Python: Fix crash when deleting files owned by library - Python: Make sure GB link object isn't GC'd before GB object - - GBA DMA: Fix Display Start DMAs - - GBA DMA: Fix DMA start/end timing + - PSP2: Fix file descriptors dying on suspend (fixes mgba.io/i/1123) + - Qt: Fix tile and sprite views not always displaying at first + - Qt: Fix audio context holding onto closed game controller + - Qt: Fix color picking in sprite view (fixes mgba.io/i/1307) - Qt: Fix window icon on X11 - - GB, GBA Serialize: Fix loading two states in a row - - GBA Video: Fix enabling layers in non-tile modes (fixes mgba.io/i/1317) - Qt: Fix quick load recent accidentally saving (fixes mgba.io/i/1309) - - GBA: Fix video timing when skipping BIOS (fixes mgba.io/i/1318) - - 3DS: Work around menu freezing (fixes mgba.io/i/1294) + - Switch: Fix final cleanup (fixes mgba.io/i/1283) + - Switch: Fix gyroscope orientation (fixes mgba.io/i/1300) Misc: - - GBA Savedata: EEPROM performance fixes - - GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash - - GB Memory: Support running from blocked memory - - Qt: Don't unload ROM immediately if it crashes - GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1274) - - Debugger: Add breakpoint and watchpoint listing - Qt: Updated Italian translation (by Vecna) 0.7.0: (2019-01-26)
@@ -143,8 +143,8 @@
void GBAReset(struct ARMCore* cpu); void GBASkipBIOS(struct GBA* gba); -void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq); -void GBATestIRQ(struct ARMCore* cpu); +void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq, uint32_t cyclesLate); +void GBATestIRQ(struct GBA* gba, uint32_t cyclesLate); void GBAHalt(struct GBA* gba); void GBAStop(struct GBA* gba); void GBADebug(struct GBA* gba, uint16_t value);
@@ -1,8 +1,8 @@
-Elijah Chondropoulos Jaime J. Denizard Fog Philip Horton Oskenso Kashi +Mored1984 Rohit Nirmal Rhys Powell Yuri Kunde Schlesner
@@ -271,7 +271,17 @@ size_t len = snprintf(log2, sizeof(log2) - 1, "%s: %s\n", mLogCategoryName(category), log);
if (len >= sizeof(log2)) { len = sizeof(log2) - 1; } - guiLogger->vf->write(guiLogger->vf, log2, len); + if (guiLogger->vf->write(guiLogger->vf, log2, len) < 0) { + char path[PATH_MAX]; + mCoreConfigDirectory(path, PATH_MAX); + strncat(path, PATH_SEP "log", PATH_MAX - strlen(path)); + guiLogger->vf->close(guiLogger->vf); + guiLogger->vf = VFileOpen(path, O_CREAT | O_WRONLY | O_APPEND); + if (guiLogger->vf->write(guiLogger->vf, log2, len) < 0) { + guiLogger->vf->close(guiLogger->vf); + guiLogger->vf = NULL; + } + } } void mGUIRun(struct mGUIRunner* runner, const char* path) {
@@ -193,7 +193,7 @@ if (GBADMARegisterGetDestControl(dma->reg) == GBA_DMA_INCREMENT_RELOAD) {
dma->nextDest = dma->dest; } if (GBADMARegisterIsDoIRQ(dma->reg)) { - GBARaiseIRQ(gba, IRQ_DMA0 + memory->activeDMA); + GBARaiseIRQ(gba, IRQ_DMA0 + memory->activeDMA, cyclesLate); } GBADMAUpdate(gba); }@@ -260,15 +260,13 @@ memory->dmaTransferRegister = cpu->memory.load32(cpu, source, 0);
} gba->bus = memory->dmaTransferRegister; cpu->memory.store32(cpu, dest, memory->dmaTransferRegister, 0); - memory->dmaTransferRegister &= 0xFFFF0000; - memory->dmaTransferRegister |= memory->dmaTransferRegister >> 16; } else { if (sourceRegion == REGION_CART2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) { memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata); - } else { - if (source) { - memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0); - } + memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; + } else if (source) { + memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0); + memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; } if (destRegion == REGION_CART2_EX) { if (memory->savedata.type == SAVEDATA_AUTODETECT) {@@ -282,7 +280,6 @@ } else {
cpu->memory.store16(cpu, dest, memory->dmaTransferRegister, 0); } - memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; gba->bus = memory->dmaTransferRegister; } int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
@@ -101,7 +101,7 @@ gate->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0;
gate->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0; gate->d.p->normalControl.start = 0; if (gate->d.p->normalControl.irq) { - GBARaiseIRQ(gate->d.p->p, IRQ_SIO); + GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate); } return; }@@ -193,6 +193,6 @@
gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply; if (gate->d.p->multiplayerControl.irq) { - GBARaiseIRQ(gate->d.p->p, IRQ_SIO); + GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate); } }
@@ -46,6 +46,7 @@ static void GBAProcessEvents(struct ARMCore* cpu);
static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode); static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode); static void GBABreakpoint(struct ARMCore* cpu, int immediate); +static void GBATestIRQNoDelay(struct ARMCore* cpu); static void _triggerIRQ(struct mTiming*, void* user, uint32_t cyclesLate);@@ -180,7 +181,7 @@ irqh->processEvents = GBAProcessEvents;
irqh->swi16 = GBASwi16; irqh->swi32 = GBASwi32; irqh->hitIllegal = GBAIllegal; - irqh->readCPSR = GBATestIRQ; + irqh->readCPSR = GBATestIRQNoDelay; irqh->hitStub = GBAHitStub; irqh->bkpt16 = GBABreakpoint; irqh->bkpt32 = GBABreakpoint;@@ -433,7 +434,7 @@ void GBAYankROM(struct GBA* gba) {
gba->yankedRomSize = gba->memory.romSize; gba->memory.romSize = 0; gba->memory.romMask = 0; - GBARaiseIRQ(gba, IRQ_GAMEPAK); + GBARaiseIRQ(gba, IRQ_GAMEPAK, 0); } void GBALoadBIOS(struct GBA* gba, struct VFile* vf) {@@ -486,16 +487,20 @@ gba->memory.romMask = SIZE_CART0 - 1;
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); } -void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq) { +void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq, uint32_t cyclesLate) { gba->memory.io[REG_IF >> 1] |= 1 << irq; - GBATestIRQ(gba->cpu); + GBATestIRQ(gba, cyclesLate); } -void GBATestIRQ(struct ARMCore* cpu) { +void GBATestIRQNoDelay(struct ARMCore* cpu) { struct GBA* gba = (struct GBA*) cpu->master; + GBATestIRQ(gba, 0); +} + +void GBATestIRQ(struct GBA* gba, uint32_t cyclesLate) { if (gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) { if (!mTimingIsScheduled(&gba->timing, &gba->irqEvent)) { - mTimingSchedule(&gba->timing, &gba->irqEvent, GBA_IRQ_DELAY); + mTimingSchedule(&gba->timing, &gba->irqEvent, GBA_IRQ_DELAY - cyclesLate); } } }@@ -845,9 +850,9 @@ keycnt &= 0x3FF;
uint16_t keyInput = *gba->keySource & keycnt; if (isAnd && keycnt == keyInput) { - GBARaiseIRQ(gba, IRQ_KEYPAD); + GBARaiseIRQ(gba, IRQ_KEYPAD, 0); } else if (!isAnd && keyInput) { - GBARaiseIRQ(gba, IRQ_KEYPAD); + GBARaiseIRQ(gba, IRQ_KEYPAD, 0); } }
@@ -578,7 +578,7 @@ ++gbp->p->gbpTxPosition;
gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] = tx; gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] = tx >> 16; if (gbp->d.p->normalControl.irq) { - GBARaiseIRQ(gbp->p->p, IRQ_SIO); + GBARaiseIRQ(gbp->p->p, IRQ_SIO, cyclesLate); } gbp->d.p->normalControl.start = 0; gbp->p->p->memory.io[REG_SIOCNT >> 1] = gbp->d.p->siocnt & ~0x0080;
@@ -548,16 +548,16 @@ GBAAdjustWaitstates(gba, value);
break; case REG_IE: gba->memory.io[REG_IE >> 1] = value; - GBATestIRQ(gba->cpu); + GBATestIRQ(gba->cpu, 1); return; case REG_IF: value = gba->memory.io[REG_IF >> 1] & ~value; gba->memory.io[REG_IF >> 1] = value; - GBATestIRQ(gba->cpu); + GBATestIRQ(gba->cpu, 1); return; case REG_IME: gba->memory.io[REG_IME >> 1] = value; - GBATestIRQ(gba->cpu); + GBATestIRQ(gba->cpu, 1); return; case REG_MAX: // Some bad interrupt libraries will write to this
@@ -158,7 +158,7 @@ value |= 0x0004;
if ((value & 0x0081) == 0x0081) { if (value & 0x4000) { // TODO: Test this on hardware to see if this is correct - GBARaiseIRQ(sio->p, IRQ_SIO); + GBARaiseIRQ(sio->p, IRQ_SIO, 0); } value &= ~0x0080; }
@@ -37,7 +37,7 @@ switch (command) {
case JOY_RESET: sio->p->p->memory.io[REG_JOYCNT >> 1] |= 1; if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) { - GBARaiseIRQ(sio->p->p, IRQ_SIO); + GBARaiseIRQ(sio->p->p, IRQ_SIO, 0); } // Fall through case JOY_POLL:@@ -55,7 +55,7 @@
data[0] = sio->p->p->memory.io[REG_JOYSTAT >> 1]; if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) { - GBARaiseIRQ(sio->p->p, IRQ_SIO); + GBARaiseIRQ(sio->p->p, IRQ_SIO, 0); } return 1; case JOY_TRANS:@@ -68,7 +68,7 @@ data[3] = sio->p->p->memory.io[REG_JOY_TRANS_HI >> 1] >> 8;
data[4] = sio->p->p->memory.io[REG_JOYSTAT >> 1]; if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) { - GBARaiseIRQ(sio->p->p, IRQ_SIO); + GBARaiseIRQ(sio->p->p, IRQ_SIO, 0); } return 5; }
@@ -166,7 +166,7 @@ sio->rcnt |= 1;
sio->multiplayerControl.busy = 0; sio->multiplayerControl.id = node->id; if (sio->multiplayerControl.irq) { - GBARaiseIRQ(sio->p, IRQ_SIO); + GBARaiseIRQ(sio->p, IRQ_SIO, 0); } break; case SIO_NORMAL_8:@@ -179,7 +179,7 @@ } else {
node->d.p->p->memory.io[REG_SIODATA8 >> 1] = 0xFFFF; } if (sio->multiplayerControl.irq) { - GBARaiseIRQ(sio->p, IRQ_SIO); + GBARaiseIRQ(sio->p, IRQ_SIO, 0); } break; case SIO_NORMAL_32:@@ -194,7 +194,7 @@ node->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0xFFFF;
node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0xFFFF; } if (sio->multiplayerControl.irq) { - GBARaiseIRQ(sio->p, IRQ_SIO); + GBARaiseIRQ(sio->p, IRQ_SIO, 0); } break; default:
@@ -13,10 +13,10 @@ #define TIMER_STARTUP_DELAY 2
#define REG_TMCNT_LO(X) (REG_TM0CNT_LO + ((X) << 2)) -static void GBATimerIrq(struct GBA* gba, int timerId) { +static void GBATimerIrq(struct GBA* gba, int timerId, uint32_t cyclesLate) { struct GBATimer* timer = &gba->timers[timerId]; if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER0 + timerId); + GBARaiseIRQ(gba, IRQ_TIMER0 + timerId, cyclesLate); } }@@ -56,9 +56,9 @@ static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBA* gba = context; GBATimerUpdateAudio(gba, 0, cyclesLate); GBATimerUpdate(timing, &gba->timers[0], &gba->memory.io[REG_TM0CNT_LO >> 1], cyclesLate); - GBATimerIrq(gba, 0); + GBATimerIrq(gba, 0, cyclesLate); if (GBATimerUpdateCountUp(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate)) { - GBATimerIrq(gba, 1); + GBATimerIrq(gba, 1, cyclesLate); } }@@ -66,25 +66,25 @@ static void GBATimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBA* gba = context; GBATimerUpdateAudio(gba, 1, cyclesLate); GBATimerUpdate(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate); - GBATimerIrq(gba, 1); + GBATimerIrq(gba, 1, cyclesLate); if (GBATimerUpdateCountUp(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate)) { - GBATimerIrq(gba, 2); + GBATimerIrq(gba, 2, cyclesLate); } } static void GBATimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBA* gba = context; GBATimerUpdate(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate); - GBATimerIrq(gba, 2); + GBATimerIrq(gba, 2, cyclesLate); if (GBATimerUpdateCountUp(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate)) { - GBATimerIrq(gba, 3); + GBATimerIrq(gba, 3, cyclesLate); } } static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBA* gba = context; GBATimerUpdate(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate); - GBATimerIrq(gba, 3); + GBATimerIrq(gba, 3, cyclesLate); } void GBATimerInit(struct GBA* gba) {
@@ -154,7 +154,7 @@
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) { dispstat = GBARegisterDISPSTATFillVcounter(dispstat); if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) { - GBARaiseIRQ(video->p, IRQ_VCOUNTER); + GBARaiseIRQ(video->p, IRQ_VCOUNTER, cyclesLate); } } else { dispstat = GBARegisterDISPSTATClearVcounter(dispstat);@@ -173,7 +173,7 @@ video->renderer->finishFrame(video->renderer);
} GBADMARunVblank(video->p, -cyclesLate); if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) { - GBARaiseIRQ(video->p, IRQ_VBLANK); + GBARaiseIRQ(video->p, IRQ_VBLANK, cyclesLate); } GBAFrameEnded(video->p); mCoreSyncPostFrame(video->p->sync);@@ -209,7 +209,7 @@ if (video->vcount >= 2 && video->vcount < VIDEO_VERTICAL_PIXELS + 2) {
GBADMARunDisplayStart(video->p, -cyclesLate); } if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) { - GBARaiseIRQ(video->p, IRQ_HBLANK); + GBARaiseIRQ(video->p, IRQ_HBLANK, cyclesLate); } video->p->memory.io[REG_DISPSTAT >> 1] = dispstat; }
@@ -160,7 +160,8 @@ .paused = mPSP2Paused,
.unpaused = mPSP2Unpaused, .incrementScreenMode = mPSP2IncrementScreenMode, .setFrameLimiter = mPSP2SetFrameLimiter, - .pollGameInput = mPSP2PollInput + .pollGameInput = mPSP2PollInput, + .running = mPSP2SystemPoll }; sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START);
@@ -25,6 +25,7 @@ #include <mgba-util/threading.h>
#include <mgba-util/vfs.h> #include <mgba-util/platform/psp2/sce-vfs.h> +#include <psp2/appmgr.h> #include <psp2/audioout.h> #include <psp2/camera.h> #include <psp2/ctrl.h>@@ -37,6 +38,9 @@ #include <vita2d.h>
#define RUMBLE_PWM 8 #define CDRAM_ALIGN 0x40000 + +mLOG_DECLARE_CATEGORY(GUI_PSP2); +mLOG_DEFINE_CATEGORY(GUI_PSP2, "Vita", "gui.psp2"); static enum ScreenMode { SM_BACKDROP,@@ -537,6 +541,18 @@
void mPSP2IncrementScreenMode(struct mGUIRunner* runner) { screenMode = (screenMode + 1) % SM_MAX; mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode); +} + +bool mPSP2SystemPoll(struct mGUIRunner* runner) { + SceAppMgrSystemEvent event; + if (sceAppMgrReceiveSystemEvent(&event) < 0) { + return true; + } + if (event.systemEvent == SCE_APPMGR_SYSTEMEVENT_ON_RESUME) { + mLOG(GUI_PSP2, INFO, "Suspend detected, reloading save"); + mCoreAutoloadSave(runner->core); + } + return true; } __attribute__((noreturn, weak)) void __assert_func(const char* file, int line, const char* func, const char* expr) {
@@ -25,5 +25,6 @@ void mPSP2DrawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded);
void mPSP2IncrementScreenMode(struct mGUIRunner* runner); void mPSP2SetFrameLimiter(struct mGUIRunner* runner, bool limit); uint16_t mPSP2PollInput(struct mGUIRunner* runner); +bool mPSP2SystemPoll(struct mGUIRunner* runner); #endif
@@ -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; +}; + +}
@@ -0,0 +1,186 @@
+/* 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 "BattleChipModel.h" + +#include "ConfigController.h" +#include "GBAApp.h" + +#include <QFile> +#include <QMimeData> +#include <QResource> + +using namespace QGBA; + +BattleChipModel::BattleChipModel(QObject* parent) + : QAbstractListModel(parent) +{ + QResource::registerResource(GBAApp::dataDir() + "/chips.rcc", "/exe"); + QResource::registerResource(ConfigController::configDir() + "/chips.rcc", "/exe"); +} + +int BattleChipModel::rowCount(const QModelIndex& parent) const { + if (parent.isValid()) { + return 0; + } + return m_deck.count(); +} + +QVariant BattleChipModel::data(const QModelIndex& index, int role) const { + const BattleChip& item = m_deck[index.row()]; + + switch (role) { + case Qt::DisplayRole: + return item.name; + case Qt::DecorationRole: + return item.icon.scaled(item.icon.size() * m_scale); + case Qt::UserRole: + return item.id; + } + return QVariant(); +} + +Qt::ItemFlags BattleChipModel::flags(const QModelIndex& index) const { + return Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; +} + +bool BattleChipModel::removeRows(int row, int count, const QModelIndex& parent) { + if (parent.isValid()) { + return false; + } + beginRemoveRows(QModelIndex(), row, row + count - 1); + for (size_t i = 0; i < count; ++i) { + m_deck.removeAt(row); + } + endRemoveRows(); + return true; +} + +QStringList BattleChipModel::mimeTypes() const { + return {"text/plain"}; +} + +Qt::DropActions BattleChipModel::supportedDropActions() const { + return Qt::MoveAction; +} + +QMimeData* BattleChipModel::mimeData(const QModelIndexList& indices) const { + QStringList deck; + for (const QModelIndex& index : indices) { + if (index.parent().isValid()) { + continue; + } + deck.append(QString::number(m_deck[index.row()].id)); + } + + QMimeData* mimeData = new QMimeData(); + mimeData->setData("text/plain", deck.join(',').toLocal8Bit()); + return mimeData; +} + +bool BattleChipModel::dropMimeData(const QMimeData* data, Qt::DropAction, int row, int, const QModelIndex& parent) { + if (parent.parent().isValid()) { + return false; + } + QStringList deck = QString::fromLocal8Bit(data->data("text/plain")).split(','); + if (deck.isEmpty()) { + return true; + } + + row = parent.row(); + beginInsertRows(QModelIndex(), row, row + deck.count() - 1); + for (int i = 0; i < deck.count(); ++i) { + int id = deck[i].toInt(); + m_deck.insert(row + i, createChip(id)); + } + endInsertRows(); + return true; +} + +void BattleChipModel::setFlavor(int flavor) { + m_chipIdToName.clear(); + if (flavor == GBA_FLAVOR_BEAST_LINK_GATE_US) { + flavor = GBA_FLAVOR_BEAST_LINK_GATE; + } + m_flavor = flavor; + + QFile file(QString(":/exe/exe%1/chip-names.txt").arg(flavor)); + file.open(QIODevice::ReadOnly | QIODevice::Text); + int id = 0; + while (true) { + QByteArray line = file.readLine(); + if (line.isEmpty()) { + break; + } + ++id; + if (line.trimmed().isEmpty()) { + continue; + } + QString name = QString::fromUtf8(line).trimmed(); + m_chipIdToName[id] = name; + } + +} + +void BattleChipModel::addChip(int id) { + beginInsertRows(QModelIndex(), m_deck.count(), m_deck.count()); + m_deck.append(createChip(id)); + endInsertRows(); +} + +void BattleChipModel::removeChip(const QModelIndex& index) { + beginRemoveRows(QModelIndex(), index.row(), index.row()); + m_deck.removeAt(index.row()); + endRemoveRows(); +} + +void BattleChipModel::setChips(QList<int> ids) { + beginResetModel(); + m_deck.clear(); + for (int id : ids) { + m_deck.append(createChip(id)); + } + endResetModel(); +} + +void BattleChipModel::clear() { + beginResetModel(); + m_deck.clear(); + endResetModel(); +} + +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()) { + path = QString(":/exe/exe%1/placeholder.png").arg(m_flavor); + } + QPixmap icon(path); + + BattleChip chip = { + id, + m_chipIdToName[id], + icon + }; + return chip; +}
@@ -0,0 +1,58 @@
+/* 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 <QAbstractListModel> +#include <QPixmap> + +namespace QGBA { + +class BattleChipUpdater; + +class BattleChipModel : public QAbstractListModel { +Q_OBJECT + +public: + BattleChipModel(QObject* parent = nullptr); + + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; + virtual Qt::DropActions supportedDropActions() const override; + virtual QStringList mimeTypes() const override; + virtual QMimeData* mimeData(const QModelIndexList& indices) const override; + virtual bool dropMimeData(const QMimeData* data, Qt::DropAction, int row, int column, const QModelIndex& parent) override; + + int flavor() const { return m_flavor; } + QMap<int, QString> chipNames() const { return m_chipIdToName; } + +public slots: + void setFlavor(int); + void addChip(int id); + void removeChip(const QModelIndex&); + void setChips(QList<int> ids); + void clear(); + void setScale(int); + void reloadAssets(); + +private: + struct BattleChip { + int id; + QString name; + QPixmap icon; + }; + + BattleChip createChip(int id) const; + + QMap<int, QString> m_chipIdToName; + int m_flavor; + int m_scale = 1; + + QList<BattleChip> m_deck; +}; + +}
@@ -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,6 +5,7 @@ * 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 "BattleChipView.h" +#include "BattleChipUpdater.h" #include "ConfigController.h" #include "CoreController.h" #include "GBAApp.h"@@ -12,11 +13,10 @@ #include "ShortcutController.h"
#include "Window.h" #include <QtAlgorithms> -#include <QFile> +#include <QFileInfo> #include <QFontMetrics> #include <QMessageBox> #include <QMultiMap> -#include <QResource> #include <QSettings> #include <QStringList>@@ -27,10 +27,8 @@ : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
, m_controller(controller) , m_window(window) { - QResource::registerResource(GBAApp::dataDir() + "/chips.rcc", "/exe"); - QResource::registerResource(ConfigController::configDir() + "/chips.rcc", "/exe"); - m_ui.setupUi(this); + m_ui.chipList->setModel(&m_model); char title[9]; CoreController::Interrupter interrupter(m_controller);@@ -46,12 +44,16 @@ int size = QFontMetrics(QFont()).height() / (devicePixelRatio() * 12);
#endif m_ui.chipList->setIconSize(m_ui.chipList->iconSize() * size); m_ui.chipList->setGridSize(m_ui.chipList->gridSize() * size); + m_model.setScale(size); connect(m_ui.chipId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_ui.inserted, [this]() { m_ui.inserted->setChecked(Qt::Unchecked); }); connect(m_ui.chipName, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), m_ui.chipId, [this](int id) { - m_ui.chipId->setValue(m_chipIndexToId[id]); + if (id < 0) { + return; + } + m_ui.chipId->setValue(m_model.chipNames().keys()[id]); }); connect(m_ui.inserted, &QAbstractButton::toggled, this, &BattleChipView::insertChip);@@ -61,7 +63,8 @@ connect(m_ui.remove, &QAbstractButton::clicked, this, &BattleChipView::removeChip);
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.buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, m_ui.chipList, &QListWidget::clear); + 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) { if (on) {@@ -85,13 +88,14 @@ });
connect(m_controller.get(), &CoreController::frameAvailable, this, &BattleChipView::advanceFrameCounter); - connect(m_ui.chipList, &QListWidget::itemClicked, this, [this](QListWidgetItem* item) { - QVariant chip = item->data(Qt::UserRole); + connect(m_ui.chipList, &QAbstractItemView::clicked, this, [this](const QModelIndex& index) { + QVariant chip = m_model.data(index, Qt::UserRole); bool blocked = m_ui.chipId->blockSignals(true); m_ui.chipId->setValue(chip.toInt()); m_ui.chipId->blockSignals(blocked); reinsert(); }); + connect(m_ui.chipList, &QListView::indexesMoved, this, &BattleChipView::resort); m_controller->attachBattleChipGate(); setFlavor(4);@@ -102,6 +106,18 @@ m_ui.gateProgress->setChecked(Qt::Checked);
} 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() {@@ -110,7 +126,9 @@ }
void BattleChipView::setFlavor(int flavor) { m_controller->setBattleChipFlavor(flavor); - loadChipNames(flavor); + m_model.setFlavor(flavor); + m_ui.chipName->clear(); + m_ui.chipName->addItems(m_model.chipNames().values()); } void BattleChipView::insertChip(bool inserted) {@@ -141,55 +159,13 @@ int insertedChip = m_ui.chipId->value();
if (insertedChip < 1) { return; } - addChipId(insertedChip); -} - -void BattleChipView::addChipId(int id) { - QListWidgetItem* add = new QListWidgetItem(m_chipIdToName[id]); - add->setData(Qt::UserRole, id); - QString path = QString(":/exe/exe%1/%2.png").arg(m_flavor).arg(id, 3, 10, QLatin1Char('0')); - if (!QFile(path).exists()) { - path = QString(":/exe/exe%1/placeholder.png").arg(m_flavor); - } - add->setIcon(QPixmap(path).scaled(m_ui.chipList->iconSize())); - m_ui.chipList->addItem(add); + m_model.addChip(insertedChip); } void BattleChipView::removeChip() { - qDeleteAll(m_ui.chipList->selectedItems()); -} - -void BattleChipView::loadChipNames(int flavor) { - QStringList chipNames; - chipNames.append(tr("(None)")); - - m_chipIndexToId.clear(); - m_chipIdToName.clear(); - if (flavor == GBA_FLAVOR_BEAST_LINK_GATE_US) { - flavor = GBA_FLAVOR_BEAST_LINK_GATE; - } - m_flavor = flavor; - - QFile file(QString(":/exe/exe%1/chip-names.txt").arg(flavor)); - file.open(QIODevice::ReadOnly | QIODevice::Text); - int id = 0; - while (true) { - QByteArray line = file.readLine(); - if (line.isEmpty()) { - break; - } - ++id; - if (line.trimmed().isEmpty()) { - continue; - } - QString name = QString::fromUtf8(line).trimmed(); - m_chipIndexToId[chipNames.length()] = id; - m_chipIdToName[id] = name; - chipNames.append(name); + for (const auto& index : m_ui.chipList->selectionModel()->selectedIndexes()) { + m_model.removeChip(index); } - - m_ui.chipName->clear(); - m_ui.chipName->addItems(chipNames); } void BattleChipView::advanceFrameCounter() {@@ -208,14 +184,14 @@ return;
} QStringList deck; - for (int i = 0; i < m_ui.chipList->count(); ++i) { - deck.append(m_ui.chipList->item(i)->data(Qt::UserRole).toString()); + for (int i = 0; i < m_model.rowCount(); ++i) { + deck.append(m_model.data(m_model.index(i, 0), Qt::UserRole).toString()); } QSettings ini(filename, QSettings::IniFormat); ini.clear(); ini.beginGroup("BattleChipDeck"); - ini.setValue("version", m_flavor); + ini.setValue("version", m_model.flavor()); ini.setValue("deck", deck.join(',')); ini.sync(); }@@ -229,7 +205,7 @@
QSettings ini(filename, QSettings::IniFormat); ini.beginGroup("BattleChipDeck"); int flavor = ini.value("version").toInt(); - if (flavor != m_flavor) { + if (flavor != m_model.flavor()) { QMessageBox* error = new QMessageBox(this); error->setIcon(QMessageBox::Warning); error->setStandardButtons(QMessageBox::Ok);@@ -240,13 +216,47 @@ error->show();
return; } - m_ui.chipList->clear(); + QList<int> newDeck; QStringList deck = ini.value("deck").toString().split(','); for (const auto& item : deck) { bool ok; int id = item.toInt(&ok); if (ok) { - addChipId(id); + newDeck.append(id); } } + m_model.setChips(newDeck); } + +void BattleChipView::resort() { + QMap<int, int> chips; + for (int i = 0; i < m_model.rowCount(); ++i) { + QModelIndex index = m_model.index(i, 0); + QRect visualRect = m_ui.chipList->visualRect(index); + QSize gridSize = m_ui.chipList->gridSize(); + int x = visualRect.y() / gridSize.height(); + x *= m_ui.chipList->viewport()->width(); + x += visualRect.x(); + x *= m_model.rowCount(); + x += index.row(); + 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(); +}
@@ -5,6 +5,8 @@ * 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 "BattleChipModel.h" + #include <QDialog> #include <memory>@@ -29,15 +31,17 @@ public slots:
void setFlavor(int); void insertChip(bool); void reinsert(); + void resort(); private slots: void advanceFrameCounter(); void addChip(); - void addChipId(int); void removeChip(); void saveDeck(); void loadDeck(); + + void updateData(); private: static const int UNINSERTED_TIME = 10;@@ -46,15 +50,15 @@ void loadChipNames(int);
Ui::BattleChipView m_ui; - QMap<int, int> m_chipIndexToId; - QMap<int, QString> m_chipIdToName; + BattleChipModel m_model; std::shared_ptr<CoreController> m_controller; - int m_flavor; int m_frameCounter = -1; bool m_next = false; Window* m_window; + + BattleChipUpdater* m_updater = nullptr; }; }
@@ -6,7 +6,7 @@ <property name="geometry">
<rect> <x>0</x> <y>0</y> - <width>630</width> + <width>658</width> <height>722</height> </rect> </property>@@ -15,7 +15,7 @@ <string>BattleChip Gate</string>
</property> <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0,0,0,0"> <item> - <widget class="QListWidget" name="chipList"> + <widget class="QListView" name="chipList"> <property name="acceptDrops"> <bool>true</bool> </property>@@ -35,7 +35,7 @@ <height>48</height>
</size> </property> <property name="movement"> - <enum>QListView::Static</enum> + <enum>QListView::Free</enum> </property> <property name="isWrapping" stdset="0"> <bool>true</bool>@@ -51,12 +51,6 @@ </size>
</property> <property name="viewMode"> <enum>QListView::IconMode</enum> - </property> - <property name="uniformItemSizes"> - <bool>false</bool> - </property> - <property name="selectionRectVisible"> - <bool>true</bool> </property> </widget> </item>@@ -132,9 +126,9 @@ </layout>
</item> <item> <widget class="QWidget" name="advanced" native="true"> - <property name="visible"> - <bool>false</bool> - </property> + <property name="visible"> + <bool>false</bool> + </property> <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> <layout class="QFormLayout" name="formLayout_2">@@ -201,6 +195,19 @@ <string>Chip ID</string>
</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>@@ -257,22 +264,6 @@ </hint>
<hint type="destinationlabel"> <x>396</x> <y>654</y> - </hint> - </hints> - </connection> - <connection> - <sender>chipList</sender> - <signal>indexesMoved(QModelIndexList)</signal> - <receiver>chipList</receiver> - <slot>doItemsLayout()</slot> - <hints> - <hint type="sourcelabel"> - <x>314</x> - <y>203</y> - </hint> - <hint type="destinationlabel"> - <x>314</x> - <y>203</y> </hint> </hints> </connection>
@@ -67,6 +67,7 @@ endif()
set(SOURCE_FILES AboutScreen.cpp + AbstractUpdater.cpp AssetTile.cpp AssetView.cpp AudioProcessor.cpp@@ -149,6 +150,8 @@ TileView.ui
VideoView.ui) set(GBA_SRC + BattleChipModel.cpp + BattleChipUpdater.cpp BattleChipView.cpp GBAOverride.cpp)
@@ -18,7 +18,6 @@ #include <QFileInfo>
#include <QFileOpenEvent> #include <QIcon> -#include <mgba/core/version.h> #include <mgba-util/socket.h> #include <mgba-util/vfs.h>@@ -42,16 +41,9 @@ #ifdef BUILD_SDL
SDL_Init(SDL_INIT_NOPARACHUTE); #endif -#ifndef Q_OS_MAC - setWindowIcon(QIcon(":/res/mgba-512.png")); -#endif - SocketSubsystemInit(); qRegisterMetaType<const uint32_t*>("const uint32_t*"); qRegisterMetaType<mCoreThread*>("mCoreThread*"); - - QApplication::setApplicationName(projectName); - QApplication::setApplicationVersion(projectVersion); if (!m_configController->getQtOption("displayDriver").isNull()) { Display::setDriver(static_cast<Display::Driver>(m_configController->getQtOption("displayDriver").toInt()));
@@ -107,7 +107,6 @@ updateTitle();
m_logo.setDevicePixelRatio(m_screenWidget->devicePixelRatio()); m_logo = m_logo; // Free memory left over in old pixmap - setWindowIcon(m_logo); #if defined(M_CORE_GBA) float i = 2;
@@ -61,7 +61,14 @@ usage(argv[0], subparser.usage);
return 0; } + QApplication::setApplicationName(projectName); + QApplication::setApplicationVersion(projectVersion); + GBAApp application(argc, argv, &configController); + +#ifndef Q_OS_MAC + QApplication::setWindowIcon(QIcon(":/res/mgba-1024.png")); +#endif QTranslator qtTranslator; qtTranslator.load(locale, "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath));