all repos — mgba @ 5966f46355d18ba472404a7f7ed3cbeebf846635

mGBA Game Boy Advance Emulator

Merge branch 'master' into port/psp2
Jeffrey Pfau jeffrey@endrift.com
Fri, 21 Aug 2015 21:22:03 -0700
commit

5966f46355d18ba472404a7f7ed3cbeebf846635

parent

9f36284fd1b62a62bdd0bee765895bd8cac8eb72

M CHANGESCHANGES

@@ -1,3 +1,15 @@

+0.4.0: (Future) +Bugfixes: + - Qt: Windows no longer spawn in the top left on first launch + - Qt: Fix install path of XDG desktop file with DESTDIR + - Qt: Fix drag and drop on Windows + - Qt: Reenable double buffering, as disabling it broke some Windows configs +Misc: + - Qt: Window size command line options are now supported + - Qt: Increase usability of key mapper + - GBA Memory: Use a dynamically sized mask for ROM memory + - Qt: Remove useless help icons in dialogs + 0.3.0: (2015-08-16) Features: - Ability to hide individual background layers, or OBJs
M CMakeLists.txtCMakeLists.txt

@@ -18,6 +18,7 @@ set(BUILD_QT ON CACHE BOOL "Build Qt frontend")

set(BUILD_SDL ON CACHE BOOL "Build SDL frontend") set(BUILD_LIBRETRO OFF CACHE BOOL "Build libretro core") set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool") +set(BUILD_TEST OFF CACHE BOOL "Build testing harness") set(BUILD_STATIC OFF CACHE BOOL "Build a static library") set(BUILD_SHARED ON CACHE BOOL "Build a shared library") set(BUILD_GL ON CACHE STRING "Build with OpenGL")

@@ -439,8 +440,12 @@ if(BUILD_QT)

add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/qt ${CMAKE_BINARY_DIR}/qt) endif() +if(PSP2) + add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/psp2 ${CMAKE_BINARY_DIR}/psp2) +endif() + if(BUILD_PERF) - set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/perf-main.c) + set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/test/perf-main.c) if(UNIX AND NOT APPLE) list(APPEND PERF_LIB rt) endif()

@@ -451,8 +456,10 @@ install(TARGETS ${BINARY_NAME}-perf DESTINATION bin COMPONENT ${BINARY_NAME}-perf)

install(FILES ${CMAKE_SOURCE_DIR}/tools/perf.py DESTINATION "${CMAKE_INSTALL_LIBDIR}/${BINARY_NAME}" COMPONENT ${BINARY_NAME}-perf) endif() -if(PSP2) - add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/psp2 ${CMAKE_BINARY_DIR}/psp2) +if(BUILD_TEST) + add_executable(${BINARY_NAME}-fuzz ${CMAKE_SOURCE_DIR}/src/platform/test/fuzz-main.c) + target_link_libraries(${BINARY_NAME}-fuzz ${BINARY_NAME}) + install(TARGETS ${BINARY_NAME}-fuzz DESTINATION bin COMPONENT ${BINARY_NAME}-test) endif() # Packaging

@@ -492,6 +499,7 @@ message(STATUS " Qt: ${BUILD_QT}")

message(STATUS " SDL (${SDL_VERSION}): ${BUILD_SDL}") message(STATUS " Libretro core: ${BUILD_LIBRETRO}") message(STATUS " Profiling: ${BUILD_PERF}") +message(STATUS " Test harness: ${BUILD_TEST}") message(STATUS "Library summary:") message(STATUS " Static: ${BUILD_STATIC}") message(STATUS " Shared: ${BUILD_SHARED}")
M doc/mgba-qt.6doc/mgba-qt.6

@@ -11,6 +11,7 @@ .Nm mgba-qt

.Nd Game Boy Advance emulator .Sh SYNOPSIS .Nm mgba-qt +.Op Fl 123456f .Op Fl b Ar biosfile .Op Fl l Ar loglevel .Op Fl p Ar patchfile

@@ -21,12 +22,26 @@ .Nm

is a Game Boy Advance emulator. The options are as follows: .Bl -tag -width Ds +.It Fl 1 +Scale the window 1\(mu. +.It Fl 2 +Scale the window 2\(mu. +.It Fl 3 +Scale the window 3\(mu. +.It Fl 4 +Scale the window 4\(mu. +.It Fl 5 +Scale the window 5\(mu. +.It Fl 6 +Scale the window 6\(mu. .It Fl b Ar biosfile , Fl -bios Ar biosfile Specify a BIOS file to use during boot. If this flag is omitted, .Nm will use the BIOS specified in the configuration file, or a high\(hylevel emulated BIOS if none is specified. +.It Fl f +Start the emulator full\(hyscreen. .It Fl l Ar loglevel Log messages during emulation. .Ar loglevel
M src/debugger/memory-debugger.csrc/debugger/memory-debugger.c

@@ -7,15 +7,11 @@ #include "memory-debugger.h"

#include "debugger.h" +#include "util/math.h" + #include <string.h> static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct DebuggerEntryInfo* info, int width); - -static uint32_t _popcount32(unsigned bits) { - bits = bits - ((bits >> 1) & 0x55555555); - bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); - return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; -} #define FIND_DEBUGGER(DEBUGGER, CPU) \ { \

@@ -51,7 +47,7 @@ #define CREATE_MULTIPLE_WATCHPOINT_SHIM(NAME) \

static uint32_t ARMDebuggerShim_ ## NAME (struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) { \ struct ARMDebugger* debugger; \ FIND_DEBUGGER(debugger, cpu); \ - uint32_t popcount = _popcount32(mask); \ + uint32_t popcount = popcount32(mask); \ int offset = 4; \ int base = address; \ if (direction & LSM_D) { \
M src/gba/gba.csrc/gba/gba.c

@@ -17,6 +17,7 @@ #include "isa-inlines.h"

#include "util/crc32.h" #include "util/memory.h" +#include "util/math.h" #include "util/patch.h" #include "util/vfs.h"

@@ -156,6 +157,7 @@ }

if (gba->yankedRomSize) { gba->memory.romSize = gba->yankedRomSize; + gba->memory.romMask = toPow2(gba->memory.romSize) - 1; gba->yankedRomSize = 0; } GBAMemoryReset(gba);

@@ -403,6 +405,7 @@ gba->yankedRomSize = 0;

gba->memory.rom = gba->pristineRom; gba->activeFile = fname; gba->memory.romSize = gba->pristineRomSize; + gba->memory.romMask = toPow2(gba->memory.romSize) - 1; gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); GBASavedataInit(&gba->memory.savedata, sav); GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]);

@@ -412,6 +415,7 @@

void GBAYankROM(struct GBA* gba) { gba->yankedRomSize = gba->memory.romSize; gba->memory.romSize = 0; + gba->memory.romMask = 0; GBARaiseIRQ(gba, IRQ_GAMEPAK); }

@@ -452,6 +456,7 @@ gba->memory.rom = gba->pristineRom;

return; } gba->memory.romSize = patchedSize; + gba->memory.romMask = SIZE_CART0 - 1; gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); }
M src/gba/memory.csrc/gba/memory.c

@@ -12,11 +12,11 @@ #include "gba/hardware.h"

#include "gba/io.h" #include "gba/serialize.h" #include "gba/hle-bios.h" +#include "util/math.h" #include "util/memory.h" #define IDLE_LOOP_THRESHOLD 10000 -static uint32_t _popcount32(unsigned bits); static void _pristineCow(struct GBA* gba); static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb

@@ -50,6 +50,7 @@ gba->memory.wram = 0;

gba->memory.iwram = 0; gba->memory.rom = 0; gba->memory.romSize = 0; + gba->memory.romMask = 0; gba->memory.hw.p = gba; int i;

@@ -269,7 +270,7 @@ case REGION_CART1_EX:

case REGION_CART2: case REGION_CART2_EX: cpu->memory.activeRegion = memory->rom; - cpu->memory.activeMask = SIZE_CART0 - 1; + cpu->memory.activeMask = memory->romMask; if ((address & (SIZE_CART0 - 1)) < memory->romSize) { break; }

@@ -893,6 +894,7 @@ case REGION_CART2_EX:

_pristineCow(gba); if ((address & (SIZE_CART0 - 4)) >= gba->memory.romSize) { gba->memory.romSize = (address & (SIZE_CART0 - 4)) + 4; + gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } LOAD_32(oldValue, address & (SIZE_CART0 - 4), gba->memory.rom); STORE_32(value, address & (SIZE_CART0 - 4), gba->memory.rom);

@@ -960,6 +962,7 @@ case REGION_CART2_EX:

_pristineCow(gba); if ((address & (SIZE_CART0 - 1)) >= gba->memory.romSize) { gba->memory.romSize = (address & (SIZE_CART0 - 2)) + 2; + gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } LOAD_16(oldValue, address & (SIZE_CART0 - 2), gba->memory.rom); STORE_16(value, address & (SIZE_CART0 - 2), gba->memory.rom);

@@ -1017,6 +1020,7 @@ case REGION_CART2_EX:

_pristineCow(gba); if ((address & (SIZE_CART0 - 1)) >= gba->memory.romSize) { gba->memory.romSize = (address & (SIZE_CART0 - 2)) + 2; + gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } oldValue = ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)]; ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)] = value;

