mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into port/psp2
This commit is contained in:
commit
5966f46355
12
CHANGES
12
CHANGES
|
@ -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
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) { \
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -118,6 +118,7 @@ struct GBAMemory {
|
|||
struct GBACartridgeHardware hw;
|
||||
struct GBASavedata savedata;
|
||||
size_t romSize;
|
||||
uint32_t romMask;
|
||||
uint16_t romID;
|
||||
int fullBios;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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':
|
||||
|
|
|
@ -42,6 +42,7 @@ struct SubParser {
|
|||
|
||||
struct GraphicsOpts {
|
||||
int multiplier;
|
||||
bool fullscreen;
|
||||
};
|
||||
|
||||
struct GBAThread;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue