Merge branch 'master' into medusa

This commit is contained in:
Vicki Pfau 2021-06-29 20:59:21 -07:00
commit bf97b7b52e
90 changed files with 14346 additions and 1619 deletions

20
CHANGES
View File

@ -38,24 +38,44 @@ Features:
- Presets for Game Boy palettes
- Add Super Game Boy palettes for original Game Boy games
- Tool for converting scanned pictures of e-Reader cards to raw dotcode data
- Options for muting when inactive, minimized, or for different players in multiplayer
- Cheat code support in homebrew ports
- Support for combo "Super Game Boy Color" SGB + GBC ROM hacks
- Support for 64 kiB SRAM saves used in some bootlegs
- Discord Rich Presence now supports time elapsed
- Additional scaling shaders
Emulation fixes:
- GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032)
- GB Video: Clear VRAM on reset (fixes mgba.io/i/2152)
- GBA: Improve timing when not booting from BIOS
- GBA SIO: Add missing NORMAL8 implementation bits (fixes mgba.io/i/2172)
- GBA SIO: Fix missing interrupt on an unattached NORMAL transfer
- GBA SIO: Fix SI value for unattached MULTI mode
- GBA Memory: Fix prefetch mask when swapping modes within a region
- GBA Serialize: Fix loading audio enable bit late (fixes mgba.io/i/2230)
- GBA Video: Revert scanline latching changes (fixes mgba.io/i/2153, mgba.io/i/2149)
Other fixes:
- 3DS: Fix disabling "wide" mode on 2DS (fixes mgba.io/i/2167)
- ARM Debugger: Fix disassembly alignment (fixes mgba.io/i/2204)
- Core: Fix memory leak in opening games from the library
- Core: Fix memory searches for relative values (fixes mgba.io/i/2135)
- Core: Fix portable mode on macOS
- Core: Don't attempt to restore rewind diffs past start of rewind
- GB Audio: Fix audio channel 4 being slow to deserialize
- GB Core: Fix GBC colors setting breaking default model overrides (fixes mgba.io/i/2161)
- GBA: Fix out of bounds ROM accesses on patched ROMs smaller than 32 MiB
- mGUI: Cache save state screenshot validity in state menu (fixes mgba.io/i/2005)
- Qt: Fix infrequent deadlock when using sync to video
- Qt: Fix applying savetype-only overrides
- Qt: Fix crash in sprite view for partially out-of-bounds sprites (fixes mgba.io/i/2165)
- Qt: Fix having to press controller buttons twice for menu items (fixes mgba.io/i/2143)
- Qt: Redo sensor binding to be less fragile
- Util: Fix loading UPS patches that affect the last byte of the file
Misc:
- Core: Suspend runloop when a core crashes
- Qt: Rearrange menus some
- Qt: Clean up cheats dialog
- Qt: Only set default controller bindings if loading fails (fixes mgba.io/i/799)
- Util: Improve speed of UPS patch loading
0.9.1: (2021-04-18)

View File

@ -232,7 +232,8 @@ endif()
if(APPLE)
add_definitions(-D_DARWIN_C_SOURCE)
if(CMAKE_SYSTEM_VERSION VERSION_GREATER "10.5.8")
list(APPEND OS_LIB "-framework Foundation")
if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "10.0") # Darwin 10.x is Mac OS X 10.6
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6")
endif()
endif()
@ -599,8 +600,7 @@ if(USE_FFMPEG)
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libavcodec-extra|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}")
endif()
if(APPLE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework VideoDecodeAcceleration -framework CoreVideo")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework VideoDecodeAcceleration -framework CoreVideo")
list(APPEND DEPENDENCY_LIB "-framework VideoDecodeAcceleration" "-framework CoreVideo")
endif()
endif()

View File

