diff --git a/CHANGES b/CHANGES index 7350425e4..f6f468d83 100644 --- a/CHANGES +++ b/CHANGES @@ -42,6 +42,7 @@ Emulation fixes: - GB Timer: Fix timing adjustments when writing to TAC (fixes mgba.io/i/1340) - GBA Memory: Fix writing to OBJ memory in modes 3 and 5 - GBA: Fix RTC on non-standard sized ROMs (fixes mgba.io/i/1400) + - GBA Memory: Prevent writing to mirrored BG VRAM (fixes mgba.io/i/743) Other fixes: - Qt: More app metadata fixes - Qt: Fix load recent from archive (fixes mgba.io/i/1325) @@ -57,6 +58,8 @@ Other fixes: - Qt: Fix adjusting magnification in tile viewer when not fitting to window - FFmpeg: Improve initialization reliability and cleanup - Wii: Fix aspect ratio (fixes mgba.io/i/500) + - Qt: Fix some Qt display driver race conditions + - FFmpeg: Fix audio conversion producing gaps Misc: - GBA Savedata: EEPROM performance fixes - GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash @@ -74,6 +77,8 @@ Misc: - Debugger: Make tracing compatible with breakpoints/watchpoints - Debugger: Print breakpoint/watchpoint number when inserting - Qt: Open a message box for Qt frontend errors + - GBA Video: Clean up dead code in sprite rendering loop + - FFmpeg: Support audio-only recording 0.7.1: (2019-02-24) Bugfixes: diff --git a/cinema/gb/window/kdt-battle/baseline_0000.png b/cinema/gb/window/kdt-battle/baseline_0000.png new file mode 100644 index 000000000..2ba1aa28b Binary files /dev/null and b/cinema/gb/window/kdt-battle/baseline_0000.png differ diff --git a/cinema/gb/window/kdt-battle/baseline_0001.png b/cinema/gb/window/kdt-battle/baseline_0001.png new file mode 100644 index 000000000..2ba1aa28b Binary files /dev/null and b/cinema/gb/window/kdt-battle/baseline_0001.png differ diff --git a/cinema/gb/window/kdt-battle/baseline_0002.png b/cinema/gb/window/kdt-battle/baseline_0002.png new file mode 100644 index 000000000..2ba1aa28b Binary files /dev/null and b/cinema/gb/window/kdt-battle/baseline_0002.png differ diff --git a/cinema/gb/window/kdt-battle/baseline_0003.png b/cinema/gb/window/kdt-battle/baseline_0003.png new file mode 100644 index 000000000..2ba1aa28b Binary files /dev/null and b/cinema/gb/window/kdt-battle/baseline_0003.png differ diff --git a/cinema/gb/window/kdt-battle/baseline_0004.png b/cinema/gb/window/kdt-battle/baseline_0004.png new file mode 100644 index 000000000..2ba1aa28b Binary files /dev/null and b/cinema/gb/window/kdt-battle/baseline_0004.png differ diff --git a/cinema/gb/window/kdt-battle/baseline_0005.png b/cinema/gb/window/kdt-battle/baseline_0005.png new file mode 100644 index 000000000..2ba1aa28b Binary files /dev/null and b/cinema/gb/window/kdt-battle/baseline_0005.png differ diff --git a/cinema/gb/window/kdt-battle/baseline_0006.png b/cinema/gb/window/kdt-battle/baseline_0006.png new file mode 100644 index 000000000..2ba1aa28b Binary files /dev/null and b/cinema/gb/window/kdt-battle/baseline_0006.png differ diff --git a/cinema/gb/window/kdt-battle/baseline_0007.png b/cinema/gb/window/kdt-battle/baseline_0007.png new file mode 100644 index 000000000..2491849a8 Binary files /dev/null and b/cinema/gb/window/kdt-battle/baseline_0007.png differ diff --git a/cinema/gb/window/kdt-battle/baseline_0008.png b/cinema/gb/window/kdt-battle/baseline_0008.png new file mode 100644 index 000000000..2491849a8 Binary files /dev/null and b/cinema/gb/window/kdt-battle/baseline_0008.png differ diff --git a/cinema/gb/window/kdt-battle/baseline_0009.png b/cinema/gb/window/kdt-battle/baseline_0009.png new file mode 100644 index 000000000..2491849a8 Binary files /dev/null and b/cinema/gb/window/kdt-battle/baseline_0009.png differ diff --git a/cinema/gb/window/kdt-battle/test.mvl b/cinema/gb/window/kdt-battle/test.mvl new file mode 100644 index 000000000..f665f969c Binary files /dev/null and b/cinema/gb/window/kdt-battle/test.mvl differ diff --git a/include/mgba-util/common.h b/include/mgba-util/common.h index 7f120858a..1047b5aa3 100644 --- a/include/mgba-util/common.h +++ b/include/mgba-util/common.h @@ -81,17 +81,33 @@ typedef intptr_t ssize_t; #define ATOMIC_STORE(DST, SRC) __atomic_store_n(&DST, SRC, __ATOMIC_RELEASE) #define ATOMIC_LOAD(DST, SRC) DST = __atomic_load_n(&SRC, __ATOMIC_ACQUIRE) #define ATOMIC_ADD(DST, OP) __atomic_add_fetch(&DST, OP, __ATOMIC_RELEASE) +#define ATOMIC_SUB(DST, OP) __atomic_sub_fetch(&DST, OP, __ATOMIC_RELEASE) #define ATOMIC_OR(DST, OP) __atomic_or_fetch(&DST, OP, __ATOMIC_RELEASE) #define ATOMIC_AND(DST, OP) __atomic_and_fetch(&DST, OP, __ATOMIC_RELEASE) #define ATOMIC_CMPXCHG(DST, EXPECTED, SRC) __atomic_compare_exchange_n(&DST, &EXPECTED, SRC, true,__ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) +#define ATOMIC_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC) +#define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC) +#elif defined _MSC_VER +#define ATOMIC_STORE(DST, SRC) InterlockedExchange(&DST, SRC) +#define ATOMIC_LOAD(DST, SRC) DST = InterlockedOrAcquire(&SRC, 0) +#define ATOMIC_ADD(DST, OP) InterlockedAddRelease(&DST, OP) +#define ATOMIC_SUB(DST, OP) InterlockedAddRelease(&DST, -OP) +#define ATOMIC_OR(DST, OP) InterlockedOrRelease(&DST, OP) +#define ATOMIC_AND(DST, OP) InterlockedAndRelease(&DST, OP) +#define ATOMIC_CMPXCHG(DST, EXPECTED, SRC) (InterlockedCompareExchange(&DST, SRC, EXPECTED) == EXPECTED) +#define ATOMIC_STORE_PTR(DST, SRC) InterlockedExchangePointer(DST, SRC) +#define ATOMIC_LOAD_PTR(DST, SRC) DST = InterlockedCompareExchangePointer(SRC, 0, 0) #else // TODO #define ATOMIC_STORE(DST, SRC) DST = SRC #define ATOMIC_LOAD(DST, SRC) DST = SRC #define ATOMIC_ADD(DST, OP) DST += OP +#define ATOMIC_SUB(DST, OP) DST -= OP #define ATOMIC_OR(DST, OP) DST |= OP #define ATOMIC_AND(DST, OP) DST &= OP #define ATOMIC_CMPXCHG(DST, EXPECTED, OP) ((DST == EXPECTED) ? ((DST = OP), true) : false) +#define ATOMIC_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC) +#define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC) #endif #if defined(_3DS) || defined(GEKKO) || defined(PSP2) diff --git a/include/mgba/core/lockstep.h b/include/mgba/core/lockstep.h index 06c1bc6b6..ac6cb3f84 100644 --- a/include/mgba/core/lockstep.h +++ b/include/mgba/core/lockstep.h @@ -23,10 +23,14 @@ struct mLockstep { enum mLockstepPhase transferActive; int32_t transferCycles; + void (*lock)(struct mLockstep*); + void (*unlock)(struct mLockstep*); + bool (*signal)(struct mLockstep*, unsigned mask); bool (*wait)(struct mLockstep*, unsigned mask); void (*addCycles)(struct mLockstep*, int id, int32_t cycles); int32_t (*useCycles)(struct mLockstep*, int id, int32_t cycles); + int32_t (*unusedCycles)(struct mLockstep*, int id); void (*unload)(struct mLockstep*, int id); void* context; #ifndef NDEBUG @@ -35,6 +39,19 @@ struct mLockstep { }; void mLockstepInit(struct mLockstep*); +void mLockstepDeinit(struct mLockstep*); + +static inline void mLockstepLock(struct mLockstep* lockstep) { + if (lockstep->lock) { + lockstep->lock(lockstep); + } +} + +static inline void mLockstepUnlock(struct mLockstep* lockstep) { + if (lockstep->unlock) { + lockstep->unlock(lockstep); + } +} CXX_GUARD_END diff --git a/include/mgba/internal/arm/arm.h b/include/mgba/internal/arm/arm.h index b19d73e81..6ee342eb9 100644 --- a/include/mgba/internal/arm/arm.h +++ b/include/mgba/internal/arm/arm.h @@ -126,7 +126,7 @@ struct ARMMemory { uint32_t (*storeMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, int* cycleCounter); - uint32_t* activeRegion; + const uint32_t* activeRegion; uint32_t activeMask; uint32_t activeSeqCycles32; uint32_t activeSeqCycles16; diff --git a/include/mgba/internal/lr35902/lr35902.h b/include/mgba/internal/lr35902/lr35902.h index c5bfd1381..4f2a29c71 100644 --- a/include/mgba/internal/lr35902/lr35902.h +++ b/include/mgba/internal/lr35902/lr35902.h @@ -56,7 +56,7 @@ struct LR35902Memory { int (*currentSegment)(struct LR35902Core*, uint16_t address); - uint8_t* activeRegion; + const uint8_t* activeRegion; uint16_t activeMask; uint16_t activeRegionEnd; void (*setActiveRegion)(struct LR35902Core*, uint16_t address); diff --git a/src/core/cheats.c b/src/core/cheats.c index 0abcbd537..464ebfc39 100644 --- a/src/core/cheats.c +++ b/src/core/cheats.c @@ -230,7 +230,7 @@ bool mCheatSaveFile(struct mCheatDevice* device, struct VFile* vf) { char directive[64]; ssize_t len = snprintf(directive, sizeof(directive) - 1, "!%s\n", *StringListGetPointer(&directives, d)); if (len > 1) { - vf->write(vf, directive, (size_t) len > sizeof(directive) ? sizeof(directive) : len); + vf->write(vf, directive, (size_t) len > sizeof(directive) ? sizeof(directive) : (size_t) len); } } diff --git a/src/core/core.c b/src/core/core.c index baaed50f2..11f9844cb 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -230,13 +230,13 @@ bool mCoreLoadState(struct mCore* core, int slot, int flags) { } struct VFile* mCoreGetState(struct mCore* core, int slot, bool write) { - char name[PATH_MAX]; + char name[PATH_MAX + 14]; // Quash warning snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot); return core->dirs.state->openFile(core->dirs.state, name, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY); } void mCoreDeleteState(struct mCore* core, int slot) { - char name[PATH_MAX]; + char name[PATH_MAX + 14]; // Quash warning snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot); core->dirs.state->deleteFile(core->dirs.state, name); } diff --git a/src/core/library.c b/src/core/library.c index 632496e99..60e16592f 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -341,7 +341,7 @@ static void _mLibraryDeleteEntry(struct mLibrary* library, struct mLibraryEntry* } void mLibraryClear(struct mLibrary* library) { - int result = sqlite3_exec(library->db, + sqlite3_exec(library->db, " BEGIN TRANSACTION;" "\n DELETE FROM roots;" "\n DELETE FROM roms;" diff --git a/src/core/lockstep.c b/src/core/lockstep.c index 40b1f1e2e..587cff2b5 100644 --- a/src/core/lockstep.c +++ b/src/core/lockstep.c @@ -11,6 +11,12 @@ void mLockstepInit(struct mLockstep* lockstep) { #ifndef NDEBUG lockstep->transferId = 0; #endif + lockstep->lock = NULL; + lockstep->unlock = NULL; +} + +void mLockstepDeinit(struct mLockstep* lockstep) { + UNUSED(lockstep); } // TODO: Migrate nodes diff --git a/src/core/timing.c b/src/core/timing.c index 7059659ea..88bcf2743 100644 --- a/src/core/timing.c +++ b/src/core/timing.c @@ -14,6 +14,7 @@ void mTimingInit(struct mTiming* timing, int32_t* relativeCycles, int32_t* nextE } void mTimingDeinit(struct mTiming* timing) { + UNUSED(timing); } void mTimingClear(struct mTiming* timing) { diff --git a/src/feature/sqlite3/no-intro.c b/src/feature/sqlite3/no-intro.c index d2b9e23a5..7a1b60091 100644 --- a/src/feature/sqlite3/no-intro.c +++ b/src/feature/sqlite3/no-intro.c @@ -263,6 +263,8 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) { free((void*) dbType); free((void*) dbVersion); + sqlite3_finalize(gamedbTable); + sqlite3_finalize(gamedbDrop); sqlite3_finalize(gameTable); sqlite3_finalize(romTable); @@ -275,6 +277,7 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) { } void NoIntroDBDestroy(struct NoIntroDB* db) { + sqlite3_finalize(db->crc32); sqlite3_close(db->db); free(db); } diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 4960b395c..93b2d3085 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -508,7 +508,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && wy == y && wx <= endX) { softwareRenderer->hasWindow = true; } - if (softwareRenderer->hasWindow && wx <= endX) { + if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->hasWindow && wx <= endX) { if (wx > 0 && !softwareRenderer->d.disableBG) { GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, wx, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy); } diff --git a/src/gb/sio/lockstep.c b/src/gb/sio/lockstep.c index c60978ffe..b1d925ebc 100644 --- a/src/gb/sio/lockstep.c +++ b/src/gb/sio/lockstep.c @@ -17,7 +17,6 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate); void GBSIOLockstepInit(struct GBSIOLockstep* lockstep) { - mLockstepInit(&lockstep->d); lockstep->players[0] = NULL; lockstep->players[1] = NULL; lockstep->pendingSB[0] = 0xFF; @@ -236,6 +235,8 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu mTimingDeschedule(&driver->p->p->timing, &driver->p->event); mTimingDeschedule(&driver->p->p->timing, &node->event); mTimingSchedule(&driver->p->p->timing, &node->event, 0); + } else { + mLOG(GB_SIO, FATAL, "GBSIOLockstepNodeWriteSC() failed to write to masterClaimed\n"); } } return value; diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c index b76247cd0..fb411eef5 100644 --- a/src/gba/extra/battlechip.c +++ b/src/gba/extra/battlechip.c @@ -94,6 +94,7 @@ void _battlechipTransfer(struct GBASIOBattlechipGate* gate) { } void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) { + UNUSED(timing); struct GBASIOBattlechipGate* gate = user; if (gate->d.p->mode == SIO_NORMAL_32) { diff --git a/src/gba/memory.c b/src/gba/memory.c index 1ba58d553..e866d15de 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -390,11 +390,15 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { wait += waitstatesRegion[REGION_PALETTE_RAM]; #define LOAD_VRAM \ - if ((address & 0x0001FFFF) < SIZE_VRAM) { \ - LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \ - } else { \ - LOAD_32(value, address & 0x00017FFC, gba->video.vram); \ + if ((address & 0x0001FFFF) >= SIZE_VRAM) { \ + if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \ + mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load32: 0x%08X", address); \ + value = 0; \ + break; \ + } \ + address &= 0x00017FFC; \ } \ + LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \ wait += waitstatesRegion[REGION_VRAM]; #define LOAD_OAM LOAD_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); @@ -520,11 +524,15 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { LOAD_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); break; case REGION_VRAM: - if ((address & 0x0001FFFF) < SIZE_VRAM) { - LOAD_16(value, address & 0x0001FFFE, gba->video.vram); - } else { - LOAD_16(value, address & 0x00017FFE, gba->video.vram); + if ((address & 0x0001FFFF) >= SIZE_VRAM) { + if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { + mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load16: 0x%08X", address); + value = 0; + break; + } + address &= 0x00017FFE; } + LOAD_16(value, address & 0x0001FFFE, gba->video.vram); break; case REGION_OAM: LOAD_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); @@ -631,11 +639,15 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = ((uint8_t*) gba->video.palette)[address & (SIZE_PALETTE_RAM - 1)]; break; case REGION_VRAM: - if ((address & 0x0001FFFF) < SIZE_VRAM) { - value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF]; - } else { - value = ((uint8_t*) gba->video.vram)[address & 0x00017FFF]; + if ((address & 0x0001FFFF) >= SIZE_VRAM) { + if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { + mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load8: 0x%08X", address); + value = 0; + break; + } + address &= 0x00017FFF; } + value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF]; break; case REGION_OAM: value = ((uint8_t*) gba->video.oam.raw)[address & (SIZE_OAM - 1)]; @@ -717,20 +729,18 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { wait += waitstatesRegion[REGION_PALETTE_RAM]; #define STORE_VRAM \ - if ((address & 0x0001FFFF) < SIZE_VRAM) { \ - LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); \ - if (oldValue != value) { \ - STORE_32(value, address & 0x0001FFFC, gba->video.vram); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \ - } \ - } else { \ - LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); \ - if (oldValue != value) { \ - STORE_32(value, address & 0x00017FFC, gba->video.vram); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \ + if ((address & 0x0001FFFF) >= SIZE_VRAM) { \ + if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \ + mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Store32: 0x%08X", address); \ + break; \ } \ + address &= 0x00017FFC; \ + } \ + LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); \ + if (oldValue != value) { \ + STORE_32(value, address & 0x0001FFFC, gba->video.vram); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \ } \ wait += waitstatesRegion[REGION_VRAM]; @@ -840,18 +850,17 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle } break; case REGION_VRAM: - if ((address & 0x0001FFFF) < SIZE_VRAM) { - LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram); - if (value != oldValue) { - STORE_16(value, address & 0x0001FFFE, gba->video.vram); - gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); - } - } else { - LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram); - if (value != oldValue) { - STORE_16(value, address & 0x00017FFE, gba->video.vram); - gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); + if ((address & 0x0001FFFF) >= SIZE_VRAM) { + if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { + mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Store16: 0x%08X", address); + break; } + address &= 0x00017FFE; + } + LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram); + if (value != oldValue) { + STORE_16(value, address & 0x0001FFFE, gba->video.vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); } break; case REGION_OAM: @@ -939,7 +948,6 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo break; case REGION_VRAM: if ((address & 0x0001FFFF) >= ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { - // TODO: check BG mode mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address); break; } diff --git a/src/gba/renderers/software-obj.c b/src/gba/renderers/software-obj.c index c98a080bf..b4573483c 100644 --- a/src/gba/renderers/software-obj.c +++ b/src/gba/renderers/software-obj.c @@ -67,12 +67,8 @@ LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \ tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \ current = renderer->spriteLayer[outX]; \ - if ((current & FLAG_ORDER_MASK) > flags) { \ - if (tileData) { \ - renderer->spriteLayer[outX] = palette[tileData] | flags; \ - } else if (current != FLAG_UNWRITTEN) { \ - renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \ - } \ + if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \ + renderer->spriteLayer[outX] = palette[tileData] | flags; \ } #define SPRITE_DRAW_PIXEL_16_NORMAL_OBJWIN(localX) \ @@ -84,13 +80,9 @@ LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \ tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \ current = renderer->spriteLayer[outX]; \ - if ((current & FLAG_ORDER_MASK) > flags) { \ - if (tileData) { \ - unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \ - renderer->spriteLayer[outX] = color | flags; \ - } else if (current != FLAG_UNWRITTEN) { \ - renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \ - } \ + if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \ + unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \ + renderer->spriteLayer[outX] = color | flags; \ } #define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \ @@ -117,12 +109,8 @@ LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \ tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \ current = renderer->spriteLayer[outX]; \ - if ((current & FLAG_ORDER_MASK) > flags) { \ - if (tileData) { \ - renderer->spriteLayer[outX] = palette[tileData] | flags; \ - } else if (current != FLAG_UNWRITTEN) { \ - renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \ - } \ + if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \ + renderer->spriteLayer[outX] = palette[tileData] | flags; \ } #define SPRITE_DRAW_PIXEL_256_NORMAL_OBJWIN(localX) \ @@ -134,13 +122,9 @@ LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \ tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \ current = renderer->spriteLayer[outX]; \ - if ((current & FLAG_ORDER_MASK) > flags) { \ - if (tileData) { \ - unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \ - renderer->spriteLayer[outX] = color | flags; \ - } else if (current != FLAG_UNWRITTEN) { \ - renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \ - } \ + if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \ + unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \ + renderer->spriteLayer[outX] = color | flags; \ } #define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \ diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 848dc9b4d..356fa130a 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -8,7 +8,8 @@ #include #include -#define LOCKSTEP_INCREMENT 3000 +#define LOCKSTEP_INCREMENT 2000 +#define LOCKSTEP_TRANSFER 512 static bool GBASIOLockstepNodeInit(struct GBASIODriver* driver); static void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver); @@ -17,9 +18,9 @@ static bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver); static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate); +static void _finishTransfer(struct GBASIOLockstepNode* node); void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) { - mLockstepInit(&lockstep->d); lockstep->players[0] = 0; lockstep->players[1] = 0; lockstep->players[2] = 0; @@ -88,12 +89,16 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) { node->nextEvent = 0; node->eventDiff = 0; mTimingSchedule(&driver->p->p->timing, &node->event, 0); + + mLockstepLock(&node->p->d); + node->mode = driver->p->mode; + switch (node->mode) { case SIO_MULTI: node->d.writeRegister = GBASIOLockstepNodeMultiWriteRegister; node->d.p->rcnt |= 3; - ++node->p->attachedMulti; + ATOMIC_ADD(node->p->attachedMulti, 1); node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached; if (node->id) { node->d.p->rcnt |= 4; @@ -110,35 +115,83 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) { node->phase = node->p->d.transferActive; node->transferId = node->p->d.transferId; #endif + + mLockstepUnlock(&node->p->d); + return true; } bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; + + mLockstepLock(&node->p->d); + node->mode = driver->p->mode; switch (node->mode) { case SIO_MULTI: - --node->p->attachedMulti; + ATOMIC_SUB(node->p->attachedMulti, 1); break; default: break; } + + // Flush ongoing transfer + if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) { + int oldWhen = node->event.when; + + mTimingDeschedule(&driver->p->p->timing, &node->event); + mTimingSchedule(&driver->p->p->timing, &node->event, 0); + node->eventDiff -= oldWhen - node->event.when; + mTimingDeschedule(&driver->p->p->timing, &node->event); + } + node->p->d.unload(&node->p->d, node->id); - mTimingDeschedule(&driver->p->p->timing, &node->event); + + node->p->multiRecv[0] = 0xFFFF; + node->p->multiRecv[1] = 0xFFFF; + node->p->multiRecv[2] = 0xFFFF; + node->p->multiRecv[3] = 0xFFFF; + + _finishTransfer(node); + + if (!node->id) { + ATOMIC_STORE(node->p->d.transferActive, TRANSFER_IDLE); + } + + // Invalidate SIO mode + node->mode = SIO_GPIO; + + mLockstepUnlock(&node->p->d); + return true; } static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; + + mLockstepLock(&node->p->d); + if (address == REG_SIOCNT) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value); - if (value & 0x0080 && node->p->d.transferActive == TRANSFER_IDLE) { + + enum mLockstepPhase transferActive; + ATOMIC_LOAD(transferActive, node->p->d.transferActive); + + if (value & 0x0080 && transferActive == TRANSFER_IDLE) { if (!node->id && node->d.p->multiplayerControl.ready) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); - node->p->d.transferActive = TRANSFER_STARTING; - node->p->d.transferCycles = GBASIOCyclesPerTransfer[node->d.p->multiplayerControl.baud][node->p->d.attached - 1]; + ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); + ATOMIC_STORE(node->p->d.transferCycles, GBASIOCyclesPerTransfer[node->d.p->multiplayerControl.baud][node->p->d.attached - 1]); + + bool scheduled = mTimingIsScheduled(&driver->p->p->timing, &node->event); + int oldWhen = node->event.when; + mTimingDeschedule(&driver->p->p->timing, &node->event); mTimingSchedule(&driver->p->p->timing, &node->event, 0); + + if (scheduled) { + node->eventDiff -= oldWhen - node->event.when; + } } else { value &= ~0x0080; } @@ -148,6 +201,9 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver } else if (address == REG_SIOMLT_SEND) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOMLT_SEND <- %04x", node->id, value); } + + mLockstepUnlock(&node->p->d); + return value; } @@ -155,6 +211,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) { if (node->transferFinished) { return; } + struct GBASIO* sio = node->d.p; switch (node->mode) { case SIO_MULTI: @@ -209,27 +266,38 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) { static int32_t _masterUpdate(struct GBASIOLockstepNode* node) { bool needsToWait = false; int i; - switch (node->p->d.transferActive) { + + enum mLockstepPhase transferActive; + int attachedMulti, attached; + + ATOMIC_LOAD(transferActive, node->p->d.transferActive); + ATOMIC_LOAD(attachedMulti, node->p->attachedMulti); + ATOMIC_LOAD(attached, node->p->d.attached); + + switch (transferActive) { case TRANSFER_IDLE: // If the master hasn't initiated a transfer, it can keep going. node->nextEvent += LOCKSTEP_INCREMENT; - node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached; + node->d.p->multiplayerControl.ready = attachedMulti == attached; break; case TRANSFER_STARTING: // Start the transfer, but wait for the other GBAs to catch up node->transferFinished = false; - node->p->multiRecv[0] = 0xFFFF; + node->p->multiRecv[0] = node->d.p->p->memory.io[REG_SIOMLT_SEND >> 1]; + node->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = 0xFFFF; + node->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = 0xFFFF; + node->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF; + node->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF; node->p->multiRecv[1] = 0xFFFF; node->p->multiRecv[2] = 0xFFFF; node->p->multiRecv[3] = 0xFFFF; needsToWait = true; ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTED); - node->nextEvent += 512; + node->nextEvent += LOCKSTEP_TRANSFER; break; case TRANSFER_STARTED: // All the other GBAs have caught up and are sleeping, we can all continue now - node->p->multiRecv[0] = node->d.p->p->memory.io[REG_SIOMLT_SEND >> 1]; - node->nextEvent += 512; + node->nextEvent += LOCKSTEP_TRANSFER; ATOMIC_STORE(node->p->d.transferActive, TRANSFER_FINISHING); break; case TRANSFER_FINISHING: @@ -269,6 +337,7 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) { #ifndef NDEBUG node->phase = node->p->d.transferActive; #endif + if (needsToWait) { return 0; } @@ -276,9 +345,16 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) { } static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) { - node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached; + enum mLockstepPhase transferActive; + int attachedMulti, attached; + + ATOMIC_LOAD(transferActive, node->p->d.transferActive); + ATOMIC_LOAD(attachedMulti, node->p->attachedMulti); + ATOMIC_LOAD(attached, node->p->d.attached); + + node->d.p->multiplayerControl.ready = attachedMulti == attached; bool signal = false; - switch (node->p->d.transferActive) { + switch (transferActive) { case TRANSFER_IDLE: if (!node->d.p->multiplayerControl.ready) { node->p->d.addCycles(&node->p->d, node->id, LOCKSTEP_INCREMENT); @@ -288,6 +364,9 @@ static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) { case TRANSFER_FINISHING: break; case TRANSFER_STARTED: + if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) { + break; + } node->transferFinished = false; switch (node->mode) { case SIO_MULTI: @@ -315,6 +394,9 @@ static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) { signal = true; break; case TRANSFER_FINISHED: + if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) { + break; + } _finishTransfer(node); signal = true; break; @@ -325,16 +407,20 @@ static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) { if (signal) { node->p->d.signal(&node->p->d, 1 << node->id); } + return 0; } static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) { struct GBASIOLockstepNode* node = user; + mLockstepLock(&node->p->d); if (node->p->d.attached < 2) { + mLockstepUnlock(&node->p->d); return; } int32_t cycles = 0; node->nextEvent -= cyclesLate; + node->eventDiff += cyclesLate; if (node->nextEvent <= 0) { if (!node->id) { cycles = _masterUpdate(node); @@ -353,12 +439,18 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, mTimingSchedule(timing, &node->event, cycles); } else { node->d.p->p->earlyExit = true; - mTimingSchedule(timing, &node->event, cyclesLate + 1); + node->eventDiff += 1; + mTimingSchedule(timing, &node->event, 1); } + + mLockstepUnlock(&node->p->d); } static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; + + mLockstepLock(&node->p->d); + if (address == REG_SIOCNT) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value); value &= 0xFF8B; @@ -368,7 +460,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive if (value & 0x0080 && !node->id) { // Internal shift clock if (value & 1) { - node->p->d.transferActive = TRANSFER_STARTING; + ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); } // Frequency if (value & 2) { @@ -382,5 +474,8 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive } else if (address == REG_SIODATA32_HI) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04x", node->id, value); } + + mLockstepUnlock(&node->p->d); + return value; } diff --git a/src/platform/3ds/CMakeToolchain.txt b/src/platform/3ds/CMakeToolchain.txt index 2da8df26a..cccfb6a78 100644 --- a/src/platform/3ds/CMakeToolchain.txt +++ b/src/platform/3ds/CMakeToolchain.txt @@ -1,14 +1,4 @@ -if(DEFINED ENV{DEVKITPRO}) - set(DEVKITPRO $ENV{DEVKITPRO}) -else() - message(FATAL_ERROR "Could not find DEVKITPRO in environment") -endif() - -if(DEFINED ENV{DEVKITARM}) - set(DEVKITARM $ENV{DEVKITARM}) -else() - set(DEVKITARM ${DEVKITPRO}/devkitARM) -endif() +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/devkitPro.cmake) if(DEFINED ENV{CTRULIB}) set(CTRULIB $ENV{CTRULIB}) @@ -16,40 +6,17 @@ else() set(CTRULIB ${DEVKITPRO}/libctru) endif() -set(extension) -if (CMAKE_HOST_WIN32) - set(extension .exe) -endif() - -set(CMAKE_PROGRAM_PATH ${DEVKITARM}/bin) set(cross_prefix arm-none-eabi-) set(arch_flags "-march=armv6k -mtune=mpcore -mfloat-abi=hard -ffunction-sections") set(inc_flags "-I${CTRULIB}/include ${arch_flags} -mword-relocations") set(link_flags "-L${CTRULIB}/lib -lctru -specs=3dsx.specs ${arch_flags} -Wl,--gc-sections") -set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name") set(CMAKE_SYSTEM_PROCESSOR arm CACHE INTERNAL "processor") set(CMAKE_LIBRARY_ARCHITECTURE arm-none-eabi CACHE INTERNAL "abi") -find_program(CMAKE_AR ${cross_prefix}gcc-ar${extension}) -find_program(CMAKE_RANLIB ${cross_prefix}gcc-ranlib${extension}) -find_program(CMAKE_C_COMPILER ${cross_prefix}gcc${extension}) -find_program(CMAKE_CXX_COMPILER ${cross_prefix}g++${extension}) -find_program(CMAKE_ASM_COMPILER ${cross_prefix}gcc${extension}) -find_program(CMAKE_LINKER ${cross_prefix}ld${extension}) -set(CMAKE_C_FLAGS ${inc_flags} CACHE INTERNAL "c compiler flags") -set(CMAKE_ASM_FLAGS ${inc_flags} CACHE INTERNAL "assembler flags") -set(CMAKE_CXX_FLAGS ${inc_flags} CACHE INTERNAL "cxx compiler flags") - -set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags") -set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags") -set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags") - -set(CMAKE_FIND_ROOT_PATH ${DEVKITARM}/arm-none-eabi ${DEVKITPRO}/portlibs/3ds) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE INTERNAL "") -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE INTERNAL "") -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "") -set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE) - set(3DS ON) add_definitions(-D_3DS -DARM11) + +create_devkit(ARM) + +set(CMAKE_FIND_ROOT_PATH ${DEVKITARM}/${CMAKE_LIBRARY_ARCHITECTURE} ${DEVKITPRO}/portlibs/3ds) diff --git a/src/platform/cmake/devkitPro.cmake b/src/platform/cmake/devkitPro.cmake new file mode 100644 index 000000000..23f20cc28 --- /dev/null +++ b/src/platform/cmake/devkitPro.cmake @@ -0,0 +1,41 @@ +if(DEFINED ENV{DEVKITPRO}) + set(DEVKITPRO $ENV{DEVKITPRO}) +else() + message(FATAL_ERROR "Could not find DEVKITPRO in environment") +endif() + +set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name") + +function(create_devkit DEVKIT) + if(DEFINED ENV{DEVKIT${DEVKIT}}) + set(DEVKIT${DEVKIT} $ENV{DEVKIT${DEVKIT}} PARENT_SCOPE) + else() + set(DEVKIT${DEVKIT} ${DEVKITPRO}/devkit${DEVKIT} PARENT_SCOPE) + endif() + + set(CMAKE_PROGRAM_PATH ${DEVKIT${DEVKIT}}/bin CACHE INTERNAL "program path") + + set(extension) + if (CMAKE_HOST_WIN32) + set(extension .exe) + endif() + + find_program(CMAKE_AR ${cross_prefix}gcc-ar${extension}) + find_program(CMAKE_RANLIB ${cross_prefix}gcc-ranlib${extension}) + find_program(CMAKE_C_COMPILER ${cross_prefix}gcc${extension}) + find_program(CMAKE_CXX_COMPILER ${cross_prefix}g++${extension}) + find_program(CMAKE_ASM_COMPILER ${cross_prefix}gcc${extension}) + find_program(CMAKE_LINKER ${cross_prefix}ld${extension}) + set(CMAKE_C_FLAGS ${inc_flags} CACHE INTERNAL "c compiler flags") + set(CMAKE_ASM_FLAGS ${inc_flags} CACHE INTERNAL "assembler flags") + set(CMAKE_CXX_FLAGS ${inc_flags} CACHE INTERNAL "cxx compiler flags") + + set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags") + set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags") + set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags") + + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE INTERNAL "") + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE INTERNAL "") + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "") + set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE) +endfunction() \ No newline at end of file diff --git a/src/platform/qt/ActionMapper.cpp b/src/platform/qt/ActionMapper.cpp index e03c4dd1a..13c059b4c 100644 --- a/src/platform/qt/ActionMapper.cpp +++ b/src/platform/qt/ActionMapper.cpp @@ -30,8 +30,11 @@ void ActionMapper::clearMenu(const QString& name) { emit menuCleared(name); } -void ActionMapper::rebuildMenu(QMenuBar* menubar, const ShortcutController& shortcuts) { +void ActionMapper::rebuildMenu(QMenuBar* menubar, QWidget* context, const ShortcutController& shortcuts) { menubar->clear(); + for (QAction* action : context->actions()) { + context->removeAction(action); + } for (const QString& m : m_menus[{}]) { if (m_hiddenActions.contains(m)) { continue; @@ -39,11 +42,11 @@ void ActionMapper::rebuildMenu(QMenuBar* menubar, const ShortcutController& shor QString menu = m.mid(1); QMenu* qmenu = menubar->addMenu(m_menuNames[menu]); - rebuildMenu(menu, qmenu, shortcuts); + rebuildMenu(menu, qmenu, context, shortcuts); } } -void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, const ShortcutController& shortcuts) { +void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, QWidget* context, const ShortcutController& shortcuts) { for (const QString& actionName : m_menus[menu]) { if (actionName.isNull()) { qmenu->addSeparator(); @@ -55,12 +58,13 @@ void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, const Shortcut if (actionName[0] == '.') { QString name = actionName.mid(1); QMenu* newMenu = qmenu->addMenu(m_menuNames[name]); - rebuildMenu(name, newMenu, shortcuts); + rebuildMenu(name, newMenu, context, shortcuts); continue; } Action* action = &m_actions[actionName]; QAction* qaction = qmenu->addAction(action->visibleName()); qaction->setEnabled(action->isEnabled()); + qaction->setShortcutContext(Qt::WidgetShortcut); if (action->isExclusive() || action->booleanAction()) { qaction->setCheckable(true); } @@ -88,6 +92,7 @@ void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, const Shortcut qaction->setShortcut(QKeySequence(shortcut)); }); } + context->addAction(qaction); } } diff --git a/src/platform/qt/ActionMapper.h b/src/platform/qt/ActionMapper.h index 1ffcda2a2..f99290bd2 100644 --- a/src/platform/qt/ActionMapper.h +++ b/src/platform/qt/ActionMapper.h @@ -29,7 +29,7 @@ public: void addMenu(const QString& visibleName, const QString& name, const QString& parent = {}); void addHiddenMenu(const QString& visibleName, const QString& name, const QString& parent = {}); void clearMenu(const QString& name); - void rebuildMenu(QMenuBar*, const ShortcutController&); + void rebuildMenu(QMenuBar*, QWidget* context, const ShortcutController&); void addSeparator(const QString& menu); @@ -59,7 +59,7 @@ signals: void menuCleared(const QString& name); private: - void rebuildMenu(const QString& menu, QMenu* qmenu, const ShortcutController&); + void rebuildMenu(const QString& menu, QMenu* qmenu, QWidget* context, const ShortcutController&); Action* addAction(const Action& act, const QString& name, const QString& menu, const QKeySequence& shortcut); QHash m_actions; diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 6d8bae787..de9c3193f 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -37,6 +37,7 @@ DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent) m_painter = new PainterGL(format.majorVersion() < 2 ? 1 : m_gl->format().majorVersion(), m_gl); m_gl->setMouseTracking(true); m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work? + setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions } DisplayGL::~DisplayGL() { @@ -223,6 +224,9 @@ PainterGL::PainterGL(int majorVersion, QGLWidget* parent) #endif m_backend->swap = [](VideoBackend* v) { PainterGL* painter = static_cast(v->user); + if (!painter->m_gl->isVisible()) { + return; + } painter->m_gl->swapBuffers(); }; diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 85d4ee08e..fbf44a547 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -32,7 +32,7 @@ public: EmptyGLWidget(const QGLFormat& format, QWidget* parent) : QGLWidget(format, parent) { setAutoBufferSwap(false); } protected: - void paintEvent(QPaintEvent*) override {} + void paintEvent(QPaintEvent* event) override { event->ignore(); } void resizeEvent(QResizeEvent*) override {} void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); } }; diff --git a/src/platform/qt/MessagePainter.cpp b/src/platform/qt/MessagePainter.cpp index de24ade24..379fc7bdf 100644 --- a/src/platform/qt/MessagePainter.cpp +++ b/src/platform/qt/MessagePainter.cpp @@ -79,7 +79,9 @@ void MessagePainter::redraw() { } void MessagePainter::paint(QPainter* painter) { - painter->drawPixmap(m_local, m_pixmap); + if (!m_message.text().isEmpty()) { + painter->drawPixmap(m_local, m_pixmap); + } } diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index ac68dd4bf..8387d3f03 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -16,26 +16,43 @@ using namespace QGBA; +MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node) + : controller(coreController) + , gbNode(node) +{ +} + +MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node) + : controller(coreController) + , gbaNode(node) +{ +} + MultiplayerController::MultiplayerController() { mLockstepInit(&m_lockstep); m_lockstep.context = this; + m_lockstep.lock = [](mLockstep* lockstep) { + MultiplayerController* controller = static_cast(lockstep->context); + controller->m_lock.lock(); + }; + m_lockstep.unlock = [](mLockstep* lockstep) { + MultiplayerController* controller = static_cast(lockstep->context); + controller->m_lock.unlock(); + }; m_lockstep.signal = [](mLockstep* lockstep, unsigned mask) { MultiplayerController* controller = static_cast(lockstep->context); Player* player = &controller->m_players[0]; bool woke = false; - controller->m_lock.lock(); player->waitMask &= ~mask; if (!player->waitMask && player->awake < 1) { mCoreThreadStopWaiting(player->controller->thread()); player->awake = 1; woke = true; } - controller->m_lock.unlock(); return woke; }; m_lockstep.wait = [](mLockstep* lockstep, unsigned mask) { MultiplayerController* controller = static_cast(lockstep->context); - controller->m_lock.lock(); Player* player = &controller->m_players[0]; bool slept = false; player->waitMask |= mask; @@ -44,7 +61,6 @@ MultiplayerController::MultiplayerController() { player->awake = 0; slept = true; } - controller->m_lock.unlock(); return slept; }; m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) { @@ -52,7 +68,6 @@ MultiplayerController::MultiplayerController() { abort(); } MultiplayerController* controller = static_cast(lockstep->context); - controller->m_lock.lock(); if (!id) { for (int i = 1; i < controller->m_players.count(); ++i) { Player* player = &controller->m_players[i]; @@ -85,11 +100,9 @@ MultiplayerController::MultiplayerController() { controller->m_players[id].controller->setSync(true); controller->m_players[id].cyclesPosted += cycles; } - controller->m_lock.unlock(); }; m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) { MultiplayerController* controller = static_cast(lockstep->context); - controller->m_lock.lock(); Player* player = &controller->m_players[id]; player->cyclesPosted -= cycles; if (player->cyclesPosted <= 0) { @@ -97,15 +110,23 @@ MultiplayerController::MultiplayerController() { player->awake = 0; } cycles = player->cyclesPosted; - controller->m_lock.unlock(); + return cycles; + }; + m_lockstep.unusedCycles= [](mLockstep* lockstep, int id) { + MultiplayerController* controller = static_cast(lockstep->context); + Player* player = &controller->m_players[id]; + auto cycles = player->cyclesPosted; return cycles; }; m_lockstep.unload = [](mLockstep* lockstep, int id) { MultiplayerController* controller = static_cast(lockstep->context); - controller->m_lock.lock(); - Player* player = &controller->m_players[id]; if (id) { + Player* player = &controller->m_players[id]; player->controller->setSync(true); + player->cyclesPosted = 0; + + // release master GBA if it is waiting for this GBA + player = &controller->m_players[0]; player->waitMask &= ~(1 << id); if (!player->waitMask && player->awake < 1) { mCoreThreadStopWaiting(player->controller->thread()); @@ -149,10 +170,13 @@ MultiplayerController::MultiplayerController() { } } } - controller->m_lock.unlock(); }; } +MultiplayerController::~MultiplayerController() { + mLockstepDeinit(&m_lockstep); +} + bool MultiplayerController::attachGame(CoreController* controller) { if (m_lockstep.attached == MAX_GBAS) { return false; @@ -188,14 +212,7 @@ bool MultiplayerController::attachGame(CoreController* controller) { GBASIOLockstepNode* node = new GBASIOLockstepNode; GBASIOLockstepNodeCreate(node); GBASIOLockstepAttachNode(&m_gbaLockstep, node); - m_players.append({ - controller, - nullptr, - node, - 1, - 0, - 0 - }); + m_players.append({controller, node}); GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI); @@ -210,14 +227,7 @@ bool MultiplayerController::attachGame(CoreController* controller) { GBSIOLockstepNode* node = new GBSIOLockstepNode; GBSIOLockstepNodeCreate(node); GBSIOLockstepAttachNode(&m_gbLockstep, node); - m_players.append({ - controller, - node, - nullptr, - 1, - 0, - 0 - }); + m_players.append({controller, node}); GBSIOSetDriver(&gb->sio, &node->d); diff --git a/src/platform/qt/MultiplayerController.h b/src/platform/qt/MultiplayerController.h index fefba4cb4..4614c5c6f 100644 --- a/src/platform/qt/MultiplayerController.h +++ b/src/platform/qt/MultiplayerController.h @@ -17,6 +17,8 @@ #include #endif +#include + struct GBSIOLockstepNode; struct GBASIOLockstepNode; @@ -29,6 +31,7 @@ Q_OBJECT public: MultiplayerController(); + ~MultiplayerController(); bool attachGame(CoreController*); void detachGame(CoreController*); @@ -42,12 +45,15 @@ signals: private: struct Player { + Player(CoreController* controller, GBSIOLockstepNode* node); + Player(CoreController* controller, GBASIOLockstepNode* node); + CoreController* controller; - GBSIOLockstepNode* gbNode; - GBASIOLockstepNode* gbaNode; - int awake; - int32_t cyclesPosted; - unsigned waitMask; + GBSIOLockstepNode* gbNode = nullptr; + GBASIOLockstepNode* gbaNode = nullptr; + int awake = 1; + int32_t cyclesPosted = 0; + unsigned waitMask = 0; }; union { mLockstep m_lockstep; diff --git a/src/platform/qt/VideoView.cpp b/src/platform/qt/VideoView.cpp index 8ee77f7ed..f9614441f 100644 --- a/src/platform/qt/VideoView.cpp +++ b/src/platform/qt/VideoView.cpp @@ -279,7 +279,11 @@ void VideoView::setAudioCodec(const QString& codec, bool manual) { void VideoView::setVideoCodec(const QString& codec, bool manual) { free(m_videoCodecCstr); m_videoCodec = sanitizeCodec(codec, s_vcodecMap); - m_videoCodecCstr = strdup(m_videoCodec.toUtf8().constData()); + if (m_videoCodec == "none") { + m_videoCodecCstr = nullptr; + } else { + m_videoCodecCstr = strdup(m_videoCodec.toUtf8().constData()); + } if (!FFmpegEncoderSetVideo(&m_encoder, m_videoCodecCstr, m_vbr)) { free(m_videoCodecCstr); m_videoCodecCstr = nullptr; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index edaa6407e..fafd009a9 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -608,9 +608,7 @@ void Window::resizeEvent(QResizeEvent* event) { } m_savedScale = factor; for (QMap::iterator iter = m_frameSizes.begin(); iter != m_frameSizes.end(); ++iter) { - bool enableSignals = iter.value()->blockSignals(true); iter.value()->setActive(iter.key() == factor); - iter.value()->blockSignals(enableSignals); } m_config->setOption("fullscreen", isFullScreen()); @@ -636,6 +634,7 @@ void Window::showEvent(QShowEvent* event) { m_fullscreenOnStart = false; } reloadDisplayDriver(); + setFocus(); } void Window::closeEvent(QCloseEvent* event) { @@ -835,7 +834,7 @@ void Window::gameStarted() { action->setActive(true); } } - m_actions.rebuildMenu(menuBar(), *m_shortcutController); + m_actions.rebuildMenu(menuBar(), this, *m_shortcutController); #ifdef USE_DISCORD_RPC @@ -1382,9 +1381,7 @@ void Window::setupMenu(QMenuBar* menubar) { m_savedScale = i; m_config->setOption("scaleMultiplier", i); // TODO: Port to other resizeFrame(size); - bool enableSignals = setSize->blockSignals(true); setSize->setActive(true); - setSize->blockSignals(enableSignals); }, "frame"); setSize->setExclusive(true); if (m_savedScale == i) { @@ -1481,18 +1478,13 @@ void Window::setupMenu(QMenuBar* menubar) { #endif #ifdef USE_FFMPEG - addGameAction(tr("Record output..."), "recordOutput", this, &Window::openVideoWindow, "av"); + addGameAction(tr("Record A/V..."), "recordOutput", this, &Window::openVideoWindow, "av"); #endif #ifdef USE_MAGICK addGameAction(tr("Record GIF..."), "recordGIF", this, &Window::openGIFWindow, "av"); #endif - addGameAction(tr("Record video log..."), "recordVL", this, &Window::startVideoLog, "av"); - addGameAction(tr("Stop video log"), "stopVL", [this]() { - m_controller->endVideoLog(); - }, "av"); - m_actions.addSeparator("av"); m_actions.addMenu(tr("Video layers"), "videoLayers", "av"); m_actions.addMenu(tr("Audio channels"), "audioChannels", "av"); @@ -1552,6 +1544,12 @@ void Window::setupMenu(QMenuBar* menubar) { m_platformActions.insert(PLATFORM_GBA, ioViewer); #endif + m_actions.addSeparator("tools"); + addGameAction(tr("Record debug video log..."), "recordVL", this, &Window::startVideoLog, "tools"); + addGameAction(tr("Stop debug video log"), "stopVL", [this]() { + m_controller->endVideoLog(); + }, "tools"); + ConfigOption* skipBios = m_config->addOption("skipBios"); skipBios->connect([this](const QVariant& value) { reloadConfig(); @@ -1642,7 +1640,7 @@ void Window::setupMenu(QMenuBar* menubar) { } m_shortcutController->rebuildItems(); - m_actions.rebuildMenu(menubar, *m_shortcutController); + m_actions.rebuildMenu(menuBar(), this, *m_shortcutController); } void Window::attachWidget(QWidget* widget) { @@ -1679,7 +1677,7 @@ void Window::updateMRU() { } m_config->setMRU(m_mruFiles); m_config->write(); - m_actions.rebuildMenu(menuBar(), *m_shortcutController); + m_actions.rebuildMenu(menuBar(), this, *m_shortcutController); } Action* Window::addGameAction(const QString& visibleName, const QString& name, Action::Function function, const QString& menu, const QKeySequence& shortcut) { diff --git a/src/platform/qt/ts/mgba-zh_CN.ts b/src/platform/qt/ts/mgba-zh_CN.ts index 16f9ec4a6..b60a570ad 100644 --- a/src/platform/qt/ts/mgba-zh_CN.ts +++ b/src/platform/qt/ts/mgba-zh_CN.ts @@ -11,12 +11,12 @@ <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> - <a href="http://mgba.io/">主页</a> • <a href="https://forums.mgba.io/">论坛 / 支持</a> • <a href="https://patreon.com/mgba">捐助</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">源代码</a> + <a href="http://mgba.io/">网站</a> • <a href="https://forums.mgba.io/">论坛、支持</a> • <a href="https://patreon.com/mgba">捐助</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">源代码</a> Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> - 分支: <tt>{gitBranch}</tt><br/>修订: <tt>{gitCommit}</tt> + 分支: <tt>{gitBranch}</tt><br/>修订版: <tt>{gitCommit}</tt> @@ -32,8 +32,8 @@ © 2013 – 2018 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 Game Boy Advance is a registered trademark of Nintendo Co., Ltd. - © 2013 – 2018 Jeffrey Pfau, 授权基于Mozilla公共授权 MPLv2.0 -Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 + © 2013 – 2018 Jeffrey Pfau,基于Mozilla公共许可证(版本 2.0)授权 +Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标。 @@ -61,7 +61,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Open in archive... - 打开记录... + 在压缩文件中打开... @@ -125,6 +125,84 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 0x00 (00) + + BattleChipView + + + BattleChip Gate + BattleChip Gate + + + + Chip name + 晶片名称 + + + + Insert + 插入 + + + + Save + 保存 + + + + Load + 载入 + + + + Add + 添加 + + + + Remove + 移除 + + + + Gate type + 装置类型 + + + + Ba&ttleChip Gate + BattleChip Gate(&T) + + + + Progress &Gate + Progress Gate(&G) + + + + Beast &Link Gate + Beast Link(&L) + + + + Inserted + 已插入 + + + + Chip ID + 晶片ID + + + + Update Chip data + 更新晶片数据 + + + + Show advanced + 显示高级选项 + + CheatsView @@ -168,7 +246,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Enter command (try `help` for more info) - 输入命令(尝试 `help` 以获取更多信息) + 输入命令(尝试输入 `help` 以获取更多信息) @@ -181,7 +259,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Record GIF - 录制GIF + 录制 GIF @@ -196,7 +274,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Select File - 选取文件 + 选择文件 @@ -211,7 +289,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Automatic - 自动设置帧延迟 + 自动设置 @@ -341,7 +419,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 %1 State - %1 状态 + %1 即时存档 @@ -359,47 +437,47 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 1 - + 1 2 - + 2 3 - + 3 4 - + 4 5 - + 5 6 - + 6 7 - + 7 8 - + 8 9 - + 9 @@ -422,7 +500,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Stub - + 桩件 @@ -530,22 +608,22 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Guess - + 猜测 1 Byte (8-bit) - + 1 字节 (8 位) 2 Bytes (16-bit) - + 2 字节 (16 位) 4 Bytes (32-bit) - + 4 字节 (32 位) @@ -570,7 +648,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Equal - 相等 + 等于 @@ -623,7 +701,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 0x - + 0 @@ -632,56 +710,56 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 - 1 Byte - + &1 Byte + 1 字节(&1) - 2 Bytes - + &2 Bytes + 2 字节(&2) - 4 Bytes - + &4 Bytes + 4 字节(&4) - + Unsigned Integer: 无符号整数: - + Signed Integer: 有符号整数: - + String: - 字符串 : + 字符串: - + Load TBL 载入 TBL - + Copy Selection 复制所选 - + Paste 粘贴 - + Save Selection 保存所选 - + Load 载入 @@ -691,18 +769,18 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Sprites - Sprites + 精灵图 × - + × Magnification - 放大率 + 缩放率 @@ -712,7 +790,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Attributes - Attributes + 属性 @@ -722,7 +800,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Off - Off + @@ -740,7 +818,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Double Size - Double Size + 双倍大小 @@ -748,22 +826,22 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Return, Ctrl+R - Return, Ctrl+R + 回车键、Ctrl+R Flipped - Flipped + 已翻转 H - H + 水平 V - V + 垂直 @@ -773,22 +851,22 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Normal - Normal + 普通 Mosaic - Mosaic + 马赛克 Enabled - Enabled + 已启用 Priority - Priority + 优先级 @@ -798,7 +876,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Geometry - Geometry + 几何图像 @@ -807,8 +885,8 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 - , - , + , + , @@ -837,12 +915,12 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Game Overrides - + 游戏覆盖 Game Boy Advance - + Game Boy Advance @@ -865,12 +943,12 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Tilt - 贴图 + 图块 Light sensor - 光传感器 + 光线传感器 @@ -891,92 +969,92 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 SRAM - + SRAM Flash 512kb - + Flash 512kb Flash 1Mb - + Flash 1Mb EEPROM - + EEPROM Idle loop - + 空循环 Game Boy Player features - Game Boy Player 特性 + Game Boy Player 功能 Game Boy - + Game Boy Game Boy model - Game Boy 模式 + Game Boy 模型 Game Boy (DMG) - + Game Boy (DMG) Super Game Boy (SGB) - + Super Game Boy (SGB) Game Boy Color (CGB) - + Game Boy Color (CGB) Game Boy Advance (AGB) - + Game Boy Advance (AGB) Memory bank controller - 内部存储控制器 + 内存 bank 控制器 MBC1 - + MBC1 MBC2 - + MBC2 MBC3 - + MBC3 MBC3 + RTC - + MBC3 + RTC MBC5 - + MBC5 @@ -986,22 +1064,22 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 MBC7 - + MBC7 Pocket Cam - + Pocket Cam TAMA5 - + TAMA5 HuC-3 - + HuC-3 @@ -1011,12 +1089,12 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Sprite Colors 1 - + 精灵图颜色 1 Sprite Colors 2 - + 精灵图颜色 2 @@ -1061,12 +1139,12 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 0x00 (00) - + 0x00 (00) 16-bit value - 16-bit 值 + 16 位数值 @@ -1076,33 +1154,31 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Palette index - + 调色板索引 0x0000 - + 0x0000 #000000 - + #000000 000 - + 000 Export BG - BG是什么 - 导出 BG + 导出背景 Export OBJ - OBJ是什么 导出 OBJ @@ -1111,7 +1187,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Adjust placement - 更改部署 + 更改布局 @@ -1121,17 +1197,17 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Offset - 抵消 + 偏移 X - + X Y - + Y @@ -1139,17 +1215,17 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Game Boy Printer - + Game Boy 打印机 Hurry up! - + Hurry up! Tear off - + Tear off @@ -1157,14 +1233,14 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 %0%1%2 - + %0%1%2 0x%0 (%1) - + 0x%0 (%1) @@ -1172,17 +1248,17 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Can't set format of context-less audio device - + 无法设置无上下文音频设备的格式 Audio device is missing its core - + 音频设备缺少核心 Writing data to read-only audio device - + 正在将数据写入只读音频设备 @@ -1190,7 +1266,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Can't start an audio processor without input - + 无法在无输入的情况下启动音频处理器 @@ -1198,7 +1274,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Can't start an audio processor without input - + 无法在无输入的情况下启动音频处理器 @@ -1211,7 +1287,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Failed to open cheats file: %1 - 打开作弊码文件失败 : %1 + 打开作弊码文件失败: %1 @@ -1247,24 +1323,24 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 QGBA::CoreController - + Failed to open save file: %1 无法打开存档: %1 - + Failed to open game file: %1 - 无法打开游戏文件 : %1 + 无法打开游戏文件: %1 - + Failed to open snapshot file for reading: %1 - 无法读取快照文件 : %1 + 无法读取快照文件: %1 - + Failed to open snapshot file for writing: %1 - 无法写入快照文件 : %1 + 无法写入快照文件: %1 @@ -1272,7 +1348,15 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Failed to open game file: %1 - 无法打开游戏文件 : %1 + 无法打开游戏文件: %1 + + + + QGBA::GBAApp + + + Enable Discord Rich Presence + 启用 Discord Rich Presence @@ -1285,7 +1369,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Clear Analog - 清除 Analog + 清除模拟控制 @@ -1303,22 +1387,22 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Server settings - + 服务器设置 Local port - + 本地端口 Bind address - + 绑定地址 Break - + 断点 @@ -1338,7 +1422,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Could not start GDB server - 无法打开GDB服务端 + 无法打开 GDB 服务端 @@ -1346,7 +1430,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Failed to open output GIF file: %1 - 无法打开输出的GIF文件 : %1 + 无法打开输出的 GIF 文件: %1 @@ -1356,7 +1440,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Graphics Interchange Format (*.gif) - + 图形交换格式 (*.gif) @@ -1369,37 +1453,37 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Mode 0: 4 tile layers - + 模式0: 4个图块层 Mode 1: 2 tile layers + 1 rotated/scaled tile layer - + 模式 1: 2个图块层 + 1个已旋转/缩放图块层 Mode 2: 2 rotated/scaled tile layers - + 模式 2: 2个已旋转/缩放图块层 Mode 3: Full 15-bit bitmap - + 模式 3: 完整15位位图 Mode 4: Full 8-bit bitmap - + 模式 4: 完整8位位图 Mode 5: Small 15-bit bitmap - + 模式 5: 15位小位图 CGB Mode - + CGB 模式 @@ -1409,12 +1493,12 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Unlocked HBlank - + 已解锁 HBlank Linear OBJ tile mapping - + 线性 OBJ 图块映射 @@ -1424,82 +1508,82 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Enable background 0 - + 启用背景 0 Enable background 1 - + 启用背景 1 Enable background 2 - + 启用背景 2 Enable background 3 - + 启用背景 3 Enable OBJ - + 启用 OBJ Enable Window 0 - + 启用窗口 0 Enable Window 1 - + 启用窗口 1 Enable OBJ Window - + 启用 OBJ 窗口 Currently in VBlank - + 当前 (VBlank) Currently in HBlank - + 当前 (HBlank) Currently in VCounter - + 当前 (VCounter) Enable VBlank IRQ generation - + 启用 VBlank IRQ 生成 Enable HBlank IRQ generation - + 启用 HBlank IRQ 生成 Enable VCounter IRQ generation - + 启用 VCounter IRQ 生成 VCounter scanline - + VCounter 扫描线 Current scanline - + 当前扫描线 @@ -1515,7 +1599,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Tile data base (* 16kB) - + 图块数据基 (* 16kB) @@ -1523,7 +1607,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Enable mosaic - + 启用马赛克 @@ -1531,7 +1615,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Enable 256-color - 启用256色 + 启用 256 色 @@ -1539,7 +1623,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Tile map base (* 2kB) - + 图块映射基 (* 2kB) @@ -1547,13 +1631,13 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Background dimensions - + 背景维度 Overflow wraps - + 溢出包装 @@ -1561,7 +1645,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Horizontal offset - + 水平偏移量 @@ -1569,7 +1653,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Vertical offset - + 垂直偏移量 @@ -1585,7 +1669,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Fractional part - + 分数部分 @@ -1597,7 +1681,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Integer part - + 整数部分 @@ -1605,7 +1689,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Integer part (bottom) - + 整数部分 (低位) @@ -1613,286 +1697,286 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Integer part (top) - + 整数部分 (高位) End x - + 结束 x 轴 Start x - + 起始 x 轴 End y - + 结束 y 轴 Start y - + 起始 y 轴 Window 0 enable BG 0 - + 窗口 0 启用背景 0 Window 0 enable BG 1 - + 窗口 0 启用背景 1 Window 0 enable BG 2 - + 窗口 0 启用背景 2 Window 0 enable BG 3 - + 窗口 0 启用背景 3 Window 0 enable OBJ - + 窗口 0 启用 OBJ Window 0 enable blend - + Windows 0 启用叠加 Window 1 enable BG 0 - + 窗口 1 启用背景 0 Window 1 enable BG 1 - + 窗口 1 启用背景 1 Window 1 enable BG 2 - + 窗口 1 启用背景 2 Window 1 enable BG 3 - + 窗口 1 启用背景 3 Window 1 enable OBJ - + 窗口 1 启用 OBJ Window 1 enable blend - + 窗口 1 启用叠加 Outside window enable BG 0 - + 外部窗口启用背景 0 Outside window enable BG 1 - + 外部窗口启用背景 1 Outside window enable BG 2 - + 外部窗口启用背景 2 Outside window enable BG 3 - + 外部窗口启用背景 3 Outside window enable OBJ - + 外部窗口启用 OBJ Outside window enable blend - + 外部窗口启用叠加 OBJ window enable BG 0 - + OBJ 窗口启用背景 0 OBJ window enable BG 1 - + OBJ 窗口启用背景 1 OBJ window enable BG 2 - + OBJ 窗口启用背景 2 OBJ window enable BG 3 - + OBJ 窗口启用背景 3 OBJ window enable OBJ - + OBJ 窗口启用 OBJ OBJ window enable blend - + OBJ 窗口启用叠加 Background mosaic size vertical - + 背景马赛克垂直大小 Background mosaic size horizontal - + 背景马赛克水平大小 Object mosaic size vertical - + 对象马赛克垂直大小 Object mosaic size horizontal - + 对象马赛克水平大小 BG 0 target 1 - + 背景 0 目标 1 BG 1 target 1 - + 背景 1 目标 1 BG 2 target 1 - + 背景 2 目标 1 BG 3 target 1 - + 背景 3 目标 1 OBJ target 1 - + OBJ 目标 1 Backdrop target 1 - + 背景目标 1 Blend mode - + 叠加模式 Disabled - 禁用 + 已禁用 Additive blending - + 加性叠加 Brighten - + 增加亮度 Darken - + 降低亮度 BG 0 target 2 - + 背景 0 目标 2 BG 1 target 2 - + 背景 1 目标 2 BG 2 target 2 - + 背景 2 目标 2 BG 3 target 2 - + 背景 3 目标 2 OBJ target 2 - + OBJ 目标 2 Backdrop target 2 - + 背景目标 2 Blend A (target 1) - + 叠加 A (目标 1) Blend B (target 2) - + 叠加 B (目标 2) Blend Y - + 叠加 Y Sweep shifts - + 扫描 shifts Sweep subtract - + 扫描 subtract Sweep time (in 1/128s) - + 扫描时间 (1/128秒) @@ -1900,27 +1984,27 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Sound length - + 声音长度 Duty cycle - + 占空比 Envelope step time - + 包络步进时间 Envelope increase - + 增加包络 @@ -1942,7 +2026,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Timed - + 已时控 @@ -1955,45 +2039,45 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Double-size wave table - + 双尺寸波形表 Active wave table - + 激活波形表 Enable channel 3 - + 启用通道 3 Volume - + 音量 0% - + 0% 100% - + 100% 50% - + 50% 25% - + 25% @@ -2001,118 +2085,118 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 75% - + 75% Clock divider - + 时钟分频器 Register stages - + 寄存器阶段 15 - + 15 7 - + 7 Shifter frequency - + 移位器频率 PSG volume right - + PSG 右侧音量 PSG volume left - + PSG 左侧音量 Enable channel 1 right - + 启用右侧通道 1 Enable channel 2 right - + 启用右侧通道 2 Enable channel 3 right - + 启用右侧通道 3 Enable channel 4 right - + 启用右侧通道 4 Enable channel 1 left - + 启用左侧通道 1 Enable channel 2 left - + 启用左侧通道 2 Enable channel 3 left - + 启用左侧通道 3 Enable channel 4 left - + 启用左侧通道 4 PSG master volume - + PSG 主音量 Loud channel A - + 响音通道 A Loud channel B - + 响音通道 B Enable channel A right - + 启用右侧通道 A Enable channel A left - + 启用右侧通道 A Channel A timer - + 通道 A 时控器 0 - + 0 @@ -2125,52 +2209,52 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 1 - + 1 Channel A reset - + 通道 A 重置 Enable channel B right - + 启用右侧通道 B Enable channel B left - + 启用左侧通道 B Channel B timer - + 通道 B 时控器 Channel B reset - + 通道 B 重置 Active channel 1 - + 激活通道 1 Active channel 2 - + 激活通道 2 Active channel 3 - + 激活通道 3 Active channel 4 - + 激活通道 4 @@ -2180,7 +2264,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Bias - + 偏差 @@ -2229,7 +2313,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Sample - + 采样 @@ -2241,7 +2325,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Address (bottom) - + 地址 (低位) @@ -2253,7 +2337,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Address (top) - + 地址 (高位) @@ -2261,7 +2345,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Word count - + 字数 @@ -2269,7 +2353,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Destination offset - + 目标偏移 @@ -2281,7 +2365,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Increment - + 增量 @@ -2293,7 +2377,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Decrement - + 减量 @@ -2305,7 +2389,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Fixed - + 固定 @@ -2313,7 +2397,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Increment and reload - + 增量并重新加载 @@ -2321,7 +2405,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Source offset - + 源偏移 @@ -2329,7 +2413,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Repeat - + 重复 @@ -2337,7 +2421,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 32-bit - + 32 位 @@ -2345,7 +2429,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Start timing - + 开始定时 @@ -2353,7 +2437,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Immediate - + 立即 @@ -2363,7 +2447,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 VBlank - + VBlank @@ -2373,7 +2457,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 HBlank - + HBlank @@ -2386,7 +2470,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 IRQ - + IRQ @@ -2398,24 +2482,24 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Enable - + 启用 Audio FIFO - + 音频 FIFO Video Capture - + 视频截取 DRQ - + DRQ @@ -2423,7 +2507,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Value - + @@ -2431,7 +2515,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Scale - + 比例 @@ -2439,7 +2523,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 1/64 - + 1/64 @@ -2447,7 +2531,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 1/256 - + 1/256 @@ -2455,38 +2539,38 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 1/1024 - + 1/1024 Cascade - + 级联 A - + A B - + B Select - 选择 + Select Start - 开始 + Start @@ -2516,115 +2600,115 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 R - + R L - + L Condition - + 状态 SC - + SC SD - + SD SI - + SI SO - + SO VCounter - + VCounter Timer 0 - + Timer 0 Timer 1 - + Timer 1 Timer 2 - + Timer 2 Timer 3 - + Timer 3 SIO - + SIO DMA 0 - + DMA 0 DMA 1 - + DMA 1 DMA 2 - + DMA 2 DMA 3 - + DMA 3 Keypad - + 键盘 Gamepak - + 游戏卡带 SRAM wait - + SRAM 等待 @@ -2633,7 +2717,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 4 - + 4 @@ -2641,7 +2725,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 3 - + 3 @@ -2650,7 +2734,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 2 - + 2 @@ -2659,42 +2743,42 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 8 - + 8 Cart 0 non-sequential - + Cart 0 (非顺序) Cart 0 sequential - + Cart 0 (顺序) Cart 1 non-sequential - + Cart 1 (非顺序) Cart 1 sequential - + Cart 1 (顺序) Cart 2 non-sequential - + Cart 2 (非顺序) Cart 2 sequential - + Cart 2 (顺序) PHI terminal - + PHI 终端 @@ -2704,27 +2788,27 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 4.19MHz - + 4.19MHz 8.38MHz - + 8.38MHz 16.78MHz - + 16.78MHz Gamepak prefetch - + 游戏卡带预读取 Enable IRQs - + 启用 IRQ @@ -2733,73 +2817,122 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 --- - + --- QGBA::LoadSaveState - + Load State - 载入快照 + 载入即时存档 - + Save State - 存档 + 即时存档 - + Empty - + - + Corrupted - + 已损坏 - + Slot %1 - + 插槽 %1 + + + + QGBA::LogConfigModel + + + + Default + 默认 + + + + Fatal + 致命错误 + + + + Error + 错误 + + + + Warning + 警告 + + + + Info + 信息 + + + + Debug + 调试 + + + + Stub + 桩位 + + + + Game Error + 游戏错误 QGBA::LogController - + + [%1] %2: %3 + [%1] %2: %3 + + + DEBUG - + DEBUG - + STUB - + STUB - + INFO - + INFO - + WARN - + WARN - + ERROR - + ERROR - + FATAL - + FATAL - + GAME ERROR - + GAME ERROR @@ -2807,47 +2940,47 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Map Addr. - + 映射地址 Mirror - + 镜像 None - + Both - + 两者 Horizontal - + 水平 Vertical - + 垂直 Export map - + 导出映射 Portable Network Graphics (*.png) - + 便携式网络图形 (*.png) Failed to open output PNG file: %1 - 输出PNG文件开启失败: %1 + 打开输出 PNG 文件失败: %1 @@ -2874,7 +3007,6 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 - All 全部 @@ -2884,34 +3016,34 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 载入 TBL - + Save selected memory 保存所选内存 - + Failed to open output file: %1 - 无法打开输出文件 : %1 + 无法打开输出文件: %1 - + Load memory 载入内存 - + Failed to open input file: %1 - 无法打开输入文件 : %1 + 无法打开输入文件: %1 - + TBL - + TBL - + ISO-8859-1 - + ISO-8859-1 @@ -2919,77 +3051,77 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 (%0/%1×) - + (%0/%1×) (⅟%0×) - + (⅟%0×) (%0×) - + (%0×) %1 byte%2 - + %1 字节%2 QGBA::ObjView - - + + 0x%0 - + 0x%0 - + Off - - - - - Normal - + - Trans - + Normal + 一般 - OBJWIN - + Trans + Trans + OBJWIN + OBJWIN + + + Invalid 无效 - - + + N/A - + - + Export sprite - - - - - Portable Network Graphics (*.png) - + 导出精灵图 + Portable Network Graphics (*.png) + 便携式网络图形 (*.png) + + + Failed to open output PNG file: %1 - 无法打开输出的PNG文件: %1 + 无法打开输出的 PNG 文件: %1 @@ -2997,39 +3129,39 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 #%0 - + #%0 0x%0 - + 0x%0 %0 - + %0 0x%0 (%1) - + 0x%0 (%1) Export palette - 导出 调色板 + 导出调色板 Windows PAL (*.pal);;Adobe Color Table (*.act) - + Windows PAL (*.pal);;Adobe 颜色表 (*.act) Failed to open output palette file: %1 - 已输出的调色板文件打开失败: %1 + 打开输出调色板文件失败: %1 @@ -3042,7 +3174,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Portable Network Graphics (*.png) - + 便携式网络图形 (*.png) @@ -3060,72 +3192,72 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 bytes - + 字节 (no database present) - + (无现存数据库) QGBA::SettingsView - - + + Qt Multimedia - - - - - SDL - - - - - Software (Qt) - + Qt Multimedia + SDL + SDL + + + + Software (Qt) + 软件渲染 (Qt) + + + OpenGL - + OpenGL - + OpenGL (force version 1.x) - + OpenGL (强制版本 1.x) - + None (Still Image) - + 无 (静止图像) - + Keyboard 键盘 - + Controllers 控制器 - + Shortcuts 快捷键 - - + + Shaders 着色器 - + Select BIOS - 选择BIOS + 选择 BIOS @@ -3148,7 +3280,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 by %1 - + by %1 @@ -3158,7 +3290,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Pass %1 - 进行 %1 + Pass %1 @@ -3166,7 +3298,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Action - + 动作 @@ -3182,17 +3314,17 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 QGBA::VideoView - + Failed to open output video file: %1 - 无法代开输出的视频文件 : %1 + 无法打开输出的视频文件: %1 - + Native (%0x%1) - + 原生 (%0x%1) - + Select output file 选取输出文件 @@ -3200,97 +3332,108 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 QGBA::Window - + Game Boy Advance ROMs (%1) - + Game Boy Advance ROM (%1) - + Game Boy ROMs (%1) - + Game Boy ROM (%1) - + All ROMs (%1) - + 所有 ROM (%1) - + %1 Video Logs (*.mvl) %1 视频日志 (*.mvl) - + Archives (%1) - + 压缩文件 (%1) - - - + + + Select ROM - 选择ROM + 选择 ROM - + Select folder 选择文件夹 - + Game Boy Advance save files (%1) - Game Boy Advance 保存文件 (%1) + Game Boy Advance 存档文件 (%1) - - - + + + Select save - 选取存档 + 选择存档 + + mGBA savestate files (%1) + mGBA 即时存档文件 (%1) + + + + Select savestate + 选择即时存档 + + + Select patch - 选择 patch + 选择补丁 - + Patches (*.ips *.ups *.bps) - + 补丁文件 (*.ips *.ups *.bps) - + Select image - 选取图片 + 选择图片 - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - 图片文件 (*.png *.gif *.jpg *.jpeg);;Tous les fichiers (*) + 图像文件 (*.png *.gif *.jpg *.jpeg);;所有文件 (*) - - + + GameShark saves (*.sps *.xps) - GameShark存档 (*.sps *.xps) + GameShark 存档 (*.sps *.xps) - + Select video log 选择视频日志 - + Video logs (*.mvl) 视频日志 (*.mvl) - + Crash 崩溃 - + The game has crashed with the following error: %1 @@ -3299,652 +3442,630 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 %1 - + Couldn't Load 无法载入 - + Could not load game. Are you sure it's in the correct format? - 无法载入游戏. 是否确认游戏格式无误 ? + 无法载入游戏。请确认游戏格式是否无误 - + Unimplemented BIOS call - 未实现的BIOS调用 + 未实现的 BIOS 调用 - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - 该游戏使用了尚未实现的BIOS调用。请使用官方BIOS以获得最佳的游戏体验。 + 该游戏使用了尚未实现的 BIOS 调用。请使用官方 BIOS 以获得最佳游戏体验。 - + Really make portable? - 确定进行便携化 ? + 确定进行程序便携化? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + 进行此操作后,模拟器将从模拟器可执行文件所在目录内加载模拟器配置。您想继续吗? - + Restart needed - 需要重启 + 需要重新启动 - + Some changes will not take effect until the emulator is restarted. - 一些更改将在模拟器下次启动时生效。 + 更改将在模拟器下次启动时生效。 - + - Player %1 of %2 - + - 玩家 %1 共 %2 - + %1 - %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + %1 - %2 (%3 fps) - %4 - + &File 文件(&F) - + Load &ROM... - 载入 &ROM... + 载入 ROM(&R)... - + Load ROM in archive... - 从目录中载入ROM... + 从压缩文件中载入 ROM... - + Add folder to library... - 在库中添加文件夹... + 将文件夹添加到库中... - + Load alternate save... 读取其他的存档... - + Load temporary save... 读取临时存档... - - - Load &patch... - 载入&patch文件... - - - - Boot BIOS - - - - - Replace ROM... - 替换ROM... - - - - ROM &info... - ROM信息(&I)... - - - - Recent - - - - - Make portable - 便携化 - - - - &Load state - (&L)读取存档 - - - - F10 - - - - - &Save state - &S存档 - - Shift+F10 - + Load &patch... + 载入补丁文件(&P)... - + + Boot BIOS + 引导 BIOS + + + + Replace ROM... + 替换 ROM... + + + + ROM &info... + ROM 信息(&I)... + + + + Recent + 最近打开的游戏 + + + + Make portable + 程序便携化 + + + + &Load state + 读取即时存档(&L) + + + + F10 + F10 + + + + Load state file... + 载入即时存档文件... + + + + &Save state + 保存即时存档(&S) + + + + Shift+F10 + Shift+F10 + + + + Save state file... + 保存即时存档文件... + + + Quick load 快速读档 - + Quick save 快速存档 - + Load recent - 载入最近的存档 + 载入最近的即时存档 - + Save recent - 保存最近的状态 + 保存最近的即时存档 - + Undo load state - 撤消读档操作 + 撤消读档 - + F11 - + F11 - + Undo save state 撤消存档 - + Shift+F11 - + Shift+F11 - - + + State &%1 - + 即时存档 1(&1) - + F%1 - + F%1 - + Shift+F%1 - + Shift+F%1 - + Load camera image... 读取相机图片... - + Import GameShark Save - 导入GameShark存档 + 导入 GameShark 存档 - + Export GameShark Save - 导出GameShark存档 + 导出 GameShark 存档 - + New multiplayer window - 新多人游戏窗口 + 新建多人游戏窗口 - - About - 关于 + + About... + 关于... - + E&xit - 退出&x - - - - &Emulation - &E模拟 - - - - &Reset - &R复位 - - - - Ctrl+R - - - - - Sh&utdown - 关闭&u - - - - Yank game pak - - - - - &Pause - 暂停&P - - - - Ctrl+P - - - - - &Next frame - 下一帧&N + 退出(&X) + &Emulation + 模拟(&E) + + + + &Reset + 复位(&R) + + + + Ctrl+R + Ctrl+R + + + + Sh&utdown + 关机(&U) + + + + Yank game pak + 快速抽出游戏卡带 + + + + &Pause + 暂停(&P) + + + + Ctrl+P + Ctrl+P + + + + &Next frame + 下一帧(&N) + + + Ctrl+N - + Ctrl+N - + Fast forward (held) - 快进 (held) + 快进 (长按) - + &Fast forward - 快进&F + 快进(&F) - + Shift+Tab - + Shift+Tab - + Fast forward speed 快进速度 - + Unbounded 不限制 - + %0x - + %0x - + Rewind (held) - 回退 (held) + 回退 (长按) - + Re&wind - 回退&w + 回退(&W) - + ~ - + ~ - + Step backwards 后退 - + Ctrl+B - + Ctrl+B - + Sync to &video - 视频同步&v + 视频同步(&V) - + Sync to &audio - 音频同步&a + 音频同步(&A) - + Solar sensor - 日光传感器 + 光线传感器 - + Increase solar level - 增加日光级别 + 增加光线级别 - + Decrease solar level - 降低日光级别 + 降低光线级别 - + Brightest solar level - 日光级别为最亮 + 光线级别为最亮 - + Darkest solar level - 日光级别为最暗 + 光线级别为最暗 - + Brightness %1 亮度 %1 - - Audio/&Video - 音频/视频&V + + Game Boy Printer... + Game Boy 打印机.. - + + BattleChip Gate... + BattleChip Gate... + + + + Audio/&Video + 音频/视频(&V) + + + Frame size 帧率 - + %1x - + %1x - + Toggle fullscreen 切换全屏 - + Lock aspect ratio 锁定纵横比 - + Force integer scaling 强制整数缩放 - + Bilinear filtering 双线性过滤 - + Frame&skip - 跳帧&s + 跳帧(&S) - + Mute 静音 - + FPS target - 最高FPS - - - - 15 - - - - - 30 - - - - - 45 - + 目标 FPS - Native (59.7) - + Native (59.7275) + 原生 (59.7275) - - 60 - - - - - 90 - - - - - 120 - - - - - 240 - - - - + Take &screenshot - 生成截图&s + 截图(&S) - + F12 - + F12 - + Record output... 录制导出... - + Record GIF... - 录制GIF... + 录制 GIF... - + Record video log... 记录视频日志... - + Stop video log 停止记录视频日志 - - Game Boy Printer... - - - - + Video layers - + 视频图层 - + Audio channels - 音频 + 音频通道 - + Adjust layer placement... - + 调整图层布局 - + &Tools - 工具&T + 工具(&T) - + View &logs... - 查看日志&l... + 查看日志(&L)... - + Game &overrides... - 覆盖游戏&o... + 覆盖游戏(&O)... - + Game &Pak sensors... - + 游戏卡带传感器(&P)... - + &Cheats... - 作弊码&C... + 作弊码(&C)... - + Settings... 设置... - + Open debugger console... - 打开调试终端... + 打开调试器控制台... - + Start &GDB server... - 打开&GDB服务端... + 打开 GDB 服务器(&G)... - + View &palette... - 查看调色板&p... + 查看调色板(&P)... - + View &sprites... - + 查看精灵图(&S) - + View &tiles... - + 查看图块(&T) - + View &map... - 不译 - + 查看映射(&M) - + View memory... 查看内存... - + Search memory... - 查看内存... + 搜索内存... - + View &I/O registers... - registers 翻译存疑 - 查看&I/O注册器... + 查看 I/O 寄存器(&I)... - + Exit fullscreen 退出全屏 - + GameShark Button (held) - GameShark 键 (held) + GameShark 键 (长按) - + Autofire - 自动开火 + 连发 - + Autofire A - 自动开火 A + 连发 A - + Autofire B - 自动开火 B + 连发 B - + Autofire L - 自动开火 L + 连发 L - + Autofire R - 自动开火 R + 连发 R - + Autofire Start - 自动开火 开始 + 连发 Start - + Autofire Select - 自动开火 选择 + 连发 Select - + Autofire Up - 自动开火 上 + 连发 上 - + Autofire Right - 自动开火 右 + 连发 右 - + Autofire Down - 自动开火 下 + 连发 下 - + Autofire Left - 自动开火 左 + 连发 左 @@ -3952,17 +4073,17 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 GBA - + GBA GB - + GB ? - + ? @@ -3975,7 +4096,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Game name: - 游戏名 : + 游戏名称: @@ -3985,7 +4106,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Internal name: - 内部名 : + 内部名称: @@ -3995,7 +4116,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Game ID: - 游戏ID : + 游戏 ID: @@ -4005,7 +4126,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 File size: - 文件大小 : + 文件大小: @@ -4015,12 +4136,12 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 CRC32: - CRC32 : + CRC32: {CRC} - + {CRC} @@ -4058,12 +4179,12 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 MM/dd/yy hh:mm:ss AP - + yy/MM/dd hh:mm:ss AP Light sensor - 光传感器 + 光线传感器 @@ -4073,19 +4194,19 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Tilt sensor - 倾角传感器 + 倾斜传感器 Set Y - 设定 Y + 设定 Y 轴 Set X - 设定 X + 设定 X 轴 @@ -4106,502 +4227,538 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 设置 - + Audio/Video 音频/视频 - + Interface 用户界面 - + Emulation 模拟器 - + BIOS - + BIOS - + Paths 路径 - + + Logging + 日志记录 + + + Game Boy - + Game Boy - + Audio driver: - 音频驱动 : + 音频驱动: - + Audio buffer: 音频缓冲: - - + + 1536 - - - - - 512 - + 1536 - 768 - + 512 + 512 - 1024 - + 768 + 768 - - 2048 - + + 1024 + 1024 - 3072 - + 2048 + 2048 - 4096 - + 3072 + 3072 - + + 4096 + 4096 + + + samples 采样 - + Sample rate: 采样率: - - + + 44100 - - - - - 22050 - + 44100 + 22050 + 22050 + + + 32000 - + 32000 - + 48000 - + 48000 - + Hz - + Hz - + Volume: - 音量 : + 音量: - + + Mute 静音 - + + Fast forward volume: + 快进音量: + + + Display driver: - 显示驱动 : + 显示驱动: - + Frameskip: - 跳帧 : + 跳帧: - + Skip every 每间隔 - - + + frames - + FPS target: - 最高FPS : + 目标 FPS: - + frames per second 帧每秒 - + Sync: - 同步 : + 同步: - + Video 视频 - + Audio 音频 - + Lock aspect ratio 锁定纵横比 - - Bilinear filtering - 双线性过滤 - - - + Force integer scaling 强制整数缩放 - + + Bilinear filtering + 双线性过滤 + + + Language 语言 - + English 英语 - + Library: - 库 : + 库: - + List view 列表查看 - + Tree view 树状查看 - + Show when no game open - 在无游戏打开时显示 + 未打开游戏时显示 - + Clear cache 清除缓存 - + Allow opposing input directions 允许逆向输入 - + Suspend screensaver - 停用休眠 + 停用屏幕保护程序 - + Pause when inactive - 不活动时暂停 + 非活动时暂停 - + Show FPS in title bar - 在标题栏显示FPS + 在标题栏显示 FPS - + Automatically save cheats 自动保存作弊码 - + Automatically load cheats 自动载入作弊码 - + Automatically save state 自动存档 - + Automatically load state 自动读档 + + + + Enable Discord Rich Presence + 启用 Enable Discord Rich Presence - + Fast forward speed: - 快进速度 : + 快进速度: - + × - + × - + Unbounded 不限制 - + + Autofire interval: + 连发间隔: + + + Enable rewind 启用回退 - + Rewind history: - 回退历史 : + 回退历史: - + Idle loops: - + 空循环: - + Run all 运行所有 - + Remove known 移除选定 - + Detect and remove 检测并移除 - - Savestate extra data: - 存档时的额外数据 : + + Preload entire ROM into memory + 将整个 ROM 预加载到内存中 - - + + Savestate extra data: + 即时存档额外数据: + + + + Screenshot 截图 - - + + Save data 保存数据 - - + + Cheat codes 作弊码 - + Load extra data: 读档时载入额外数据: - - Rewind affects save data - 影响保存数据时回退 - - - - Preload entire ROM into memory - 预加载全部ROM到内存 - - - - Autofire interval: - 自动开火间隔 : - - - + GB BIOS file: GB BIOS 文件: - - - - - - - - - + + + + + + + + + Browse 浏览 - + Use BIOS file if found - 当可用时使用BIOS文件 + 当可用时使用 BIOS 文件 - + Skip BIOS intro - 跳过BIOS载入 + 跳过 BIOS 启动画面 - + GBA BIOS file: GBA BIOS 文件: - + GBC BIOS file: GBC BIOS 文件: - + SGB BIOS file: SGB BIOS 文件: - + Save games 已保存的游戏 - - - - - + + + + + Same directory as the ROM - 保存在ROM所在目录 + 保存在 ROM 所在目录 - + Save states - 保存快照 + 保存即时存档 - + Screenshots 截图 - + Patches 补丁 - + Cheats 作弊码 - - Game Boy model - + + Log to file + 记录日志到文件 - - - + + Log to console + 记录日志到控制台 + + + + Select Log File + 选择日志文件 + + + + Game Boy model + Game Boy 模型 + + + + + Autodetect 自动检测 - - - + + + Game Boy (DMG) - + Game Boy (DMG) - - - + + + Super Game Boy (SGB) - + Super Game Boy (SGB) - - - + + + Game Boy Color (CGB) - + Game Boy Color (CGB) - - - + + + Game Boy Advance (AGB) - + Game Boy Advance (AGB) - - Super Game Boy model - + + Super Game Boy model: + Super Game Boy 模型: - - Game Boy Color model - + + Game Boy Color model: + Game Boy Color 模型: - + Default BG colors: - 默认 BG 颜色: + 默认背景颜色: - + Super Game Boy borders - + Super Game Boy 边框 - + Camera driver: - 相机驱动 : + 相机驱动: - + Default sprite colors 1: - + 默认精灵图颜色 1: - + Default sprite colors 2: - + 默认精灵图颜色 2: + + + + Use GBC colors in GB games + 在 GB 游戏中使用 GBC 颜色 + + + + Camera: + 相机 @@ -4614,7 +4771,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Active Shader: - 主动着色器 : + 活动着色器: @@ -4634,7 +4791,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Unload Shader - 未载入 着色器 + 未载入着色器 @@ -4670,22 +4827,32 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Tiles - 贴图 + 图块 - + 256 colors - + 256 色 - + × - + × - + Magnification - 放大率 + 缩放率 + + + + Tiles per row + 每行图块 + + + + Fit to window + 自适应窗口 @@ -4708,7 +4875,7 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 Select File - 选取文件 + 选择文件 @@ -4717,44 +4884,44 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 - High Quality - 高质量 + High &Quality + 高质量(&Q) - YouTube - + &YouTube + YouTube(&Y) WebM - + WebM - Lossless - 无损 + &Lossless + 无损(&L) - 1080p - + &1080p + 1080p(&1) - 720p - + &720p + 720p(&7) - 480p - + &480p + 480p(&4) - Native - 原生 + &Native + 原生(&N) @@ -4764,110 +4931,120 @@ Game Boy Advance 是 Nintendo Co., Ltd. 的注册商标。 MKV - + MKV AVI - + AVI MP4 - + MP4 h.264 - + h.264 h.264 (NVENC) - + h.264 (NVENC) HEVC - + HEVC - VP8 - + HEVC (NVENC) + HEVC (NVENC) + VP8 + VP8 + + + + VP9 + VP9 + + + FFV1 - - - - - FLAC - - - - - Opus - + FFV1 - Vorbis - + FLAC + FLAC - MP3 - + Opus + Opus - AAC - + Vorbis + Vorbis + MP3 + MP3 + + + + AAC + AAC + + + Uncompressed 未压缩 - - Bitrate (kbps) - + + Bitrate (kbps) + 比特率 (kbps) - - VBR - + + VBR + VBR - + ABR - + ABR - + Dimensions - 方向 - - - - : - + 维度 - × - + : + : - + + × + × + + + Lock aspect ratio 锁定纵横比 - + Show advanced 显示高级选项 diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 829332043..fbfaece3c 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -60,7 +60,7 @@ set(MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/main.c) if(BUILD_RASPI) add_definitions(-DBUILD_RASPI) - list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c) + list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/rpi-common.c) list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gles2-sdl.c) set(OPENGLES2_LIBRARY "-lEGL -lGLESv2 -lbcm_host") set(BUILD_GLES2 ON CACHE BOOL "Using OpenGL|ES 2" FORCE) diff --git a/src/platform/sdl/gles2-sdl.c b/src/platform/sdl/gles2-sdl.c index 582df9f9b..6cc046769 100644 --- a/src/platform/sdl/gles2-sdl.c +++ b/src/platform/sdl/gles2-sdl.c @@ -6,6 +6,9 @@ #include "main.h" #include "gl-common.h" +#ifdef BUILD_RASPI +#include "rpi-common.h" +#endif #include #include @@ -26,74 +29,7 @@ void mSDLGLES2Create(struct mSDLRenderer* renderer) { bool mSDLGLES2Init(struct mSDLRenderer* renderer) { #ifdef BUILD_RASPI - bcm_host_init(); - renderer->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - int major, minor; - if (EGL_FALSE == eglInitialize(renderer->display, &major, &minor)) { - printf("Failed to initialize EGL"); - return false; - } - - if (EGL_FALSE == eglBindAPI(EGL_OPENGL_ES_API)) { - printf("Failed to get GLES API"); - return false; - } - - const EGLint requestConfig[] = { - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 5, - EGL_BLUE_SIZE, 5, - EGL_ALPHA_SIZE, 1, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_NONE - }; - - EGLConfig config; - EGLint numConfigs; - - if (EGL_FALSE == eglChooseConfig(renderer->display, requestConfig, &config, 1, &numConfigs)) { - printf("Failed to choose EGL config\n"); - return false; - } - - const EGLint contextAttributes[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - - int dispWidth = 240, dispHeight = 160, adjWidth; - renderer->context = eglCreateContext(renderer->display, config, EGL_NO_CONTEXT, contextAttributes); - graphics_get_display_size(0, &dispWidth, &dispHeight); - adjWidth = dispHeight / 2 * 3; - - DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0); - DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0); - - VC_RECT_T destRect = { - .x = (dispWidth - adjWidth) / 2, - .y = 0, - .width = adjWidth, - .height = dispHeight - }; - - VC_RECT_T srcRect = { - .x = 0, - .y = 0, - .width = 240 << 16, - .height = 160 << 16 - }; - - DISPMANX_ELEMENT_HANDLE_T element = vc_dispmanx_element_add(update, display, 0, &destRect, 0, &srcRect, DISPMANX_PROTECTION_NONE, 0, 0, 0); - vc_dispmanx_update_submit_sync(update); - - renderer->window.element = element; - renderer->window.width = dispWidth; - renderer->window.height = dispHeight; - - renderer->surface = eglCreateWindowSurface(renderer->display, config, &renderer->window, 0); - if (EGL_FALSE == eglMakeCurrent(renderer->display, renderer->surface, renderer->surface, renderer->context)) { - return false; - } + mRPIGLCommonInit(renderer); #else mSDLGLCommonInit(renderer); #endif @@ -112,7 +48,11 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) { renderer->gl2.d.lockAspectRatio = renderer->lockAspectRatio; renderer->gl2.d.lockIntegerScaling = renderer->lockIntegerScaling; renderer->gl2.d.filter = renderer->filter; +#ifdef BUILD_RASPI + renderer->gl2.d.swap = mRPIGLCommonSwap; +#else renderer->gl2.d.swap = mSDLGLCommonSwap; +#endif renderer->gl2.d.init(&renderer->gl2.d, 0); renderer->gl2.d.setDimensions(&renderer->gl2.d, renderer->width, renderer->height); @@ -147,11 +87,7 @@ void mSDLGLES2Runloop(struct mSDLRenderer* renderer, void* user) { } mCoreSyncWaitFrameEnd(&context->impl->sync); v->drawFrame(v); -#ifdef BUILD_RASPI - eglSwapBuffers(renderer->display, renderer->surface); -#else v->swap(v); -#endif } } @@ -160,10 +96,10 @@ void mSDLGLES2Deinit(struct mSDLRenderer* renderer) { renderer->gl2.d.deinit(&renderer->gl2.d); } #ifdef BUILD_RASPI - eglMakeCurrent(renderer->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroySurface(renderer->display, renderer->surface); - eglDestroyContext(renderer->display, renderer->context); - eglTerminate(renderer->display); + eglMakeCurrent(renderer->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(renderer->eglDisplay, renderer->eglSurface); + eglDestroyContext(renderer->eglDisplay, renderer->eglContext); + eglTerminate(renderer->eglDisplay); bcm_host_deinit(); #elif SDL_VERSION_ATLEAST(2, 0, 0) SDL_GL_DeleteContext(renderer->glCtx); diff --git a/src/platform/sdl/main.h b/src/platform/sdl/main.h index eb435a1c7..bd4f2cecc 100644 --- a/src/platform/sdl/main.h +++ b/src/platform/sdl/main.h @@ -81,10 +81,10 @@ struct mSDLRenderer { #endif #ifdef BUILD_RASPI - EGLDisplay display; - EGLSurface surface; - EGLContext context; - EGL_DISPMANX_WINDOW_T window; + EGLDisplay eglDisplay; + EGLSurface eglSurface; + EGLContext eglContext; + EGL_DISPMANX_WINDOW_T eglWindow; #endif #ifdef BUILD_PANDORA diff --git a/src/platform/sdl/rpi-common.c b/src/platform/sdl/rpi-common.c new file mode 100644 index 000000000..be141718e --- /dev/null +++ b/src/platform/sdl/rpi-common.c @@ -0,0 +1,84 @@ +/* Copyright (c) 2013-2015 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 "main.h" + +#include + +void mRPIGLCommonSwap(struct VideoBackend* context) { + struct mSDLRenderer* renderer = (struct mSDLRenderer*) context->user; + eglSwapBuffers(renderer->eglDisplay, renderer->eglSurface); +} + +void mRPIGLCommonInit(struct mSDLRenderer* renderer) { + bcm_host_init(); + renderer->eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + int major, minor; + if (EGL_FALSE == eglInitialize(renderer->eglDisplay, &major, &minor)) { + printf("Failed to initialize EGL"); + return false; + } + + if (EGL_FALSE == eglBindAPI(EGL_OPENGL_ES_API)) { + printf("Failed to get GLES API"); + return false; + } + + const EGLint requestConfig[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 5, + EGL_BLUE_SIZE, 5, + EGL_ALPHA_SIZE, 1, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + EGLConfig config; + EGLint numConfigs; + + if (EGL_FALSE == eglChooseConfig(renderer->eglDisplay, requestConfig, &config, 1, &numConfigs)) { + printf("Failed to choose EGL config\n"); + return false; + } + + const EGLint contextAttributes[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + int dispWidth = 240, dispHeight = 160, adjWidth; + renderer->eglContext = eglCreateContext(renderer->eglDisplay, config, EGL_NO_CONTEXT, contextAttributes); + graphics_get_display_size(0, &dispWidth, &dispHeight); + adjWidth = dispHeight / 2 * 3; + + DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0); + DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0); + + VC_RECT_T destRect = { + .x = (dispWidth - adjWidth) / 2, + .y = 0, + .width = adjWidth, + .height = dispHeight + }; + + VC_RECT_T srcRect = { + .x = 0, + .y = 0, + .width = 240 << 16, + .height = 160 << 16 + }; + + DISPMANX_ELEMENT_HANDLE_T element = vc_dispmanx_element_add(update, display, 0, &destRect, 0, &srcRect, DISPMANX_PROTECTION_NONE, 0, 0, 0); + vc_dispmanx_update_submit_sync(update); + + renderer->eglWindow.element = element; + renderer->eglWindow.width = dispWidth; + renderer->eglWindow.height = dispHeight; + + renderer->eglSurface = eglCreateWindowSurface(renderer->eglDisplay, config, &renderer->eglWindow, 0); + if (EGL_FALSE == eglMakeCurrent(renderer->eglDisplay, renderer->eglSurface, renderer->eglSurface, renderer->eglContext)) { + return false; + } +} diff --git a/src/platform/sdl/rpi-common.h b/src/platform/sdl/rpi-common.h new file mode 100644 index 000000000..b58532de5 --- /dev/null +++ b/src/platform/sdl/rpi-common.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2013-2015 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/. */ +#ifndef SDL_RPI_COMMON_H +#define SDL_RPI_COMMON_H + +#include + +CXX_GUARD_START + +#include "main.h" + +void mRPIGLCommonSwap(struct VideoBackend* context); +void mRPIGLCommonInit(struct mSDLRenderer* renderer); + +CXX_GUARD_END + +#endif diff --git a/src/platform/switch/CMakeToolchain.txt b/src/platform/switch/CMakeToolchain.txt index 3dbc1ad87..38c4ff943 100644 --- a/src/platform/switch/CMakeToolchain.txt +++ b/src/platform/switch/CMakeToolchain.txt @@ -1,14 +1,4 @@ -if(DEFINED ENV{DEVKITPRO}) - set(DEVKITPRO $ENV{DEVKITPRO}) -else() - message(FATAL_ERROR "Could not find DEVKITPRO in environment") -endif() - -if(DEFINED ENV{DEVKITA64}) - set(DEVKITA64 $ENV{DEVKITA64}) -else() - set(DEVKITA64 ${DEVKITPRO}/devkitA64) -endif() +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/devkitPro.cmake) if(DEFINED ENV{LIBNX}) set(LIBNX $ENV{LIBNX}) @@ -16,41 +6,17 @@ else() set(LIBNX ${DEVKITPRO}/libnx) endif() -set(extension) -if (CMAKE_HOST_WIN32) - set(extension .exe) -endif() - -set(CMAKE_PROGRAM_PATH ${DEVKITA64}/bin) set(cross_prefix aarch64-none-elf-) set(arch_flags "-mtune=cortex-a57 -ffunction-sections -march=armv8-a -mtp=soft -fPIC -ftls-model=local-exec") set(inc_flags "-I${LIBNX}/include ${arch_flags}") set(link_flags "-L${LIBNX}/lib -lnx -specs=${LIBNX}/switch.specs ${arch_flags}") -set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name") set(CMAKE_SYSTEM_PROCESSOR aarch64 CACHE INTERNAL "processor") set(CMAKE_LIBRARY_ARCHITECTURE aarch64-none-elf CACHE INTERNAL "abi") -find_program(CMAKE_AR ${cross_prefix}gcc-ar${extension}) -find_program(CMAKE_RANLIB ${cross_prefix}gcc-ranlib${extension}) -find_program(CMAKE_C_COMPILER ${cross_prefix}gcc${extension}) -find_program(CMAKE_CXX_COMPILER ${cross_prefix}g++${extension}) -find_program(CMAKE_ASM_COMPILER ${cross_prefix}gcc${extension}) -find_program(CMAKE_LINKER ${cross_prefix}ld${extension}) -set(CMAKE_C_FLAGS ${inc_flags} CACHE INTERNAL "c compiler flags") -set(CMAKE_ASM_FLAGS ${inc_flags} CACHE INTERNAL "assembler flags") -set(CMAKE_CXX_FLAGS ${inc_flags} CACHE INTERNAL "cxx compiler flags") -SET(CMAKE_ASM_COMPILE_OBJECT " -o -c ") - -set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags") -set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags") -set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags") - -set(CMAKE_FIND_ROOT_PATH ${DEVKITARM}/aarch64-none-elf ${DEVKITPRO}/portlibs/switch) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE INTERNAL "") -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE INTERNAL "") -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "") -set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE) - set(SWITCH ON) add_definitions(-D__SWITCH__) + +create_devkit(A64) + +set(CMAKE_FIND_ROOT_PATH ${DEVKITA64}/${CMAKE_LIBRARY_ARCHITECTURE} ${DEVKITPRO}/portlibs/switch) diff --git a/src/platform/wii/CMakeToolchain.txt b/src/platform/wii/CMakeToolchain.txt index 652cbb098..887efd669 100644 --- a/src/platform/wii/CMakeToolchain.txt +++ b/src/platform/wii/CMakeToolchain.txt @@ -1,49 +1,16 @@ -if(DEFINED ENV{DEVKITPRO}) - set(DEVKITPRO $ENV{DEVKITPRO}) -else() - message(FATAL_ERROR "Could not find DEVKITPRO in environment") -endif() +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/devkitPro.cmake) -if(DEFINED ENV{DEVKITPPC}) - set(DEVKITPPC $ENV{DEVKITPPC}) -else() - set(DEVKITPPC ${DEVKITPRO}/devkitPPC) -endif() - -set(extension) -if (CMAKE_HOST_WIN32) - set(extension .exe) -endif() - -set(CMAKE_PROGRAM_PATH ${DEVKITPPC}/bin) set(cross_prefix powerpc-eabi-) set(arch_flags "-mrvl -mcpu=750 -meabi -mhard-float -g") set(inc_flags "-I${DEVKITPRO}/libogc/include ${arch_flags}") set(link_flags "-L${DEVKITPRO}/libogc/lib/wii ${arch_flags}") -set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name") set(CMAKE_SYSTEM_PROCESSOR powerpc CACHE INTERNAL "processor") set(CMAKE_LIBRARY_ARCHITECTURE powerpc-none-eabi CACHE INTERNAL "abi") -find_program(CMAKE_AR ${cross_prefix}gcc-ar${extension}) -find_program(CMAKE_RANLIB ${cross_prefix}gcc-ranlib${extension}) -find_program(CMAKE_C_COMPILER ${cross_prefix}gcc${extension}) -find_program(CMAKE_CXX_COMPILER ${cross_prefix}g++${extension}) -find_program(CMAKE_ASM_COMPILER ${cross_prefix}gcc${extension}) -find_program(CMAKE_LINKER ${cross_prefix}ld${extension}) -set(CMAKE_C_FLAGS ${inc_flags} CACHE INTERNAL "c compiler flags") -set(CMAKE_ASM_FLAGS ${inc_flags} CACHE INTERNAL "assembler flags") -set(CMAKE_CXX_FLAGS ${inc_flags} CACHE INTERNAL "cxx compiler flags") - -set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags") -set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags") -set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags") - -set(CMAKE_FIND_ROOT_PATH ${DEVKITPPC}/powerpc-eabi ${DEVKITPRO}/portlibs/ppc) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE INTERNAL "") -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE INTERNAL "") -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "") -set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE) - set(WII ON) add_definitions(-DGEKKO) + +create_devkit(PPC) + +set(CMAKE_FIND_ROOT_PATH ${DEVKITPPC}/powerpc-eabi ${DEVKITPRO}/portlibs/ppc) diff --git a/src/util/ring-fifo.c b/src/util/ring-fifo.c index bdaac75aa..04d4d1022 100644 --- a/src/util/ring-fifo.c +++ b/src/util/ring-fifo.c @@ -25,8 +25,8 @@ size_t RingFIFOCapacity(const struct RingFIFO* buffer) { size_t RingFIFOSize(const struct RingFIFO* buffer) { const void* read; const void* write; - ATOMIC_LOAD(read, buffer->readPtr); - ATOMIC_LOAD(write, buffer->writePtr); + ATOMIC_LOAD_PTR(read, buffer->readPtr); + ATOMIC_LOAD_PTR(write, buffer->writePtr); if (read <= write) { return (uintptr_t) write - (uintptr_t) read; } else { @@ -35,14 +35,14 @@ size_t RingFIFOSize(const struct RingFIFO* buffer) { } void RingFIFOClear(struct RingFIFO* buffer) { - ATOMIC_STORE(buffer->readPtr, buffer->data); - ATOMIC_STORE(buffer->writePtr, buffer->data); + ATOMIC_STORE_PTR(buffer->readPtr, buffer->data); + ATOMIC_STORE_PTR(buffer->writePtr, buffer->data); } size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length) { void* data = buffer->writePtr; void* end; - ATOMIC_LOAD(end, buffer->readPtr); + ATOMIC_LOAD_PTR(end, buffer->readPtr); // Wrap around if we can't fit enough in here if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) { @@ -67,14 +67,14 @@ size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length) if (value) { memcpy(data, value, length); } - ATOMIC_STORE(buffer->writePtr, (void*) ((intptr_t) data + length)); + ATOMIC_STORE_PTR(buffer->writePtr, (void*) ((intptr_t) data + length)); return length; } size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) { void* data = buffer->readPtr; void* end; - ATOMIC_LOAD(end, buffer->writePtr); + ATOMIC_LOAD_PTR(end, buffer->writePtr); // Wrap around if we can't fit enough in here if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) { @@ -99,6 +99,6 @@ size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) { if (output) { memcpy(output, data, length); } - ATOMIC_STORE(buffer->readPtr, (void*) ((uintptr_t) data + length)); + ATOMIC_STORE_PTR(buffer->readPtr, (void*) ((uintptr_t) data + length)); return length; } diff --git a/src/util/test/suite.h b/src/util/test/suite.h index 5fc85938a..fb9d87468 100644 --- a/src/util/test/suite.h +++ b/src/util/test/suite.h @@ -18,7 +18,7 @@ #define M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(NAME, ...) M_TEST_SUITE_DEFINE_EX(NAME, _testSuite_setup_ ## NAME, _testSuite_teardown_ ## NAME, __VA_ARGS__) #define M_TEST_SUITE_DEFINE_EX(NAME, SETUP, TEARDOWN, ...) \ int main(void) { \ - const static struct CMUnitTest tests[] = { \ + static const struct CMUnitTest tests[] = { \ __VA_ARGS__ \ }; \ return cmocka_run_group_tests_name(# NAME, tests, SETUP, TEARDOWN); \