@@ -1083,7 +1087,7 @@ int offset = 4;

int popcount = 0; if (direction & LSM_D) { offset = -4; - popcount = _popcount32(mask); + popcount = popcount32(mask); address -= (popcount << 2) - 4; }

@@ -1196,7 +1200,7 @@ int offset = 4;

int popcount = 0; if (direction & LSM_D) { offset = -4; - popcount = _popcount32(mask); + popcount = popcount32(mask); address -= (popcount << 2) - 4; }

@@ -1586,12 +1590,6 @@

void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state) { memcpy(memory->wram, state->wram, SIZE_WORKING_RAM); memcpy(memory->iwram, state->iwram, SIZE_WORKING_IRAM); -} - -uint32_t _popcount32(unsigned bits) { - bits = bits - ((bits >> 1) & 0x55555555); - bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); - return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; } void _pristineCow(struct GBA* gba) {
M src/gba/memory.hsrc/gba/memory.h

@@ -118,6 +118,7 @@

struct GBACartridgeHardware hw; struct GBASavedata savedata; size_t romSize; + uint32_t romMask; uint16_t romID; int fullBios;
A src/gba/supervisor/context.c

@@ -0,0 +1,156 @@

+/* 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 "gba/supervisor/context.h" + +#include "gba/supervisor/overrides.h" + +#include "util/memory.h" +#include "util/vfs.h" + +bool GBAContextInit(struct GBAContext* context, const char* port) { + context->gba = anonymousMemoryMap(sizeof(struct GBA)); + context->cpu = anonymousMemoryMap(sizeof(struct ARMCore)); + context->rom = 0; + context->save = 0; + context->renderer = 0; + + if (!context->gba || !context->cpu) { + if (context->gba) { + mappedMemoryFree(context->gba, sizeof(struct GBA)); + } + if (context->cpu) { + mappedMemoryFree(context->cpu, sizeof(struct ARMCore)); + } + return false; + } + + GBAConfigInit(&context->config, port); + if (port) { + GBAConfigLoad(&context->config); + } + + GBACreate(context->gba); + ARMSetComponents(context->cpu, &context->gba->d, 0, 0); + ARMInit(context->cpu); + + context->gba->sync = 0; + return true; +} + +void GBAContextDeinit(struct GBAContext* context) { + if (context->bios) { + context->bios->close(context->bios); + context->bios = 0; + } + if (context->rom) { + context->rom->close(context->rom); + context->rom = 0; + } + if (context->save) { + context->save->close(context->save); + context->save = 0; + } + ARMDeinit(context->cpu); + GBADestroy(context->gba); + mappedMemoryFree(context->gba, 0); + mappedMemoryFree(context->cpu, 0); + GBAConfigDeinit(&context->config); +} + +bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave) { + context->rom = VFileOpen(path, O_RDONLY); + if (!context->rom) { + return false; + } + + if (!GBAIsROM(context->rom)) { + context->rom->close(context->rom); + context->rom = 0; + return false; + } + + if (autoloadSave) { + context->save = VDirOptionalOpenFile(0, path, 0, ".sav", O_RDWR | O_CREAT); + } + return true; +} + +bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save) { + context->rom = rom; + if (!GBAIsROM(context->rom)) { + context->rom = 0; + return false; + } + context->save = save; + return true; +} + +bool GBAContextLoadBIOS(struct GBAContext* context, const char* path) { + context->bios = VFileOpen(path, O_RDONLY); + if (!context->bios) { + return false; + } + + if (!GBAIsBIOS(context->bios)) { + context->bios->close(context->bios); + context->bios = 0; + return false; + } + return true; +} + +bool GBAContextLoadBIOSFromVFile(struct GBAContext* context, struct VFile* bios) { + context->bios = bios; + if (!GBAIsBIOS(context->bios)) { + context->bios = 0; + return false; + } + return true; +} + +bool GBAContextStart(struct GBAContext* context) { + struct GBAOptions opts = {}; + GBAConfigMap(&context->config, &opts); + + if (context->renderer) { + GBAVideoAssociateRenderer(&context->gba->video, context->renderer); + } + + GBALoadROM(context->gba, context->rom, context->save, 0); + if (opts.useBios && context->bios) { + GBALoadBIOS(context->gba, context->bios); + } + + ARMReset(context->cpu); + + if (opts.skipBios) { + GBASkipBIOS(context->cpu); + } + + struct GBACartridgeOverride override; + const struct GBACartridge* cart = (const struct GBACartridge*) context->gba->memory.rom; + memcpy(override.id, &cart->id, sizeof(override.id)); + if (GBAOverrideFind(GBAConfigGetOverrides(&context->config), &override)) { + GBAOverrideApply(context->gba, &override); + } + GBAConfigFreeOpts(&opts); + return true; +} + +void GBAContextStop(struct GBAContext* context) { + UNUSED(context); + // TODO? +} + +void GBAContextFrame(struct GBAContext* context, uint16_t keys) { + int activeKeys = keys; + context->gba->keySource = &activeKeys; + + int frameCounter = context->gba->video.frameCounter; + while (frameCounter == context->gba->video.frameCounter) { + ARMRunLoop(context->cpu); + } +}
A src/gba/supervisor/context.h

@@ -0,0 +1,38 @@

+/* 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 CONTEXT_H +#define CONTEXT_H + +#include "util/common.h" + +#include "gba/supervisor/config.h" +#include "gba/input.h" + +struct GBAContext { + struct GBA* gba; + struct ARMCore* cpu; + struct GBAVideoRenderer* renderer; + struct VFile* rom; + struct VFile* save; + struct VFile* bios; + struct GBAConfig config; + struct GBAOptions opts; + struct GBAInputMap inputMap; +}; + +bool GBAContextInit(struct GBAContext* context, const char* port); +void GBAContextDeinit(struct GBAContext* context); + +bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave); +bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save); +bool GBAContextLoadBIOS(struct GBAContext* context, const char* path); +bool GBAContextLoadBIOSFromVFile(struct GBAContext* context, struct VFile* bios); + +bool GBAContextStart(struct GBAContext* context); +void GBAContextStop(struct GBAContext* context); +void GBAContextFrame(struct GBAContext* context, uint16_t keys); + +#endif
M src/platform/commandline.csrc/platform/commandline.c

@@ -50,7 +50,7 @@ { "patch", required_argument, 0, 'p' },

{ 0, 0, 0, 0 } }; -bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg); +static bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg); bool parseArguments(struct GBAArguments* opts, struct GBAConfig* config, int argc, char* const* argv, struct SubParser* subparser) { int ch;

@@ -145,6 +145,7 @@ parser->opts = opts;

parser->parse = _parseGraphicsArg; parser->extraOptions = GRAPHICS_OPTIONS; opts->multiplier = 0; + opts->fullscreen = false; } bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg) {

@@ -152,6 +153,7 @@ UNUSED(arg);

struct GraphicsOpts* graphicsOpts = parser->opts; switch (option) { case 'f': + graphicsOpts->fullscreen = true; GBAConfigSetDefaultIntValue(config, "fullscreen", 1); return true; case '1':
M src/platform/commandline.hsrc/platform/commandline.h

@@ -42,6 +42,7 @@ };

struct GraphicsOpts { int multiplier; + bool fullscreen; }; struct GBAThread;
M src/platform/libretro/libretro.csrc/platform/libretro/libretro.c

@@ -7,12 +7,9 @@ #include "libretro.h"

#include "util/common.h" -#include "gba/gba.h" -#include "gba/interface.h" #include "gba/renderers/video-software.h" #include "gba/serialize.h" -#include "gba/supervisor/overrides.h" -#include "gba/video.h" +#include "gba/supervisor/context.h" #include "util/circle-buffer.h" #include "util/vfs.h"

@@ -37,14 +34,10 @@ static void _setRumble(struct GBARumble* rumble, int enable);

static uint8_t _readLux(struct GBALuminanceSource* lux); static void _updateLux(struct GBALuminanceSource* lux); -static struct GBA gba; -static struct ARMCore cpu; +static struct GBAContext context; static struct GBAVideoSoftwareRenderer renderer; -static struct VFile* rom; static void* data; -static struct VFile* save; static void* savedata; -static struct VFile* bios; static struct GBAAVStream stream; static int rumbleLevel; static struct CircleBuffer rumbleHistory;

@@ -162,53 +155,49 @@ stream.postAudioFrame = 0;

stream.postAudioBuffer = _postAudioBuffer; stream.postVideoFrame = _postVideoFrame; - GBACreate(&gba); - ARMSetComponents(&cpu, &gba.d, 0, 0); - ARMInit(&cpu); - gba.logLevel = 0; // TODO: Settings - gba.logHandler = GBARetroLog; - gba.stream = &stream; - gba.idleOptimization = IDLE_LOOP_REMOVE; // TODO: Settings + GBAContextInit(&context, 0); + struct GBAOptions opts = { + .useBios = true, + .logLevel = 0, + .idleOptimization = IDLE_LOOP_REMOVE + }; + GBAConfigLoadDefaults(&context.config, &opts); + context.gba->logHandler = GBARetroLog; + context.gba->stream = &stream; if (rumbleCallback) { - gba.rumble = &rumble; + context.gba->rumble = &rumble; } - gba.luminanceSource = &lux; - rom = 0; + context.gba->luminanceSource = &lux; const char* sysDir = 0; if (environCallback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysDir)) { char biosPath[PATH_MAX]; snprintf(biosPath, sizeof(biosPath), "%s%s%s", sysDir, PATH_SEP, "gba_bios.bin"); - bios = VFileOpen(biosPath, O_RDONLY); + struct VFile* bios = VFileOpen(biosPath, O_RDONLY); if (bios) { - GBALoadBIOS(&gba, bios); + GBAContextLoadBIOSFromVFile(&context, bios); } } GBAVideoSoftwareRendererCreate(&renderer); renderer.outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL); renderer.outputBufferStride = 256; - GBAVideoAssociateRenderer(&gba.video, &renderer.d); + GBAVideoAssociateRenderer(&context.gba->video, &renderer.d); - GBAAudioResizeBuffer(&gba.audio, SAMPLES); + GBAAudioResizeBuffer(&context.gba->audio, SAMPLES); #if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF - blip_set_rates(gba.audio.left, GBA_ARM7TDMI_FREQUENCY, 32768); - blip_set_rates(gba.audio.right, GBA_ARM7TDMI_FREQUENCY, 32768); + blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 32768); + blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768); #endif } void retro_deinit(void) { - if (bios) { - bios->close(bios); - bios = 0; - } - GBADestroy(&gba); + GBAContextDeinit(&context); } void retro_run(void) { - int keys; - gba.keySource = &keys; + uint16_t keys; inputPollCallback(); keys = 0;

@@ -243,14 +232,11 @@ wasAdjustingLux = true;

} } - int frameCount = gba.video.frameCounter; - while (gba.video.frameCounter == frameCount) { - ARMRunLoop(&cpu); - } + GBAContextFrame(&context, keys); } void retro_reset(void) { - ARMReset(&cpu); + ARMReset(context.cpu); if (rumbleCallback) { CircleBufferClear(&rumbleHistory);

@@ -258,6 +244,7 @@ }

} bool retro_load_game(const struct retro_game_info* game) { + struct VFile* rom; if (game->data) { data = malloc(game->size); memcpy(data, game->data, game->size);

@@ -270,26 +257,23 @@ if (!rom) {

return false; } if (!GBAIsROM(rom)) { + rom->close(rom); + free(data); return false; } savedata = malloc(SIZE_CART_FLASH1M); - save = VFileFromMemory(savedata, SIZE_CART_FLASH1M); - - GBALoadROM(&gba, rom, save, game->path); - GBAOverrideApplyDefaults(&gba); + struct VFile* save = VFileFromMemory(savedata, SIZE_CART_FLASH1M); - ARMReset(&cpu); + GBAContextLoadROMFromVFile(&context, rom, save); + GBAContextStart(&context); return true; } void retro_unload_game(void) { - rom->close(rom); - rom = 0; + GBAContextStop(&context); free(data); data = 0; - save->close(save); - save = 0; free(savedata); savedata = 0; CircleBufferDeinit(&rumbleHistory);

@@ -303,7 +287,7 @@ bool retro_serialize(void* data, size_t size) {

if (size != retro_serialize_size()) { return false; } - GBASerialize(&gba, data); + GBASerialize(context.gba, data); return true; }

@@ -311,7 +295,7 @@ bool retro_unserialize(const void* data, size_t size) {

if (size != retro_serialize_size()) { return false; } - GBADeserialize(&gba, data); + GBADeserialize(context.gba, data); return true; }

@@ -353,7 +337,7 @@ size_t retro_get_memory_size(unsigned id) {

if (id != RETRO_MEMORY_SAVE_RAM) { return 0; } - switch (gba.memory.savedata.type) { + switch (context.gba->memory.savedata.type) { case SAVEDATA_AUTODETECT: case SAVEDATA_FLASH1M: return SIZE_CART_FLASH1M;
M src/platform/qt/AboutScreen.cppsrc/platform/qt/AboutScreen.cpp

@@ -12,7 +12,7 @@

using namespace QGBA; AboutScreen::AboutScreen(QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) { m_ui.setupUi(this);
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -167,7 +167,7 @@ BUNDLE DESTINATION Applications COMPONENT ${BINARY_NAME}-qt)

if(UNIX AND NOT APPLE) find_program(DESKTOP_FILE_INSTALL desktop-file-install) if(DESKTOP_FILE_INSTALL) - install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/share/applications/\")") + install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications/\")") endif() endif() if(UNIX)
M src/platform/qt/ConfigController.cppsrc/platform/qt/ConfigController.cpp

@@ -126,8 +126,8 @@ GBAConfigDeinit(&m_config);

GBAConfigFreeOpts(&m_opts); } -bool ConfigController::parseArguments(GBAArguments* args, int argc, char* argv[]) { - if (::parseArguments(args, &m_config, argc, argv, 0)) { +bool ConfigController::parseArguments(GBAArguments* args, int argc, char* argv[], SubParser* subparser) { + if (::parseArguments(args, &m_config, argc, argv, subparser)) { GBAConfigMap(&m_config, &m_opts); return true; }
M src/platform/qt/ConfigController.hsrc/platform/qt/ConfigController.h

@@ -16,6 +16,7 @@

extern "C" { #include "gba/supervisor/config.h" #include "util/configuration.h" +#include "platform/commandline.h" } class QAction;

@@ -64,7 +65,7 @@ ConfigController(QObject* parent = nullptr);

~ConfigController(); const GBAOptions* options() const { return &m_opts; } - bool parseArguments(GBAArguments* args, int argc, char* argv[]); + bool parseArguments(GBAArguments* args, int argc, char* argv[], SubParser* subparser = nullptr); ConfigOption* addOption(const char* key); void updateOption(const char* key);
M src/platform/qt/Display.cppsrc/platform/qt/Display.cpp

@@ -22,7 +22,7 @@ #endif

Display* Display::create(QWidget* parent) { #ifdef BUILD_GL - QGLFormat format(QGLFormat(QGL::Rgba | QGL::SingleBuffer)); + QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer)); format.setSwapInterval(1); #endif
M src/platform/qt/GBAApp.cppsrc/platform/qt/GBAApp.cpp

@@ -50,9 +50,12 @@ Display::setDriver(static_cast<Display::Driver>(m_configController.getQtOption("displayDriver").toInt()));

} GBAArguments args; - bool loaded = m_configController.parseArguments(&args, argc, argv); + GraphicsOpts graphicsOpts; + SubParser subparser; + initParserForGraphics(&subparser, &graphicsOpts); + bool loaded = m_configController.parseArguments(&args, argc, argv, &subparser); if (loaded && args.showHelp) { - usage(argv[0], 0); + usage(argv[0], subparser.usage); ::exit(0); return; }

@@ -72,6 +75,14 @@ } else {

w->loadConfig(); } freeArguments(&args); + + if (graphicsOpts.multiplier) { + w->resizeFrame(VIDEO_HORIZONTAL_PIXELS * graphicsOpts.multiplier, VIDEO_VERTICAL_PIXELS * graphicsOpts.multiplier); + } + if (graphicsOpts.fullscreen) { + w->enterFullScreen(); + } + w->show(); w->controller()->setMultiplayerController(&m_multiplayer);
M src/platform/qt/GDBWindow.cppsrc/platform/qt/GDBWindow.cpp

@@ -18,7 +18,7 @@

using namespace QGBA; GDBWindow::GDBWindow(GDBController* controller, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_gdbController(controller) { setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint);
M src/platform/qt/KeyEditor.cppsrc/platform/qt/KeyEditor.cpp

@@ -7,6 +7,7 @@ #include "KeyEditor.h"

#include "GamepadAxisEvent.h" #include "GamepadButtonEvent.h" +#include "ShortcutController.h" #include <QKeyEvent>

@@ -20,6 +21,7 @@ , m_axis(-1)

, m_button(false) { setAlignment(Qt::AlignCenter); + setFocusPolicy(Qt::ClickFocus); } void KeyEditor::setValue(int key) {

@@ -27,7 +29,11 @@ m_key = key;

if (m_button) { updateButtonText(); } else { - setText(QKeySequence(key).toString(QKeySequence::NativeText)); + if (key < 0) { + setText(tr("---")); + } else { + setText(QKeySequence(key).toString(QKeySequence::NativeText)); + } } emit valueChanged(key); }

@@ -71,27 +77,60 @@ }

void KeyEditor::keyPressEvent(QKeyEvent* event) { if (!m_button) { - setValue(event->key()); + if (m_key < 0) { + m_key = 0; + } + if (ShortcutController::isModifierKey(event->key())) { + switch (event->key()) { + case Qt::Key_Shift: + setValue(m_key | Qt::ShiftModifier); + break; + case Qt::Key_Control: + setValue(m_key | Qt::ControlModifier); + break; + case Qt::Key_Alt: + setValue(m_key | Qt::AltModifier); + break; + case Qt::Key_Meta: + setValue(m_key | Qt::MetaModifier); + break; + default: + setValue(m_key); + } + } else { + setValue(event->key() | (m_key & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))); + } } event->accept(); } bool KeyEditor::event(QEvent* event) { if (!m_button) { - return QWidget::event(event); - } - if (event->type() == GamepadButtonEvent::Down()) { - setValueButton(static_cast<GamepadButtonEvent*>(event)->value()); - event->accept(); - return true; - } - if (event->type() == GamepadAxisEvent::Type()) { - GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event); - if (gae->isNew()) { - setValueAxis(gae->axis(), gae->direction()); + if (event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); + if (keyEvent->key() != Qt::Key_Tab && keyEvent->key() != Qt::Key_Backtab) { + return QWidget::event(event); + } + if (!(keyEvent->modifiers() & ~Qt::ShiftModifier)) { + keyPressEvent(keyEvent); + keyEvent->accept(); + return true; + } } - event->accept(); - return true; + } else { + if (event->type() == GamepadButtonEvent::Down()) { + setValueButton(static_cast<GamepadButtonEvent*>(event)->value()); + event->accept(); + return true; + } + if (event->type() == GamepadAxisEvent::Type()) { + GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event); + if (gae->isNew()) { + setValueAxis(gae->axis(), gae->direction()); + } + event->accept(); + return true; + } } return QWidget::event(event); }
M src/platform/qt/OverrideView.cppsrc/platform/qt/OverrideView.cpp

@@ -15,7 +15,7 @@

using namespace QGBA; OverrideView::OverrideView(GameController* controller, ConfigController* config, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) , m_config(config) {
M src/platform/qt/SensorView.cppsrc/platform/qt/SensorView.cpp

@@ -12,7 +12,7 @@

using namespace QGBA; SensorView::SensorView(GameController* controller, InputController* input, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) , m_input(input) , m_rotation(input->rotationSource())
M src/platform/qt/SettingsView.cppsrc/platform/qt/SettingsView.cpp

@@ -13,7 +13,7 @@

using namespace QGBA; SettingsView::SettingsView(ConfigController* controller, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) { m_ui.setupUi(this);
M src/platform/qt/ShortcutController.cppsrc/platform/qt/ShortcutController.cpp

@@ -37,7 +37,7 @@ switch (index.column()) {

case 0: return item->visibleName(); case 1: - return item->shortcut().toString(QKeySequence::NativeText); + return QKeySequence(item->shortcut()).toString(QKeySequence::NativeText); case 2: if (item->button() >= 0) { return item->button();

@@ -125,7 +125,7 @@ createIndex(smenu->items().count() - 1, 2, item));

} void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, - const QKeySequence& shortcut, const QString& visibleName, const QString& name) { + int shortcut, const QString& visibleName, const QString& name) { ShortcutItem* smenu = m_menuMap[menu]; if (!smenu) { return;

@@ -143,6 +143,11 @@ }

m_heldKeys[shortcut] = item; emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), createIndex(smenu->items().count() - 1, 2, item)); +} + +void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, + const QKeySequence& shortcut, const QString& visibleName, const QString& name) { + addFunctions(menu, press, release, shortcut[0], visibleName, name); } void ShortcutController::addMenu(QMenu* menu, QMenu* parentMenu) {

@@ -179,10 +184,10 @@ }

return static_cast<const ShortcutItem*>(index.internalPointer()); } -QKeySequence ShortcutController::shortcutAt(const QModelIndex& index) const { +int ShortcutController::shortcutAt(const QModelIndex& index) const { const ShortcutItem* item = itemAt(index); if (!item) { - return QKeySequence(); + return 0; } return item->shortcut(); }

@@ -195,7 +200,7 @@ }

return item->menu(); } -void ShortcutController::updateKey(const QModelIndex& index, const QKeySequence& keySequence) { +void ShortcutController::updateKey(const QModelIndex& index, int keySequence) { if (!index.isValid()) { return; }

@@ -204,21 +209,26 @@ if (!parent.isValid()) {

return; } ShortcutItem* item = itemAt(index); + updateKey(item, keySequence); + if (m_config) { + m_config->setQtOption(item->name(), QKeySequence(keySequence).toString(), KEY_SECTION); + } + emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), + createIndex(index.row(), 2, index.internalPointer())); +} + +void ShortcutController::updateKey(ShortcutItem* item, int keySequence) { + int oldShortcut = item->shortcut(); if (item->functions().first) { - QKeySequence oldShortcut = item->shortcut(); - if (!oldShortcut.isEmpty()) { + if (oldShortcut > 0) { m_heldKeys.take(oldShortcut); } - if (!keySequence.isEmpty()) { + if (keySequence > 0) { m_heldKeys[keySequence] = item; } } + item->setShortcut(keySequence); - if (m_config) { - m_config->setQtOption(item->name(), keySequence.toString(), KEY_SECTION); - } - emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), - createIndex(index.row(), 2, index.internalPointer())); } void ShortcutController::updateButton(const QModelIndex& index, int button) {

@@ -286,7 +296,7 @@ createIndex(index.row(), 2, index.internalPointer()));

} void ShortcutController::clearKey(const QModelIndex& index) { - updateKey(index, QKeySequence()); + updateKey(index, 0); } void ShortcutController::clearButton(const QModelIndex& index) {

@@ -299,22 +309,27 @@ QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);

if (keyEvent->isAutoRepeat()) { return false; } - auto item = m_heldKeys.find(keyEventToSequence(keyEvent)); - if (item == m_heldKeys.end()) { - return false; - } - ShortcutItem::Functions pair = item.value()->functions(); - if (event->type() == QEvent::KeyPress) { - if (pair.first) { - pair.first(); - } + int key = keyEvent->key(); + if (!isModifierKey(key)) { + key |= keyEvent->modifiers(); } else { - if (pair.second) { - pair.second(); + key = toModifierKey(key | keyEvent->modifiers()); + } + auto item = m_heldKeys.find(key); + if (item != m_heldKeys.end()) { + ShortcutItem::Functions pair = item.value()->functions(); + if (event->type() == QEvent::KeyPress) { + if (pair.first) { + pair.first(); + } + } else { + if (pair.second) { + pair.second(); + } } + event->accept(); + return true; } - event->accept(); - return true; } if (event->type() == GamepadButtonEvent::Down()) { auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());

@@ -378,15 +393,11 @@ return;

} QVariant shortcut = m_config->getQtOption(item->name(), KEY_SECTION); if (!shortcut.isNull()) { - QKeySequence keySequence(shortcut.toString()); - if (item->functions().first) { - QKeySequence oldShortcut = item->shortcut(); - if (!oldShortcut.isEmpty()) { - m_heldKeys.take(oldShortcut); - } - m_heldKeys[keySequence] = item; + if (shortcut.toString().endsWith("+")) { + updateKey(item, toModifierShortcut(shortcut.toString())); + } else { + updateKey(item, QKeySequence(shortcut.toString())[0]); } - item->setShortcut(keySequence); } loadGamepadShortcuts(item); }

@@ -446,26 +457,6 @@ }

} } -QKeySequence ShortcutController::keyEventToSequence(const QKeyEvent* event) { - QString modifier = QString::null; - - if (event->modifiers() & Qt::ShiftModifier) { - modifier += "Shift+"; - } - if (event->modifiers() & Qt::ControlModifier) { - modifier += "Ctrl+"; - } - if (event->modifiers() & Qt::AltModifier) { - modifier += "Alt+"; - } - if (event->modifiers() & Qt::MetaModifier) { - modifier += "Meta+"; - } - - QString key = QKeySequence(event->key()).toString(); - return QKeySequence(modifier + key); -} - void ShortcutController::loadProfile(const QString& profile) { m_profileName = profile; m_profile = InputProfile::findProfile(profile);

@@ -481,9 +472,69 @@ onSubitems(&subitem, func);

} } +int ShortcutController::toModifierShortcut(const QString& shortcut) { + // Qt doesn't seem to work with raw modifier shortcuts! + QStringList modifiers = shortcut.split('+'); + int value = 0; + for (const auto& mod : modifiers) { + if (mod == QLatin1String("Shift")) { + value |= Qt::ShiftModifier; + continue; + } + if (mod == QLatin1String("Ctrl")) { + value |= Qt::ControlModifier; + continue; + } + if (mod == QLatin1String("Alt")) { + value |= Qt::AltModifier; + continue; + } + if (mod == QLatin1String("Meta")) { + value |= Qt::MetaModifier; + continue; + } + } + return value; +} + +bool ShortcutController::isModifierKey(int key) { + switch (key) { + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Alt: + case Qt::Key_Meta: + return true; + default: + return false; + } +} + +int ShortcutController::toModifierKey(int key) { + int modifiers = key & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier); + key ^= modifiers; + switch (key) { + case Qt::Key_Shift: + modifiers |= Qt::ShiftModifier; + break; + case Qt::Key_Control: + modifiers |= Qt::ControlModifier; + break; + case Qt::Key_Alt: + modifiers |= Qt::AltModifier; + break; + case Qt::Key_Meta: + modifiers |= Qt::MetaModifier; + break; + default: + break; + } + return modifiers; + +} + ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent) : m_action(action) - , m_shortcut(action->shortcut()) + , m_shortcut(action->shortcut().isEmpty() ? 0 : action->shortcut()[0]) , m_menu(nullptr) , m_name(name) , m_button(-1)

@@ -496,7 +547,7 @@ .remove(QRegExp("&(?!&)"))

.remove("..."); } -ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent) +ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, int shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent) : m_action(nullptr) , m_shortcut(shortcut) , m_functions(functions)

@@ -512,6 +563,7 @@ }

ShortcutController::ShortcutItem::ShortcutItem(QMenu* menu, ShortcutItem* parent) : m_action(nullptr) + , m_shortcut(0) , m_menu(menu) , m_button(-1) , m_axis(-1)

@@ -530,7 +582,7 @@ m_items.append(ShortcutItem(action, name, this));

} void ShortcutController::ShortcutItem::addFunctions(ShortcutController::ShortcutItem::Functions functions, - const QKeySequence& shortcut, const QString& visibleName, + int shortcut, const QString& visibleName, const QString& name) { m_items.append(ShortcutItem(functions, shortcut, visibleName, name, this)); }

@@ -539,10 +591,10 @@ void ShortcutController::ShortcutItem::addSubmenu(QMenu* menu) {

m_items.append(ShortcutItem(menu, this)); } -void ShortcutController::ShortcutItem::setShortcut(const QKeySequence& shortcut) { +void ShortcutController::ShortcutItem::setShortcut(int shortcut) { m_shortcut = shortcut; if (m_action) { - m_action->setShortcut(shortcut); + m_action->setShortcut(QKeySequence(shortcut)); } }
M src/platform/qt/ShortcutController.hsrc/platform/qt/ShortcutController.h

@@ -9,7 +9,6 @@

#include "GamepadAxisEvent.h" #include <QAbstractItemModel> -#include <QKeySequence> #include <functional>

@@ -38,13 +37,13 @@ public:

typedef QPair<std::function<void ()>, std::function<void ()>> Functions; ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent = nullptr); - ShortcutItem(Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name, + ShortcutItem(Functions functions, int shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent = nullptr); ShortcutItem(QMenu* action, ShortcutItem* parent = nullptr); QAction* action() { return m_action; } const QAction* action() const { return m_action; } - const QKeySequence& shortcut() const { return m_shortcut; } + const int shortcut() const { return m_shortcut; } Functions functions() const { return m_functions; } QMenu* menu() { return m_menu; } const QMenu* menu() const { return m_menu; }

@@ -55,11 +54,11 @@ const QList<ShortcutItem>& items() const { return m_items; }

ShortcutItem* parent() { return m_parent; } const ShortcutItem* parent() const { return m_parent; } void addAction(QAction* action, const QString& name); - void addFunctions(Functions functions, const QKeySequence& shortcut, const QString& visibleName, + void addFunctions(Functions functions, int shortcut, const QString& visibleName, const QString& name); void addSubmenu(QMenu* menu); int button() const { return m_button; } - void setShortcut(const QKeySequence& sequence); + void setShortcut(int sequence); void setButton(int button) { m_button = button; } int axis() const { return m_axis; } GamepadAxisEvent::Direction direction() const { return m_direction; }

@@ -71,7 +70,7 @@ }

private: QAction* m_action; - QKeySequence m_shortcut; + int m_shortcut; QMenu* m_menu; Functions m_functions; QString m_name;

@@ -99,23 +98,26 @@ virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;

virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; void addAction(QMenu* menu, QAction* action, const QString& name); + void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, + int shortcut, const QString& visibleName, const QString& name); void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, const QKeySequence& shortcut, const QString& visibleName, const QString& name); void addMenu(QMenu* menu, QMenu* parent = nullptr); QAction* getAction(const QString& name); - - QKeySequence shortcutAt(const QModelIndex& index) const; + int shortcutAt(const QModelIndex& index) const; bool isMenuAt(const QModelIndex& index) const; - void updateKey(const QModelIndex& index, const QKeySequence& keySequence); + void updateKey(const QModelIndex& index, int keySequence); void updateButton(const QModelIndex& index, int button); void updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction); void clearKey(const QModelIndex& index); void clearButton(const QModelIndex& index); - static QKeySequence keyEventToSequence(const QKeyEvent*); + static int toModifierShortcut(const QString& shortcut); + static bool isModifierKey(int key); + static int toModifierKey(int key); public slots: void loadProfile(const QString& profile);

@@ -129,12 +131,13 @@ const ShortcutItem* itemAt(const QModelIndex& index) const;

void loadShortcuts(ShortcutItem*); void loadGamepadShortcuts(ShortcutItem*); void onSubitems(ShortcutItem*, std::function<void(ShortcutItem*)> func); + void updateKey(ShortcutItem* item, int keySequence); ShortcutItem m_rootMenu; QMap<QMenu*, ShortcutItem*> m_menuMap; QMap<int, ShortcutItem*> m_buttons; QMap<QPair<int, GamepadAxisEvent::Direction>, ShortcutItem*> m_axes; - QMap<QKeySequence, ShortcutItem*> m_heldKeys; + QMap<int, ShortcutItem*> m_heldKeys; ConfigController* m_config; QString m_profileName; const InputProfile* m_profile;
M src/platform/qt/ShortcutView.cppsrc/platform/qt/ShortcutView.cpp

@@ -19,10 +19,14 @@ , m_controller(nullptr)

, m_input(nullptr) { m_ui.setupUi(this); - m_ui.keyEdit->setValueButton(-1); - m_ui.keySequenceEdit->installEventFilter(this); + m_ui.keyEdit->setValueKey(0); - connect(m_ui.keySequenceEdit, SIGNAL(keySequenceChanged(const QKeySequence&)), this, SLOT(updateKey(const QKeySequence&))); + connect(m_ui.gamepadButton, &QAbstractButton::pressed, [this]() { + m_ui.keyEdit->setValueButton(-1); + }); + connect(m_ui.keyboardButton, &QAbstractButton::pressed, [this]() { + m_ui.keyEdit->setValueKey(0); + }); connect(m_ui.keyEdit, SIGNAL(valueChanged(int)), this, SLOT(updateButton(int))); connect(m_ui.keyEdit, SIGNAL(axisChanged(int, int)), this, SLOT(updateAxis(int, int))); connect(m_ui.shortcutTable, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(load(const QModelIndex&)));

@@ -42,21 +46,6 @@ m_input = controller;

m_input->stealFocus(this); } -bool ShortcutView::eventFilter(QObject*, QEvent* event) { - if (event->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); - if (keyEvent->key() != Qt::Key_Tab && keyEvent->key() != Qt::Key_Backtab) { - return false; - } - if (!(keyEvent->modifiers() & ~Qt::ShiftModifier)) { - m_ui.keySequenceEdit->setKeySequence(ShortcutController::keyEventToSequence(keyEvent)); - keyEvent->accept(); - return true; - } - } - return false; -} - void ShortcutView::load(const QModelIndex& index) { if (!m_controller) { return;

@@ -64,23 +53,20 @@ }

if (m_controller->isMenuAt(index)) { return; } - QKeySequence sequence = m_controller->shortcutAt(index); + int shortcut = m_controller->shortcutAt(index); if (index.column() == 1) { m_ui.keyboardButton->click(); } else if (index.column() == 2) { m_ui.gamepadButton->click(); } + bool blockSignals = m_ui.keyEdit->blockSignals(true); + m_ui.keyEdit->setFocus(Qt::MouseFocusReason); if (m_ui.gamepadButton->isChecked()) { - bool blockSignals = m_ui.keyEdit->blockSignals(true); - m_ui.keyEdit->setFocus(); m_ui.keyEdit->setValueButton(-1); // There are no default bindings - m_ui.keyEdit->blockSignals(blockSignals); } else { - bool blockSignals = m_ui.keySequenceEdit->blockSignals(true); - m_ui.keySequenceEdit->setFocus(); - m_ui.keySequenceEdit->setKeySequence(sequence); - m_ui.keySequenceEdit->blockSignals(blockSignals); + m_ui.keyEdit->setValueKey(shortcut); } + m_ui.keyEdit->blockSignals(blockSignals); } void ShortcutView::clear() {

@@ -96,22 +82,19 @@ m_controller->clearButton(index);

m_ui.keyEdit->setValueButton(-1); } else { m_controller->clearKey(index); - m_ui.keySequenceEdit->setKeySequence(QKeySequence()); - } -} - -void ShortcutView::updateKey(const QKeySequence& shortcut) { - if (!m_controller || m_controller->isMenuAt(m_ui.shortcutTable->selectionModel()->currentIndex())) { - return; + m_ui.keyEdit->setValueKey(-1); } - m_controller->updateKey(m_ui.shortcutTable->selectionModel()->currentIndex(), shortcut); } void ShortcutView::updateButton(int button) { if (!m_controller || m_controller->isMenuAt(m_ui.shortcutTable->selectionModel()->currentIndex())) { return; } - m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + if (m_ui.gamepadButton->isChecked()) { + m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + } else { + m_controller->updateKey(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + } } void ShortcutView::updateAxis(int axis, int direction) {
M src/platform/qt/ShortcutView.hsrc/platform/qt/ShortcutView.h

@@ -27,14 +27,12 @@ void setController(ShortcutController* controller);

void setInputController(InputController* input); protected: - virtual bool eventFilter(QObject* obj, QEvent* event) override; virtual bool event(QEvent*) override; virtual void closeEvent(QCloseEvent*) override; private slots: void load(const QModelIndex&); void clear(); - void updateKey(const QKeySequence&); void updateButton(int button); void updateAxis(int axis, int direction);
M src/platform/qt/ShortcutView.uisrc/platform/qt/ShortcutView.ui

@@ -6,7 +6,7 @@ <property name="geometry">

<rect> <x>0</x> <y>0</y> - <width>425</width> + <width>432</width> <height>443</height> </rect> </property>

@@ -61,16 +61,6 @@ </property>

</spacer> </item> <item> - <widget class="QKeySequenceEdit" name="keySequenceEdit"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> - <item> <widget class="QGBA::KeyEditor" name="keyEdit"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">

@@ -78,9 +68,6 @@ <horstretch>0</horstretch>

<verstretch>0</verstretch> </sizepolicy> </property> - <property name="visible"> - <bool>false</bool> - </property> </widget> </item> </layout>

@@ -95,38 +82,5 @@ <header>KeyEditor.h</header>

</customwidget> </customwidgets> <resources/> - <connections> - <connection> - <sender>keyboardButton</sender> - <signal>toggled(bool)</signal> - <receiver>keySequenceEdit</receiver> - <slot>setVisible(bool)</slot> - <hints> - <hint type="sourcelabel"> - <x>86</x> - <y>374</y> - </hint> - <hint type="destinationlabel"> - <x>66</x> - <y>20</y> - </hint> - </hints> - </connection> - <connection> - <sender>gamepadButton</sender> - <signal>toggled(bool)</signal> - <receiver>keyEdit</receiver> - <slot>setVisible(bool)</slot> - <hints> - <hint type="sourcelabel"> - <x>213</x> - <y>374</y> - </hint> - <hint type="destinationlabel"> - <x>206</x> - <y>340</y> - </hint> - </hints> - </connection> - </connections> + <connections/> </ui>
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -5,6 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "Window.h" +#include <QDesktopWidget> #include <QKeyEvent> #include <QKeySequence> #include <QMenuBar>

@@ -87,11 +88,6 @@ m_screenWidget->setSizeHint(m_display->minimumSize() * 2);

m_screenWidget->setPixmap(m_logo); m_screenWidget->setLockAspectRatio(m_logo.width(), m_logo.height()); setCentralWidget(m_screenWidget); - - QVariant windowPos = m_config->getQtOption("windowPos"); - if (!windowPos.isNull()) { - move(windowPos.toPoint()); - } connect(m_controller, SIGNAL(gameStarted(GBAThread*)), this, SLOT(gameStarted(GBAThread*))); connect(m_controller, SIGNAL(gameStarted(GBAThread*)), &m_inputController, SLOT(suspendScreensaver()));

@@ -465,6 +461,14 @@ }

void Window::showEvent(QShowEvent* event) { resizeFrame(m_screenWidget->sizeHint().width(), m_screenWidget->sizeHint().height()); + QVariant windowPos = m_config->getQtOption("windowPos"); + if (!windowPos.isNull()) { + move(windowPos.toPoint()); + } else { + QRect rect = frameGeometry(); + rect.moveCenter(QApplication::desktop()->availableGeometry().center()); + move(rect.topLeft()); + } } void Window::closeEvent(QCloseEvent* event) {

@@ -503,7 +507,7 @@ // No remote loading

return; } event->accept(); - m_controller->loadGame(url.path()); + m_controller->loadGame(url.toLocalFile()); } void Window::mouseDoubleClickEvent(QMouseEvent* event) {
A src/platform/test/fuzz-main.c

@@ -0,0 +1,174 @@

+/* 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 "gba/supervisor/config.h" +#include "gba/supervisor/context.h" +#include "gba/gba.h" +#include "gba/renderers/video-software.h" +#include "gba/serialize.h" + +#include "platform/commandline.h" +#include "util/memory.h" +#include "util/string.h" +#include "util/vfs.h" + +#include <errno.h> +#include <signal.h> + +#define FUZZ_OPTIONS "F:NO:S:V:" +#define FUZZ_USAGE \ + "\nAdditional options:\n" \ + " -F FRAMES Run for the specified number of FRAMES before exiting\n" \ + " -N Disable video rendering entirely\n" \ + " -O OFFSET Offset to apply savestate overlay\n" \ + " -S FILE Load a savestate when starting the test\n" \ + " -V FILE Overlay a second savestate over the loaded savestate\n" \ + +struct FuzzOpts { + bool noVideo; + int frames; + size_t overlayOffset; + char* savestate; + char* ssOverlay; +}; + +static void _GBAFuzzRunloop(struct GBAContext* context, int frames); +static void _GBAFuzzShutdown(int signal); +static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg); + +static bool _dispatchExiting = false; + +int main(int argc, char** argv) { + signal(SIGINT, _GBAFuzzShutdown); + + struct FuzzOpts fuzzOpts = { false, 0, 0, 0, 0 }; + struct SubParser subparser = { + .usage = FUZZ_USAGE, + .parse = _parseFuzzOpts, + .extraOptions = FUZZ_OPTIONS, + .opts = &fuzzOpts + }; + + struct GBAContext context; + GBAContextInit(&context, "fuzz"); + struct GBAOptions opts = { + .idleOptimization = IDLE_LOOP_DETECT + }; + GBAConfigLoadDefaults(&context.config, &opts); + GBAConfigFreeOpts(&opts); + + struct GBAArguments args; + bool parsed = parseArguments(&args, &context.config, argc, argv, &subparser); + if (!parsed || args.showHelp) { + usage(argv[0], FUZZ_USAGE); + freeArguments(&args); + GBAContextDeinit(&context); + return !parsed; + } + + struct VFile* rom = VFileOpen(args.fname, O_RDONLY); + + context.gba->hardCrash = false; + GBAContextLoadROMFromVFile(&context, rom, 0); + + struct GBAVideoSoftwareRenderer renderer; + renderer.outputBuffer = 0; + + struct VFile* savestate = 0; + struct VFile* savestateOverlay = 0; + size_t overlayOffset; + + if (!fuzzOpts.noVideo) { + GBAVideoSoftwareRendererCreate(&renderer); + renderer.outputBuffer = malloc(256 * 256 * 4); + renderer.outputBufferStride = 256; + GBAVideoAssociateRenderer(&context.gba->video, &renderer.d); + } + + GBAContextStart(&context); + + if (fuzzOpts.savestate) { + savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY); + free(fuzzOpts.savestate); + } + if (fuzzOpts.ssOverlay) { + overlayOffset = fuzzOpts.overlayOffset; + if (overlayOffset < sizeof(struct GBASerializedState)) { + savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY); + } + free(fuzzOpts.ssOverlay); + } + if (savestate) { + if (!savestateOverlay) { + GBALoadStateNamed(context.gba, savestate); + } else { + struct GBASerializedState* state = GBAAllocateState(); + savestate->read(savestate, state, sizeof(*state)); + savestateOverlay->read(savestateOverlay, (uint8_t*) state + overlayOffset, sizeof(*state) - overlayOffset); + GBADeserialize(context.gba, state); + GBADeallocateState(state); + savestateOverlay->close(savestateOverlay); + savestateOverlay = 0; + } + savestate->close(savestate); + savestate = 0; + } + + blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 0x8000); + blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 0x8000); + + _GBAFuzzRunloop(&context, fuzzOpts.frames); + + if (savestate) { + savestate->close(savestate); + } + if (savestateOverlay) { + savestateOverlay->close(savestateOverlay); + } + GBAContextStop(&context); + GBAContextDeinit(&context); + freeArguments(&args); + if (renderer.outputBuffer) { + free(renderer.outputBuffer); + } + + return 0; +} + +static void _GBAFuzzRunloop(struct GBAContext* context, int frames) { + do { + GBAContextFrame(context, 0); + } while (context->gba->video.frameCounter < frames && !_dispatchExiting); +} + +static void _GBAFuzzShutdown(int signal) { + UNUSED(signal); + _dispatchExiting = true; +} + +static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg) { + UNUSED(config); + struct FuzzOpts* opts = parser->opts; + errno = 0; + switch (option) { + case 'F': + opts->frames = strtoul(arg, 0, 10); + return !errno; + case 'N': + opts->noVideo = true; + return true; + case 'O': + opts->overlayOffset = strtoul(arg, 0, 10); + return !errno; + case 'S': + opts->savestate = strdup(arg); + return true; + case 'V': + opts->ssOverlay = strdup(arg); + return true; + default: + return false; + } +}
A src/util/math.h

@@ -0,0 +1,59 @@

+/* 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 UTIL_MATH_H +#define UTIL_MATH_H + +#include "util/common.h" + +static inline uint32_t popcount32(unsigned bits) { + bits = bits - ((bits >> 1) & 0x55555555); + bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); + return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +static inline unsigned clz32(uint32_t bits) { +#if defined(__GNUC__) || __clang__ + return __builtin_clz(bits); +#else + static const int table[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + if (bits & 0xFF000000) { + return table[bits >> 24]; + } else if (bits & 0x00FF0000) { + return table[bits >> 16] + 8; + } else if (bits & 0x0000FF00) { + return table[bits >> 8] + 16; + } + return table[bits] + 24; +#endif +} + +static inline uint32_t toPow2(uint32_t bits) { + if (!bits) { + return 0; + } + unsigned lz = clz32(bits - 1); + return 1 << (32 - lz); +} + +#endif