Merge branch 'master' into port/psp2

This commit is contained in:
Jeffrey Pfau 2015-08-21 21:22:03 -07:00
commit 5966f46355
32 changed files with 757 additions and 263 deletions

12
CHANGES
View File

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

View File

@ -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 @@ if(BUILD_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}")

View File

@ -11,6 +11,7 @@
.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 @@
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

View File

@ -7,16 +7,12 @@
#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) \
{ \
DEBUGGER = 0; \
@ -51,7 +47,7 @@ static uint32_t _popcount32(unsigned bits) {
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) { \

View File

@ -17,6 +17,7 @@
#include "util/crc32.h"
#include "util/memory.h"
#include "util/math.h"
#include "util/patch.h"
#include "util/vfs.h"
@ -156,6 +157,7 @@ void GBAReset(struct ARMCore* cpu) {
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 @@ void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char
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 GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char
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 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
return;
}
gba->memory.romSize = patchedSize;
gba->memory.romMask = SIZE_CART0 - 1;
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
}

View File

@ -12,11 +12,11 @@
#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 @@ void GBAMemoryInit(struct GBA* gba) {
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 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
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 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o
_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 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o
_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 @@ void GBAPatch8(struct ARMCore* cpu, uint32_t address, int8_t value, int8_t* old)
_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 @@ uint32_t GBALoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum L
int popcount = 0;
if (direction & LSM_D) {
offset = -4;
popcount = _popcount32(mask);
popcount = popcount32(mask);
address -= (popcount << 2) - 4;
}
@ -1196,7 +1200,7 @@ uint32_t GBAStoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum
int popcount = 0;
if (direction & LSM_D) {
offset = -4;
popcount = _popcount32(mask);
popcount = popcount32(mask);
address -= (popcount << 2) - 4;
}
@ -1588,12 +1592,6 @@ void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedSt
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) {
if (gba->memory.rom != gba->pristineRom) {
return;

View File

@ -118,6 +118,7 @@ struct GBAMemory {
struct GBACartridgeHardware hw;
struct GBASavedata savedata;
size_t romSize;
uint32_t romMask;
uint16_t romID;
int fullBios;

View File

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

View File

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

View File

@ -50,7 +50,7 @@ static const struct option _options[] = {
{ 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 @@ void initParserForGraphics(struct SubParser* parser, struct GraphicsOpts* 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 @@ bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config, int o
struct GraphicsOpts* graphicsOpts = parser->opts;
switch (option) {
case 'f':
graphicsOpts->fullscreen = true;
GBAConfigSetDefaultIntValue(config, "fullscreen", 1);
return true;
case '1':

View File

@ -42,6 +42,7 @@ struct SubParser {
struct GraphicsOpts {
int multiplier;
bool fullscreen;
};
struct GBAThread;

View File

@ -7,12 +7,9 @@
#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 @@ void retro_init(void) {
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 @@ void retro_run(void) {
}
}
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 @@ void retro_reset(void) {
}
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 @@ bool retro_load_game(const struct retro_game_info* game) {
return false;
}
if (!GBAIsROM(rom)) {
rom->close(rom);
free(data);
return false;
}
savedata = malloc(SIZE_CART_FLASH1M);
save = VFileFromMemory(savedata, SIZE_CART_FLASH1M);
struct VFile* save = VFileFromMemory(savedata, SIZE_CART_FLASH1M);
GBALoadROM(&gba, rom, save, game->path);
GBAOverrideApplyDefaults(&gba);
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;

View File

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

View File

@ -167,7 +167,7 @@ install(TARGETS ${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)

View File

@ -126,8 +126,8 @@ ConfigController::~ConfigController() {
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;
}

View File

@ -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 @@ public:
~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);

View File

@ -22,7 +22,7 @@ Display::Driver Display::s_driver = Display::Driver::QT;
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

View File

@ -50,9 +50,12 @@ GBAApp::GBAApp(int& argc, char* argv[])
}
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 @@ GBAApp::GBAApp(int& argc, char* argv[])
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);

View File

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

View File

@ -7,6 +7,7 @@
#include "GamepadAxisEvent.h"
#include "GamepadButtonEvent.h"
#include "ShortcutController.h"
#include <QKeyEvent>
@ -20,6 +21,7 @@ KeyEditor::KeyEditor(QWidget* parent)
, m_button(false)
{
setAlignment(Qt::AlignCenter);
setFocusPolicy(Qt::ClickFocus);
}
void KeyEditor::setValue(int key) {
@ -27,7 +29,11 @@ void KeyEditor::setValue(int 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 @@ QSize KeyEditor::sizeHint() const {
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;
}
}
} 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;
}
event->accept();
return true;
}
return QWidget::event(event);
}

View File

@ -15,7 +15,7 @@ extern "C" {
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)
{

View File

@ -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())

View File

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

View File

@ -37,7 +37,7 @@ QVariant ShortcutController::data(const QModelIndex& index, int role) const {
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 @@ void ShortcutController::addAction(QMenu* menu, QAction* action, const QString&
}
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;
@ -145,6 +145,11 @@ void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press,
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) {
ShortcutItem* smenu = m_menuMap[parentMenu];
if (!smenu) {
@ -179,10 +184,10 @@ const ShortcutController::ShortcutItem* ShortcutController::itemAt(const QModelI
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 @@ bool ShortcutController::isMenuAt(const QModelIndex& index) const {
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,23 +209,28 @@ void ShortcutController::updateKey(const QModelIndex& index, const QKeySequence&
return;
}
ShortcutItem* item = itemAt(index);
if (item->functions().first) {
QKeySequence oldShortcut = item->shortcut();
if (!oldShortcut.isEmpty()) {
m_heldKeys.take(oldShortcut);
}
if (!keySequence.isEmpty()) {
m_heldKeys[keySequence] = item;
}
}
item->setShortcut(keySequence);
updateKey(item, keySequence);
if (m_config) {
m_config->setQtOption(item->name(), keySequence.toString(), KEY_SECTION);
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) {
if (oldShortcut > 0) {
m_heldKeys.take(oldShortcut);
}
if (keySequence > 0) {
m_heldKeys[keySequence] = item;
}
}
item->setShortcut(keySequence);
}
void ShortcutController::updateButton(const QModelIndex& index, int button) {
if (!index.isValid()) {
return;
@ -286,7 +296,7 @@ void ShortcutController::updateAxis(const QModelIndex& index, int axis, GamepadA
}
void ShortcutController::clearKey(const QModelIndex& index) {
updateKey(index, QKeySequence());
updateKey(index, 0);
}
void ShortcutController::clearButton(const QModelIndex& index) {
@ -299,22 +309,27 @@ bool ShortcutController::eventFilter(QObject*, QEvent* 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 @@ void ShortcutController::loadShortcuts(ShortcutItem* item) {
}
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 @@ void ShortcutController::loadGamepadShortcuts(ShortcutItem* item) {
}
}
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 @@ void ShortcutController::onSubitems(ShortcutItem* item, std::function<void(Short
}
}
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 @@ ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& n
.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(ShortcutController::ShortcutItem:
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 @@ void ShortcutController::ShortcutItem::addAction(QAction* action, const QString&
}
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));
}
}

View File

@ -9,7 +9,6 @@
#include "GamepadAxisEvent.h"
#include <QAbstractItemModel>
#include <QKeySequence>
#include <functional>
@ -38,13 +37,13 @@ private:
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 @@ private:
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:
private:
QAction* m_action;
QKeySequence m_shortcut;
int m_shortcut;
QMenu* m_menu;
Functions m_functions;
QString m_name;
@ -99,23 +98,26 @@ public:
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 @@ private:
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;

View File

@ -19,10 +19,14 @@ ShortcutView::ShortcutView(QWidget* parent)
, 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 @@ void ShortcutView::setInputController(InputController* 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 @@ void ShortcutView::load(const QModelIndex& index) {
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 @@ void ShortcutView::clear() {
m_ui.keyEdit->setValueButton(-1);
} else {
m_controller->clearKey(index);
m_ui.keySequenceEdit->setKeySequence(QKeySequence());
m_ui.keyEdit->setValueKey(-1);
}
}
void ShortcutView::updateKey(const QKeySequence& shortcut) {
if (!m_controller || m_controller->isMenuAt(m_ui.shortcutTable->selectionModel()->currentIndex())) {
return;
}
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) {

View File

@ -27,14 +27,12 @@ public:
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);

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>425</width>
<width>432</width>
<height>443</height>
</rect>
</property>
@ -60,16 +60,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">
@ -78,9 +68,6 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="visible">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
@ -95,38 +82,5 @@
</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>

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Window.h"
#include <QDesktopWidget>
#include <QKeyEvent>
#include <QKeySequence>
#include <QMenuBar>
@ -88,11 +89,6 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
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()));
connect(m_controller, SIGNAL(gameStopped(GBAThread*)), m_display, SLOT(stopDrawing()));
@ -465,6 +461,14 @@ void Window::resizeEvent(QResizeEvent* event) {
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 @@ void Window::dropEvent(QDropEvent* event) {
return;
}
event->accept();
m_controller->loadGame(url.path());
m_controller->loadGame(url.toLocalFile());
}
void Window::mouseDoubleClickEvent(QMouseEvent* event) {

View File

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

59
src/util/math.h Normal file
View File

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