@ -83,7 +83,7 @@ The following mappers are partially supported:
Supported Platforms
-------------------
- Windows Vista or newer
- Windows 7 or newer
- OS X 10.8 (Mountain Lion)[<sup>[4]</sup>](#osxver) or newer
- Linux
- FreeBSD

View File

@ -79,7 +79,7 @@ Die folgenden Mapper werden teilweise unterstützt:
Unterstützte Plattformen
------------------------
- Windows Vista oder neuer
- Windows 7 oder neuer
- OS X 10.8 (Mountain Lion)[<sup>[3]</sup>](#osxver) oder neuer
- Linux
- FreeBSD

View File

@ -79,7 +79,7 @@ Estos mappers tienen soporte parcial:
Plataformas soportadas
-------------------
- Windows Vista o más reciente
- Windows 7 o más reciente
- OS X 10.8 (Mountain Lion)[<sup>[3]</sup>](#osxver) o más reciente
- Linux
- FreeBSD

View File

@ -77,7 +77,7 @@ mGBA 是一个运行 Game Boy Advance 游戏的模拟器。mGBA 的目标是比
支持平台
-------------------
- Windows Vista 或更新
- Windows 7 或更新
- OS X 10.8(山狮 / Mountain Lion[<sup>[3]</sup>](#osxver) 或更新
- Linux
- FreeBSD

View File

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 528 B

View File

@ -1,17 +1,17 @@
; this file was created with wlalink by ville helin <vhelin@iki.fi>.
; wla symbolic information for "/home/vicki/Scratch/mooneye-gb/tests/build/emulator-only/mbc1/ram_256Kb.gb".
; wla symbolic information for "/home/vicki/Scratch/mooneye-gb/tests/build/emulator-only/mbc1/ram_256kb.gb".
[labels]
01:48bd clear_vram
01:487a disable_lcd_safe
01:4880 disable_lcd_safe@wait_ly_0
01:4898 memcmp
01:48c7 memcpy
01:48d0 memset
01:48e0 print_inline_string
01:48a6 print_load_font
01:48b2 print_newline
01:48d9 print_string
01:48c7 clear_vram
01:4884 disable_lcd_safe
01:488a disable_lcd_safe@wait_ly_0
01:48a2 memcmp
01:48d1 memcpy
01:48da memset
01:48ea print_inline_string
01:48b0 print_load_font
01:48bc print_newline
01:48e3 print_string
01:47f0 quit
01:4805 quit@cb_return
01:480a quit@wait_ly_1
@ -20,24 +20,24 @@
01:481c quit@wait_ly_4
01:4826 quit@success
01:484d quit@failure
01:4862 quit@halt
01:4863 quit@halt_execution_0
01:4866 reset_screen
01:4889 serial_send_byte
01:486c quit@halt
01:486d quit@halt_execution_0
01:4870 reset_screen
01:4893 serial_send_byte
01:4000 font
00:0150 main
00:0150 test_round1
00:016e test_round2
00:01d2 test_round3
00:0232 test_round4
00:02a0 test_round5
00:02fc test_round6
00:0330 test_finish
00:033a test_finish@quit_inline_1
00:034b copy_bank_data
00:036a check_bank_data
00:038a all_ff
00:039a all_00
00:015f test_round1
00:017d test_round2
00:01e1 test_round3
00:0241 test_round4
00:02af test_round5
00:030b test_round6
00:033f test_finish
00:0349 test_finish@quit_inline_1
00:035a copy_bank_data
00:0379 check_bank_data
00:0399 all_ff
00:03a9 all_00
00:1000 bank_data
00:1040 clear_ram
00:1062 fail_round1
@ -52,6 +52,7 @@
00:10f0 fail_round5@quit_inline_6
00:1107 fail_round6
00:1111 fail_round6@quit_inline_7
00:ff80 memcmp_hram
[definitions]
0000000a _sizeof_clear_vram
@ -63,12 +64,13 @@
0000000c _sizeof_print_load_font
0000000b _sizeof_print_newline
00000007 _sizeof_print_string
00000076 _sizeof_quit
00000080 _sizeof_quit
00000014 _sizeof_reset_screen
0000000f _sizeof_serial_send_byte
000007f0 _sizeof_font
00000000 _sizeof_test_round1
0000001e _sizeof_main
00000020 _sizeof_memcmp_hram
0000000f _sizeof_main
0000001e _sizeof_test_round1
00000064 _sizeof_test_round2
00000060 _sizeof_test_round3
0000006e _sizeof_test_round4
@ -78,7 +80,7 @@
0000001f _sizeof_copy_bank_data
00000020 _sizeof_check_bank_data
00000010 _sizeof_all_ff
00000c66 _sizeof_all_00
00000c57 _sizeof_all_00
00000040 _sizeof_bank_data
00000022 _sizeof_clear_ram
00000021 _sizeof_fail_round1

View File

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 528 B

View File

@ -1,17 +1,17 @@
; this file was created with wlalink by ville helin <vhelin@iki.fi>.
; wla symbolic information for "/home/vicki/Scratch/mooneye-gb/tests/build/emulator-only/mbc1/ram_64Kb.gb".
; wla symbolic information for "/home/vicki/Scratch/mooneye-gb/tests/build/emulator-only/mbc1/ram_64kb.gb".
[labels]
01:48bd clear_vram
01:487a disable_lcd_safe
01:4880 disable_lcd_safe@wait_ly_0
01:4898 memcmp
01:48c7 memcpy
01:48d0 memset
01:48e0 print_inline_string
01:48a6 print_load_font
01:48b2 print_newline
01:48d9 print_string
01:48c7 clear_vram
01:4884 disable_lcd_safe
01:488a disable_lcd_safe@wait_ly_0
01:48a2 memcmp
01:48d1 memcpy
01:48da memset
01:48ea print_inline_string
01:48b0 print_load_font
01:48bc print_newline
01:48e3 print_string
01:47f0 quit
01:4805 quit@cb_return
01:480a quit@wait_ly_1
@ -20,23 +20,23 @@
01:481c quit@wait_ly_4
01:4826 quit@success
01:484d quit@failure
01:4862 quit@halt
01:4863 quit@halt_execution_0
01:4866 reset_screen
01:4889 serial_send_byte
01:486c quit@halt
01:486d quit@halt_execution_0
01:4870 reset_screen
01:4893 serial_send_byte
01:4000 font
00:0150 main
00:0150 test_round1
00:016e test_round2
00:01c2 test_round3
00:01cb test_round4
00:01fb test_round5
00:022c test_finish
00:0236 test_finish@quit_inline_1
00:0247 copy_bank_data
00:025f check_bank_data
00:0278 all_ff
00:0288 all_00
00:015f test_round1
00:017d test_round2
00:01d1 test_round3
00:01da test_round4
00:020a test_round5
00:023b test_finish
00:0245 test_finish@quit_inline_1
00:0256 copy_bank_data
00:026e check_bank_data
00:0287 all_ff
00:0297 all_00
00:1000 bank_data
00:1010 clear_ram
00:1032 fail_round1
@ -49,6 +49,7 @@
00:109f fail_round4@quit_inline_5
00:10b6 fail_round5
00:10c0 fail_round5@quit_inline_6
00:ff80 memcmp_hram
[definitions]
0000000a _sizeof_clear_vram
@ -60,12 +61,13 @@
0000000c _sizeof_print_load_font
0000000b _sizeof_print_newline
00000007 _sizeof_print_string
00000076 _sizeof_quit
00000080 _sizeof_quit
00000014 _sizeof_reset_screen
0000000f _sizeof_serial_send_byte
000007f0 _sizeof_font
00000000 _sizeof_test_round1
0000001e _sizeof_main
00000020 _sizeof_memcmp_hram
0000000f _sizeof_main
0000001e _sizeof_test_round1
00000054 _sizeof_test_round2
00000009 _sizeof_test_round3
00000030 _sizeof_test_round4
@ -74,7 +76,7 @@
00000018 _sizeof_copy_bank_data
00000019 _sizeof_check_bank_data
00000010 _sizeof_all_ff
00000d78 _sizeof_all_00
00000d69 _sizeof_all_00
00000010 _sizeof_bank_data
00000022 _sizeof_clear_ram
00000021 _sizeof_fail_round1

View File

@ -17,13 +17,27 @@ CXX_GUARD_START
#define GUI_V_I(I) (struct GUIVariant) { .type = GUI_VARIANT_INT, .v.i = (I) }
#define GUI_V_F(F) (struct GUIVariant) { .type = GUI_VARIANT_FLOAT, .v.f = (F) }
#define GUI_V_S(S) (struct GUIVariant) { .type = GUI_VARIANT_STRING, .v.s = (S) }
#define GUI_V_P(P) (struct GUIVariant) { .type = GUI_VARIANT_POINTER, .v.p = (P) }
#define GUIVariantIs(V, T) ((V).type == GUI_VARIANT_##T)
#define GUIVariantIsVoid(V) GUIVariantIs(V, VOID)
#define GUIVariantIsUInt(V) GUIVariantIs(V, UNSIGNED)
#define GUIVariantIsInt(V) GUIVariantIs(V, INT)
#define GUIVariantIsFloat(V) GUIVariantIs(V, FLOAT)
#define GUIVariantIsString(V) GUIVariantIs(V, STRING)
#define GUIVariantIsPointer(V) GUIVariantIs(V, POINTER)
#define GUIVariantCompareUInt(V, X) (GUIVariantIsUInt(V) && (V).v.u == (X))
#define GUIVariantCompareInt(V, X) (GUIVariantIsInt(V) && (V).v.i == (X))
#define GUIVariantCompareString(V, X) (GUIVariantIsString(V) && strcmp((V).v.s, (X)) == 0)
enum GUIVariantType {
GUI_VARIANT_VOID = 0,
GUI_VARIANT_UNSIGNED,
GUI_VARIANT_INT,
GUI_VARIANT_FLOAT,
GUI_VARIANT_STRING
GUI_VARIANT_STRING,
GUI_VARIANT_POINTER,
};
struct GUIVariant {
@ -33,13 +47,14 @@ struct GUIVariant {
int i;
float f;
const char* s;
void* p;
} v;
};
struct GUIMenu;
struct GUIMenuItem {
const char* title;
void* data;
struct GUIVariant data;
unsigned state;
const char* const* validStates;
const struct GUIVariant* stateMappings;

View File

@ -70,7 +70,7 @@ bool mInputQueryHat(const struct mInputMap*, uint32_t type, int id, struct mInpu
void mInputUnbindHat(struct mInputMap*, uint32_t type, int id);
void mInputUnbindAllHats(struct mInputMap*, uint32_t type);
void mInputMapLoad(struct mInputMap*, uint32_t type, const struct Configuration*);
bool mInputMapLoad(struct mInputMap*, uint32_t type, const struct Configuration*);
void mInputMapSave(const struct mInputMap*, uint32_t type, struct Configuration*);
bool mInputProfileLoad(struct mInputMap*, uint32_t type, const struct Configuration*, const char* profile);

View File

@ -181,6 +181,9 @@ struct GBMemory {
union GBMBCState mbcState;
int currentBank;
int currentBank0;
unsigned cartBusDecay;
uint16_t cartBusPc;
uint8_t cartBus;
uint8_t* wram;
uint8_t* wramBank;

View File

@ -157,9 +157,11 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | bit 3: IME
* | bit 4: Is HDMA active?
* | bits 5 - 7: Active RTC register
* | 0x00196 - 0x00197: Reserved (leave zero)
* | 0x00196: Cartridge bus value
* | 0x00197: Reserved (leave zero)
* 0x00198 - 0x0019F: Global cycle counter
* 0x001A0 - 0x0025F: Reserved (leave zero)
* 0x001A0 - 0x001A1: Program counter for last cartridge read
* 0x001A2 - 0x0025F: Reserved (leave zero)
* 0x00260 - 0x002FF: OAM
* 0x00300 - 0x0037F: I/O memory
* 0x00380 - 0x003FE: HRAM
@ -401,12 +403,14 @@ struct GBSerializedState {
};
GBSerializedMemoryFlags flags;
uint16_t reserved;
uint8_t cartBus;
uint8_t reserved;
} memory;
uint64_t globalCycles;
uint32_t reserved[48];
uint16_t cartBusPc;
uint16_t reserved[95];
uint8_t oam[GB_SIZE_OAM];

View File

@ -70,6 +70,7 @@ enum {
SIZE_CART1 = 0x02000000,
SIZE_CART2 = 0x02000000,
SIZE_CART_SRAM = 0x00008000,
SIZE_CART_SRAM512 = 0x00010000,
SIZE_CART_FLASH512 = 0x00010000,
SIZE_CART_FLASH1M = 0x00020000,
SIZE_CART_EEPROM = 0x00002000,

View File

@ -24,7 +24,8 @@ enum SavedataType {
SAVEDATA_FLASH512 = 2,
SAVEDATA_FLASH1M = 3,
SAVEDATA_EEPROM = 4,
SAVEDATA_EEPROM512 = 5
SAVEDATA_EEPROM512 = 5,
SAVEDATA_SRAM512 = 6,
};
enum SavedataCommand {
@ -110,6 +111,7 @@ void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type);
void GBASavedataInitFlash(struct GBASavedata* savedata);
void GBASavedataInitEEPROM(struct GBASavedata* savedata);
void GBASavedataInitSRAM(struct GBASavedata* savedata);
void GBASavedataInitSRAM512(struct GBASavedata* savedata);
uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address);
void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,11 @@
[shader]
name=Scale2x
author=singron
description=AdvanceMAME's Scale2x algorithm
passes=1
[pass.0]
fragmentShader=scale2x.fs
blend=1
width=-2
height=-2

View File

@ -0,0 +1,39 @@
/* Shader implementation of Scale2x is adapted from https://gist.github.com/singron/3161079 */
varying vec2 texCoord;
uniform sampler2D tex;
uniform vec2 texSize;
void main() {
// o = offset, the width of a pixel
vec2 o = 1.0 / texSize;
// texel arrangement
// A B C
// D E F
// G H I
vec4 B = texture2D(tex, texCoord + vec2( 0.0, o.y));
vec4 D = texture2D(tex, texCoord + vec2( -o.x, 0.0));
vec4 E = texture2D(tex, texCoord + vec2( 0.0, 0.0));
vec4 F = texture2D(tex, texCoord + vec2( o.x, 0.0));
vec4 H = texture2D(tex, texCoord + vec2( 0.0, -o.y));
vec2 p = texCoord * texSize;
// p = the texCoord within a pixel [0...1]
p = fract(p);
if (p.x > .5) {
if (p.y > .5) {
// Top Right
gl_FragColor = B == F && B != D && F != H ? F : E;
} else {
// Bottom Right
gl_FragColor = H == F && D != H && B != F ? F : E;
}
} else {
if (p.y > .5) {
// Top Left
gl_FragColor = D == B && B != F && D != H ? D : E;
} else {
// Bottom Left
gl_FragColor = D == H && D != B && H != F ? D : E;
}
}
}

View File

@ -0,0 +1,11 @@
[shader]
name=Scale4x
author=singron, endrift
description=AdvanceMAME's Scale4x algorithm
passes=1
[pass.0]
fragmentShader=scale4x.fs
blend=1
width=-4
height=-4

View File

@ -0,0 +1,54 @@
/* Shader implementation of Scale2x is adapted from https://gist.github.com/singron/3161079 */
varying vec2 texCoord;
uniform sampler2D tex;
uniform vec2 texSize;
vec4 scale2x(vec4 pixels[5], vec2 p) {
// texel arrangement
// x 0 x
// 1 2 3
// x 4 x
// p = the texCoord within a pixel [0...1]
p = fract(p);
if (p.x > .5) {
if (p.y > .5) {
// Top Right
return pixels[0] == pixels[3] && pixels[0] != pixels[1] && pixels[3] != pixels[4] ? pixels[3] : pixels[2];
} else {
// Bottom Right
return pixels[4] == pixels[3] && pixels[1] != pixels[4] && pixels[0] != pixels[3] ? pixels[3] : pixels[2];
}
} else {
if (p.y > .5) {
// Top Left
return pixels[1] == pixels[0] && pixels[0] != pixels[3] && pixels[1] != pixels[4] ? pixels[1] : pixels[2];
} else {
// Bottom Left
return pixels[1] == pixels[4] && pixels[1] != pixels[0] && pixels[4] != pixels[3] ? pixels[1] : pixels[2];
}
}
}
vec4 scaleNeighborhood(vec2 p, vec2 x, vec2 o) {
vec4 neighborhood[5];
neighborhood[0] = texture2D(tex, texCoord + x + vec2( 0.0, o.y));
neighborhood[1] = texture2D(tex, texCoord + x + vec2(-o.x, 0.0));
neighborhood[2] = texture2D(tex, texCoord + x + vec2( 0.0, 0.0));
neighborhood[3] = texture2D(tex, texCoord + x + vec2( o.x, 0.0));
neighborhood[4] = texture2D(tex, texCoord + x + vec2( 0.0, -o.y));
return scale2x(neighborhood, p + x * texSize);
}
void main() {
// o = offset, the width of a pixel
vec2 o = 1.0 / texSize;
vec2 p = texCoord * texSize;
vec4 pixels[5];
pixels[0] = scaleNeighborhood(p, vec2( 0.0, o.y / 2.0), o);
pixels[1] = scaleNeighborhood(p, vec2(-o.x / 2.0, 0.0), o);
pixels[2] = scaleNeighborhood(p, vec2( 0.0, 0.0), o);
pixels[3] = scaleNeighborhood(p, vec2( o.x / 2.0, 0.0), o);
pixels[4] = scaleNeighborhood(p, vec2( 0.0, -o.y / 2.0), o);
gl_FragColor = scale2x(pixels, p * 2.0);
}

View File

@ -101,9 +101,10 @@ static inline uint32_t _printLine(struct CLIDebugger* debugger, uint32_t address
struct mCore* core = debugger->d.core;
char disassembly[64];
struct ARMInstructionInfo info;
address &= ~(WORD_SIZE_THUMB - 1);
be->printf(be, "%08X: ", address);
if (mode == MODE_ARM) {
uint32_t instruction = core->busRead32(core, address);
uint32_t instruction = core->busRead32(core, address & ~(WORD_SIZE_ARM - 1));
ARMDecodeARM(instruction, &info);
ARMDisassemble(&info, core->cpu, core->symbolTable, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly));
be->printf(be, "%08X\t%s\n", instruction, disassembly);

View File

@ -19,6 +19,12 @@
#include <strsafe.h>
#endif
#ifdef __APPLE__
#include <CoreFoundation/CFBundle.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFURL.h>
#endif
#ifdef PSP2
#include <psp2/io/stat.h>
#endif
@ -274,6 +280,16 @@ void mCoreConfigPortablePath(char* out, size_t outLength) {
out[0] = '\0';
#else
getcwd(out, outLength);
#ifdef __APPLE__
CFBundleRef mainBundle = CFBundleGetMainBundle();
if (strcmp(out, "/") == 0 && mainBundle) {
CFURLRef url = CFBundleCopyBundleURL(mainBundle);
CFURLRef suburl = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, url);
CFRelease(url);
CFURLGetFileSystemRepresentation(suburl, true, (UInt8*) out, outLength);
CFRelease(suburl);
}
#endif
strncat(out, PATH_SEP "portable.ini", outLength - strlen(out));
#endif
}

View File

@ -578,10 +578,10 @@ void mInputUnbindAllHats(struct mInputMap* map, uint32_t type) {
}
}
void mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) {
bool mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) {
char sectionName[SECTION_NAME_MAX];
_makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type);
_loadAll(map, type, sectionName, config);
return _loadAll(map, type, sectionName, config);
}
void mInputMapSave(const struct mInputMap* map, uint32_t type, struct Configuration* config) {

View File

@ -47,7 +47,7 @@ static size_t _search32(const void* mem, size_t size, const struct mCoreMemoryBl
res->segment = -1; // TODO
res->guessDivisor = 1;
res->guessMultiplier = 1;
res->oldValue = value32;
res->oldValue = mem32[i >> 2];
++found;
}
}
@ -70,7 +70,7 @@ static size_t _search16(const void* mem, size_t size, const struct mCoreMemoryBl
res->segment = -1; // TODO
res->guessDivisor = 1;
res->guessMultiplier = 1;
res->oldValue = value16;
res->oldValue = mem16[i >> 1];
++found;
}
}
@ -92,7 +92,7 @@ static size_t _search8(const void* mem, size_t size, const struct mCoreMemoryBlo
res->segment = -1; // TODO
res->guessDivisor = 1;
res->guessMultiplier = 1;
res->oldValue = value8;
res->oldValue = mem8[i];
++found;
}
}
@ -248,42 +248,46 @@ void mCoreMemorySearch(struct mCore* core, const struct mCoreMemorySearchParams*
}
}
bool _testGuess(struct mCore* core, struct mCoreMemorySearchResult* res, const struct mCoreMemorySearchParams* params) {
int64_t value;
bool _testSpecificGuess(struct mCore* core, struct mCoreMemorySearchResult* res, int64_t opValue, enum mCoreMemorySearchOp op) {
int32_t offset = 0;
char* end;
if (params->op >= mCORE_MEMORY_SEARCH_DELTA) {
if (op >= mCORE_MEMORY_SEARCH_DELTA) {
offset = res->oldValue;
}
value = strtoll(params->valueStr, &end, 10);
if (end) {
res->oldValue += value;
if (_op(core->rawRead8(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
res->oldValue += opValue;
int64_t value = core->rawRead8(core, res->address, res->segment);
if (_op(value * res->guessDivisor / res->guessMultiplier - offset, opValue, op)) {
res->oldValue = value;
return true;
}
if (!(res->address & 1) && (res->width >= 2 || res->width == -1)) {
value = core->rawRead16(core, res->address, res->segment);
if (_op(value * res->guessDivisor / res->guessMultiplier - offset, opValue, op)) {
res->oldValue = value;
return true;
}
if (!(res->address & 1) && (res->width >= 2 || res->width == -1) && _op(core->rawRead16(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
}
if (!(res->address & 3) && (res->width >= 4 || res->width == -1)) {
value = core->rawRead32(core, res->address, res->segment);
if (_op(value * res->guessDivisor / res->guessMultiplier - offset, opValue, op)) {
res->oldValue = value;
return true;
}
if (!(res->address & 3) && (res->width >= 4 || res->width == -1) && _op(core->rawRead32(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
return true;
}
res->oldValue -= value;
}
res->oldValue -= opValue;
return false;
}
bool _testGuess(struct mCore* core, struct mCoreMemorySearchResult* res, const struct mCoreMemorySearchParams* params) {
char* end;
int64_t value = strtoll(params->valueStr, &end, 10);
if (end && _testSpecificGuess(core, res, value, params->op)) {
return true;
}
value = strtoll(params->valueStr, &end, 16);
if (end) {
res->oldValue += value;
if (_op(core->rawRead8(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
return true;
}
if (!(res->address & 1) && (res->width >= 2 || res->width == -1) && _op(core->rawRead16(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
return true;
}
if (!(res->address & 3) && (res->width >= 4 || res->width == -1) && _op(core->rawRead32(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
return true;
}
res->oldValue -= value;
if (end && _testSpecificGuess(core, res, value, params->op)) {
return true;
}
return false;
}

View File

@ -12,7 +12,7 @@
DEFINE_VECTOR(mCoreRewindPatches, struct PatchFast);
void _rewindDiff(struct mCoreRewindContext* context);
static void _rewindDiff(struct mCoreRewindContext* context);
#ifndef DISABLE_THREADING
THREAD_ENTRY _rewindThread(void* context);
@ -136,17 +136,19 @@ bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core)
}
--context->current;
struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
size_t size2 = context->previousState->size(context->previousState);
size_t size = context->currentState->size(context->currentState);
if (size2 < size) {
size = size2;
if (context->size) {
struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
size_t size2 = context->previousState->size(context->previousState);
size_t size = context->currentState->size(context->currentState);
if (size2 < size) {
size = size2;
}
void* current = context->currentState->map(context->currentState, size, MAP_READ);
void* previous = context->previousState->map(context->previousState, size, MAP_WRITE);
patch->d.applyPatch(&patch->d, previous, size, current, size);
context->currentState->unmap(context->currentState, current, size);
context->previousState->unmap(context->previousState, previous, size);
}
void* current = context->currentState->map(context->currentState, size, MAP_READ);
void* previous = context->previousState->map(context->previousState, size, MAP_WRITE);
patch->d.applyPatch(&patch->d, previous, size, current, size);
context->currentState->unmap(context->currentState, current, size);
context->previousState->unmap(context->previousState, previous, size);
struct VFile* nextState = context->previousState;
context->previousState = context->currentState;
context->currentState = nextState;

View File

@ -35,19 +35,19 @@ static void mGUIShowCheatSet(struct mGUIRunner* runner, struct mCheatDevice* dev
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Add line",
.data = (void*) CHEAT_ADD_LINE,
.data = GUI_V_U(CHEAT_ADD_LINE),
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Rename",
.data = (void*) CHEAT_RENAME,
.data = GUI_V_U(CHEAT_RENAME),
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Delete",
.data = (void*) CHEAT_DELETE,
.data = GUI_V_U(CHEAT_DELETE),
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Back",
.data = 0,
.data = GUI_V_V,
};
while (true) {
@ -56,11 +56,11 @@ static void mGUIShowCheatSet(struct mGUIRunner* runner, struct mCheatDevice* dev
struct GUIMenuItem* item;
enum GUIMenuExitReason reason = GUIShowMenu(&runner->params, &menu, &item);
set->enabled = GUIMenuItemListGetPointer(&menu.items, 0)->state;
if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) {
if (reason != GUI_MENU_EXIT_ACCEPT || GUIVariantIsVoid(item->data)) {
break;
}
enum mGUICheatAction action = (enum mGUICheatAction) item->data;
enum mGUICheatAction action = (enum mGUICheatAction) item->data.v.u;
switch (action) {
case CHEAT_ADD_LINE:
strlcpy(keyboard.title, "Add line", sizeof(keyboard.title));
@ -108,7 +108,7 @@ void mGUIShowCheats(struct mGUIRunner* runner) {
struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = set->name,
.data = set,
.data = GUI_V_P(set),
.state = set->enabled,
.validStates = offOn,
.nStates = 2
@ -116,11 +116,11 @@ void mGUIShowCheats(struct mGUIRunner* runner) {
}
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Add new cheat set",
.data = 0,
.data = GUI_V_V,
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Back",
.data = (void*) -1,
.data = GUI_V_I(-1),
};
struct GUIMenuItem* item;
@ -131,11 +131,11 @@ void mGUIShowCheats(struct mGUIRunner* runner) {
set->enabled = item->state;
}
if (reason != GUI_MENU_EXIT_ACCEPT || item->data == (void*) -1) {
if (reason != GUI_MENU_EXIT_ACCEPT || GUIVariantCompareInt(item->data, -1)) {
break;
}
struct mCheatSet* set = NULL;
if (!item->data) {
if (GUIVariantIsVoid(item->data)) {
struct GUIKeyboardParams keyboard;
GUIKeyboardParamsInit(&keyboard);
keyboard.maxLen = 50;
@ -146,7 +146,7 @@ void mGUIShowCheats(struct mGUIRunner* runner) {
mCheatAddSet(device, set);
}
} else {
set = item->data;
set = item->data.v.p;
}
if (set) {
mGUIShowCheatSet(runner, device, set);

View File

@ -22,6 +22,11 @@
#define GUI_MAX_INPUTS 7
#endif
enum {
CONFIG_REMAP,
CONFIG_SAVE,
};
static bool _biosNamed(const char* name) {
char ext[PATH_MAX + 1] = {};
separatePath(name, NULL, NULL, ext);
@ -45,7 +50,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
GUIMenuItemListInit(&menu.items, 0);
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Frameskip",
.data = "frameskip",
.data = GUI_V_S("frameskip"),
.submenu = 0,
.state = 0,
.validStates = (const char*[]) {
@ -55,7 +60,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Show framerate",
.data = "fpsCounter",
.data = GUI_V_S("fpsCounter"),
.submenu = 0,
.state = false,
.validStates = (const char*[]) {
@ -65,7 +70,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Show status OSD",
.data = "showOSD",
.data = GUI_V_S("showOSD"),
.submenu = 0,
.state = true,
.validStates = (const char*[]) {
@ -75,7 +80,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Autosave state",
.data = "autosave",
.data = GUI_V_S("autosave"),
.submenu = 0,
.state = true,
.validStates = (const char*[]) {
@ -85,7 +90,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Autoload state",
.data = "autoload",
.data = GUI_V_S("autoload"),
.submenu = 0,
.state = true,
.validStates = (const char*[]) {
@ -95,7 +100,17 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Mute",
.data = "mute",
.data = GUI_V_S("mute"),
.submenu = 0,
.state = false,
.validStates = (const char*[]) {
"Off", "On"
},
.nStates = 2
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Fast forward mute",
.data = GUI_V_S("fastForwardMute"),
.submenu = 0,
.state = false,
.validStates = (const char*[]) {
@ -105,7 +120,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Use BIOS if found",
.data = "useBios",
.data = GUI_V_S("useBios"),
.submenu = 0,
.state = true,
.validStates = (const char*[]) {
@ -116,26 +131,26 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
#ifdef M_CORE_GBA
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Select GBA BIOS path",
.data = "gba.bios",
.data = GUI_V_S("gba.bios"),
};
#endif
#ifdef M_CORE_GB
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Select GB BIOS path",
.data = "gb.bios",
.data = GUI_V_S("gb.bios"),
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Select GBC BIOS path",
.data = "gbc.bios",
.data = GUI_V_S("gbc.bios"),
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Select SGB BIOS path",
.data = "sgb.bios",
.data = GUI_V_S("sgb.bios"),
};
struct GUIMenuItem* palette = GUIMenuItemListAppend(&menu.items);
*palette = (struct GUIMenuItem) {
.title = "GB palette",
.data = "gb.pal",
.data = GUI_V_S("gb.pal"),
};
const struct GBColorPreset* colorPresets;
palette->nStates = GBColorPresetList(&colorPresets);
@ -147,7 +162,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
#endif
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Interframe blending",
.data = "interframeBlending",
.data = GUI_V_S("interframeBlending"),
.submenu = 0,
.state = false,
.validStates = (const char*[]) {
@ -158,7 +173,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
#if defined(M_CORE_GBA) && (defined(GEKKO) || defined(__SWITCH__) || defined(PSP2))
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Enable GBP features",
.data = "gba.forceGbp",
.data = GUI_V_S("gba.forceGbp"),
.submenu = 0,
.state = false,
.validStates = (const char*[]) {
@ -170,7 +185,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
#ifdef M_CORE_GB
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Enable SGB features",
.data = "sgb.model",
.data = GUI_V_S("sgb.model"),
.submenu = 0,
.state = true,
.validStates = (const char*[]) {
@ -184,7 +199,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Enable SGB borders",
.data = "sgb.borders",
.data = GUI_V_S("sgb.borders"),
.submenu = 0,
.state = true,
.validStates = (const char*[]) {
@ -194,7 +209,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Crop SGB borders",
.data = "sgb.borderCrop",
.data = GUI_V_S("sgb.borderCrop"),
.submenu = 0,
.state = false,
.validStates = (const char*[]) {
@ -214,7 +229,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
}
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Remap controls",
.data = "*REMAP",
.data = GUI_V_U(CONFIG_REMAP),
.state = 0,
.validStates = i ? mapNames : 0,
.nStates = i
@ -225,11 +240,11 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
}
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Save",
.data = "*SAVE",
.data = GUI_V_U(CONFIG_SAVE),
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Cancel",
.data = 0,
.data = GUI_V_V,
};
enum GUIMenuExitReason reason;
char gbaBiosPath[256] = "";
@ -242,59 +257,63 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
struct GUIMenuItem* item;
for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) {
item = GUIMenuItemListGetPointer(&menu.items, i);
if (!item->validStates || !item->data) {
if (!item->validStates || GUIVariantIsVoid(item->data)) {
continue;
}
if (item->stateMappings) {
size_t j;
for (j = 0; j < item->nStates; ++j) {
const struct GUIVariant* v = &item->stateMappings[j];
struct GUIVariant test;
switch (v->type) {
case GUI_VARIANT_VOID:
if (!mCoreConfigGetValue(&runner->config, item->data)) {
item->state = j;
if (GUIVariantIsString(item->data)) {
if (item->stateMappings) {
size_t j;
for (j = 0; j < item->nStates; ++j) {
const struct GUIVariant* v = &item->stateMappings[j];
struct GUIVariant test;
switch (v->type) {
case GUI_VARIANT_VOID:
if (!mCoreConfigGetValue(&runner->config, item->data.v.s)) {
item->state = j;
break;
}
break;
case GUI_VARIANT_UNSIGNED:
if (mCoreConfigGetUIntValue(&runner->config, item->data.v.s, &test.v.u) && test.v.u == v->v.u) {
item->state = j;
break;
}
break;
case GUI_VARIANT_INT:
if (mCoreConfigGetIntValue(&runner->config, item->data.v.s, &test.v.i) && test.v.i == v->v.i) {
item->state = j;
break;
}
break;
case GUI_VARIANT_FLOAT:
if (mCoreConfigGetFloatValue(&runner->config, item->data.v.s, &test.v.f) && fabsf(test.v.f - v->v.f) <= 1e-3f) {
item->state = j;
break;
}
break;
case GUI_VARIANT_STRING:
test.v.s = mCoreConfigGetValue(&runner->config, item->data.v.s);
if (test.v.s && strcmp(test.v.s, v->v.s) == 0) {
item->state = j;
break;
}
break;
case GUI_VARIANT_POINTER:
break;
}
break;
case GUI_VARIANT_UNSIGNED:
if (mCoreConfigGetUIntValue(&runner->config, item->data, &test.v.u) && test.v.u == v->v.u) {
item->state = j;
break;
}
break;
case GUI_VARIANT_INT:
if (mCoreConfigGetIntValue(&runner->config, item->data, &test.v.i) && test.v.i == v->v.i) {
item->state = j;
break;
}
break;
case GUI_VARIANT_FLOAT:
if (mCoreConfigGetFloatValue(&runner->config, item->data, &test.v.f) && fabsf(test.v.f - v->v.f) <= 1e-3f) {
item->state = j;
break;
}
break;
case GUI_VARIANT_STRING:
test.v.s = mCoreConfigGetValue(&runner->config, item->data);
if (test.v.s && strcmp(test.v.s, v->v.s) == 0) {
item->state = j;
break;
}
break;
}
} else {
mCoreConfigGetUIntValue(&runner->config, item->data.v.s, &item->state);
}
} else {
mCoreConfigGetUIntValue(&runner->config, item->data, &item->state);
}
}
while (true) {
reason = GUIShowMenu(&runner->params, &menu, &item);
if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) {
if (reason != GUI_MENU_EXIT_ACCEPT || GUIVariantIsVoid(item->data)) {
break;
}
if (!strcmp(item->data, "*SAVE")) {
if (GUIVariantCompareUInt(item->data, CONFIG_SAVE)) {
if (gbaBiosPath[0]) {
mCoreConfigSetValue(&runner->config, "gba.bios", gbaBiosPath);
}
@ -309,30 +328,32 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
}
for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) {
item = GUIMenuItemListGetPointer(&menu.items, i);
if (!item->validStates || !item->data || ((const char*) item->data)[0] == '*') {
if (!item->validStates || !GUIVariantIsString(item->data)) {
continue;
}
if (item->stateMappings) {
const struct GUIVariant* v = &item->stateMappings[item->state];
switch (v->type) {
case GUI_VARIANT_VOID:
mCoreConfigSetValue(&runner->config, item->data, NULL);
mCoreConfigSetValue(&runner->config, item->data.v.s, NULL);
break;
case GUI_VARIANT_UNSIGNED:
mCoreConfigSetUIntValue(&runner->config, item->data, v->v.u);
mCoreConfigSetUIntValue(&runner->config, item->data.v.s, v->v.u);
break;
case GUI_VARIANT_INT:
mCoreConfigSetUIntValue(&runner->config, item->data, v->v.i);
mCoreConfigSetUIntValue(&runner->config, item->data.v.s, v->v.i);
break;
case GUI_VARIANT_FLOAT:
mCoreConfigSetFloatValue(&runner->config, item->data, v->v.f);
mCoreConfigSetFloatValue(&runner->config, item->data.v.s, v->v.f);
break;
case GUI_VARIANT_STRING:
mCoreConfigSetValue(&runner->config, item->data, v->v.s);
mCoreConfigSetValue(&runner->config, item->data.v.s, v->v.s);
break;
case GUI_VARIANT_POINTER:
break;
}
#ifdef M_CORE_GB
} else if (!strcmp(item->data, "gb.pal")) {
} else if (GUIVariantCompareString(item->data, "gb.pal")) {
const struct GBColorPreset* preset = &colorPresets[item->state];
mCoreConfigSetUIntValue(&runner->config, "gb.pal[0]", preset->colors[0] & 0xFFFFFF);
mCoreConfigSetUIntValue(&runner->config, "gb.pal[1]", preset->colors[1] & 0xFFFFFF);
@ -346,10 +367,10 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
mCoreConfigSetUIntValue(&runner->config, "gb.pal[9]", preset->colors[9] & 0xFFFFFF);
mCoreConfigSetUIntValue(&runner->config, "gb.pal[10]", preset->colors[10] & 0xFFFFFF);
mCoreConfigSetUIntValue(&runner->config, "gb.pal[11]", preset->colors[11] & 0xFFFFFF);
mCoreConfigSetUIntValue(&runner->config, item->data, item->state);
mCoreConfigSetUIntValue(&runner->config, "gb.pal", item->state);
#endif
} else {
mCoreConfigSetUIntValue(&runner->config, item->data, item->state);
mCoreConfigSetUIntValue(&runner->config, item->data.v.s, item->state);
}
}
if (runner->keySources) {
@ -363,11 +384,11 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
mCoreLoadForeignConfig(runner->core, &runner->config);
break;
}
if (!strcmp(item->data, "*REMAP")) {
if (GUIVariantCompareUInt(item->data, CONFIG_REMAP)) {
mGUIRemapKeys(&runner->params, &runner->core->inputMap, &runner->keySources[item->state]);
continue;
}
if (!strcmp(item->data, "gba.bios")) {
if (GUIVariantCompareString(item->data, "gba.bios")) {
// TODO: show box if failed
if (!GUISelectFile(&runner->params, gbaBiosPath, sizeof(gbaBiosPath), _biosNamed, GBAIsBIOS, NULL)) {
gbaBiosPath[0] = '\0';
@ -375,21 +396,21 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
continue;
}
#ifdef M_CORE_GB
if (!strcmp(item->data, "gb.bios")) {
if (GUIVariantCompareString(item->data, "gb.bios")) {
// TODO: show box if failed
if (!GUISelectFile(&runner->params, gbBiosPath, sizeof(gbBiosPath), _biosNamed, GBIsBIOS, NULL)) {
gbBiosPath[0] = '\0';
}
continue;
}
if (!strcmp(item->data, "gbc.bios")) {
if (GUIVariantCompareString(item->data, "gbc.bios")) {
// TODO: show box if failed
if (!GUISelectFile(&runner->params, gbcBiosPath, sizeof(gbcBiosPath), _biosNamed, GBIsBIOS, NULL)) {
gbcBiosPath[0] = '\0';
}
continue;
}
if (!strcmp(item->data, "sgb.bios")) {
if (GUIVariantCompareString(item->data, "sgb.bios")) {
// TODO: show box if failed
if (!GUISelectFile(&runner->params, sgbBiosPath, sizeof(sgbBiosPath), _biosNamed, GBIsBIOS, NULL)) {
sgbBiosPath[0] = '\0';

View File

@ -42,6 +42,11 @@ enum {
#define RUNNER_STATE(X) ((X) << 16)
enum {
SCREENSHOT_VALID = 0x10000,
SCREENSHOT_INVALID = 0x20000,
};
static const struct mInputPlatformInfo _mGUIKeyInfo = {
.platformName = "gui",
.keyId = (const char*[GUI_INPUT_MAX]) {
@ -107,39 +112,49 @@ static void _drawBackground(struct GUIBackground* background, void* context) {
static void _drawState(struct GUIBackground* background, void* id) {
struct mGUIBackground* gbaBackground = (struct mGUIBackground*) background;
int stateId = ((int) id) >> 16;
unsigned stateId = ((uint32_t) id) >> 16;
if (gbaBackground->p->drawScreenshot) {
unsigned w, h;
gbaBackground->p->core->desiredVideoDimensions(gbaBackground->p->core, &w, &h);
if (gbaBackground->screenshot && gbaBackground->screenshotId == (int) id) {
gbaBackground->p->drawScreenshot(gbaBackground->p, gbaBackground->screenshot, w, h, true);
size_t size = w * h * BYTES_PER_PIXEL;
if (size != gbaBackground->imageSize) {
mappedMemoryFree(gbaBackground->image, gbaBackground->imageSize);
gbaBackground->image = NULL;
}
if (gbaBackground->image && gbaBackground->screenshotId == (stateId | SCREENSHOT_VALID)) {
gbaBackground->p->drawScreenshot(gbaBackground->p, gbaBackground->image, w, h, true);
return;
}
struct VFile* vf = mCoreGetState(gbaBackground->p->core, stateId, false);
color_t* pixels = gbaBackground->screenshot;
if (!pixels) {
pixels = anonymousMemoryMap(w * h * 4);
gbaBackground->screenshot = pixels;
}
bool success = false;
if (vf && isPNG(vf) && pixels) {
png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
png_infop info = png_create_info_struct(png);
png_infop end = png_create_info_struct(png);
if (png && info && end) {
success = PNGReadHeader(png, info);
success = success && PNGReadPixels(png, info, pixels, w, h, w);
success = success && PNGReadFooter(png, end);
} else if (gbaBackground->screenshotId != (stateId | SCREENSHOT_INVALID)) {
struct VFile* vf = mCoreGetState(gbaBackground->p->core, stateId, false);
color_t* pixels = gbaBackground->image;
if (!pixels) {
pixels = anonymousMemoryMap(size);
gbaBackground->image = pixels;
gbaBackground->imageSize = size;
}
bool success = false;
if (vf && isPNG(vf) && pixels) {
png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
png_infop info = png_create_info_struct(png);
png_infop end = png_create_info_struct(png);
if (png && info && end) {
success = PNGReadHeader(png, info);
success = success && PNGReadPixels(png, info, pixels, w, h, w);
success = success && PNGReadFooter(png, end);
}
PNGReadClose(png, info, end);
}
if (vf) {
vf->close(vf);
}
if (success) {
gbaBackground->p->drawScreenshot(gbaBackground->p, pixels, w, h, true);
gbaBackground->screenshotId = stateId | SCREENSHOT_VALID;
} else {
gbaBackground->screenshotId = stateId | SCREENSHOT_INVALID;
}
PNGReadClose(png, info, end);
}
if (vf) {
vf->close(vf);
}
if (success) {
gbaBackground->p->drawScreenshot(gbaBackground->p, pixels, w, h, true);
gbaBackground->screenshotId = (int) id;
} else if (gbaBackground->p->drawFrame) {
if (gbaBackground->p->drawFrame && gbaBackground->screenshotId == (stateId | SCREENSHOT_INVALID)) {
gbaBackground->p->drawFrame(gbaBackground->p, true);
}
}
@ -315,7 +330,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
.draw = _drawState
},
.p = runner,
.screenshot = 0,
.image = 0,
.screenshotId = 0
};
struct GUIMenu pauseMenu = {
@ -336,38 +351,38 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
GUIMenuItemListInit(&pauseMenu.items, 0);
GUIMenuItemListInit(&stateSaveMenu.items, 9);
GUIMenuItemListInit(&stateLoadMenu.items, 10);
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Unpause", .data = (void*) RUNNER_CONTINUE };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Unpause", .data = GUI_V_U(RUNNER_CONTINUE) };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Save state", .submenu = &stateSaveMenu };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Load state", .submenu = &stateLoadMenu };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(1)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(2)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(3)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(4)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(5)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(6)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(7)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(8)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(9)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = GUI_V_U(RUNNER_SAVE_STATE | RUNNER_STATE(1)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = GUI_V_U(RUNNER_SAVE_STATE | RUNNER_STATE(2)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = GUI_V_U(RUNNER_SAVE_STATE | RUNNER_STATE(3)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = GUI_V_U(RUNNER_SAVE_STATE | RUNNER_STATE(4)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = GUI_V_U(RUNNER_SAVE_STATE | RUNNER_STATE(5)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = GUI_V_U(RUNNER_SAVE_STATE | RUNNER_STATE(6)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = GUI_V_U(RUNNER_SAVE_STATE | RUNNER_STATE(7)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = GUI_V_U(RUNNER_SAVE_STATE | RUNNER_STATE(8)) };
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = GUI_V_U(RUNNER_SAVE_STATE | RUNNER_STATE(9)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "Autosave", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(0)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(1)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(2)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(3)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(4)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(5)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(6)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(7)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(8)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(9)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "Autosave", .data = GUI_V_U(RUNNER_LOAD_STATE | RUNNER_STATE(0)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = GUI_V_U(RUNNER_LOAD_STATE | RUNNER_STATE(1)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = GUI_V_U(RUNNER_LOAD_STATE | RUNNER_STATE(2)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = GUI_V_U(RUNNER_LOAD_STATE | RUNNER_STATE(3)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = GUI_V_U(RUNNER_LOAD_STATE | RUNNER_STATE(4)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = GUI_V_U(RUNNER_LOAD_STATE | RUNNER_STATE(5)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = GUI_V_U(RUNNER_LOAD_STATE | RUNNER_STATE(6)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = GUI_V_U(RUNNER_LOAD_STATE | RUNNER_STATE(7)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = GUI_V_U(RUNNER_LOAD_STATE | RUNNER_STATE(8)) };
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = GUI_V_U(RUNNER_LOAD_STATE | RUNNER_STATE(9)) };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Take screenshot", .data = (void*) RUNNER_SCREENSHOT };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Take screenshot", .data = GUI_V_U(RUNNER_SCREENSHOT) };
if (runner->params.getText) {
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Cheats", .data = (void*) RUNNER_CHEATS };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Cheats", .data = GUI_V_U(RUNNER_CHEATS) };
}
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Configure", .data = (void*) RUNNER_CONFIG };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Reset game", .data = (void*) RUNNER_RESET };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Exit game", .data = (void*) RUNNER_EXIT };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Configure", .data = GUI_V_U(RUNNER_CONFIG) };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Reset game", .data = GUI_V_U(RUNNER_RESET) };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Exit game", .data = GUI_V_U(RUNNER_EXIT) };
runner->params.drawStart();
if (runner->params.guiPrepare) {
@ -455,6 +470,12 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
int drawFps = false;
mCoreConfigGetIntValue(&runner->config, "fpsCounter", &drawFps);
int mute = false;
mCoreConfigGetIntValue(&runner->config, "mute", &mute);
int fastForwardMute = false;
mCoreConfigGetIntValue(&runner->config, "fastForwardMute", &fastForwardMute);
bool running = true;
#ifndef DISABLE_THREADING
@ -506,21 +527,33 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
if (guiKeys & (1 << mGUI_INPUT_SCREENSHOT)) {
mCoreTakeScreenshot(runner->core);
}
bool muteTogglePressed = guiKeys & (1 << mGUI_INPUT_MUTE_TOGGLE);
if (muteTogglePressed) {
mute = !mute;
mCoreConfigSetUIntValue(&runner->config, "mute", mute);
runner->core->reloadConfigOption(runner->core, "mute", &runner->config);
}
if (runner->setFrameLimiter) {
if (guiKeys & (1 << mGUI_INPUT_FAST_FORWARD_TOGGLE)) {
fastForward = !fastForward;
}
if (fastForward || (heldKeys & (1 << mGUI_INPUT_FAST_FORWARD_HELD))) {
bool fastForwarding = fastForward || (heldKeys & (1 << mGUI_INPUT_FAST_FORWARD_HELD));
if (fastForwarding) {
if (fastForwardMute && !mute && !muteTogglePressed) {
mCoreConfigSetUIntValue(&runner->core->config, "mute", fastForwardMute);
runner->core->reloadConfigOption(runner->core, "mute", NULL);
}
runner->setFrameLimiter(runner, false);
} else {
runner->setFrameLimiter(runner, true);
if (fastForwardMute && !mute && !muteTogglePressed) {
mCoreConfigSetUIntValue(&runner->core->config, "mute", !fastForwardMute);
runner->core->reloadConfigOption(runner->core, "mute", NULL);
}
}
}
if (guiKeys & (1 << mGUI_INPUT_MUTE_TOGGLE)) {
int mute = !runner->core->opts.mute;
mCoreConfigSetUIntValue(&runner->config, "mute", mute);
runner->core->reloadConfigOption(runner->core, "mute", &runner->config);
}
uint16_t keys = runner->pollGameInput(runner);
if (runner->prepareForFrame) {
runner->prepareForFrame(runner);
@ -600,8 +633,8 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
uint32_t keys = 0xFFFFFFFF; // Huge hack to avoid an extra variable!
struct GUIMenuItem* item;
enum GUIMenuExitReason reason = GUIShowMenu(&runner->params, &pauseMenu, &item);
if (reason == GUI_MENU_EXIT_ACCEPT) {
switch (((int) item->data) & RUNNER_COMMAND_MASK) {
if (reason == GUI_MENU_EXIT_ACCEPT && item->data.type == GUI_VARIANT_UNSIGNED) {
switch (item->data.v.u & RUNNER_COMMAND_MASK) {
case RUNNER_EXIT:
running = false;
keys = 0;
@ -610,10 +643,10 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
runner->core->reset(runner->core);
break;
case RUNNER_SAVE_STATE:
mCoreSaveState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
mCoreSaveState(runner->core, item->data.v.u >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
break;
case RUNNER_LOAD_STATE:
mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
mCoreLoadState(runner->core, item->data.v.u >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
break;
case RUNNER_SCREENSHOT:
mCoreTakeScreenshot(runner->core);
@ -651,6 +684,8 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
}
mCoreConfigGetIntValue(&runner->config, "fpsCounter", &drawFps);
mCoreConfigGetIntValue(&runner->config, "showOSD", &showOSD);
mCoreConfigGetIntValue(&runner->config, "mute", &mute);
mCoreConfigGetIntValue(&runner->config, "fastForwardMute", &fastForwardMute);
#ifdef M_CORE_GB
if (runner->core->platform(runner->core) == mPLATFORM_GB) {
runner->core->reloadConfigOption(runner->core, "gb.pal", &runner->config);
@ -676,10 +711,8 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
mLOG(GUI_RUNNER, DEBUG, "Unloading game...");
runner->core->unloadROM(runner->core);
drawState.screenshotId = 0;
if (drawState.screenshot) {
unsigned w, h;
runner->core->desiredVideoDimensions(runner->core, &w, &h);
mappedMemoryFree(drawState.screenshot, w * h * 4);
if (drawState.image) {
mappedMemoryFree(drawState.image, drawState.imageSize);
}
if (runner->config.port) {

View File

@ -31,8 +31,10 @@ struct mGUIBackground {
struct GUIBackground d;
struct mGUIRunner* p;
color_t* screenshot;
int screenshotId;
color_t* image;
size_t imageSize;
unsigned screenshotId;
};
struct mCore;

View File

@ -21,12 +21,12 @@ void mGUIRemapKeys(struct GUIParams* params, struct mInputMap* map, const struct
size_t i;
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Game keys:",
.data = 0,
.data = GUI_V_V,
};
for (i = 0; i < map->info->nKeys; ++i) {
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = map->info->keyId[i],
.data = (void*) (GUI_INPUT_MAX + i + 1),
.data = GUI_V_U(GUI_INPUT_MAX + i + 1),
.submenu = 0,
.state = mInputQueryBinding(map, keys->id, i) + 1,
.validStates = keyNames,
@ -35,7 +35,7 @@ void mGUIRemapKeys(struct GUIParams* params, struct mInputMap* map, const struct
}
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Interface keys:",
.data = 0,
.data = GUI_V_V,
};
for (i = 0; i < params->keyMap.info->nKeys; ++i) {
if (!params->keyMap.info->keyId[i]) {
@ -43,7 +43,7 @@ void mGUIRemapKeys(struct GUIParams* params, struct mInputMap* map, const struct
}
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = params->keyMap.info->keyId[i],
.data = (void*) i + 1,
.data = GUI_V_U(i + 1),
.submenu = 0,
.state = mInputQueryBinding(&params->keyMap, keys->id, i) + 1,
.validStates = keyNames,
@ -52,30 +52,30 @@ void mGUIRemapKeys(struct GUIParams* params, struct mInputMap* map, const struct
}
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Save",
.data = (void*) (GUI_INPUT_MAX + map->info->nKeys + 2),
.data = GUI_V_I(-2),
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Cancel",
.data = 0,
.data = GUI_V_I(-1),
};
struct GUIMenuItem* item;
while (true) {
enum GUIMenuExitReason reason;
reason = GUIShowMenu(params, &menu, &item);
if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) {
if (reason != GUI_MENU_EXIT_ACCEPT || GUIVariantCompareInt(item->data, -1)) {
break;
}
if (item->data == (void*) (GUI_INPUT_MAX + map->info->nKeys + 2)) {
if (GUIVariantCompareInt(item->data, -2)) {
for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) {
item = GUIMenuItemListGetPointer(&menu.items, i);
if ((uintptr_t) item->data < 1) {
if (!GUIVariantIsUInt(item->data)) {
continue;
}
if ((uintptr_t) item->data < GUI_INPUT_MAX + 1) {
mInputBindKey(&params->keyMap, keys->id, item->state - 1, (uintptr_t) item->data - 1);
} else if ((uintptr_t) item->data < GUI_INPUT_MAX + map->info->nKeys + 1) {
mInputBindKey(map, keys->id, item->state - 1, (uintptr_t) item->data - GUI_INPUT_MAX - 1);
if (item->data.v.u < GUI_INPUT_MAX + 1) {
mInputBindKey(&params->keyMap, keys->id, item->state - 1, item->data.v.u - 1);
} else if (item->data.v.u < GUI_INPUT_MAX + map->info->nKeys + 1) {
mInputBindKey(map, keys->id, item->state - 1, item->data.v.u - GUI_INPUT_MAX - 1);
}
}
break;

View File

@ -724,6 +724,7 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->audio.enable = GBAudioEnableGetEnable(*gb->audio.nr52);
if (gb->audio.enable) {
gb->audio.playingCh1 = false;
GBIOWrite(gb, GB_REG_NR10, gb->memory.io[GB_REG_NR10]);
GBIOWrite(gb, GB_REG_NR11, gb->memory.io[GB_REG_NR11]);
GBIOWrite(gb, GB_REG_NR12, gb->memory.io[GB_REG_NR12]);
@ -731,12 +732,14 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->audio.ch1.control.frequency &= 0xFF;
gb->audio.ch1.control.frequency |= GBAudioRegisterControlGetFrequency(gb->memory.io[GB_REG_NR14] << 8);
gb->audio.ch1.control.stop = GBAudioRegisterControlGetStop(gb->memory.io[GB_REG_NR14] << 8);
gb->audio.playingCh2 = false;
GBIOWrite(gb, GB_REG_NR21, gb->memory.io[GB_REG_NR21]);
GBIOWrite(gb, GB_REG_NR22, gb->memory.io[GB_REG_NR22]);
GBIOWrite(gb, GB_REG_NR23, gb->memory.io[GB_REG_NR23]);
gb->audio.ch2.control.frequency &= 0xFF;
gb->audio.ch2.control.frequency |= GBAudioRegisterControlGetFrequency(gb->memory.io[GB_REG_NR24] << 8);
gb->audio.ch2.control.stop = GBAudioRegisterControlGetStop(gb->memory.io[GB_REG_NR24] << 8);
gb->audio.playingCh3 = false;
GBIOWrite(gb, GB_REG_NR30, gb->memory.io[GB_REG_NR30]);
GBIOWrite(gb, GB_REG_NR31, gb->memory.io[GB_REG_NR31]);
GBIOWrite(gb, GB_REG_NR32, gb->memory.io[GB_REG_NR32]);
@ -744,6 +747,7 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->audio.ch3.rate &= 0xFF;
gb->audio.ch3.rate |= GBAudioRegisterControlGetRate(gb->memory.io[GB_REG_NR34] << 8);
gb->audio.ch3.stop = GBAudioRegisterControlGetStop(gb->memory.io[GB_REG_NR34] << 8);
gb->audio.playingCh4 = false;
GBIOWrite(gb, GB_REG_NR41, gb->memory.io[GB_REG_NR41]);
GBIOWrite(gb, GB_REG_NR42, gb->memory.io[GB_REG_NR42]);
GBIOWrite(gb, GB_REG_NR43, gb->memory.io[GB_REG_NR43]);

View File

@ -322,6 +322,7 @@ void GBMBCInit(struct GB* gb) {
}
gb->memory.mbcRead = NULL;
gb->memory.directSramAccess = true;
gb->memory.cartBusDecay = 4;
switch (gb->memory.mbcType) {
case GB_MBC_NONE:
gb->memory.mbcWrite = _GBMBCNone;
@ -442,6 +443,9 @@ void GBMBCInit(struct GB* gb) {
void GBMBCReset(struct GB* gb) {
gb->memory.currentBank0 = 0;
gb->memory.romBank = &gb->memory.rom[GB_SIZE_CART_BANK0];
gb->memory.cartBus = 0xFF;
gb->memory.cartBusPc = 0;
gb->memory.cartBusDecay = 1;
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
GBMBCInit(gb);

View File

@ -51,12 +51,17 @@ static const uint8_t _blockedRegion[1] = { 0xFF };
static void _pristineCow(struct GB* gba);
static uint8_t GBFastLoad8(struct SM83Core* cpu, uint16_t address) {
static uint8_t GBCartLoad8(struct SM83Core* cpu, uint16_t address) {
if (UNLIKELY(address >= cpu->memory.activeRegionEnd)) {
cpu->memory.setActiveRegion(cpu, address);
return cpu->memory.cpuLoad8(cpu, address);
}
return cpu->memory.activeRegion[address & cpu->memory.activeMask];
struct GB* gb = (struct GB*) cpu->master;
struct GBMemory* memory = &gb->memory;
memory->cartBusPc = address;
uint8_t value = cpu->memory.activeRegion[address & cpu->memory.activeMask];
memory->cartBus = value;
return value;
}
static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
@ -67,7 +72,7 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
case GB_REGION_CART_BANK0 + 1:
case GB_REGION_CART_BANK0 + 2:
case GB_REGION_CART_BANK0 + 3:
cpu->memory.cpuLoad8 = GBFastLoad8;
cpu->memory.cpuLoad8 = GBCartLoad8;
cpu->memory.activeRegion = memory->romBase;
cpu->memory.activeRegionEnd = GB_BASE_CART_BANK1;
cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1;
@ -88,7 +93,7 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
cpu->memory.cpuLoad8 = GBLoad8;
break;
}
cpu->memory.cpuLoad8 = GBFastLoad8;
cpu->memory.cpuLoad8 = GBCartLoad8;
if (gb->memory.mbcType != GB_MBC6) {
cpu->memory.activeRegion = memory->romBank;
cpu->memory.activeRegionEnd = GB_BASE_VRAM;
@ -238,24 +243,31 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
case GB_REGION_CART_BANK0 + 2:
case GB_REGION_CART_BANK0 + 3:
if (address >= memory->romSize) {
return 0xFF;
memory->cartBus = 0xFF;
} else {
memory->cartBus = memory->romBase[address & (GB_SIZE_CART_BANK0 - 1)];
}
return memory->romBase[address & (GB_SIZE_CART_BANK0 - 1)];
memory->cartBusPc = cpu->pc;
return memory->cartBus;
case GB_REGION_CART_BANK1 + 2:
case GB_REGION_CART_BANK1 + 3:
if (memory->mbcType == GB_MBC6) {
return memory->mbcState.mbc6.romBank1[address & (GB_SIZE_CART_HALFBANK - 1)];
memory->cartBus = memory->mbcState.mbc6.romBank1[address & (GB_SIZE_CART_HALFBANK - 1)];
memory->cartBusPc = cpu->pc;
return memory->cartBus;
}
// Fall through
case GB_REGION_CART_BANK1:
case GB_REGION_CART_BANK1 + 1:
if (address >= memory->romSize) {
return 0xFF;
memory->cartBus = 0xFF;
} else if ((memory->mbcType & GB_UNL_BBD) == GB_UNL_BBD) {
memory->cartBus = memory->mbcRead(memory, address);
} else {
memory->cartBus = memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
}
if ((memory->mbcType & GB_UNL_BBD) == GB_UNL_BBD) {
return memory->mbcRead(memory, address);
}
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
memory->cartBusPc = cpu->pc;
return memory->cartBus;
case GB_REGION_VRAM:
case GB_REGION_VRAM + 1:
if (gb->video.mode != 3) {
@ -265,15 +277,18 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
case GB_REGION_EXTERNAL_RAM:
case GB_REGION_EXTERNAL_RAM + 1:
if (memory->rtcAccess) {
return memory->rtcRegs[memory->activeRtcReg];
memory->cartBus = memory->rtcRegs[memory->activeRtcReg];
} else if (memory->mbcRead) {
return memory->mbcRead(memory, address);
memory->cartBus = memory->mbcRead(memory, address);
} else if (memory->sramAccess && memory->sram) {
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
memory->cartBus = memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
} else if (memory->mbcType == GB_HuC3) {
return 0x01; // TODO: Is this supposed to be the current SRAM bank?
memory->cartBus = 0x01; // TODO: Is this supposed to be the current SRAM bank?
} else if (cpu->tMultiplier * (cpu->pc - memory->cartBusPc) >= memory->cartBusDecay) {
memory->cartBus = 0xFF;
}
return 0xFF;
memory->cartBusPc = cpu->pc;
return memory->cartBus;
case GB_REGION_WORKING_RAM_BANK0:
case GB_REGION_WORKING_RAM_BANK0 + 2:
return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
@ -705,6 +720,9 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
flags = GBSerializedMemoryFlagsSetActiveRtcReg(flags, memory->activeRtcReg);
STORE_16LE(flags, 0, &state->memory.flags);
state->memory.cartBus = memory->cartBus;
STORE_16LE(memory->cartBusPc, 0, &state->cartBusPc);
switch (memory->mbcType) {
case GB_MBC1:
state->memory.mbc1.mode = memory->mbcState.mbc1.mode;
@ -784,6 +802,9 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
memory->isHdma = GBSerializedMemoryFlagsGetIsHdma(flags);
memory->activeRtcReg = GBSerializedMemoryFlagsGetActiveRtcReg(flags);
memory->cartBus = state->memory.cartBus;
LOAD_16LE(memory->cartBusPc, 0, &state->cartBusPc);
switch (memory->mbcType) {
case GB_MBC1:
memory->mbcState.mbc1.mode = state->memory.mbc1.mode;

View File

@ -265,8 +265,10 @@ void GBASkipBIOS(struct GBA* gba) {
} else {
cpu->gprs[ARM_PC] = BASE_WORKING_RAM + 0xC0;
}
gba->video.vcount = 0x7D;
gba->memory.io[REG_VCOUNT >> 1] = 0x7D;
gba->video.vcount = 0x7E;
gba->memory.io[REG_VCOUNT >> 1] = 0x7E;
mTimingDeschedule(&gba->timing, &gba->video.event);
mTimingSchedule(&gba->timing, &gba->video.event, 117);
gba->memory.io[REG_POSTFLG >> 1] = 1;
ARMWritePC(cpu);
}

View File

@ -973,6 +973,8 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
}
void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
GBAAudioWriteSOUNDCNT_X(&gba->audio, state->io[REG_SOUNDCNT_X >> 1]);
int i;
for (i = 0; i < REG_MAX; i += 2) {
if (_isWSpecialRegister[i >> 1]) {
@ -1003,7 +1005,6 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
LOAD_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount);
LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].when);
}
GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]);
gba->sio.siocnt = gba->memory.io[REG_SIOCNT >> 1];
GBASIOWriteRCNT(&gba->sio, gba->memory.io[REG_RCNT >> 1]);

View File

@ -127,6 +127,7 @@ void GBAMemoryReset(struct GBA* gba) {
memset(gba->memory.io, 0, sizeof(gba->memory.io));
GBAAdjustWaitstates(gba, 0);
gba->memory.activeRegion = -1;
gba->memory.agbPrintProtect = 0;
gba->memory.agbPrintBase = 0;
memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx));
@ -266,6 +267,11 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
gba->lastJump = address;
memory->lastPrefetchedPc = 0;
if (newRegion == memory->activeRegion) {
if (cpu->cpsr.t) {
cpu->memory.activeMask |= WORD_SIZE_THUMB;
} else {
cpu->memory.activeMask &= -WORD_SIZE_ARM;
}
if (newRegion < REGION_CART0 || (address & (SIZE_CART0 - 1)) < memory->romSize) {
return;
}
@ -717,6 +723,8 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
value = GBASavedataReadFlash(&memory->savedata, address);
} else if (memory->hw.devices & HW_TILT) {
value = GBAHardwareTiltRead(&memory->hw, address & OFFSET_MASK);
} else if (memory->savedata.type == SAVEDATA_SRAM512) {
value = memory->savedata.data[address & (SIZE_CART_SRAM512 - 1)];
} else {
mLOG(GBA_MEM, GAME_ERROR, "Reading from non-existent SRAM: 0x%08X", address);
value = 0xFF;
@ -1070,6 +1078,9 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
memory->savedata.dirty |= SAVEDATA_DIRT_NEW;
} else if (memory->hw.devices & HW_TILT) {
GBAHardwareTiltWrite(&memory->hw, address & OFFSET_MASK, value);
} else if (memory->savedata.type == SAVEDATA_SRAM512) {
memory->savedata.data[address & (SIZE_CART_SRAM512 - 1)] = value;
memory->savedata.dirty |= SAVEDATA_DIRT_NEW;
} else {
mLOG(GBA_MEM, GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address);
}

View File

@ -241,6 +241,9 @@ bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOver
if (strcasecmp(savetype, "SRAM") == 0) {
found = true;
override->savetype = SAVEDATA_SRAM;
} else if (strcasecmp(savetype, "SRAM512") == 0) {
found = true;
override->savetype = SAVEDATA_SRAM512;
} else if (strcasecmp(savetype, "EEPROM") == 0) {
found = true;
override->savetype = SAVEDATA_EEPROM;
@ -288,6 +291,9 @@ void GBAOverrideSave(struct Configuration* config, const struct GBACartridgeOver
case SAVEDATA_SRAM:
savetype = "SRAM";
break;
case SAVEDATA_SRAM512:
savetype = "SRAM512";
break;
case SAVEDATA_EEPROM:
savetype = "EEPROM";
break;

View File

@ -68,6 +68,9 @@ void GBASavedataDeinit(struct GBASavedata* savedata) {
case SAVEDATA_SRAM:
mappedMemoryFree(savedata->data, SIZE_CART_SRAM);
break;
case SAVEDATA_SRAM512:
mappedMemoryFree(savedata->data, SIZE_CART_SRAM512);
break;
case SAVEDATA_FLASH512:
mappedMemoryFree(savedata->data, SIZE_CART_FLASH512);
break;
@ -124,6 +127,8 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
switch (savedata->type) {
case SAVEDATA_SRAM:
return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM;
case SAVEDATA_SRAM512:
return out->write(out, savedata->data, SIZE_CART_SRAM512) == SIZE_CART_SRAM512;
case SAVEDATA_FLASH512:
return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512;
case SAVEDATA_FLASH1M:
@ -153,6 +158,8 @@ size_t GBASavedataSize(const struct GBASavedata* savedata) {
switch (savedata->type) {
case SAVEDATA_SRAM:
return SIZE_CART_SRAM;
case SAVEDATA_SRAM512:
return SIZE_CART_SRAM512;
case SAVEDATA_FLASH512:
return SIZE_CART_FLASH512;
case SAVEDATA_FLASH1M:
@ -233,6 +240,9 @@ void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type)
case SAVEDATA_SRAM:
GBASavedataInitSRAM(savedata);
break;
case SAVEDATA_SRAM512:
GBASavedataInitSRAM512(savedata);
break;
case SAVEDATA_FORCE_NONE:
savedata->type = SAVEDATA_FORCE_NONE;
break;
@ -322,6 +332,30 @@ void GBASavedataInitSRAM(struct GBASavedata* savedata) {
}
}
void GBASavedataInitSRAM512(struct GBASavedata* savedata) {
if (savedata->type == SAVEDATA_AUTODETECT) {
savedata->type = SAVEDATA_SRAM512;
} else {
mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
return;
}
off_t end;
if (!savedata->vf) {
end = 0;
savedata->data = anonymousMemoryMap(SIZE_CART_SRAM512);
} else {
end = savedata->vf->size(savedata->vf);
if (end < SIZE_CART_SRAM512) {
savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM512);
}
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM512, savedata->mapMode);
}
if (end < SIZE_CART_SRAM512) {
memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM512 - end);
}
}
uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
if (savedata->command == FLASH_COMMAND_ID) {
if (savedata->type == SAVEDATA_FLASH512) {

View File

@ -158,6 +158,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
mLOG(GBA_STATE, WARN, "Savestate has unaligned PC and is probably corrupted");
gba->cpu->gprs[ARM_PC] &= ~1;
}
gba->memory.activeRegion = -1;
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
if (state->biosPrefetch) {
LOAD_32(gba->memory.biosPrefetch, 0, &state->biosPrefetch);

View File

@ -141,6 +141,7 @@ bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
case SIO_MULTI:
ATOMIC_SUB(node->p->attachedMulti, 1);
break;
case SIO_NORMAL_8:
case SIO_NORMAL_32:
ATOMIC_SUB(node->p->attachedNormal, 1);
break;
@ -179,10 +180,14 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value);
enum mLockstepPhase transferActive;
int attached;
ATOMIC_LOAD(transferActive, node->p->d.transferActive);
ATOMIC_LOAD(attached, node->p->d.attached);
driver->p->siocnt = GBASIOMultiplayerSetSlave(driver->p->siocnt, node->id || attached < 2);
if (value & 0x0080 && transferActive == TRANSFER_IDLE) {
if (!node->id && GBASIOMultiplayerIsReady(node->d.p->siocnt)) {
if (!node->id && attached > 1 && GBASIOMultiplayerIsReady(node->d.p->siocnt)) {
mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id);
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING);
ATOMIC_STORE(node->p->d.transferCycles, GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(node->d.p->siocnt)][node->p->d.attached - 1]);
@ -442,11 +447,22 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
struct GBASIOLockstepNode* node = user;
mLockstepLock(&node->p->d);
int32_t cycles = 0;
int32_t cycles = cycles = node->nextEvent;
node->nextEvent -= cyclesLate;
node->eventDiff += cyclesLate;
if (node->p->d.attached < 2) {
cycles = GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(node->d.p->siocnt)][0];
switch (node->mode) {
case SIO_MULTI:
cycles = GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(node->d.p->siocnt)][0];
break;
case SIO_NORMAL_8:
case SIO_NORMAL_32:
if (node->nextEvent <= 0) {
cycles = _masterUpdate(node);
node->eventDiff = 0;
}
break;
}
} else if (node->nextEvent <= 0) {
if (!node->id) {
cycles = _masterUpdate(node);
@ -455,8 +471,6 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
cycles += node->p->d.useCycles(&node->p->d, node->id, node->eventDiff);
}
node->eventDiff = 0;
} else {
cycles = node->nextEvent;
}
if (cycles > 0) {
node->nextEvent = 0;

View File

@ -72,7 +72,7 @@ void GBAVideoReset(struct GBAVideo* video) {
} else {
// TODO: Verify exact scanline on hardware
video->vcount = 0x7E;
nextEvent = 170;
nextEvent = 117;
}
video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;

View File

@ -954,7 +954,7 @@ int main() {
.configExtra = (struct GUIMenuItem[]) {
{
.title = "Screen mode",
.data = "screenMode",
.data = GUI_V_S("screenMode"),
.submenu = 0,
.state = SM_PA_TOP,
.validStates = (const char*[]) {
@ -969,7 +969,7 @@ int main() {
},
{
.title = "Filtering",
.data = "filterMode",
.data = GUI_V_S("filterMode"),
.submenu = 0,
.state = FM_LINEAR_2x,
.validStates = (const char*[]) {
@ -981,7 +981,7 @@ int main() {
},
{
.title = "Screen darkening",
.data = "darkenMode",
.data = GUI_V_S("darkenMode"),
.submenu = 0,
.state = DM_NATIVE,
.validStates = (const char*[]) {
@ -994,7 +994,7 @@ int main() {
},
{
.title = "Camera",
.data = "camera",
.data = GUI_V_S("camera"),
.submenu = 0,
.state = 1,
.validStates = (const char*[]) {

View File

@ -17,6 +17,7 @@
#include <mgba/gb/core.h>
#include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/mbc.h>
#include <mgba/internal/gb/overrides.h>
#endif
#ifdef M_CORE_GBA
#include <mgba/gba/core.h>
@ -146,6 +147,38 @@ static void _initRumble(void) {
rumbleInitDone = true;
}
#ifdef M_CORE_GB
static void _updateGbPal(void) {
struct retro_variable var;
var.key = "mgba_gb_colors";
var.value = 0;
if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
const struct GBColorPreset* presets;
size_t listSize = GBColorPresetList(&presets);
size_t i;
for (i = 0; i < listSize; ++i) {
if (strcmp(presets[i].name, var.value) != 0) {
continue;
}
mCoreConfigSetUIntValue(&core->config, "gb.pal[0]", presets[i].colors[0] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[1]", presets[i].colors[1] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[2]", presets[i].colors[2] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[3]", presets[i].colors[3] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[4]", presets[i].colors[4] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[5]", presets[i].colors[5] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[6]", presets[i].colors[6] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[7]", presets[i].colors[7] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[8]", presets[i].colors[8] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[9]", presets[i].colors[9] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[10]", presets[i].colors[10] & 0xFFFFFF);
mCoreConfigSetUIntValue(&core->config, "gb.pal[11]", presets[i].colors[11] & 0xFFFFFF);
core->reloadConfigOption(core, "gb.pal", NULL);
break;
}
}
}
#endif
static void _reloadSettings(void) {
struct mCoreOptions opts = {
.useBios = true,
@ -177,6 +210,14 @@ static void _reloadSettings(void) {
mCoreConfigSetDefaultValue(&core->config, "sgb.model", modelName);
mCoreConfigSetDefaultValue(&core->config, "cgb.model", modelName);
}
var.key = "mgba_sgb_borders";
var.value = 0;
if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
mCoreConfigSetDefaultIntValue(&core->config, "sgb.borders", strcmp(var.value, "ON") == 0);
}
_updateGbPal();
#endif
var.key = "mgba_use_bios";
@ -191,14 +232,6 @@ static void _reloadSettings(void) {
opts.skipBios = strcmp(var.value, "ON") == 0;
}
#ifdef M_CORE_GB
var.key = "mgba_sgb_borders";
var.value = 0;
if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
mCoreConfigSetDefaultIntValue(&core->config, "sgb.borders", strcmp(var.value, "ON") == 0);
}
#endif
var.key = "mgba_frameskip";
var.value = 0;
if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
@ -248,6 +281,22 @@ unsigned retro_api_version(void) {
void retro_set_environment(retro_environment_t env) {
environCallback = env;
#ifdef M_CORE_GB
const struct GBColorPreset* presets;
size_t listSize = GBColorPresetList(&presets);
size_t colorOpt;
for (colorOpt = 0; option_defs_us[colorOpt].key; ++colorOpt) {
if (strcmp(option_defs_us[colorOpt].key, "mgba_gb_colors") == 0) {
break;
}
}
size_t i;
for (i = 0; i < listSize && i < RETRO_NUM_CORE_OPTION_VALUES_MAX; ++i) {
option_defs_us[colorOpt].values[i].value = presets[i].name;
}
#endif
libretro_set_core_options(environCallback);
}
@ -429,6 +478,10 @@ void retro_run(void) {
mCoreConfigSetIntValue(&core->config, "frameskip", strtol(var.value, NULL, 10));
core->reloadConfigOption(core, "frameskip", NULL);
}
#ifdef M_CORE_GB
_updateGbPal();
#endif
}
keys = 0;

View File

@ -186,6 +186,17 @@ struct retro_core_option_definition option_defs_us[] = {
},
"OFF"
},
{
"mgba_gb_colors",
"Set default Game Boy palette",
"Selects which palette is used for Game Boy games that are not Game Boy Color or Super Game Boy compatible, or if the model is forced to Game Boy.",
{
// This list is populated at runtime
{ "Grayscale", NULL },
{ NULL, NULL },
},
"Grayscale"
},
{ NULL, NULL, NULL, {{0}}, NULL },
};

View File

@ -160,7 +160,7 @@ int main() {
.configExtra = (struct GUIMenuItem[]) {
{
.title = "Screen mode",
.data = "screenMode",
.data = GUI_V_S("screenMode"),
.submenu = 0,
.state = 0,
.validStates = (const char*[]) {
@ -173,7 +173,7 @@ int main() {
},
{
.title = "Camera",
.data = "camera",
.data = GUI_V_S("camera"),
.submenu = 0,
.state = 1,
.validStates = (const char*[]) {

View File

@ -87,7 +87,13 @@ void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, QWidget* conte
}
});
QObject::connect(action, &Action::enabled, qaction, &QAction::setEnabled);
QObject::connect(action, &Action::activated, qaction, &QAction::setChecked);
QObject::connect(action, &Action::activated, [qaction, action](bool active) {
if (qaction->isCheckable()) {
qaction->setChecked(active);
} else if (active) {
action->setActive(false);
}
});
QObject::connect(action, &Action::destroyed, qaction, &QAction::deleteLater);
if (shortcut) {
QObject::connect(shortcut, &Shortcut::shortcutChanged, qaction, [qaction](int shortcut) {

View File

@ -22,6 +22,7 @@ void AssetInfo::addCustomProperty(const QString& id, const QString& visibleName)
QLabel* value = new QLabel;
value->setFont(GBAApp::app()->monospaceFont());
value->setAlignment(Qt::AlignRight);
value->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
newLayout->addWidget(value);
m_customProperties[id] = value;
int index = customLocation();

View File

@ -51,7 +51,7 @@
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -74,6 +74,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
@ -96,7 +99,7 @@
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -162,6 +165,9 @@
<property name="text">
<string notr="true">0x00 (00)</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
@ -169,6 +175,9 @@
<property name="text">
<string notr="true">0x00 (00)</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
@ -176,6 +185,9 @@
<property name="text">
<string notr="true">0x00 (00)</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>

View File

@ -142,7 +142,7 @@
<item row="1" column="1">
<widget class="QRadioButton" name="gateBattleChip">
<property name="text">
<string>Ba&amp;ttleChip Gate</string>
<string notr="true">Ba&amp;ttleChip Gate</string>
</property>
<property name="checked">
<bool>true</bool>
@ -152,14 +152,14 @@
<item row="2" column="1">
<widget class="QRadioButton" name="gateProgress">
<property name="text">
<string>Progress &amp;Gate</string>
<string notr="true">Progress &amp;Gate</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QRadioButton" name="gateBeastLink">
<property name="text">
<string>Beast &amp;Link Gate</string>
<string notr="true">Beast &amp;Link Gate</string>
</property>
</widget>
</item>

View File

@ -8,8 +8,10 @@
#include "GBAApp.h"
#include "CoreController.h"
#include <QBoxLayout>
#include <QButtonGroup>
#include <QClipboard>
#include <QPushButton>
#include <QRadioButton>
#include <mgba/core/cheats.h>
#ifdef M_CORE_GBA
@ -30,58 +32,30 @@ CheatsView::CheatsView(std::shared_ptr<CoreController> controller, QWidget* pare
m_ui.cheatList->installEventFilter(this);
m_ui.cheatList->setModel(&m_model);
m_ui.codeEntry->setFont(GBAApp::app()->monospaceFont());
connect(m_ui.load, &QPushButton::clicked, this, &CheatsView::load);
connect(m_ui.save, &QPushButton::clicked, this, &CheatsView::save);
connect(m_ui.addSet, &QPushButton::clicked, this, &CheatsView::addSet);
connect(m_ui.remove, &QPushButton::clicked, this, &CheatsView::removeSet);
connect(m_ui.load, &QAbstractButton::clicked, this, &CheatsView::load);
connect(m_ui.save, &QAbstractButton::clicked, this, &CheatsView::save);
connect(m_ui.addSet, &QAbstractButton::clicked, this, &CheatsView::addSet);
connect(m_ui.remove, &QAbstractButton::clicked, this, &CheatsView::removeSet);
connect(m_ui.add, &QAbstractButton::clicked, this, &CheatsView::enterCheat);
connect(controller.get(), &CoreController::stopping, this, &CheatsView::close);
connect(controller.get(), &CoreController::stateLoaded, &m_model, &CheatsModel::invalidated);
QPushButton* add;
switch (controller->platform()) {
#ifdef M_CORE_GBA
case mPLATFORM_GBA:
connect(m_ui.add, &QPushButton::clicked, [this]() {
enterCheat(GBA_CHEAT_AUTODETECT);
});
add = new QPushButton(tr("Add GameShark"));
m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
connect(add, &QPushButton::clicked, [this]() {
enterCheat(GBA_CHEAT_GAMESHARK);
});
add = new QPushButton(tr("Add Pro Action Replay"));
m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
connect(add, &QPushButton::clicked, [this]() {
enterCheat(GBA_CHEAT_PRO_ACTION_REPLAY);
});
add = new QPushButton(tr("Add CodeBreaker"));
m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
connect(add, &QPushButton::clicked, [this]() {
enterCheat(GBA_CHEAT_CODEBREAKER);
});
registerCodeType(tr("Autodetect (recommended)"), GBA_CHEAT_AUTODETECT);
registerCodeType(QLatin1String("GameShark"), GBA_CHEAT_GAMESHARK);
registerCodeType(QLatin1String("Action Replay MAX"), GBA_CHEAT_PRO_ACTION_REPLAY);
registerCodeType(QLatin1String("CodeBreaker"), GBA_CHEAT_CODEBREAKER);
break;
#endif
#ifdef M_CORE_GB
case mPLATFORM_GB:
connect(m_ui.add, &QPushButton::clicked, [this]() {
enterCheat(GB_CHEAT_AUTODETECT);
});
add = new QPushButton(tr("Add GameShark"));
m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
connect(add, &QPushButton::clicked, [this]() {
enterCheat(GB_CHEAT_GAMESHARK);
});
add = new QPushButton(tr("Add GameGenie"));
m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
connect(add, &QPushButton::clicked, [this]() {
enterCheat(GB_CHEAT_GAME_GENIE);
});
registerCodeType(tr("Autodetect (recommended)"), GB_CHEAT_AUTODETECT);
registerCodeType(QLatin1String("GameShark"), GB_CHEAT_GAMESHARK);
registerCodeType(QLatin1String("Game Genie"), GB_CHEAT_GAME_GENIE);
break;
#endif
default:
@ -127,6 +101,7 @@ void CheatsView::addSet() {
mCheatSet* set = m_controller->cheatDevice()->createSet(m_controller->cheatDevice(), nullptr);
m_model.addSet(set);
m_ui.cheatList->selectionModel()->select(m_model.index(m_model.rowCount() - 1, 0, QModelIndex()), QItemSelectionModel::ClearAndSelect);
enterCheat();
}
void CheatsView::removeSet() {
@ -140,7 +115,21 @@ void CheatsView::removeSet() {
}
}
void CheatsView::enterCheat(int codeType) {
void CheatsView::registerCodeType(const QString& label, int type) {
QRadioButton* add = new QRadioButton(label);
m_ui.typeLayout->addWidget(add);
connect(add, &QAbstractButton::clicked, [this, type]() {
m_codeType = type;
});
if (!m_typeGroup) {
m_typeGroup = new QButtonGroup(this);
m_codeType = type;
add->setChecked(true);
}
m_typeGroup->addButton(add);
}
void CheatsView::enterCheat() {
mCheatSet* set = nullptr;
QModelIndexList selection = m_ui.cheatList->selectionModel()->selectedIndexes();
QModelIndex index;
@ -163,7 +152,7 @@ void CheatsView::enterCheat(int codeType) {
QStringList cheats = m_ui.codeEntry->toPlainText().split('\n', QString::SkipEmptyParts);
for (const QString& string : cheats) {
m_model.beginAppendRow(index);
mCheatAddLine(set, string.toUtf8().constData(), codeType);
mCheatAddLine(set, string.toUtf8().constData(), m_codeType);
m_model.endAppendRow();
}
if (set->refresh) {

View File

@ -33,13 +33,17 @@ private slots:
void save();
void addSet();
void removeSet();
void enterCheat();
private:
void enterCheat(int codeType);
void registerCodeType(const QString& label, int type);
Ui::CheatsView m_ui;
std::shared_ptr<CoreController> m_controller;
CheatsModel m_model;
QButtonGroup* m_typeGroup = nullptr;
int m_codeType = 0;
};
}

View File

@ -6,60 +6,18 @@
<rect>
<x>0</x>
<y>0</y>
<width>629</width>
<height>428</height>
<width>520</width>
<height>455</height>
</rect>
</property>
<property name="windowTitle">
<string>Cheats</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="3,0,0,0">
<item row="2" column="2" colspan="2">
<widget class="QPushButton" name="remove">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="3" column="2" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="save">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="load">
<property name="text">
<string>Load</string>
</property>
</widget>
</item>
<item row="0" column="2" colspan="2">
<widget class="QPushButton" name="addSet">
<property name="text">
<string>Add New Set</string>
</property>
</widget>
</item>
<item row="6" column="2" colspan="2">
<widget class="QPushButton" name="add">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="7" colspan="2">
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,1,0,0" columnstretch="4,0,0">
<item row="0" column="0" rowspan="7">
<widget class="QTreeView" name="cheatList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -75,18 +33,51 @@
</property>
</widget>
</item>
<item row="4" column="2" rowspan="2" colspan="2">
<item row="0" column="1" colspan="2">
<widget class="QPushButton" name="addSet">
<property name="text">
<string>Add New Code</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="load">
<property name="text">
<string>Load</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="save">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QPushButton" name="remove">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QPlainTextEdit" name="codeEntry">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Courier New</family>
</font>
<property name="tabChangesFocus">
<bool>true</bool>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
@ -96,6 +87,21 @@
</property>
</widget>
</item>
<item row="5" column="1" colspan="2">
<widget class="QPushButton" name="add">
<property name="text">
<string>Add Lines</string>
</property>
</widget>
</item>
<item row="6" column="1" colspan="2">
<widget class="QGroupBox" name="typeBox">
<property name="title">
<string>Code type</string>
</property>
<layout class="QVBoxLayout" name="typeLayout"/>
</widget>
</item>
</layout>
</widget>
<tabstops>

View File

@ -529,6 +529,21 @@ void CoreController::forceFastForward(bool enable) {
emit fastForwardChanged(enable || m_fastForward);
}
void CoreController::overrideMute(bool override) {
m_mute = override;
Interrupter interrupter(this);
mCore* core = m_threadContext.core;
if (m_mute) {
core->opts.mute = true;
} else {
int fakeBool = 0;
mCoreConfigGetIntValue(&core->config, "mute", &fakeBool);
core->opts.mute = fakeBool;
}
core->reloadConfigOption(core, NULL, NULL);
}
void CoreController::loadState(int slot) {
if (slot > 0 && slot != m_stateSlot) {
m_stateSlot = slot;
@ -1046,7 +1061,7 @@ void CoreController::updateFastForward() {
m_threadContext.core->opts.volume = m_fastForwardVolume;
}
if (m_fastForwardMute >= 0) {
m_threadContext.core->opts.mute = m_fastForwardMute;
m_threadContext.core->opts.mute = m_fastForwardMute || m_mute;
}
// If we aren't holding the fast forward button

View File

@ -141,6 +141,8 @@ public slots:
void setFastForward(bool);
void forceFastForward(bool);
void overrideMute(bool);
void loadState(int slot = 0);
void loadState(const QString& path, int flags = -1);
void loadState(QIODevice* iodev, int flags = -1);
@ -272,6 +274,8 @@ private:
float m_fastForwardHeldRatio = -1.f;
float m_fpsTarget;
bool m_mute;
InputController* m_inputController = nullptr;
LogController* m_log = nullptr;
MultiplayerController* m_multiplayer = nullptr;

View File

@ -6,6 +6,7 @@
#include "DebuggerConsole.h"
#include "DebuggerConsoleController.h"
#include "GBAApp.h"
#include <QScrollBar>
@ -18,6 +19,8 @@ DebuggerConsole::DebuggerConsole(DebuggerConsoleController* controller, QWidget*
m_ui.setupUi(this);
m_ui.prompt->installEventFilter(this);
m_ui.log->setFont(GBAApp::app()->monospaceFont());
m_ui.prompt->setFont(GBAApp::app()->monospaceFont());
connect(m_ui.prompt, &QLineEdit::returnPressed, this, &DebuggerConsole::postLine);
connect(controller, &DebuggerConsoleController::log, this, &DebuggerConsole::log);
@ -81,4 +84,4 @@ bool DebuggerConsole::eventFilter(QObject*, QEvent* event) {
m_ui.prompt->setText(m_history[m_history.size() - m_historyOffset]);
}
return true;
}
}

View File

@ -16,11 +16,6 @@
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLineEdit" name="prompt">
<property name="font">
<font>
<family>Source Code Pro</family>
</font>
</property>
<property name="placeholderText">
<string>Enter command (try `help` for more info)</string>
</property>
@ -35,11 +30,6 @@
</item>
<item row="0" column="0" colspan="2">
<widget class="QPlainTextEdit" name="log">
<property name="font">
<font>
<family>Source Code Pro</family>
</font>
</property>
<property name="readOnly">
<bool>true</bool>
</property>

View File

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DiscordCoordinator.h"
#include <QDateTime>
#include "CoreController.h"
#include "GBAApp.h"
@ -31,6 +33,11 @@ static void updatePresence() {
discordPresence.details = s_title.toUtf8().constData();
discordPresence.instance = 1;
discordPresence.largeImageKey = "mgba";
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
discordPresence.startTimestamp = QDateTime::currentSecsSinceEpoch();
#else
discordPresence.startTimestamp = QDateTime::currentMSecsSinceEpoch() / 1000;
#endif
Discord_UpdatePresence(&discordPresence);
} else {
Discord_ClearPresence();

View File

@ -447,12 +447,12 @@ void PainterGL::draw() {
m_delayTimer.start();
} else {
if (sync->audioWait || sync->videoFrameWait) {
while (m_delayTimer.nsecsElapsed() + 2000000 < 1000000000 / sync->fpsTarget) {
while (m_delayTimer.nsecsElapsed() + 1'000'000 < 1'000'000'000 / sync->fpsTarget) {
QThread::usleep(500);
}
forceRedraw = true;
} else if (!forceRedraw) {
forceRedraw = m_delayTimer.nsecsElapsed() + 2000000 >= 1000000000 / m_surface->screen()->refreshRate();
forceRedraw = m_delayTimer.nsecsElapsed() + 1'000'000 >= 1'000'000'000 / m_surface->screen()->refreshRate();
}
}
mCoreSyncWaitFrameEnd(sync);

View File

@ -25,6 +25,9 @@
<property name="text">
<string>0x0000</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>

View File

@ -82,7 +82,7 @@
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -173,6 +173,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
@ -199,6 +202,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
@ -246,6 +252,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
@ -272,6 +281,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
@ -283,6 +295,9 @@
<property name="text">
<string notr="true">+0.00</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="2">
@ -290,6 +305,9 @@
<property name="text">
<string notr="true">+1.00</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="0">
@ -304,6 +322,9 @@
<property name="text">
<string notr="true">+1.00</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="1">
@ -324,6 +345,9 @@
<property name="text">
<string notr="true">+0.00</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
@ -405,6 +429,9 @@
<property name="text">
<string>Off</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
@ -439,6 +466,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
@ -473,9 +503,6 @@
<property name="text">
<string/>
</property>
<property name="shortcut">
<string>Return, Ctrl+R</string>
</property>
</widget>
</item>
</layout>
@ -564,6 +591,9 @@
<property name="text">
<string>Normal</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
@ -669,6 +699,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>

View File

@ -143,7 +143,17 @@
</item>
<item>
<property name="text">
<string>EEPROM</string>
<string>EEPROM 8kB</string>
</property>
</item>
<item>
<property name="text">
<string>EEPROM 512 bytes</string>
</property>
</item>
<item>
<property name="text">
<string>SRAM 64kB (bootlegs only)</string>
</property>
</item>
</widget>

View File

@ -211,6 +211,9 @@
<property name="text">
<string notr="true">0x00 (00)</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
@ -218,6 +221,9 @@
<property name="text">
<string notr="true">0x00 (00)</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
@ -225,6 +231,9 @@
<property name="text">
<string notr="true">0x00 (00)</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
@ -274,6 +283,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
@ -284,6 +296,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
@ -294,6 +309,9 @@
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>

View File

@ -36,7 +36,7 @@
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -53,7 +53,7 @@
<string>{TITLE}</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -70,7 +70,7 @@
<string>{ID}</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -87,7 +87,7 @@
<string>{SIZE}</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -104,7 +104,7 @@
<string>{CRC}</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>

View File

@ -98,6 +98,7 @@ ReportView::ReportView(QWidget* parent)
QString description = m_ui.description->text();
description.replace("{projectName}", QLatin1String(projectName));
m_ui.description->setText(description);
m_ui.fileView->setFont(GBAApp::app()->monospaceFont());
connect(m_ui.fileList, &QListWidget::currentTextChanged, this, &ReportView::setShownReport);
}

View File

@ -50,11 +50,6 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="textInteractionFlags">
<set>Qt::TextEditorInteraction</set>
</property>

View File

@ -79,14 +79,11 @@ void SensorView::setController(std::shared_ptr<CoreController> controller) {
void SensorView::jiggerer(QAbstractButton* button, void (InputController::*setter)(int)) {
connect(button, &QAbstractButton::toggled, [this, button, setter](bool checked) {
if (!checked) {
m_jiggered = nullptr;
m_button = nullptr;
} else {
button->setFocus();
m_jiggered = [this, button, setter](int axis) {
(m_input->*setter)(axis);
button->setChecked(false);
button->clearFocus();
};
m_button = button;
m_setter = setter;
}
});
button->installEventFilter(this);
@ -106,8 +103,12 @@ bool SensorView::eventFilter(QObject*, QEvent* event) {
if (event->type() == GamepadAxisEvent::Type()) {
GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
gae->accept();
if (m_jiggered && gae->direction() != GamepadAxisEvent::NEUTRAL && gae->isNew()) {
m_jiggered(gae->axis());
if (m_button && gae->direction() != GamepadAxisEvent::NEUTRAL && gae->isNew()) {
m_button->removeEventFilter(this);
m_button->clearFocus();
m_button->setChecked(false);
(m_input->*m_setter)(gae->axis());
m_button = nullptr;
}
return true;
}

View File

@ -42,7 +42,9 @@ private slots:
private:
Ui::SensorView m_ui;
std::function<void(int)> m_jiggered;
QAbstractButton* m_button = nullptr;
void (InputController::*m_setter)(int);
std::shared_ptr<CoreController> m_controller;
InputController* m_input;
mRotationSource* m_rotation;

View File

@ -423,6 +423,8 @@ void SettingsView::updateConfig() {
saveSetting("suspendScreensaver", m_ui.suspendScreensaver);
saveSetting("pauseOnFocusLost", m_ui.pauseOnFocusLost);
saveSetting("pauseOnMinimize", m_ui.pauseOnMinimize);
saveSetting("muteOnFocusLost", m_ui.muteOnFocusLost);
saveSetting("muteOnMinimize", m_ui.muteOnMinimize);
saveSetting("savegamePath", m_ui.savegamePath);
saveSetting("savestatePath", m_ui.savestatePath);
saveSetting("screenshotPath", m_ui.screenshotPath);
@ -534,6 +536,14 @@ void SettingsView::updateConfig() {
emit languageChanged();
}
if (m_ui.multiplayerAudioAll->isChecked()) {
m_controller->setQtOption("multiplayerAudio", "all");
} else if (m_ui.multiplayerAudio1->isChecked()) {
m_controller->setQtOption("multiplayerAudio", "p1");
} else if (m_ui.multiplayerAudioActive->isChecked()) {
m_controller->setQtOption("multiplayerAudio", "active");
}
int hwaccelVideo = m_controller->getOption("hwaccelVideo").toInt();
saveSetting("hwaccelVideo", m_ui.hwaccelVideo->currentIndex());
if (hwaccelVideo != m_ui.hwaccelVideo->currentIndex()) {
@ -767,6 +777,15 @@ void SettingsView::reloadConfig() {
m_ui.videoScaleSize->setText(tr("(%1×%2)").arg(GBA_VIDEO_HORIZONTAL_PIXELS * value).arg(GBA_VIDEO_VERTICAL_PIXELS * value));
});
loadSetting("videoScale", m_ui.videoScale, 1);
QString multiplayerAudio = m_controller->getQtOption("multiplayerAudio").toString();
if (multiplayerAudio == QLatin1String("p1")) {
m_ui.multiplayerAudio1->setChecked(true);
} else if (multiplayerAudio == QLatin1String("active")) {
m_ui.multiplayerAudioActive->setChecked(true);
} else {
m_ui.multiplayerAudioAll->setChecked(true);
}
}
void SettingsView::addPage(const QString& name, QWidget* view, Page index) {

View File

@ -313,21 +313,61 @@
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<item row="5" column="0">
<widget class="QLabel" name="label_43">
<property name="text">
<string>Audio in multiplayer:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QRadioButton" name="multiplayerAudioAll">
<property name="text">
<string>All windows</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">multiplayerAudio</string>
</attribute>
</widget>
</item>
<item row="6" column="1">
<widget class="QRadioButton" name="multiplayerAudio1">
<property name="text">
<string>Player 1 window only</string>
</property>
<attribute name="buttonGroup">
<string notr="true">multiplayerAudio</string>
</attribute>
</widget>
</item>
<item row="7" column="1">
<widget class="QRadioButton" name="multiplayerAudioActive">
<property name="text">
<string>Currently active player window</string>
</property>
<attribute name="buttonGroup">
<string notr="true">multiplayerAudio</string>
</attribute>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="6" column="0">
<item row="9" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Display driver:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="9" column="1">
<widget class="QComboBox" name="displayDriver">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -337,14 +377,14 @@
</property>
</widget>
</item>
<item row="7" column="0">
<item row="10" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Frameskip:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="10" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_16">
<item>
<widget class="QLabel" name="label_12">
@ -365,14 +405,14 @@
</item>
</layout>
</item>
<item row="8" column="0">
<item row="11" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>FPS target:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="11" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QDoubleSpinBox" name="fpsTarget">
@ -399,28 +439,28 @@
</item>
</layout>
</item>
<item row="9" column="1">
<item row="12" column="1">
<widget class="QPushButton" name="nativeGB">
<property name="text">
<string>Native (59.7275)</string>
</property>
</widget>
</item>
<item row="10" column="0" colspan="2">
<item row="13" column="0" colspan="2">
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="11" column="0">
<item row="14" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Sync:</string>
</property>
</widget>
</item>
<item row="11" column="1">
<item row="14" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QCheckBox" name="videoSync">
@ -438,28 +478,28 @@
</item>
</layout>
</item>
<item row="12" column="1">
<item row="15" column="1">
<widget class="QCheckBox" name="lockAspectRatio">
<property name="text">
<string>Lock aspect ratio</string>
</property>
</widget>
</item>
<item row="13" column="1">
<item row="16" column="1">
<widget class="QCheckBox" name="lockIntegerScaling">
<property name="text">
<string>Force integer scaling</string>
</property>
</widget>
</item>
<item row="14" column="1">
<item row="17" column="1">
<widget class="QCheckBox" name="interframeBlending">
<property name="text">
<string>Interframe blending</string>
</property>
</widget>
</item>
<item row="15" column="1">
<item row="18" column="1">
<widget class="QCheckBox" name="resampleVideo">
<property name="text">
<string>Bilinear filtering</string>
@ -556,18 +596,22 @@
</widget>
</item>
<item row="8" column="1">
<widget class="QCheckBox" name="pauseOnFocusLost">
<property name="text">
<string>Pause when inactive</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="pauseOnMinimize">
<property name="text">
<string>Pause when minimized</string>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<item>
<widget class="QCheckBox" name="pauseOnFocusLost">
<property name="text">
<string>Pause</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="muteOnFocusLost">
<property name="text">
<string>Mute</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" column="0" colspan="2">
<widget class="Line" name="line_17">
@ -684,6 +728,38 @@
</property>
</widget>
</item>
<item row="9" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_24">
<item>
<widget class="QCheckBox" name="pauseOnMinimize">
<property name="text">
<string>Pause</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="muteOnMinimize">
<property name="text">
<string>Mute</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_41">
<property name="text">
<string>When inactive:</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_42">
<property name="text">
<string>When minimized:</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="emulation">
@ -2218,5 +2294,6 @@
</connections>
<buttongroups>
<buttongroup name="gbColors"/>
<buttongroup name="multiplayerAudio"/>
</buttongroups>
</ui>

View File

@ -250,6 +250,11 @@ void Window::resizeFrame(const QSize& size) {
}
}
void Window::updateMultiplayerActive(bool active) {
m_multiActive = active;
updateMute();
}
void Window::setConfig(ConfigController* config) {
m_config = config;
}
@ -282,6 +287,7 @@ void Window::reloadConfig() {
if (m_audioProcessor) {
m_audioProcessor->configure(m_config);
}
updateMute();
m_display->resizeContext();
}
@ -673,12 +679,17 @@ void Window::resizeEvent(QResizeEvent*) {
void Window::showEvent(QShowEvent* event) {
if (m_wasOpened) {
if (event->spontaneous() && m_config->getOption("pauseOnMinimize").toInt() && m_controller) {
if (event->spontaneous() && m_controller) {
focusCheck();
if (m_autoresume) {
if (m_config->getOption("pauseOnMinimize").toInt() && m_autoresume) {
m_controller->setPaused(false);
m_autoresume = false;
}
if (m_config->getOption("muteOnMinimize").toInt()) {
m_inactiveMute = false;
updateMute();
}
}
return;
}
@ -709,13 +720,18 @@ void Window::hideEvent(QHideEvent* event) {
if (!event->spontaneous()) {
return;
}
if (!m_config->getOption("pauseOnMinimize").toInt() || !m_controller) {
if (!m_controller) {
return;
}
if (!m_controller->isPaused()) {
if (m_config->getOption("pauseOnMinimize").toInt() && !m_controller->isPaused()) {
m_autoresume = true;
m_controller->setPaused(true);
}
if (m_config->getOption("muteOnMinimize").toInt()) {
m_inactiveMute = true;
updateMute();
}
}
void Window::closeEvent(QCloseEvent* event) {
@ -737,6 +753,13 @@ void Window::closeEvent(QCloseEvent* event) {
}
void Window::focusInEvent(QFocusEvent*) {
for (Window* window : GBAApp::app()->windows()) {
if (window != this) {
window->updateMultiplayerActive(false);
} else {
updateMultiplayerActive(true);
}
}
m_display->forceDraw();
}
@ -1873,15 +1896,25 @@ Action* Window::addGameAction(const QString& visibleName, const QString& name, A
}
void Window::focusCheck() {
if (!m_config->getOption("pauseOnFocusLost").toInt() || !m_controller) {
if (!m_controller) {
return;
}
if (QGuiApplication::focusWindow() && m_autoresume) {
m_controller->setPaused(false);
m_autoresume = false;
} else if (!QGuiApplication::focusWindow() && !m_controller->isPaused()) {
m_autoresume = true;
m_controller->setPaused(true);
if (m_config->getOption("pauseOnFocusLost").toInt()) {
if (QGuiApplication::focusWindow() && m_autoresume) {
m_controller->setPaused(false);
m_autoresume = false;
} else if (!QGuiApplication::focusWindow() && !m_controller->isPaused()) {
m_autoresume = true;
m_controller->setPaused(true);
}
}
if (m_config->getOption("muteOnFocusLost").toInt()) {
if (QGuiApplication::focusWindow()) {
m_inactiveMute = false;
} else {
m_inactiveMute = true;
}
updateMute();
}
}
@ -2020,6 +2053,26 @@ void Window::attachDisplay() {
m_display->startDrawing(m_controller);
}
void Window::updateMute() {
if (!m_controller) {
return;
}
bool mute = m_inactiveMute;
if (!mute) {
QString multiplayerAudio = m_config->getQtOption("multiplayerAudio").toString();
if (multiplayerAudio == QLatin1String("p1")) {
MultiplayerController* multiplayer = m_controller->multiplayerController();
mute = multiplayer && multiplayer->attached() > 1 && multiplayer->playerId(m_controller.get());
} else if (multiplayerAudio == QLatin1String("active")) {
mute = !m_multiActive;
}
}
m_controller->overrideMute(mute);
}
void Window::setLogo() {
m_screenWidget->setPixmap(m_logo);
m_screenWidget->setCenteredAspectRatio(m_logo.width(), m_logo.height());

View File

@ -64,6 +64,7 @@ public:
void resizeFrame(const QSize& size);
void updateMultiplayerStatus(bool canOpenAnother) { m_multiWindow->setEnabled(canOpenAnother); }
void updateMultiplayerActive(bool active);
signals:
void startDrawing();
@ -145,6 +146,7 @@ private slots:
void focusCheck();
void updateFrame();
void updateMute();
void setLogo();
@ -224,6 +226,9 @@ private:
bool m_hitUnimplementedBiosCall;
bool m_inactiveMute = false;
bool m_multiActive = true;
std::unique_ptr<OverrideView> m_overrideView;
std::unique_ptr<SensorView> m_sensorView;
std::unique_ptr<DolphinConnector> m_dolphinView;

View File

@ -59,6 +59,7 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
mSDLInitEvents(&s_sdlEvents);
}
++s_sdlInited;
m_sdlPlayer.bindings = &m_inputMap;
updateJoysticks();
#endif
@ -272,34 +273,45 @@ void InputController::setConfiguration(ConfigController* config) {
if (!m_playerAttached) {
m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
}
loadConfiguration(SDL_BINDING_BUTTON);
if (!loadConfiguration(SDL_BINDING_BUTTON)) {
mSDLInitBindingsGBA(&m_inputMap);
}
loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
#endif
restoreModel();
}
void InputController::loadConfiguration(uint32_t type) {
bool InputController::loadConfiguration(uint32_t type) {
if (!m_activeKeyInfo) {
return;
return false;
}
if (!mInputMapLoad(&m_inputMap, type, m_config->input())) {
return false;
}
mInputMapLoad(&m_inputMap, type, m_config->input());
#ifdef BUILD_SDL
if (m_playerAttached) {
mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
}
#endif
return true;
}
void InputController::loadProfile(uint32_t type, const QString& profile) {
bool InputController::loadProfile(uint32_t type, const QString& profile) {
if (profile.isEmpty()) {
return;
return false;
}
const InputProfile* ip = InputProfile::findProfile(profile);
if (ip) {
ip->apply(this);
bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
recalibrateAxes();
if (!loaded) {
const InputProfile* ip = InputProfile::findProfile(profile);
if (ip) {
ip->apply(this);
loaded = true;
}
}
recalibrateAxes();
emit profileLoaded(profile);
return loaded;
}
void InputController::saveConfiguration() {

View File

@ -73,8 +73,8 @@ public:
void setConfiguration(ConfigController* config);
void saveConfiguration();
void loadConfiguration(uint32_t type);
void loadProfile(uint32_t type, const QString& profile);
bool loadConfiguration(uint32_t type);
bool loadProfile(uint32_t type, const QString& profile);
void saveConfiguration(uint32_t type);
void saveProfile(uint32_t type, const QString& profile);
const char* profileForType(uint32_t type);

View File

@ -106,6 +106,7 @@ VFile* LibraryController::selectedVFile() {
QByteArray filenameUtf8(entry.filename.toUtf8());
libentry.base = baseUtf8.constData();
libentry.filename = filenameUtf8.constData();
libentry.platform = mPLATFORM_NONE;
return mLibraryOpenVFile(m_library.get(), &libentry);
} else {
return nullptr;

View File

@ -56,12 +56,12 @@ Game Boy Advance ist eine eingetragene Marke von Nintendo Co., Ltd.</translation
<message>
<location filename="../ArchiveInspector.ui" line="14"/>
<source>Open in archive...</source>
<translation>In Archiv öffnen </translation>
<translation>In Archiv öffnen&#xa0;</translation>
</message>
<message>
<location filename="../ArchiveInspector.ui" line="20"/>
<source>Loading...</source>
<translation>Laden </translation>
<translation>Laden&#xa0;</translation>
</message>
</context>
<context>
@ -69,12 +69,12 @@ Game Boy Advance ist eine eingetragene Marke von Nintendo Co., Ltd.</translation
<message>
<location filename="../AssetTile.ui" line="41"/>
<source>Tile #</source>
<translation>Tile Nr.</translation>
<translation>Tile #</translation>
</message>
<message>
<location filename="../AssetTile.ui" line="65"/>
<source>Palette #</source>
<translation>Palette Nr.</translation>
<translation>Palette #</translation>
</message>
<message>
<location filename="../AssetTile.ui" line="86"/>
@ -1071,7 +1071,7 @@ Game Boy Advance ist eine eingetragene Marke von Nintendo Co., Ltd.</translation
<message>
<location filename="../OverrideView.ui" line="519"/>
<source>Palette preset</source>
<translation type="unfinished"></translation>
<translation>Paletten-Voreinstellung</translation>
</message>
</context>
<context>
@ -4252,7 +4252,7 @@ Game Boy Advance ist eine eingetragene Marke von Nintendo Co., Ltd.</translation
<message>
<location filename="../Window.cpp" line="1184"/>
<source>Convert e-Reader card image to raw...</source>
<translation>Lesegerät-Kartenbild in Rohdaten umwandeln </translation>
<translation>Lesegerät-Kartenbild in Rohdaten umwandeln&#xa0;</translation>
</message>
<message>
<location filename="../Window.cpp" line="1188"/>
@ -5357,37 +5357,37 @@ Game Boy Advance ist eine eingetragene Marke von Nintendo Co., Ltd.</translation
<message>
<location filename="../SettingsView.ui" line="1852"/>
<source>Default color palette only</source>
<translation type="unfinished"></translation>
<translation>Nur Standard-Farbpalette</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1862"/>
<source>SGB color palette if available</source>
<translation type="unfinished"></translation>
<translation>SGB-Farbpalette, sofern verfügbar</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1872"/>
<source>GBC color palette if available</source>
<translation type="unfinished"></translation>
<translation>GBC-Farbpalette, sofern verfügbar</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1882"/>
<source>SGB (preferred) or GBC color palette if available</source>
<translation type="unfinished"></translation>
<translation>SGB (bevorzugt) oder GBC-Farbpalette, sofern verfügbar</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1895"/>
<source>Game Boy Camera</source>
<translation type="unfinished"></translation>
<translation>Game Boy Camera</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1901"/>
<source>Driver:</source>
<translation type="unfinished"></translation>
<translation>Treiber:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1918"/>
<source>Source:</source>
<translation type="unfinished"></translation>
<translation>Quelle:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="405"/>
@ -5448,42 +5448,42 @@ Game Boy Advance ist eine eingetragene Marke von Nintendo Co., Ltd.</translation
<message>
<location filename="../SettingsView.ui" line="1502"/>
<source>Models</source>
<translation type="unfinished"></translation>
<translation>Modelle</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1508"/>
<source>GB only:</source>
<translation type="unfinished"></translation>
<translation>Nur GB:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1518"/>
<source>SGB compatible:</source>
<translation type="unfinished"></translation>
<translation>SGB-kompatibel:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1528"/>
<source>GBC only:</source>
<translation type="unfinished"></translation>
<translation>Nur GBC:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1538"/>
<source>GBC compatible:</source>
<translation type="unfinished"></translation>
<translation>GBC-kompatibel:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1548"/>
<source>SGB and GBC compatible:</source>
<translation type="unfinished"></translation>
<translation>SGB- und GBC-kompatibel:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1568"/>
<source>Game Boy palette</source>
<translation type="unfinished"></translation>
<translation>Game Boy-Palette</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1574"/>
<source>Preset:</source>
<translation type="unfinished"></translation>
<translation>Voreinstellungen:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="879"/>
@ -5538,7 +5538,7 @@ Game Boy Advance ist eine eingetragene Marke von Nintendo Co., Ltd.</translation
<message>
<location filename="../SettingsView.ui" line="886"/>
<source>Enable VBA bug compatibility in ROM hacks</source>
<translation type="unfinished"></translation>
<translation>VBA-Bug-Kompatibilität in ROM-Hacks aktivieren</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="982"/>

View File

@ -5286,37 +5286,37 @@ Game Boy Advance es una marca registrada de Nintendo Co., Ltd.</translation>
<message>
<location filename="../SettingsView.ui" line="1852"/>
<source>Default color palette only</source>
<translation type="unfinished"></translation>
<translation>Sólo paleta de colores predeterminada</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1862"/>
<source>SGB color palette if available</source>
<translation type="unfinished"></translation>
<translation>Paleta de color SGB si está disponible</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1872"/>
<source>GBC color palette if available</source>
<translation type="unfinished"></translation>
<translation>Paleta de color GBC si está disponible</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1882"/>
<source>SGB (preferred) or GBC color palette if available</source>
<translation type="unfinished"></translation>
<translation>Paleta de colores SGB (preferida) o GBC si está disponible</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1895"/>
<source>Game Boy Camera</source>
<translation type="unfinished"></translation>
<translation>Cámara Game Boy</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1901"/>
<source>Driver:</source>
<translation type="unfinished"></translation>
<translation>Controlador:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1918"/>
<source>Source:</source>
<translation type="unfinished"></translation>
<translation>Fuente:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="405"/>
@ -5362,12 +5362,12 @@ Game Boy Advance es una marca registrada de Nintendo Co., Ltd.</translation>
<message>
<location filename="../SettingsView.ui" line="886"/>
<source>Enable VBA bug compatibility in ROM hacks</source>
<translation type="unfinished"></translation>
<translation>Activar modo de compatibilidad VBA en ROM hacks</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1574"/>
<source>Preset:</source>
<translation type="unfinished"></translation>
<translation>Ajustes:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="602"/>
@ -5662,37 +5662,37 @@ Game Boy Advance es una marca registrada de Nintendo Co., Ltd.</translation>
<message>
<location filename="../SettingsView.ui" line="1502"/>
<source>Models</source>
<translation type="unfinished"></translation>
<translation>Modelos</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1508"/>
<source>GB only:</source>
<translation type="unfinished"></translation>
<translation>Sólo GB:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1518"/>
<source>SGB compatible:</source>
<translation type="unfinished"></translation>
<translation>Compatible con SGB:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1528"/>
<source>GBC only:</source>
<translation type="unfinished"></translation>
<translation>Sólo GBC:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1538"/>
<source>GBC compatible:</source>
<translation type="unfinished"></translation>
<translation>Compatible con GBC:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1548"/>
<source>SGB and GBC compatible:</source>
<translation type="unfinished"></translation>
<translation>Compatible con SGB y GBC:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1568"/>
<source>Game Boy palette</source>
<translation type="unfinished"></translation>
<translation>Paleta de Game Boy</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1584"/>

View File

@ -1047,12 +1047,12 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../OverrideView.ui" line="342"/>
<source>Sprite Colors 1</source>
<translation>Couleurs du Sprite  1</translation>
<translation>Couleurs du Sprite &#xa0;1</translation>
</message>
<message>
<location filename="../OverrideView.ui" line="349"/>
<source>Sprite Colors 2</source>
<translation>Couleurs du Sprite  2</translation>
<translation>Couleurs du Sprite &#xa0;2</translation>
</message>
<message>
<location filename="../OverrideView.ui" line="519"/>
@ -1184,7 +1184,7 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../AboutScreen.cpp" line="77"/>
<source>2021</source>
<translation type="unfinished">2021</translation>
<translation>2021</translation>
</message>
</context>
<context>
@ -1446,17 +1446,17 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../IOViewer.cpp" line="40"/>
<source>Mode 0: 4 tile layers</source>
<translation>Mode 0 : 4 couches de tuile</translation>
<translation>Mode 0&#xa0;: 4 couches de tuile</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="41"/>
<source>Mode 1: 2 tile layers + 1 rotated/scaled tile layer</source>
<translation>Mode 1 : 2 couches de tuiles + 1 couche de tuiles pivotée / mise à l&apos;échelle</translation>
<translation>Mode 1&#xa0;: 2 couches de tuiles + 1 couche de tuiles pivotée / mise à l&apos;échelle</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="42"/>
<source>Mode 2: 2 rotated/scaled tile layers</source>
<translation>Mode 2 : 2 couches de tuiles pivotées / mises à l&apos;échelle</translation>
<translation>Mode 2&#xa0;: 2 couches de tuiles pivotées / mises à l&apos;échelle</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="43"/>
@ -1501,22 +1501,22 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../IOViewer.cpp" line="54"/>
<source>Enable background 0</source>
<translation>Activer l&apos;arrière-plan  0</translation>
<translation>Activer l&apos;arrière-plan &#xa0;0</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="55"/>
<source>Enable background 1</source>
<translation>Activer l&apos;arrière-plan  1</translation>
<translation>Activer l&apos;arrière-plan &#xa0;1</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="56"/>
<source>Enable background 2</source>
<translation>Activer l&apos;arrière-plan  2</translation>
<translation>Activer l&apos;arrière-plan &#xa0;2</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="57"/>
<source>Enable background 3</source>
<translation>Activer l&apos;arrière-plan  3</translation>
<translation>Activer l&apos;arrière-plan &#xa0;3</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="58"/>
@ -1526,12 +1526,12 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../IOViewer.cpp" line="59"/>
<source>Enable Window 0</source>
<translation>Activer la fenêtre  0</translation>
<translation>Activer la fenêtre &#xa0;0</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="60"/>
<source>Enable Window 1</source>
<translation>Actvier la fenêtre  1</translation>
<translation>Actvier la fenêtre &#xa0;1</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="61"/>
@ -1728,82 +1728,82 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../IOViewer.cpp" line="249"/>
<source>Window 0 enable BG 0</source>
<translation>La fenêtre  0 active l&apos;arrière plan  0</translation>
<translation>La fenêtre &#xa0;0 active l&apos;arrière plan &#xa0;0</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="250"/>
<source>Window 0 enable BG 1</source>
<translation>La fenêtre  0 active l&apos;arrière plan  1</translation>
<translation>La fenêtre &#xa0;0 active l&apos;arrière plan &#xa0;1</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="251"/>
<source>Window 0 enable BG 2</source>
<translation>La fenêtre  0 active l&apos;arrière plan  2</translation>
<translation>La fenêtre &#xa0;0 active l&apos;arrière plan &#xa0;2</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="252"/>
<source>Window 0 enable BG 3</source>
<translation>La fenêtre  0 active l&apos;arrière plan  3</translation>
<translation>La fenêtre &#xa0;0 active l&apos;arrière plan &#xa0;3</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="253"/>
<source>Window 0 enable OBJ</source>
<translation>La fenêtre  0 active l&apos;OBJ</translation>
<translation>La fenêtre &#xa0;0 active l&apos;OBJ</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="254"/>
<source>Window 0 enable blend</source>
<translation>La fenêtre  0 active le mixage</translation>
<translation>La fenêtre &#xa0;0 active le mixage</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="255"/>
<source>Window 1 enable BG 0</source>
<translation>La fenêtre  1 active l&apos;arrière plan  0</translation>
<translation>La fenêtre &#xa0;1 active l&apos;arrière plan &#xa0;0</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="256"/>
<source>Window 1 enable BG 1</source>
<translation>La fenêtre  1 active l&apos;arrière plan  1</translation>
<translation>La fenêtre &#xa0;1 active l&apos;arrière plan &#xa0;1</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="257"/>
<source>Window 1 enable BG 2</source>
<translation>La fenêtre  1 active l&apos;arrière plan  2</translation>
<translation>La fenêtre &#xa0;1 active l&apos;arrière plan &#xa0;2</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="258"/>
<source>Window 1 enable BG 3</source>
<translation>La fenêtre  1 active l&apos;arrière plan  3</translation>
<translation>La fenêtre &#xa0;1 active l&apos;arrière plan &#xa0;3</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="259"/>
<source>Window 1 enable OBJ</source>
<translation>La fenêtre  1 active l&apos;OBJ</translation>
<translation>La fenêtre &#xa0;1 active l&apos;OBJ</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="260"/>
<source>Window 1 enable blend</source>
<translation>La fenêtre  1 active le mixage</translation>
<translation>La fenêtre &#xa0;1 active le mixage</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="264"/>
<source>Outside window enable BG 0</source>
<translation>La fenêtre extérieure active l&apos;arrière plan  0</translation>
<translation>La fenêtre extérieure active l&apos;arrière plan &#xa0;0</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="265"/>
<source>Outside window enable BG 1</source>
<translation>La fenêtre extérieure active l&apos;arrière plan  1</translation>
<translation>La fenêtre extérieure active l&apos;arrière plan &#xa0;1</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="266"/>
<source>Outside window enable BG 2</source>
<translation>La fenêtre extérieure active l&apos;arrière plan  2</translation>
<translation>La fenêtre extérieure active l&apos;arrière plan &#xa0;2</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="267"/>
<source>Outside window enable BG 3</source>
<translation>La fenêtre extérieure active l&apos;arrière plan  3</translation>
<translation>La fenêtre extérieure active l&apos;arrière plan &#xa0;3</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="268"/>
@ -1981,7 +1981,7 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<location filename="../IOViewer.cpp" line="330"/>
<location filename="../IOViewer.cpp" line="1101"/>
<source>Sweep time (in 1/128s)</source>
<translation>Durée de balayage (en 1/128 s)</translation>
<translation>Durée de balayage (en 1/128&#xa0;s)</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="334"/>
@ -2090,28 +2090,28 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<location filename="../IOViewer.cpp" line="376"/>
<location filename="../IOViewer.cpp" line="1158"/>
<source>0%</source>
<translation>0 %</translation>
<translation>0&#xa0;%</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="377"/>
<location filename="../IOViewer.cpp" line="434"/>
<location filename="../IOViewer.cpp" line="1159"/>
<source>100%</source>
<translation>100 %</translation>
<translation>100&#xa0;%</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="378"/>
<location filename="../IOViewer.cpp" line="433"/>
<location filename="../IOViewer.cpp" line="1160"/>
<source>50%</source>
<translation>50 %</translation>
<translation>50&#xa0;%</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="379"/>
<location filename="../IOViewer.cpp" line="432"/>
<location filename="../IOViewer.cpp" line="1161"/>
<source>25%</source>
<translation>25 %</translation>
<translation>25&#xa0;%</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="380"/>
@ -2119,7 +2119,7 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<location filename="../IOViewer.cpp" line="382"/>
<location filename="../IOViewer.cpp" line="383"/>
<source>75%</source>
<translation>75 %</translation>
<translation>75&#xa0;%</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="405"/>
@ -2944,17 +2944,17 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../IOViewer.cpp" line="1016"/>
<source>4.19MHz</source>
<translation>4,19 MHz</translation>
<translation>4,19&#xa0;MHz</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="1017"/>
<source>8.38MHz</source>
<translation>8,38 MHz</translation>
<translation>8,38&#xa0;MHz</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="1018"/>
<source>16.78MHz</source>
<translation>16,78 MHz</translation>
<translation>16,78&#xa0;MHz</translation>
</message>
<message>
<location filename="../IOViewer.cpp" line="1020"/>
@ -3407,7 +3407,7 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../LogController.cpp" line="68"/>
<source>[%1] %2: %3</source>
<translation>[%1] %2 : %3</translation>
<translation>[%1] %2&#xa0;: %3</translation>
</message>
<message>
<location filename="../LogController.cpp" line="79"/>
@ -4836,7 +4836,7 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../ROMInfo.ui" line="26"/>
<source>Game name:</source>
<translation>Nom du jeu :</translation>
<translation>Nom du jeu&#xa0;:</translation>
</message>
<message>
<location filename="../ROMInfo.ui" line="33"/>
@ -5271,7 +5271,7 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../SettingsView.ui" line="371"/>
<source>FPS target:</source>
<translation>IPS ciblée :</translation>
<translation>IPS ciblée&#xa0;:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="396"/>
@ -5727,7 +5727,7 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../SettingsView.ui" line="1671"/>
<source>Default sprite colors 1:</source>
<translation>Couleurs par défaut de la sprite  1 :</translation>
<translation>Couleurs par défaut de la sprite &#xa0;1 :</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1758"/>
@ -5737,17 +5737,17 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</
<message>
<location filename="../SettingsView.ui" line="1502"/>
<source>Game Boy-only model:</source>
<translation>Modèle Game Boy uniquement :</translation>
<translation>Modèle Game Boy uniquement&#xa0;:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1522"/>
<source>Game Boy Color-only model:</source>
<translation>Modèle Game Boy uniquement :</translation>
<translation>Modèle Game Boy uniquement&#xa0;:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1532"/>
<source>Game Boy/Game Boy Color model:</source>
<translation>Modèle Game Boy / Game Boy Color :</translation>
<translation>Modèle Game Boy / Game Boy Color&#xa0;:</translation>
</message>
</context>
<context>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -48,8 +48,10 @@ endif()
if(WIN32)
list(APPEND SDL_LIBRARY imm32 setupapi version winmm)
elseif(APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AppKit -framework AudioUnit -framework Carbon -framework CoreAudio -framework AudioToolbox -framework ForceFeedback -framework IOKit")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}" PARENT_SCOPE)
list(APPEND SDL_LIBRARY "-framework AppKit" "-framework AudioUnit" "-framework Carbon" "-framework CoreAudio" "-framework AudioToolbox" "-framework ForceFeedback" "-framework IOKit")
if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "17.0") # Darwin 17.x is macOS 10.13
list(APPEND SDL_LIBRARY "-framework Metal")
endif()
endif()
if(NOT SDLMAIN_LIBRARY)

View File

@ -924,7 +924,7 @@ int main(int argc, char* argv[]) {
.configExtra = (struct GUIMenuItem[]) {
{
.title = "Screen mode",
.data = "screenMode",
.data = GUI_V_S("screenMode"),
.submenu = 0,
.state = SM_PA,
.validStates = (const char*[]) {
@ -936,7 +936,7 @@ int main(int argc, char* argv[]) {
},
{
.title = "Fast forward cap",
.data = "fastForwardCap",
.data = GUI_V_S("fastForwardCap"),
.submenu = 0,
.state = 7,
.validStates = (const char*[]) {
@ -966,7 +966,7 @@ int main(int argc, char* argv[]) {
},
{
.title = "GPU-accelerated renderer",
.data = "hwaccelVideo",
.data = GUI_V_S("hwaccelVideo"),
.submenu = 0,
.state = 0,
.validStates = (const char*[]) {
@ -977,7 +977,7 @@ int main(int argc, char* argv[]) {
},
{
.title = "Hi-res scaling (requires GPU rendering)",
.data = "videoScale",
.data = GUI_V_S("videoScale"),
.submenu = 0,
.state = 0,
.validStates = (const char*[]) {
@ -1000,7 +1000,7 @@ int main(int argc, char* argv[]) {
},
{
.title = "Use built-in brightness sensor for Boktai",
.data = "useLightSensor",
.data = GUI_V_S("useLightSensor"),
.submenu = 0,
.state = illuminanceAvailable,
.validStates = (const char*[]) {

View File

@ -490,7 +490,7 @@ int main(int argc, char* argv[]) {
.configExtra = (struct GUIMenuItem[]) {
{
.title = "Video mode",
.data = "videoMode",
.data = GUI_V_S("videoMode"),
.submenu = 0,
.state = 0,
.validStates = (const char*[]) {
@ -503,7 +503,7 @@ int main(int argc, char* argv[]) {
},
{
.title = "Screen mode",
.data = "screenMode",
.data = GUI_V_S("screenMode"),
.submenu = 0,
.state = 0,
.validStates = (const char*[]) {
@ -514,7 +514,7 @@ int main(int argc, char* argv[]) {
},
{
.title = "Filtering",
.data = "filter",
.data = GUI_V_S("filter"),
.submenu = 0,
.state = 0,
.validStates = (const char*[]) {
@ -526,7 +526,7 @@ int main(int argc, char* argv[]) {
},
{
.title = "Horizontal stretch",
.data = "stretchWidth",
.data = GUI_V_S("stretchWidth"),
.submenu = 0,
.state = 7,
.validStates = (const char*[]) {
@ -546,7 +546,7 @@ int main(int argc, char* argv[]) {
},
{
.title = "Vertical stretch",
.data = "stretchHeight",
.data = GUI_V_S("stretchHeight"),
.submenu = 0,
.state = 6,
.validStates = (const char*[]) {

View File

@ -94,7 +94,7 @@ static bool _refreshDirectory(struct GUIParams* params, const char* currentPath,
} else {
name = strdup(name);
}
*GUIMenuItemListAppend(currentFiles) = (struct GUIMenuItem) { .title = name, .data = (void*) de->type(de) };
*GUIMenuItemListAppend(currentFiles) = (struct GUIMenuItem) { .title = name, .data = GUI_V_U(de->type(de)) };
++items;
}
qsort(GUIMenuItemListGetPointer(currentFiles, 1), GUIMenuItemListSize(currentFiles) - 1, sizeof(struct GUIMenuItem), _strpcmp);
@ -124,7 +124,7 @@ static bool _refreshDirectory(struct GUIParams* params, const char* currentPath,
params->drawEnd();
}
struct GUIMenuItem* testItem = GUIMenuItemListGetPointer(currentFiles, item);
if (testItem->data != (void*) VFS_FILE) {
if (!GUIVariantCompareUInt(testItem->data, VFS_FILE)) {
++item;
continue;
}

View File

@ -137,7 +137,7 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
if (reason != GUI_MENU_EXIT_BACK) {
return reason;
}
} else if ((*item)->validStates && !(*item)->data) {
} else if ((*item)->validStates && !(*item)->data.v.p) {
_itemNext(*item, true);
} else {
return GUI_MENU_EXIT_ACCEPT;
@ -152,7 +152,7 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
params->drawStart();
if (menu->background) {
menu->background->draw(menu->background, GUIMenuItemListGetPointer(&menu->items, menu->index)->data);
menu->background->draw(menu->background, GUIMenuItemListGetPointer(&menu->items, menu->index)->data.v.p);
}
if (params->guiPrepare) {
params->guiPrepare();