Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2019-06-28 17:16:56 -07:00
commit 21e7d76320
30 changed files with 661 additions and 169 deletions

49
CHANGES
View File

@ -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
- 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
- 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
- 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)
- 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
- GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1274)
- 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
- GBA SIO: Prevent writing read-only multiplayer bits
- 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
- 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
- Qt: Fix quick load recent accidentally saving (fixes mgba.io/i/1309)
- Switch: Fix final cleanup (fixes mgba.io/i/1283)
- Switch: Fix gyroscope orientation (fixes mgba.io/i/1300)
Misc:
- GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1274)
- Qt: Updated Italian translation (by Vecna)
0.7.0: (2019-01-26)

View File

@ -143,8 +143,8 @@ void GBADestroy(struct GBA* gba);
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);

View File

@ -1,8 +1,8 @@
Elijah Chondropoulos
Jaime J. Denizard
Fog
Philip Horton
Oskenso Kashi
Mored1984
Rohit Nirmal
Rhys Powell
Yuri Kunde Schlesner

View File

@ -271,7 +271,17 @@ static void _log(struct mLogger* logger, int category, enum mLogLevel level, con
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) {

View File

@ -193,7 +193,7 @@ void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
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 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
}
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 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
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;

View File

@ -101,7 +101,7 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
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 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
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);
}
}

View File

@ -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 @@ void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) {
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 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
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 @@ void GBATestKeypadIRQ(struct GBA* gba) {
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);
}
}

View File

@ -578,7 +578,7 @@ void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLat
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;

View File

@ -548,16 +548,16 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t 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

View File

@ -158,7 +158,7 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
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;
}

View File

@ -37,7 +37,7 @@ int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand 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 @@ int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command
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 @@ int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command
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;
}

View File

@ -166,7 +166,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
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 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
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 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
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:

View File

@ -13,10 +13,10 @@
#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 cycl
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 cycl
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) {

View File

@ -154,7 +154,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
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 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
}
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 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
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;
}

View File

@ -160,7 +160,8 @@ int main() {
.unpaused = mPSP2Unpaused,
.incrementScreenMode = mPSP2IncrementScreenMode,
.setFrameLimiter = mPSP2SetFrameLimiter,
.pollGameInput = mPSP2PollInput
.pollGameInput = mPSP2PollInput,
.running = mPSP2SystemPoll
};
sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START);

View File

@ -25,6 +25,7 @@
#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>
@ -38,6 +39,9 @@
#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,
SM_PLAIN,
@ -539,6 +543,18 @@ void mPSP2IncrementScreenMode(struct mGUIRunner* runner) {
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) {
printf("ASSERT FAILED: %s in %s at %s:%i\n", expr, func, file, line);
exit(1);

View File

@ -25,5 +25,6 @@ void mPSP2DrawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsig
void mPSP2IncrementScreenMode(struct mGUIRunner* runner);
void mPSP2SetFrameLimiter(struct mGUIRunner* runner, bool limit);
uint16_t mPSP2PollInput(struct mGUIRunner* runner);
bool mPSP2SystemPoll(struct mGUIRunner* runner);
#endif

View File

@ -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);
}

View File

@ -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;
};
}

View File

@ -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;
}

View File

@ -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;
};
}

View File

@ -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";
}

View File

@ -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;
};
}

View File

@ -5,6 +5,7 @@
* 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 "Window.h"
#include <QtAlgorithms>
#include <QFile>
#include <QFileInfo>
#include <QFontMetrics>
#include <QMessageBox>
#include <QMultiMap>
#include <QResource>
#include <QSettings>
#include <QStringList>
@ -27,10 +27,8 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
, 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 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
#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 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
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 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
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 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
} 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 @@ BattleChipView::~BattleChipView() {
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 @@ void BattleChipView::addChip() {
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;
for (const auto& index : m_ui.chipList->selectionModel()->selectedIndexes()) {
m_model.removeChip(index);
}
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);
}
m_ui.chipName->clear();
m_ui.chipName->addItems(chipNames);
}
void BattleChipView::advanceFrameCounter() {
@ -208,14 +184,14 @@ void BattleChipView::saveDeck() {
}
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 @@ void BattleChipView::loadDeck() {
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 @@ void BattleChipView::loadDeck() {
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();
}

View File

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include "BattleChipModel.h"
#include <QDialog>
#include <memory>
@ -29,16 +31,18 @@ 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 @@ private:
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;
};
}

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>630</width>
<width>658</width>
<height>722</height>
</rect>
</property>
@ -15,7 +15,7 @@
</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 @@
</size>
</property>
<property name="movement">
<enum>QListView::Static</enum>
<enum>QListView::Free</enum>
</property>
<property name="isWrapping" stdset="0">
<bool>true</bool>
@ -52,12 +52,6 @@
<property name="viewMode">
<enum>QListView::IconMode</enum>
</property>
<property name="uniformItemSizes">
<bool>false</bool>
</property>
<property name="selectionRectVisible">
<bool>true</bool>
</property>
</widget>
</item>
<item>
@ -132,9 +126,9 @@
</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 @@
</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>
@ -260,21 +267,5 @@
</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>
</connections>
</ui>

View File

@ -67,6 +67,7 @@ endif()
set(SOURCE_FILES
AboutScreen.cpp
AbstractUpdater.cpp
AssetTile.cpp
AssetView.cpp
AudioProcessor.cpp
@ -149,6 +150,8 @@ set(UI_FILES
VideoView.ui)
set(GBA_SRC
BattleChipModel.cpp
BattleChipUpdater.cpp
BattleChipView.cpp
GBAOverride.cpp)

View File

@ -18,7 +18,6 @@
#include <QFileOpenEvent>
#include <QIcon>
#include <mgba/core/version.h>
#include <mgba-util/socket.h>
#include <mgba-util/vfs.h>
@ -42,17 +41,10 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config)
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()));
}

View File

@ -107,7 +107,6 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi
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;

View File

@ -61,8 +61,15 @@ int main(int argc, char* argv[]) {
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));
application.installTranslator(&qtTranslator);