Compare commits

...

118 Commits

Author SHA1 Message Date
Vicki Pfau 1163869139 CHANGES: Release 0.9.3 2021-12-17 17:56:32 -08:00
Vicki Pfau eb53dc4884 Res: Update patrons for November 2021-12-17 17:56:32 -08:00
Vicki Pfau f94687a854 CHANGES: Minor text fixes 2021-12-17 17:56:30 -08:00
Vicki Pfau c8d99705b3 mGUI: Fix crash if autosave file can't be opened (fixes #2268) 2021-11-29 14:05:03 -08:00
François Berder dea26b756a Debugger: Fix buffer overflow in _doTrace (#2361) 2021-11-29 14:04:27 -08:00
Vicki Pfau aceb1dff97 GBA I/O: Update KEYINPUT in internal I/O memory (fixes #2235) 2021-11-25 16:12:58 -08:00
Vicki Pfau d6ad731ebe Qt: Pay down technical debt with map caches 2021-11-17 17:52:57 -08:00
Vicki Pfau 04b085cf9a GBA Video: Fix cache updating with proxy and GL renderers 2021-11-17 17:52:50 -08:00
Vicki Pfau 820c68519c SDL: Use SDL_JoystickRumble where available 2021-11-16 18:53:11 -08:00
Vicki Pfau ce2815b199 Qt: Fix sprite compositing when sprite tiles go out of bounds (fixes #2348) 2021-11-09 15:58:16 -08:00
Vicki Pfau b0865c1e6f GBA: Fix maximum tile ID in caching for 256-color modes 2021-11-09 15:58:16 -08:00
Vicki Pfau 4290af21ce ARM Decoder: Fix decoding of lsl r0 (fixes #2349) 2021-11-09 15:58:14 -08:00
Vicki Pfau d1fc764bdc Wii: Fix build 2021-10-12 14:43:10 -07:00
Vicki Pfau 2ef08d8d09 Wii: Add adjustable gyroscope settings (closes #2245) 2021-10-12 14:37:01 -07:00
aldelaro5 a7ad0e61e2 gdb-stub: Add support for the T command by faking it 2021-09-28 12:37:33 -07:00
Vicki Pfau 3bc7362f08 GBA Video: Fix build 2021-09-23 22:35:11 -07:00
Vicki Pfau a0dc923042 Qt: Fix corrupted savestate and fatal error text 2021-09-23 22:31:52 -07:00
Vicki Pfau 55a3824671 GBA Video: Delay enabling backgrounds in bitmap modes (fixes #1668) 2021-09-22 15:59:11 -07:00
Vicki Pfau 57870c8802 GBA Video: Don't iterate affine backgrounds when disabled 2021-09-22 15:59:11 -07:00
Vicki Pfau c9b9525161 Qt: Only use a QPainter with OpenGL is the OSD is enabled 2021-09-22 15:55:47 -07:00
Vicki Pfau cb366ab3ea GBA Memory: Fix misaligned 32-bit I/O loads (fixes #2307) 2021-09-14 11:00:08 -07:00
Vicki Pfau daa2650cbb mGUI: Fix metrics on backtick 2021-09-14 10:58:54 -07:00
Vicki Pfau a14948fe84 Vita: Build fixes 2021-09-05 15:28:46 -07:00
Vicki Pfau c5c6948653 GB I/O: Fix incrementing SGB controller when P14 is low (fixes #2202) 2021-09-05 13:58:39 -07:00
gifvex 59939d3241 CInema: Fix Windows/MSYS2 build 2021-09-05 13:58:39 -07:00
Vicki Pfau 02ba8e2480 Vita: Build fixes 2021-09-05 13:58:39 -07:00
Vicki Pfau 782bc35ad1 CMake: Bring up DMG distribution on macOS 2021-08-21 18:32:30 -07:00
Vicki Pfau 251a434eb4 CMake: Small naming and packaging updates 2021-08-21 18:32:30 -07:00
Vicki Pfau e05982ca98 FFmpeg: Stop using deprecated AVPacket API 2021-08-21 18:31:57 -07:00
Vicki Pfau d39e521472 FFmpeg: Use refcounted buffers for frame data 2021-08-21 18:31:55 -07:00
Vicki Pfau 94124db16c FFmpeg: Don't attempt to use YUV 4:2:0 for lossless videos (fixes #2084) 2021-08-21 18:31:06 -07:00
Vicki Pfau c0d0eb9dd0 Libretro: Fix crash when using Game Boy codes (fixes #2281) 2021-08-17 16:17:53 -07:00
Adam Higerd a3b9988108 Don't make the top-left square of the logging grid checkable 2021-08-17 16:17:14 -07:00
Vicki Pfau ee5c4d49a4 AppVeyor: Clean more directories 2021-08-17 16:17:14 -07:00
Vicki Pfau 9d5bfd8582 GB Video: Render SGB border when unmasking with ATTR/PAL_SET (fixes #2261) 2021-07-28 22:33:12 -07:00
Vicki Pfau 9cc541ae7e GBA Video: Fix backdrop color if DISPCNT is first set to 0 (fixes #2260) 2021-07-28 22:32:40 -07:00
Vicki Pfau 04aede07e9 GB Video: Fix memory leak when reseting SGB games 2021-07-23 18:15:09 -07:00
Vicki Pfau 1a6f8f5381 Qt: Remove potentially deadlocking optimization 2021-07-19 20:56:18 -07:00
Vicki Pfau 669ceeecd6 CMake: Bump version 2021-07-19 20:55:04 -07:00
Vicki Pfau a5c12df57f GBA: Fix some patch loading edge cases 2021-07-10 20:06:20 -07:00
Vicki Pfau aa3f4c0e6e GBA SIO: Fix SI value for unattached MULTI mode 2021-07-10 20:02:12 -07:00
Vicki Pfau f6d5f51d23 CHANGES: Update for 0.9.2 2021-07-10 19:57:59 -07:00
Vicki Pfau 6a8b265f3d Qt: Fix bounded fast forward with enhancement OpenGL renderer 2021-07-10 16:39:54 -07:00
Vicki Pfau 96ba75330e Switch: Fix build on latest libnx 2021-07-09 22:22:01 -07:00
Vicki Pfau 1358000d74 Qt: Reuse timer when rescheduling missing frames 2021-07-09 22:04:47 -07:00
Vicki Pfau 386c4a6cc5 Res: Update patrons for June 2021-07-04 17:18:07 -07:00
Vicki Pfau 7bf80d8089 GBA I/O: Fix deserializing audio on big endian 2021-07-01 23:29:58 -07:00
Vicki Pfau 7a8ac0212f Qt: Redo sensor binding to be less fragile 2021-06-28 22:38:56 -07:00
Vicki Pfau 065eba5c4f GBA SIO: Add more missing NORMAL8 implementation bits (fixes #2044) 2021-06-28 15:22:23 -07:00
Vicki Pfau 0291bd7ec1 GBA Serialize: Fix loading audio enable bit late (fixes #2230) 2021-06-27 22:47:30 -07:00
Vicki Pfau 5e73936ff0 mGUI: Cache save state screenshot validity in state menu (fixes #2005) 2021-06-23 19:10:03 -07:00
Vicki Pfau 4e2d05d5a4 README: Drop Windows Vista mention 2021-06-23 19:09:34 -07:00
Vicki Pfau e7aed37920 Qt: Decrease early-sync timeout 2021-06-19 14:58:37 -07:00
Vicki Pfau 4eeadae8c5 GB Audio: Fix audio channel 4 being slow to deserialize 2021-06-19 14:58:36 -07:00
Vicki Pfau 2388342a1a GBA Memory: Fix prefetch mask when swapping modes within a region 2021-06-18 01:01:28 -07:00
Vicki Pfau 28156e3a53 GBA SIO: Fix missing interrupt on an unattached NORMAL transfer 2021-06-15 20:50:15 -07:00
Vicki Pfau d70fbdc3a0 CMake: MacOS cleanup 2021-06-13 00:32:35 -07:00
Vicki Pfau 74d12585a3 Qt: Fix having to press controller buttons twice for menu items (fixes #2143) 2021-06-13 00:31:32 -07:00
Vicki Pfau e1f9099446 Core: Fix portable mode on macOS 2021-06-13 00:31:32 -07:00
Vicki Pfau 001d332d5b Qt: Add missing EEPROM 8kB string 2021-06-13 00:31:32 -07:00
Vicki Pfau f3ae8f2143 ARM Debugger: Fix disassembly alignment (fixes #2204) 2021-06-13 00:31:32 -07:00
Vicki Pfau 806ce79f91 Qt: Text interaction and font cleanup 2021-06-13 00:31:10 -07:00
Vicki Pfau 5aa17c6a24 Core: Fix memory searches for relative values (fixes #2135) 2021-05-22 21:50:27 -07:00
Vicki Pfau 2d87cc1cf2 Qt: Fix non-Multimedia build (fixes #2180) 2021-05-14 23:05:10 -07:00
Vicki Pfau ec9594887c GBA SIO: Add missing NORMAL8 implementation bits (fixes #2172) 2021-05-14 23:05:10 -07:00
Vicki Pfau 8089cf8f5f 3DS: Remember to init cfg:u when querying model (fixes #2167) 2021-05-14 23:04:56 -07:00
Vicki Pfau 0ce2972fe6 Qt: Fix crash in sprite view for partially out-of-bounds sprites (fixes #2165) 2021-05-09 01:47:03 -07:00
Vicki Pfau f84a8f78ff GB Video: Clear VRAM on reset (fixes #2152) 2021-05-09 01:47:03 -07:00
Vicki Pfau 5d0f3092f7 GB Core: Fix VRAM and WRAM bank sizes 2021-05-09 01:47:03 -07:00
Vicki Pfau 821dbd4bd6 GB Core: Fix GBC colors setting breaking default model overrides (fixes #2161) 2021-05-06 13:25:28 -07:00
Vicki Pfau df0bb5cd48 Util: Improve speed of UPS patch loading 2021-05-02 23:21:49 -07:00
Vicki Pfau 0fc331e8ce Util: Fix loading UPS patches that affect the last byte of the file 2021-05-02 23:21:35 -07:00
Bonta-kun 6f5af7e744 GBA IO: Serialize JOY_RECV_HI and JOY_TRANS_HI 2021-05-01 19:44:39 -07:00
Vicki Pfau 9ccd83609a Qt: Fix UI element ordering (fixes #2156) 2021-05-01 19:34:48 -07:00
Vicki Pfau 2d03ae73da GBA Video: Revert scanline latching changes (fixes #2153, fixes #2149) 2021-05-01 19:34:47 -07:00
Vicki Pfau 745a12d00e GBA Overrides: Add missing override for Drill Dozer (Europe) 2021-05-01 19:34:12 -07:00
Vicki Pfau 31ef54ca9d Qt: Fix applying savetype-only overrides 2021-04-25 19:07:54 -07:00
sdhizumi 135d1a10ca GBA: Add override for DigiCommunication Nyo 2021-04-25 19:07:26 -07:00
Vicki Pfau 3d72ada282 Core: Fix memory leak in opening games from the library 2021-04-25 19:06:33 -07:00
Vicki Pfau 3276ebcd10 Qt: Start cleaning up library code 2021-04-25 19:06:33 -07:00
Vicki Pfau 8ede727c81 Qt: Fix infrequent deadlock when using sync to video 2021-04-25 19:06:30 -07:00
Vicki Pfau 2232e7f65f Switch: Minor cleanup 2021-04-25 19:05:48 -07:00
Vicki Pfau 0f51bc66fd mGUI: Fix some small memory leaks 2021-04-25 19:05:19 -07:00
Vicki Pfau 631499e36c CMake: Bump version 2021-04-18 21:48:45 -07:00
Vicki Pfau e932068baa CHANGES: Update for 0.9.1 2021-04-18 21:47:22 -07:00
Vicki Pfau 973f6b819e Res: Update nointro.dat 2021-04-18 21:46:55 -07:00
Vicki Pfau 62309ca5c5 Res: Update patrons for April 2021-04-18 21:26:50 -07:00
Vicki Pfau 53b8fdf856 Qt: Restore maximized state when starting (fixes #487) 2021-04-18 18:46:28 -07:00
Vicki Pfau 679f36d7c0 Qt: Improve handling of disabling VBA bug compat mode (fixes #2129) 2021-04-18 18:46:27 -07:00
Vicki Pfau 56b0f95e6f Qt: Parse --version flag 2021-04-18 18:46:04 -07:00
Vicki Pfau 338e4ec7a9 ARM: Fix LDM^ with empty rlist (fixes #2127) 2021-04-18 18:45:27 -07:00
Vicki Pfau fdbb442bad Qt: Fix frames getting backlogged (fixes #2122) 2021-04-18 18:45:27 -07:00
Vicki Pfau fcdf6c53eb Qt: Fix saving settings enabling camera when camera name changes (fixes #2125) 2021-04-18 18:45:27 -07:00
Vicki Pfau 094fe8fec0 Libretro: Add missing rotation peripheral 2021-04-18 18:44:55 -07:00
Vicki Pfau bd87563aa0 Libretro: Fix GB SRAM in memory map 2021-04-18 18:44:55 -07:00
Vicki Pfau b8f410c0bd GB: Fix skipping BIOS 2021-04-18 18:44:55 -07:00
Vicki Pfau c9cbf185c1 Qt: Fix smudged window icon on Windows, recompress icons 2021-04-12 22:17:29 -07:00
Vicki Pfau a782e8b500 Qt: Fix OpenGL renderer lagging behind when fast-forwarding (fixes #2094) 2021-04-12 22:17:29 -07:00
Vicki Pfau ce080027a4 Qt: Remove no-longer used dependency 2021-04-12 22:17:29 -07:00
Vicki Pfau 043b37c51d Qt: Fix crash when switching from high-resolution OpenGL renderer to software 2021-04-12 22:17:29 -07:00
Vicki Pfau f04311a22a GBA Memory: Remove unused variable 2021-04-12 22:17:29 -07:00
Vicki Pfau 89928b86ac GBA Memory: Log GPIO writes on non-GPIO carts as Pak Hardware instead of Memory 2021-04-12 22:17:29 -07:00
Vicki Pfau 91cfbbf676 GBA Video: Fix mode 3-5 overflow with mosaic (fixes #1691) 2021-04-12 22:16:40 -07:00
Vicki Pfau 3c25e66769 GBA Video: Fix window start on modes 2-5 with mosaic (fixes #1690) 2021-04-12 22:16:40 -07:00
Vicki Pfau 2029f7fc86 GBA SIO: Silence initial mode switch 2021-04-12 22:16:40 -07:00
Vicki Pfau 3d54fb6d02 GBA Video: Don't attempt to copy invalid registers when switching renderer 2021-04-12 22:16:40 -07:00
Vicki Pfau 86a57ed51f Core: Truncate preloading ROMs that slightly exceed max size (fixes #2093) 2021-04-12 22:16:40 -07:00
Vicki Pfau 997e462a4f GBA e-Reader: Fix bitmap short strip scanning 2021-04-12 22:16:40 -07:00
Vicki Pfau 7740295a6c GBA Video: Fix mode 5 frame 1 caching (fixes #2075) 2021-04-12 22:15:59 -07:00
Vicki Pfau 02b814d3bb GBA: Default-enable VBA bug compat for Ruby and Emerald ROM hacks 2021-04-12 22:15:59 -07:00
Vicki Pfau fefe47d7dd GBA: Fix non-USA 1.0 FireRed misdetecting as a ROM hack (fixes #2100) 2021-04-12 22:15:59 -07:00
Vicki Pfau 927b70e6fd GB Serialize: Fix switching speed modes when loading a state (fixes #2097) 2021-04-12 22:15:59 -07:00
Vicki Pfau fee8255611 GBA: Fix crash when ROM loading fails 2021-04-12 22:15:23 -07:00
Vicki Pfau b6025e4108 Qt: Add ROM filename and size to bug reporter 2021-04-12 22:15:07 -07:00
Vicki Pfau 4c0294d7ff GBA: Fix FireRed revision misdetecting as a ROM hack 2021-04-12 22:14:59 -07:00
Vicki Pfau c4aa0717b7 Core: Fix first event scheduling after loading savestate 2021-04-12 22:14:24 -07:00
Vicki Pfau a325deb65e GBA Memory: Fix loading Thumb savestates when in ARM mode 2021-04-12 22:14:08 -07:00
Vicki Pfau f75e18137f CMake: Bump version 2021-03-28 18:32:41 -07:00
128 changed files with 1386 additions and 707 deletions

View File

@ -7,7 +7,7 @@ configuration:
cache: cache:
- C:\Tools\vcpkg - C:\Tools\vcpkg
install: install:
- git -C C:\Tools\vcpkg clean -dfq ports toolsrc - git -C C:\Tools\vcpkg clean -dfq docs ports scripts toolsrc versions
- git -C C:\Tools\vcpkg pull --force --quiet - git -C C:\Tools\vcpkg pull --force --quiet
- C:\Tools\vcpkg\bootstrap-vcpkg - C:\Tools\vcpkg\bootstrap-vcpkg
- vcpkg --triplet x64-windows --recurse install ffmpeg libepoxy libpng libzip sdl2 sqlite3 - vcpkg --triplet x64-windows --recurse install ffmpeg libepoxy libpng libzip sdl2 sqlite3

81
CHANGES
View File

@ -1,3 +1,84 @@
0.9.3: (2021-12-17)
Emulation fixes:
- GB I/O: Fix incrementing SGB controller when P14 is low (fixes mgba.io/i/2202)
- GB Video: Render SGB border when unmasking with ATTR/PAL_SET (fixes mgba.io/i/2261)
- GBA SIO: Fix SI value for unattached MULTI mode
- GBA Video: Fix backdrop color if DISPCNT is first set to 0 (fixes mgba.io/i/2260)
- GBA Video: Don't iterate affine backgrounds when disabled
- GBA Video: Delay enabling backgrounds in bitmap modes (fixes mgba.io/i/1668)
Other fixes:
- ARM Decoder: Fix decoding of lsl r0 (fixes mgba.io/i/2349)
- FFmpeg: Don't attempt to use YUV 4:2:0 for lossless videos (fixes mgba.io/i/2084)
- GB Video: Fix memory leak when reseting SGB games
- GBA: Fix out of bounds ROM accesses on patched ROMs smaller than 32 MiB
- GBA: Fix maximum tile ID in caching for 256-color modes
- GBA Video: Fix cache updating with proxy and GL renderers
- Libretro: Fix crash when using Game Boy codes (fixes mgba.io/i/2281)
- mGUI: Fix crash if autosave file can't be opened (fixes mgba.io/i/2268)
- Qt: Remove potentially deadlocking optimization
- Qt: Fix corrupted savestate and fatal error text
- Qt: Fix sprite compositing when sprite tiles go out of bounds (fixes mgba.io/i/2348)
Misc:
- GBA I/O: Update KEYINPUT in internal I/O memory (fixes mgba.io/i/2235)
- SDL: Use SDL_JoystickRumble where available
- Wii: Add adjustable gyroscope settings (closes mgba.io/i/2245)
0.9.2: (2021-07-10)
Emulation fixes:
- GB Video: Clear VRAM on reset (fixes mgba.io/i/2152)
- GBA SIO: Add missing NORMAL8 implementation bits (fixes mgba.io/i/2172)
- GBA SIO: Fix missing interrupt on an unattached NORMAL transfer
- 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
- 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)
- mGUI: Cache save state screenshot validity in state menu (fixes mgba.io/i/2005)
- Qt: Fix eventual 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
- Qt: Reuse timer when rescheduling missing frames (fixes mgba.io/i/2236)
- Qt: Fix bounded fast forward with enhancement OpenGL renderer
- Util: Fix loading UPS patches that affect the last byte of the file
Misc:
- Util: Improve speed of UPS patch loading
0.9.1: (2021-04-18)
Emulation fixes:
- ARM: Fix LDM^ with empty rlist (fixes mgba.io/i/2127)
- Core: Fix first event scheduling after loading savestate
- GB Serialize: Fix switching speed modes when loading a state (fixes mgba.io/i/2097)
- GB: Fix skipping BIOS
- GBA Memory: Fix loading Thumb savestates when in ARM mode
- GBA Video: Fix window start on modes 3-5 with mosaic (fixes mgba.io/i/1690)
- GBA Video: Fix mode 3-5 overflow with mosaic (fixes mgba.io/i/1691)
Other fixes:
- GBA: Fix non-USA 1.0 FireRed misdetecting as a ROM hack (fixes mgba.io/i/2100)
- GBA: Fix crash when ROM loading fails
- GBA e-Reader: Fix bitmap short strip scanning
- GBA Video: Fix mode 5 frame 1 caching (fixes mgba.io/i/2075)
- GBA Video: Don't attempt to copy invalid registers when switching renderer
- Qt: Fix crash when switching from high-resolution OpenGL renderer to software
- Qt: Fix OpenGL renderer lagging behind when fast-forwarding (fixes mgba.io/i/2094)
- Qt: Fix smudged window icon on Windows
- Qt: Fix saving settings enabling camera when camera name changes (fixes mgba.io/i/2125)
- Qt: Fix frames getting backlogged (fixes mgba.io/i/2122)
- Qt: Restore maximized state when starting (fixes mgba.io/i/487)
Misc:
- Core: Truncate preloading ROMs that slightly exceed max size (fixes mgba.io/i/2093)
- GBA: Default-enable VBA bug compat for Ruby and Emerald ROM hacks
- GBA Memory: Log GPIO writes on non-GPIO carts as Pak Hardware instead of Memory
- Qt: Add ROM filename and size to bug reporter
- Qt: Improve handling of disabling VBA bug compat mode (fixes mgba.io/i/2129)
0.9.0: (2021-03-28) 0.9.0: (2021-03-28)
Features: Features:
- e-Reader card scanning - e-Reader card scanning

View File

@ -119,6 +119,10 @@ else()
set(CMAKE_INSTALL_INCLUDEDIR "include") set(CMAKE_INSTALL_INCLUDEDIR "include")
endif() endif()
if(APPLE AND DISTBUILD)
set(CMAKE_INSTALL_DOCDIR ".")
endif()
if(NOT DEFINED LIBDIR) if(NOT DEFINED LIBDIR)
set(LIBDIR "${CMAKE_INSTALL_LIBDIR}") set(LIBDIR "${CMAKE_INSTALL_LIBDIR}")
endif() endif()
@ -207,8 +211,7 @@ if(WIN32)
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
add_definitions(-D_UNICODE -DUNICODE) add_definitions(-D_UNICODE -DUNICODE)
else() else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -municode") add_definitions(-D_GNU_SOURCE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")
endif() endif()
list(APPEND OS_LIB ws2_32 shlwapi) list(APPEND OS_LIB ws2_32 shlwapi)
list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/vfs-w32.c) list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/vfs-w32.c)
@ -228,7 +231,8 @@ endif()
if(APPLE) if(APPLE)
add_definitions(-D_DARWIN_C_SOURCE) 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") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6")
endif() endif()
endif() endif()
@ -325,7 +329,7 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL "Generic")
check_function_exists(uselocale HAVE_USELOCALE) check_function_exists(uselocale HAVE_USELOCALE)
check_function_exists(setlocale HAVE_SETLOCALE) check_function_exists(setlocale HAVE_SETLOCALE)
else() else()
if(DEFINED 3DS OR DEFINED WII OR DEFINED SWITCH) if(DEFINED 3DS OR DEFINED WII OR DEFINED SWITCH OR DEFINED PSP2)
set(CMAKE_REQUIRED_FLAGS -Wl,--require-defined,snprintf_l) set(CMAKE_REQUIRED_FLAGS -Wl,--require-defined,snprintf_l)
check_function_exists(snprintf_l HAVE_SNPRINTF_L) check_function_exists(snprintf_l HAVE_SNPRINTF_L)
set(CMAKE_REQUIRED_FLAGS -Wl,--require-defined,strtof_l) set(CMAKE_REQUIRED_FLAGS -Wl,--require-defined,strtof_l)
@ -582,8 +586,7 @@ if(USE_FFMPEG)
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libavcodec-extra|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}") set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libavcodec-extra|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}")
endif() endif()
if(APPLE) if(APPLE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework VideoDecodeAcceleration -framework CoreVideo") list(APPEND DEPENDENCY_LIB "-framework VideoDecodeAcceleration" "-framework CoreVideo")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework VideoDecodeAcceleration -framework CoreVideo")
endif() endif()
endif() endif()
@ -1094,8 +1097,8 @@ set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE)
set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_SOURCE_DIR}/README.md) set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_SOURCE_DIR}/README.md)
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "mGBA Game Boy Advance Emulator") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "mGBA Game Boy Advance Emulator")
set(CPACK_PACKAGE_VENDOR "Jeffrey Pfau") set(CPACK_PACKAGE_VENDOR "Vicki Pfau")
set(CPACK_PACKAGE_CONTACT "Jeffrey Pfau <jeffrey@endrift.com>") set(CPACK_PACKAGE_CONTACT "Vicki Pfau <vi@endrift.com>")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_DEBIAN_PACKAGE_SECTION "games") set(CPACK_DEBIAN_PACKAGE_SECTION "games")
@ -1107,6 +1110,9 @@ endif()
if(DISTBUILD) if(DISTBUILD)
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
set(CPACK_DMG_FILESYSTEM "HFS+")
set(CPACK_DMG_FORMAT "UDBZ")
set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME} ${VERSION_STRING}")
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND BUILD_SHARED) if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND BUILD_SHARED)
if(NOT APPLE) if(NOT APPLE)
add_custom_command(TARGET ${BINARY_NAME} POST_BUILD COMMAND "${OBJCOPY}" --only-keep-debug "$<TARGET_FILE:${BINARY_NAME}>" "$<TARGET_FILE:${BINARY_NAME}>.debug") add_custom_command(TARGET ${BINARY_NAME} POST_BUILD COMMAND "${OBJCOPY}" --only-keep-debug "$<TARGET_FILE:${BINARY_NAME}>" "$<TARGET_FILE:${BINARY_NAME}>.debug")
@ -1117,7 +1123,8 @@ if(DISTBUILD)
endif() endif()
if(APPLE) if(APPLE)
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-qt-dbg ${BINARY_NAME}-sdl-dbg ${BINARY_NAME}-perf) set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-qt-dbg ${BINARY_NAME}-sdl-dbg ${BINARY_NAME}-perf)
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/cmake/DMGOverrides.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/DMGOverrides.cmake @ONLY)
set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/DMGOverrides.cmake)
elseif(WIN32) elseif(WIN32)
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-qt-dbg ${BINARY_NAME}-sdl-dbg ${BINARY_NAME}-perf installer) set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-qt-dbg ${BINARY_NAME}-sdl-dbg ${BINARY_NAME}-perf installer)
elseif(3DS) elseif(3DS)

View File

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

View File

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

View File

@ -79,7 +79,7 @@ Estos mappers tienen soporte parcial:
Plataformas soportadas 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 - OS X 10.8 (Mountain Lion)[<sup>[3]</sup>](#osxver) o más reciente
- Linux - Linux
- FreeBSD - 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) 或更新 - OS X 10.8(山狮 / Mountain Lion[<sup>[3]</sup>](#osxver) 或更新
- Linux - Linux
- FreeBSD - FreeBSD

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -31,6 +31,8 @@ DECL_BIT(mMapCacheEntryFlags, HMirror, 5);
DECL_BIT(mMapCacheEntryFlags, VMirror, 6); DECL_BIT(mMapCacheEntryFlags, VMirror, 6);
DECL_BITS(mMapCacheEntryFlags, Mirror, 5, 2); DECL_BITS(mMapCacheEntryFlags, Mirror, 5, 2);
#define mMapCacheTileCount(C) (1 << mMapCacheSystemInfoGetTilesWide((C)->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh((C)->sysConfig))
struct mMapCacheEntry { struct mMapCacheEntry {
uint32_t vramVersion; uint32_t vramVersion;
uint16_t tileId; uint16_t tileId;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 465 KiB

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -11,10 +11,10 @@ BEGIN
BLOCK "040904E4" BLOCK "040904E4"
BEGIN BEGIN
VALUE "CompanyName", "endrift" VALUE "CompanyName", "endrift"
VALUE "FileDescription", "mGBA Game Boy Advance emulator" VALUE "FileDescription", "${PROJECT_NAME} Game Boy Advance emulator"
VALUE "FileVersion", "${LIB_VERSION_STRING}.0" VALUE "FileVersion", "${LIB_VERSION_STRING}.0"
VALUE "InternalName", "${BINARY_NAME}" VALUE "InternalName", "${BINARY_NAME}"
VALUE "LegalCopyright", "(c) 2013 - 2016 Jeffrey Pfau" VALUE "LegalCopyright", "(c) 2013 - 2021 Jeffrey Pfau"
VALUE "OriginalFilename", "${BINARY_NAME}" VALUE "OriginalFilename", "${BINARY_NAME}"
VALUE "ProductName", "${PROJECT_NAME}" VALUE "ProductName", "${PROJECT_NAME}"
VALUE "ProductVersion", "${BINARY_NAME}" VALUE "ProductVersion", "${BINARY_NAME}"

View File

@ -1,7 +1,7 @@
clrmamepro ( clrmamepro (
name "Nintendo - Game Boy Advance" name "Nintendo - Game Boy Advance"
description "Nintendo - Game Boy Advance" description "Nintendo - Game Boy Advance"
version 20210326-211504 version 20210415-050230
author "aci68, Aringon, Bent, BigFred, C. V. Reynolds, chillerecke, DeadSkullzJr, Densetsu, DeriLoko3, einstein95, ElBarto, Enker, fuzzball, Gefflon, Hiccup, hking0036, hydr0x, InternalLoss, Jack, jimmsu, kazumi213, Money_114, niemand, omonim2007, Powerpuff, PPLToast, relax, RetroGamer, Rifu, SonGoku, Tauwasser, Vallaine01, Whovian9369, xuom2, zg" author "aci68, Aringon, Bent, BigFred, C. V. Reynolds, chillerecke, DeadSkullzJr, Densetsu, DeriLoko3, einstein95, ElBarto, Enker, fuzzball, Gefflon, Hiccup, hking0036, hydr0x, InternalLoss, Jack, jimmsu, kazumi213, Money_114, niemand, omonim2007, Powerpuff, PPLToast, relax, RetroGamer, Rifu, SonGoku, Tauwasser, Vallaine01, Whovian9369, xuom2, zg"
homepage No-Intro homepage No-Intro
url "http://www.no-intro.org" url "http://www.no-intro.org"
@ -11842,7 +11842,7 @@ game (
game ( game (
name "Mother 3 (Japan) (Virtual Console)" name "Mother 3 (Japan) (Virtual Console)"
description "Mother 3 (Japan) (Virtual Console)" description "Mother 3 (Japan) (Virtual Console)"
rom ( name "Mother 3 (Japan) (Virtual Console).gba" size 33554432 crc c704a567 sha1 A9FB9C36DF3B0FB24B266826F5853C56122F9D36 ) rom ( name "Mother 3 (Japan) (Virtual Console).gba" size 33554432 crc c704a567 sha1 A9FB9C36DF3B0FB24B266826F5853C56122F9D36 flags verified )
) )
game ( game (
@ -13450,7 +13450,7 @@ game (
game ( game (
name "Pokemon - Version Rouge Feu (France)" name "Pokemon - Version Rouge Feu (France)"
description "Pokemon - Version Rouge Feu (France)" description "Pokemon - Version Rouge Feu (France)"
rom ( name "Pokemon - Version Rouge Feu (France).gba" size 16777216 crc 5dc668f6 sha1 FC663907256F06A3A09E2D6B967BC9AF4919F111 ) rom ( name "Pokemon - Version Rouge Feu (France).gba" size 16777216 crc 5dc668f6 sha1 FC663907256F06A3A09E2D6B967BC9AF4919F111 flags verified )
) )
game ( game (
@ -19951,8 +19951,8 @@ game (
clrmamepro ( clrmamepro (
name "Nintendo - Game Boy" name "Nintendo - Game Boy"
description "Nintendo - Game Boy" description "Nintendo - Game Boy"
version 20210311-081758 version 20210412-150119
author "aci68, akubi, Aringon, Bent, BigFred, BitLooter, C. V. Reynolds, chillerecke, darthcloud, DeadSkullzJr, Densetsu, DeriLoko3, ElBarto, foxe, fuzzball, Gefflon, Hiccup, hking0036, jimmsu, kazumi213, leekindo, Money_114, NGEfreak, omonim2007, Powerpuff, PPLToast, relax, RetroUprising, rpg2813, SonGoku, Tauwasser, xNo, xuom2" author "aci68, akubi, Aringon, Bent, BigFred, BitLooter, C. V. Reynolds, chillerecke, darthcloud, DeadSkullzJr, Densetsu, DeriLoko3, ElBarto, foxe, fuzzball, Gefflon, Hiccup, hking0036, InternalLoss, jimmsu, kazumi213, leekindo, Money_114, NGEfreak, omonim2007, Powerpuff, PPLToast, relax, RetroUprising, rpg2813, SonGoku, Tauwasser, xNo, xuom2"
homepage No-Intro homepage No-Intro
url "http://www.no-intro.org" url "http://www.no-intro.org"
) )
@ -20170,7 +20170,7 @@ game (
game ( game (
name "Akumajou Special - Boku Dracula-kun (Japan)" name "Akumajou Special - Boku Dracula-kun (Japan)"
description "Akumajou Special - Boku Dracula-kun (Japan)" description "Akumajou Special - Boku Dracula-kun (Japan)"
rom ( name "Akumajou Special - Boku Dracula-kun (Japan).gb" size 262144 crc e8335398 sha1 F2F15DA8ED94BC8652C23965428F6D767E506E38 ) rom ( name "Akumajou Special - Boku Dracula-kun (Japan).gb" size 262144 crc e8335398 sha1 F2F15DA8ED94BC8652C23965428F6D767E506E38 flags verified )
) )
game ( game (
@ -20185,6 +20185,12 @@ game (
rom ( name "Aladdin (USA) (SGB Enhanced).gb" size 262144 crc af6bdc50 sha1 81B4E56AE235FBB123D7AD4BD7C1D96C3B69EB72 ) rom ( name "Aladdin (USA) (SGB Enhanced).gb" size 262144 crc af6bdc50 sha1 81B4E56AE235FBB123D7AD4BD7C1D96C3B69EB72 )
) )
game (
name "Aladdin (World) (Disney Classic Games) (SGB Enhanced)"
description "Aladdin (World) (Disney Classic Games) (SGB Enhanced)"
rom ( name "Aladdin (World) (Disney Classic Games) (SGB Enhanced).gb" size 262144 crc 53918941 sha1 C59C44051E41DC6AC8C2F693CFF2BA1FEFFD32F0 flags verified )
)
game ( game (
name "Alfred Chicken (Europe)" name "Alfred Chicken (Europe)"
description "Alfred Chicken (Europe)" description "Alfred Chicken (Europe)"
@ -21518,9 +21524,15 @@ game (
) )
game ( game (
name "Contra (Japan)" name "Contra (Japan) (En)"
description "Contra (Japan)" description "Contra (Japan) (En)"
rom ( name "Contra (Japan).gb" size 131072 crc cde6de15 sha1 C8B34B5ABA3D448E357B59CDF106EE9B134713DB ) rom ( name "Contra (Japan) (En).gb" size 131072 crc cde6de15 sha1 C8B34B5ABA3D448E357B59CDF106EE9B134713DB )
)
game (
name "Contra (World) (Contra Anniversary Collection)"
description "Contra (World) (Contra Anniversary Collection)"
rom ( name "Contra (World) (Contra Anniversary Collection).gb" size 131072 crc cc67a046 sha1 6FEA1350AC04F418095CCA5F157A9C770A0505BC flags verified )
) )
game ( game (
@ -22435,18 +22447,36 @@ game (
rom ( name "Final Fantasy Adventure (World) (Collection of Mana).gb" size 262144 crc 8e5e5097 sha1 BD8369977CFBBAF3CC57F6268B564D6B11C27D45 flags verified ) rom ( name "Final Fantasy Adventure (World) (Collection of Mana).gb" size 262144 crc 8e5e5097 sha1 BD8369977CFBBAF3CC57F6268B564D6B11C27D45 flags verified )
) )
game (
name "Final Fantasy Legend (World) (Collection of SaGa)"
description "Final Fantasy Legend (World) (Collection of SaGa)"
rom ( name "Final Fantasy Legend (World) (Collection of SaGa).gb" size 131072 crc 00388844 sha1 42450483C9894863C7543F9695C0CBAFFDCB7E99 )
)
game ( game (
name "Final Fantasy Legend II (USA)" name "Final Fantasy Legend II (USA)"
description "Final Fantasy Legend II (USA)" description "Final Fantasy Legend II (USA)"
rom ( name "Final Fantasy Legend II (USA).gb" size 262144 crc 58314182 sha1 6AB6890E8F688BCD87E97886A1748A4D9D341909 flags verified ) rom ( name "Final Fantasy Legend II (USA).gb" size 262144 crc 58314182 sha1 6AB6890E8F688BCD87E97886A1748A4D9D341909 flags verified )
) )
game (
name "Final Fantasy Legend II (World) (Collection of SaGa)"
description "Final Fantasy Legend II (World) (Collection of SaGa)"
rom ( name "Final Fantasy Legend II (World) (Collection of SaGa).gb" size 262144 crc e84e051a sha1 EEDF962D01B733B5EAF2413905E59E671E04A51C )
)
game ( game (
name "Final Fantasy Legend III (USA)" name "Final Fantasy Legend III (USA)"
description "Final Fantasy Legend III (USA)" description "Final Fantasy Legend III (USA)"
rom ( name "Final Fantasy Legend III (USA).gb" size 262144 crc 3e454710 sha1 3864AFA48A97DB826FFDA1D31A7FF9C6C315D5C9 ) rom ( name "Final Fantasy Legend III (USA).gb" size 262144 crc 3e454710 sha1 3864AFA48A97DB826FFDA1D31A7FF9C6C315D5C9 )
) )
game (
name "Final Fantasy Legend III (World) (Collection of SaGa)"
description "Final Fantasy Legend III (World) (Collection of SaGa)"
rom ( name "Final Fantasy Legend III (World) (Collection of SaGa).gb" size 262144 crc 0c5171ef sha1 5190B0EF6264E26AF21DC0097FD5608CB619FB63 )
)
game ( game (
name "Final Fantasy Legend, The (USA)" name "Final Fantasy Legend, The (USA)"
description "Final Fantasy Legend, The (USA)" description "Final Fantasy Legend, The (USA)"
@ -24511,6 +24541,12 @@ game (
rom ( name "Lion King, The (Europe).gb" size 524288 crc 8fc3ca73 sha1 043D29EDE2AF013C000FF650231C10B3F62D7ACA flags verified ) rom ( name "Lion King, The (Europe).gb" size 524288 crc 8fc3ca73 sha1 043D29EDE2AF013C000FF650231C10B3F62D7ACA flags verified )
) )
game (
name "Lion King, The (World) (Disney Classic Games)"
description "Lion King, The (World) (Disney Classic Games)"
rom ( name "Lion King, The (World) (Disney Classic Games).gb" size 524288 crc e435ed72 sha1 170C071DB25DA7A2B39DD1FB2675ACEB8EEB87A1 flags verified )
)
game ( game (
name "Litti's Summer Sports (Germany)" name "Litti's Summer Sports (Germany)"
description "Litti's Summer Sports (Germany)" description "Litti's Summer Sports (Germany)"
@ -24697,6 +24733,12 @@ game (
rom ( name "Makai Toushi Sa-Ga (Japan) (Rev 1).gb" size 131072 crc 1953820f sha1 CBF480BC92BD98BAE4FB79294B604D341FE58CBE flags verified ) rom ( name "Makai Toushi Sa-Ga (Japan) (Rev 1).gb" size 131072 crc 1953820f sha1 CBF480BC92BD98BAE4FB79294B604D341FE58CBE flags verified )
) )
game (
name "Makai Toushi Sa-Ga (World) (Ja) (Rev 1) (Collection of SaGa)"
description "Makai Toushi Sa-Ga (World) (Ja) (Rev 1) (Collection of SaGa)"
rom ( name "Makai Toushi Sa-Ga (World) (Ja) (Rev 1) (Collection of SaGa).gb" size 131072 crc 0006612d sha1 3CE39646BFFE38A9C8FD4913F0E950BAE061D094 )
)
game ( game (
name "Makaimura Gaiden - The Demon Darkness (Japan)" name "Makaimura Gaiden - The Demon Darkness (Japan)"
description "Makaimura Gaiden - The Demon Darkness (Japan)" description "Makaimura Gaiden - The Demon Darkness (Japan)"
@ -26041,6 +26083,12 @@ game (
rom ( name "Operation C (USA).gb" size 131072 crc 2ebbc1ae sha1 1DC3E1C62E62F77AC633408B544AC1D02B3761EB flags verified ) rom ( name "Operation C (USA).gb" size 131072 crc 2ebbc1ae sha1 1DC3E1C62E62F77AC633408B544AC1D02B3761EB flags verified )
) )
game (
name "Operation C (World) (Contra Anniversary Collection)"
description "Operation C (World) (Contra Anniversary Collection)"
rom ( name "Operation C (World) (Contra Anniversary Collection).gb" size 131072 crc a7a5d9c2 sha1 BD528F1B20051502E2B4418845E0C6698E1C5B4B flags verified )
)
game ( game (
name "Osawagase! Penguin Boy (Japan)" name "Osawagase! Penguin Boy (Japan)"
description "Osawagase! Penguin Boy (Japan)" description "Osawagase! Penguin Boy (Japan)"
@ -26758,13 +26806,13 @@ game (
game ( game (
name "Pokemon - Version Jaune - Edition Speciale Pikachu (France) (CGB+SGB Enhanced)" name "Pokemon - Version Jaune - Edition Speciale Pikachu (France) (CGB+SGB Enhanced)"
description "Pokemon - Version Jaune - Edition Speciale Pikachu (France) (CGB+SGB Enhanced)" description "Pokemon - Version Jaune - Edition Speciale Pikachu (France) (CGB+SGB Enhanced)"
rom ( name "Pokemon - Version Jaune - Edition Speciale Pikachu (France) (CGB+SGB Enhanced).gb" size 1048576 crc d03426e9 sha1 0ACEEC0EF7AA2CA5AA831554598D91F61A925591 ) rom ( name "Pokemon - Version Jaune - Edition Speciale Pikachu (France) (CGB+SGB Enhanced).gb" size 1048576 crc d03426e9 sha1 0ACEEC0EF7AA2CA5AA831554598D91F61A925591 flags verified )
) )
game ( game (
name "Pokemon - Version Rouge (France) (SGB Enhanced)" name "Pokemon - Version Rouge (France) (SGB Enhanced)"
description "Pokemon - Version Rouge (France) (SGB Enhanced)" description "Pokemon - Version Rouge (France) (SGB Enhanced)"
rom ( name "Pokemon - Version Rouge (France) (SGB Enhanced).gb" size 1048576 crc 337fce11 sha1 47A7622FA30E6402A3891FE65B3A930BF9BD7AEC ) rom ( name "Pokemon - Version Rouge (France) (SGB Enhanced).gb" size 1048576 crc 337fce11 sha1 47A7622FA30E6402A3891FE65B3A930BF9BD7AEC flags verified )
) )
game ( game (
@ -26983,6 +27031,12 @@ game (
rom ( name "Probotector (Europe).gb" size 131072 crc c9acc4f4 sha1 45482A44CE0CDFA33FC58A8A8AFE2D7284DFA498 flags verified ) rom ( name "Probotector (Europe).gb" size 131072 crc c9acc4f4 sha1 45482A44CE0CDFA33FC58A8A8AFE2D7284DFA498 flags verified )
) )
game (
name "Probotector (World) (Contra Anniversary Collection)"
description "Probotector (World) (Contra Anniversary Collection)"
rom ( name "Probotector (World) (Contra Anniversary Collection).gb" size 131072 crc 1c03c0ee sha1 BC58585F8A1B2E27712AE5D44E3D851F4535A53F flags verified )
)
game ( game (
name "Probotector 2 (Europe) (SGB Enhanced)" name "Probotector 2 (Europe) (SGB Enhanced)"
description "Probotector 2 (Europe) (SGB Enhanced)" description "Probotector 2 (Europe) (SGB Enhanced)"
@ -27505,12 +27559,24 @@ game (
rom ( name "Sa-Ga 2 - Hihou Densetsu (Japan) (Rev 1).gb" size 262144 crc f6cfcfb1 sha1 96EF7D31AD098A620BA7AC57AFEF416972707EA3 flags verified ) rom ( name "Sa-Ga 2 - Hihou Densetsu (Japan) (Rev 1).gb" size 262144 crc f6cfcfb1 sha1 96EF7D31AD098A620BA7AC57AFEF416972707EA3 flags verified )
) )
game (
name "Sa-Ga 2 - Hihou Densetsu (World) (Ja) (Rev 1) (Collection of SaGa)"
description "Sa-Ga 2 - Hihou Densetsu (World) (Ja) (Rev 1) (Collection of SaGa)"
rom ( name "Sa-Ga 2 - Hihou Densetsu (World) (Ja) (Rev 1) (Collection of SaGa).gb" size 262144 crc 2fe38e18 sha1 1DF1F6E277BF6406B865DD5A3E12C0277C3C65F2 )
)
game ( game (
name "Sa-Ga 3 - Jikuu no Hasha (Japan)" name "Sa-Ga 3 - Jikuu no Hasha (Japan)"
description "Sa-Ga 3 - Jikuu no Hasha (Japan)" description "Sa-Ga 3 - Jikuu no Hasha (Japan)"
rom ( name "Sa-Ga 3 - Jikuu no Hasha (Japan).gb" size 262144 crc 575d6d9d sha1 C8DCBEFC0352B0590FD85A683D983B5510A63519 ) rom ( name "Sa-Ga 3 - Jikuu no Hasha (Japan).gb" size 262144 crc 575d6d9d sha1 C8DCBEFC0352B0590FD85A683D983B5510A63519 )
) )
game (
name "Sa-Ga 3 - Jikuu no Hasha (World) (Ja) (Collection of SaGa)"
description "Sa-Ga 3 - Jikuu no Hasha (World) (Ja) (Collection of SaGa)"
rom ( name "Sa-Ga 3 - Jikuu no Hasha (World) (Ja) (Collection of SaGa).gb" size 262144 crc dc4f4e34 sha1 8125677EE63E9ABB4B956ED0BEE18FAE3E04193B )
)
game ( game (
name "Sagaia (Japan)" name "Sagaia (Japan)"
description "Sagaia (Japan)" description "Sagaia (Japan)"
@ -28810,7 +28876,7 @@ game (
game ( game (
name "Tecmo Bowl GB (Japan)" name "Tecmo Bowl GB (Japan)"
description "Tecmo Bowl GB (Japan)" description "Tecmo Bowl GB (Japan)"
rom ( name "Tecmo Bowl GB (Japan).gb" size 262144 crc cfd74e34 sha1 2447006C578E1DF0256E9155C8B65FA2A6B0004B ) rom ( name "Tecmo Bowl GB (Japan).gb" size 262144 crc cfd74e34 sha1 2447006C578E1DF0256E9155C8B65FA2A6B0004B flags verified )
) )
game ( game (
@ -30004,8 +30070,8 @@ game (
clrmamepro ( clrmamepro (
name "Nintendo - Game Boy Color" name "Nintendo - Game Boy Color"
description "Nintendo - Game Boy Color" description "Nintendo - Game Boy Color"
version 20210303-092238 version 20210410-081930
author "akubi, Aringon, Bent, BigFred, BitLooter, C. V. Reynolds, chillerecke, coraz, darthcloud, DeadSkullzJr, Densetsu, DeriLoko3, foxe, fuzzball, Hiccup, hking0036, kazumi213, Money_114, NGEfreak, omonim2007, PPLToast, relax, Rifu, SonGoku, Tauwasser, Whovian9369, xuom2, zg" author "akubi, Aringon, Bent, BigFred, BitLooter, C. V. Reynolds, chillerecke, coraz, darthcloud, DeadSkullzJr, Densetsu, DeriLoko3, foxe, fuzzball, Hiccup, hking0036, InternalLoss, kazumi213, Money_114, NGEfreak, omonim2007, PPLToast, relax, Rifu, SonGoku, Tauwasser, Whovian9369, xuom2, zg"
homepage No-Intro homepage No-Intro
url "http://www.no-intro.org" url "http://www.no-intro.org"
) )
@ -34873,7 +34939,7 @@ game (
game ( game (
name "Mobile Golf (Japan)" name "Mobile Golf (Japan)"
description "Mobile Golf (Japan)" description "Mobile Golf (Japan)"
rom ( name "Mobile Golf (Japan).gbc" size 4194304 crc 35fc5b32 sha1 FDE414FC9EFEF2C30D1A9E0A2ED35AD2EFC0EDEE ) rom ( name "Mobile Golf (Japan).gbc" size 4194304 crc 35fc5b32 sha1 FDE414FC9EFEF2C30D1A9E0A2ED35AD2EFC0EDEE flags verified )
) )
game ( game (
@ -35887,7 +35953,7 @@ game (
game ( game (
name "Pokemon - Silberne Edition (Germany) (Beta) (SGB Enhanced) (GB Compatible)" name "Pokemon - Silberne Edition (Germany) (Beta) (SGB Enhanced) (GB Compatible)"
description "Pokemon - Silberne Edition (Germany) (Beta) (SGB Enhanced) (GB Compatible)" description "Pokemon - Silberne Edition (Germany) (Beta) (SGB Enhanced) (GB Compatible)"
rom ( name "Pokemon - Silberne Edition (Germany) (Beta) (SGB Enhanced) (GB Compatible).gbc" size 2097152 crc 576f5ced sha1 76FA60D66B2F22A035ADC54C61AAD9A415C894CD ) rom ( name "Pokemon - Silberne Edition (Germany) (Beta) (SGB Enhanced) (GB Compatible).gbc" size 2097152 crc 576f5ced sha1 76FA60D66B2F22A035ADC54C61AAD9A415C894CD flags verified )
) )
game ( game (

View File

@ -1,8 +1,13 @@
Miras Absar Akatsuki
Emily A. Bellows
Jaime J. Denizard
Benedikt Feih Benedikt Feih
Jezzabel Brandon
NimbusFox Emily A. Bellows
gocha
Jaime J. Denizard
MichaelK__
Miras Absar
Petru-Sebastian Toader Petru-Sebastian Toader
SquidHominid
Tyler Jenkins
William K. Leung
Zach Zach

View File

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

View File

@ -24,7 +24,7 @@
#define ADDR_MODE_1_LSL \ #define ADDR_MODE_1_LSL \
ADDR_MODE_1_SHIFT(LSL) \ ADDR_MODE_1_SHIFT(LSL) \
if (!info->op3.shifterImm) { \ if ((info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_3) && !info->op3.shifterImm) { \
info->operandFormat &= ~ARM_OPERAND_SHIFT_IMMEDIATE_3; \ info->operandFormat &= ~ARM_OPERAND_SHIFT_IMMEDIATE_3; \
info->op3.shifterOp = ARM_SHIFT_NONE; \ info->op3.shifterOp = ARM_SHIFT_NONE; \
} }

View File

@ -421,7 +421,7 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
#define ARM_MS_PRE_load \ #define ARM_MS_PRE_load \
enum PrivilegeMode privilegeMode; \ enum PrivilegeMode privilegeMode; \
if (!(rs & 0x8000)) { \ if (!(rs & 0x8000) && rs) { \
privilegeMode = cpu->privilegeMode; \ privilegeMode = cpu->privilegeMode; \
ARMSetPrivilegeMode(cpu, MODE_SYSTEM); \ ARMSetPrivilegeMode(cpu, MODE_SYSTEM); \
} }
@ -429,7 +429,7 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
#define ARM_MS_POST_store ARMSetPrivilegeMode(cpu, privilegeMode); #define ARM_MS_POST_store ARMSetPrivilegeMode(cpu, privilegeMode);
#define ARM_MS_POST_load \ #define ARM_MS_POST_load \
if (!(rs & 0x8000)) { \ if (!(rs & 0x8000) && rs) { \
ARMSetPrivilegeMode(cpu, privilegeMode); \ ARMSetPrivilegeMode(cpu, privilegeMode); \
} else if (_ARMModeHasSPSR(cpu->cpsr.priv)) { \ } else if (_ARMModeHasSPSR(cpu->cpsr.priv)) { \
cpu->cpsr = cpu->spsr; \ cpu->cpsr = cpu->spsr; \

View File

@ -139,18 +139,18 @@ void mBitmapCacheCleanRow(struct mBitmapCache* cache, struct mBitmapCacheEntry*
return; return;
} }
size_t offset = cache->bitsStart[cache->buffer] + y * mBitmapCacheSystemInfoGetWidth(cache->sysConfig); size_t offset = y * mBitmapCacheSystemInfoGetWidth(cache->sysConfig);
void* vram; void* vram;
int bpe = mBitmapCacheSystemInfoGetEntryBPP(cache->sysConfig); int bpe = mBitmapCacheSystemInfoGetEntryBPP(cache->sysConfig);
uint32_t (*lookupEntry)(void*, uint32_t); uint32_t (*lookupEntry)(void*, uint32_t);
switch (bpe) { switch (bpe) {
case 3: case 3:
lookupEntry = _lookupEntry8; lookupEntry = _lookupEntry8;
vram = &cache->vram[offset]; vram = &cache->vram[offset + cache->bitsStart[cache->buffer]];
break; break;
case 4: case 4:
lookupEntry = _lookupEntry15; lookupEntry = _lookupEntry15;
vram = &cache->vram[offset << 1]; vram = &cache->vram[offset * 2 + cache->bitsStart[cache->buffer]];
break; break;
default: default:
abort(); abort();
@ -184,4 +184,4 @@ bool mBitmapCacheCheckRow(struct mBitmapCache* cache, const struct mBitmapCacheE
const color_t* mBitmapCacheGetRow(struct mBitmapCache* cache, unsigned y) { const color_t* mBitmapCacheGetRow(struct mBitmapCache* cache, unsigned y) {
color_t* row = &cache->cache[(cache->buffer * mBitmapCacheSystemInfoGetHeight(cache->sysConfig) + y) * mBitmapCacheSystemInfoGetWidth(cache->sysConfig)]; color_t* row = &cache->cache[(cache->buffer * mBitmapCacheSystemInfoGetHeight(cache->sysConfig) + y) * mBitmapCacheSystemInfoGetWidth(cache->sysConfig)];
return row; return row;
} }

View File

@ -19,6 +19,12 @@
#include <strsafe.h> #include <strsafe.h>
#endif #endif
#ifdef __APPLE__
#include <CoreFoundation/CFBundle.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFURL.h>
#endif
#ifdef PSP2 #ifdef PSP2
#include <psp2/io/stat.h> #include <psp2/io/stat.h>
#endif #endif
@ -274,6 +280,16 @@ void mCoreConfigPortablePath(char* out, size_t outLength) {
out[0] = '\0'; out[0] = '\0';
#else #else
getcwd(out, outLength); 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)); strncat(out, PATH_SEP "portable.ini", outLength - strlen(out));
#endif #endif
} }

View File

@ -159,7 +159,13 @@ bool mCorePreloadVFCB(struct mCore* core, struct VFile* vf, void (cb)(size_t, si
extern uint32_t* romBuffer; extern uint32_t* romBuffer;
extern size_t romBufferSize; extern size_t romBufferSize;
if (size > romBufferSize) { if (size > romBufferSize) {
return false; if (size - romBufferSize < romBufferSize / 2) {
// Some ROM hacks accidentally overflow the size a bit, but since those are broken
// on hardware anyway we can just silently truncate them without issue.
size = romBufferSize;
} else {
return false;
}
} }
vfm = VFileFromMemory(romBuffer, size); vfm = VFileFromMemory(romBuffer, size);
#else #else

View File

@ -456,6 +456,11 @@ struct VFile* mLibraryOpenVFile(struct mLibrary* library, const struct mLibraryE
break; break;
} }
} }
for (i = 0; i < mLibraryListingSize(&entries); ++i) {
struct mLibraryEntry* e = mLibraryListingGetPointer(&entries, i);
mLibraryEntryFree(e);
}
mLibraryListingDeinit(&entries);
return vf; return vf;
} }

View File

@ -32,7 +32,7 @@ static void _redoCacheSize(struct mMapCache* cache) {
return; return;
} }
size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig)); size_t tiles = mMapCacheTileCount(cache);
cache->cache = anonymousMemoryMap(8 * 8 * sizeof(color_t) * tiles); cache->cache = anonymousMemoryMap(8 * 8 * sizeof(color_t) * tiles);
cache->status = anonymousMemoryMap(tiles * sizeof(*cache->status)); cache->status = anonymousMemoryMap(tiles * sizeof(*cache->status));
} }
@ -54,12 +54,12 @@ void mMapCacheConfigureSystem(struct mMapCache* cache, mMapCacheSystemInfo confi
cache->sysConfig = config; cache->sysConfig = config;
_redoCacheSize(cache); _redoCacheSize(cache);
size_t mapSize = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig)); size_t mapSize = mMapCacheTileCount(cache);
cache->mapSize = mapSize << mMapCacheSystemInfoGetMapAlign(cache->sysConfig); cache->mapSize = mapSize << mMapCacheSystemInfoGetMapAlign(cache->sysConfig);
} }
void mMapCacheConfigureMap(struct mMapCache* cache, uint32_t mapStart) { void mMapCacheConfigureMap(struct mMapCache* cache, uint32_t mapStart) {
size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig)); size_t tiles = mMapCacheTileCount(cache);
memset(cache->status, 0, tiles * sizeof(*cache->status)); memset(cache->status, 0, tiles * sizeof(*cache->status));
cache->mapStart = mapStart; cache->mapStart = mapStart;
} }

View File

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

View File

@ -100,7 +100,10 @@ int32_t mTimingTick(struct mTiming* timing, int32_t cycles) {
if (timing->reroot) { if (timing->reroot) {
timing->root = timing->reroot; timing->root = timing->reroot;
timing->reroot = NULL; timing->reroot = NULL;
*timing->nextEvent = mTimingNextEvent(timing); *timing->nextEvent = mTimingNextEvent(timing);
if (*timing->nextEvent <= 0) {
return mTimingTick(timing, 0);
}
} }
return *timing->nextEvent; return *timing->nextEvent;
} }

View File

@ -744,7 +744,7 @@ static bool _doTrace(struct CLIDebugger* debugger) {
trace[sizeof(trace) - 1] = '\0'; trace[sizeof(trace) - 1] = '\0';
size_t traceSize = sizeof(trace) - 2; size_t traceSize = sizeof(trace) - 2;
debugger->d.platform->trace(debugger->d.platform, trace, &traceSize); debugger->d.platform->trace(debugger->d.platform, trace, &traceSize);
if (traceSize + 1 <= sizeof(trace)) { if (traceSize + 2 <= sizeof(trace)) {
trace[traceSize] = '\n'; trace[traceSize] = '\n';
trace[traceSize + 1] = '\0'; trace[traceSize + 1] = '\0';
} }

View File

@ -629,6 +629,7 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
_readGPRs(stub, message); _readGPRs(stub, message);
break; break;
case 'H': case 'H':
case 'T':
// This is faked because we only have one thread // This is faked because we only have one thread
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4); strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
_sendMessage(stub); _sendMessage(stub);

View File

@ -322,6 +322,7 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->audioFrame->nb_samples = encoder->audio->frame_size; encoder->audioFrame->nb_samples = encoder->audio->frame_size;
encoder->audioFrame->format = encoder->audio->sample_fmt; encoder->audioFrame->format = encoder->audio->sample_fmt;
encoder->audioFrame->pts = 0; encoder->audioFrame->pts = 0;
encoder->audioFrame->channel_layout = AV_CH_LAYOUT_STEREO;
#ifdef USE_LIBAVRESAMPLE #ifdef USE_LIBAVRESAMPLE
encoder->resampleContext = avresample_alloc_context(); encoder->resampleContext = avresample_alloc_context();
av_opt_set_int(encoder->resampleContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0); av_opt_set_int(encoder->resampleContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
@ -338,14 +339,12 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
#endif #endif
encoder->audioBufferSize = (encoder->audioFrame->nb_samples * PREFERRED_SAMPLE_RATE / encoder->sampleRate) * 4; encoder->audioBufferSize = (encoder->audioFrame->nb_samples * PREFERRED_SAMPLE_RATE / encoder->sampleRate) * 4;
encoder->audioBuffer = av_malloc(encoder->audioBufferSize); encoder->audioBuffer = av_malloc(encoder->audioBufferSize);
encoder->postaudioBufferSize = av_samples_get_buffer_size(0, encoder->audio->channels, encoder->audio->frame_size, encoder->audio->sample_fmt, 0); av_frame_get_buffer(encoder->audioFrame, 0);
encoder->postaudioBuffer = av_malloc(encoder->postaudioBufferSize);
avcodec_fill_audio_frame(encoder->audioFrame, encoder->audio->channels, encoder->audio->sample_fmt, (const uint8_t*) encoder->postaudioBuffer, encoder->postaudioBufferSize, 0);
if (encoder->audio->codec->id == AV_CODEC_ID_AAC && if (encoder->audio->codec->id == AV_CODEC_ID_AAC &&
(strcasecmp(encoder->containerFormat, "mp4") || (strcasecmp(encoder->containerFormat, "mp4") == 0||
strcasecmp(encoder->containerFormat, "m4v") || strcasecmp(encoder->containerFormat, "m4v") == 0 ||
strcasecmp(encoder->containerFormat, "mov"))) { strcasecmp(encoder->containerFormat, "mov") == 0)) {
// MP4 container doesn't support the raw ADTS AAC format that the encoder spits out // MP4 container doesn't support the raw ADTS AAC format that the encoder spits out
#ifdef FFMPEG_USE_NEW_BSF #ifdef FFMPEG_USE_NEW_BSF
av_bsf_alloc(av_bsf_get_by_name("aac_adtstoasc"), &encoder->absf); av_bsf_alloc(av_bsf_get_by_name("aac_adtstoasc"), &encoder->absf);
@ -387,9 +386,9 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
} }
if (encoder->video->codec->id == AV_CODEC_ID_H264 && if (encoder->video->codec->id == AV_CODEC_ID_H264 &&
(strcasecmp(encoder->containerFormat, "mp4") || (strcasecmp(encoder->containerFormat, "mp4") == 0 ||
strcasecmp(encoder->containerFormat, "m4v") || strcasecmp(encoder->containerFormat, "m4v") == 0 ||
strcasecmp(encoder->containerFormat, "mov"))) { strcasecmp(encoder->containerFormat, "mov") == 0)) {
// QuickTime and a few other things require YUV420 // QuickTime and a few other things require YUV420
encoder->video->pix_fmt = AV_PIX_FMT_YUV420P; encoder->video->pix_fmt = AV_PIX_FMT_YUV420P;
} }
@ -517,7 +516,7 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->videoFrame->height = encoder->video->height; encoder->videoFrame->height = encoder->video->height;
encoder->videoFrame->pts = 0; encoder->videoFrame->pts = 0;
_ffmpegSetVideoDimensions(&encoder->d, encoder->iwidth, encoder->iheight); _ffmpegSetVideoDimensions(&encoder->d, encoder->iwidth, encoder->iheight);
av_image_alloc(encoder->videoFrame->data, encoder->videoFrame->linesize, encoder->videoFrame->width, encoder->videoFrame->height, encoder->videoFrame->format, 32); av_frame_get_buffer(encoder->videoFrame, 32);
#ifdef FFMPEG_USE_CODECPAR #ifdef FFMPEG_USE_CODECPAR
avcodec_parameters_from_context(encoder->videoStream->codecpar, encoder->video); avcodec_parameters_from_context(encoder->videoStream->codecpar, encoder->video);
#endif #endif
@ -692,7 +691,6 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
return; return;
} }
int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt);
encoder->currentAudioSample = 0; encoder->currentAudioSample = 0;
#ifdef USE_LIBAVRESAMPLE #ifdef USE_LIBAVRESAMPLE
avresample_convert(encoder->resampleContext, 0, 0, 0, avresample_convert(encoder->resampleContext, 0, 0, 0,
@ -704,7 +702,7 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
#if LIBAVCODEC_VERSION_MAJOR >= 55 #if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_make_writable(encoder->audioFrame); av_frame_make_writable(encoder->audioFrame);
#endif #endif
int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize); int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->audioFrame->nb_samples);
#else #else
#if LIBAVCODEC_VERSION_MAJOR >= 55 #if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_make_writable(encoder->audioFrame); av_frame_make_writable(encoder->audioFrame);
@ -713,7 +711,7 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
swr_convert(encoder->resampleContext, NULL, 0, (const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4); swr_convert(encoder->resampleContext, NULL, 0, (const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4);
return; return;
} }
int samples = swr_convert(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize, int samples = swr_convert(encoder->resampleContext, encoder->audioFrame->data, encoder->audioFrame->nb_samples,
(const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4); (const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4);
#endif #endif
@ -724,61 +722,76 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
} }
bool _ffmpegWriteAudioFrame(struct FFmpegEncoder* encoder, struct AVFrame* audioFrame) { bool _ffmpegWriteAudioFrame(struct FFmpegEncoder* encoder, struct AVFrame* audioFrame) {
AVPacket packet; AVPacket* packet;
av_init_packet(&packet); #ifdef FFMPEG_USE_PACKET_UNREF
packet.data = 0; packet = av_packet_alloc();
packet.size = 0; #else
packet = av_malloc(sizeof(*packet));
av_init_packet(packet);
#endif
packet->data = 0;
packet->size = 0;
int gotData; int gotData;
#ifdef FFMPEG_USE_PACKETS #ifdef FFMPEG_USE_PACKETS
avcodec_send_frame(encoder->audio, audioFrame); avcodec_send_frame(encoder->audio, audioFrame);
gotData = avcodec_receive_packet(encoder->audio, &packet); gotData = avcodec_receive_packet(encoder->audio, packet);
gotData = (gotData == 0) && packet.size; gotData = (gotData == 0) && packet->size;
#else #else
avcodec_encode_audio2(encoder->audio, &packet, audioFrame, &gotData); avcodec_encode_audio2(encoder->audio, packet, audioFrame, &gotData);
#endif #endif
packet.pts = av_rescale_q(packet.pts, encoder->audio->time_base, encoder->audioStream->time_base); packet->pts = av_rescale_q(packet->pts, encoder->audio->time_base, encoder->audioStream->time_base);
packet.dts = packet.pts; packet->dts = packet->pts;
if (gotData) { if (gotData) {
if (encoder->absf) { if (encoder->absf) {
AVPacket tempPacket; AVPacket* tempPacket;
#ifdef FFMPEG_USE_PACKETS
tempPacket = av_packet_alloc();
#else
tempPacket = av_malloc(sizeof(*tempPacket));
av_init_packet(tempPacket);
#endif
#ifdef FFMPEG_USE_NEW_BSF #ifdef FFMPEG_USE_NEW_BSF
int success = av_bsf_send_packet(encoder->absf, &packet); int success = av_bsf_send_packet(encoder->absf, packet);
if (success >= 0) { if (success >= 0) {
success = av_bsf_receive_packet(encoder->absf, &tempPacket); success = av_bsf_receive_packet(encoder->absf, tempPacket);
} }
#else #else
int success = av_bitstream_filter_filter(encoder->absf, encoder->audio, 0, int success = av_bitstream_filter_filter(encoder->absf, encoder->audio, 0,
&tempPacket.data, &tempPacket.size, &tempPacket->data, &tempPacket->size,
packet.data, packet.size, 0); packet->data, packet->size, 0);
#endif #endif
if (success >= 0) { if (success >= 0) {
#if LIBAVUTIL_VERSION_MAJOR >= 53 #if LIBAVUTIL_VERSION_MAJOR >= 53
tempPacket.buf = av_buffer_create(tempPacket.data, tempPacket.size, av_buffer_default_free, 0, 0); tempPacket->buf = av_buffer_create(tempPacket->data, tempPacket->size, av_buffer_default_free, 0, 0);
#endif #endif
#ifdef FFMPEG_USE_PACKET_UNREF #ifdef FFMPEG_USE_PACKET_UNREF
av_packet_move_ref(&packet, &tempPacket); av_packet_move_ref(packet, tempPacket);
av_packet_free(&packet);
#else #else
av_free_packet(&packet); av_free_packet(packet);
av_freep(&packet);
packet = tempPacket; packet = tempPacket;
#endif #endif
packet.stream_index = encoder->audioStream->index; packet->stream_index = encoder->audioStream->index;
av_interleaved_write_frame(encoder->context, &packet); av_interleaved_write_frame(encoder->context, packet);
} }
} else { } else {
packet.stream_index = encoder->audioStream->index; packet->stream_index = encoder->audioStream->index;
av_interleaved_write_frame(encoder->context, &packet); av_interleaved_write_frame(encoder->context, packet);
} }
} }
#ifdef FFMPEG_USE_PACKET_UNREF #ifdef FFMPEG_USE_PACKET_UNREF
av_packet_unref(&packet); av_packet_unref(packet);
av_packet_free(&packet);
#else #else
av_free_packet(&packet); av_free_packet(packet);
av_freep(&packet);
#endif #endif
return gotData; return gotData;
} }
@ -825,32 +838,39 @@ void _ffmpegPostVideoFrame(struct mAVStream* stream, const color_t* pixels, size
} }
bool _ffmpegWriteVideoFrame(struct FFmpegEncoder* encoder, struct AVFrame* videoFrame) { bool _ffmpegWriteVideoFrame(struct FFmpegEncoder* encoder, struct AVFrame* videoFrame) {
AVPacket packet; AVPacket* packet;
av_init_packet(&packet); #ifdef FFMPEG_USE_PACKET_UNREF
packet.data = 0; packet = av_packet_alloc();
packet.size = 0; #else
packet = av_malloc(sizeof(*packet));
av_init_packet(packet);
#endif
packet->data = 0;
packet->size = 0;
int gotData; int gotData;
#ifdef FFMPEG_USE_PACKETS #ifdef FFMPEG_USE_PACKETS
avcodec_send_frame(encoder->video, videoFrame); avcodec_send_frame(encoder->video, videoFrame);
gotData = avcodec_receive_packet(encoder->video, &packet) == 0; gotData = avcodec_receive_packet(encoder->video, packet) == 0;
#else #else
avcodec_encode_video2(encoder->video, &packet, videoFrame, &gotData); avcodec_encode_video2(encoder->video, packet, videoFrame, &gotData);
#endif #endif
if (gotData) { if (gotData) {
#ifndef FFMPEG_USE_PACKET_UNREF #ifndef FFMPEG_USE_PACKET_UNREF
if (encoder->video->coded_frame->key_frame) { if (encoder->video->coded_frame->key_frame) {
packet.flags |= AV_PKT_FLAG_KEY; packet->flags |= AV_PKT_FLAG_KEY;
} }
#endif #endif
packet.stream_index = encoder->videoStream->index; packet->stream_index = encoder->videoStream->index;
av_interleaved_write_frame(encoder->context, &packet); av_interleaved_write_frame(encoder->context, packet);
} }
#ifdef FFMPEG_USE_PACKET_UNREF #ifdef FFMPEG_USE_PACKET_UNREF
av_packet_unref(&packet); av_packet_unref(packet);
av_packet_free(&packet);
#else #else
av_free_packet(&packet); av_free_packet(packet);
av_freep(&packet);
#endif #endif
return gotData; return gotData;

View File

@ -380,4 +380,5 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
} }
} }
} }
GUIMenuItemListDeinit(&menu.items);
} }

View File

@ -40,6 +40,11 @@ enum {
#define RUNNER_STATE(X) ((X) << 16) #define RUNNER_STATE(X) ((X) << 16)
enum {
SCREENSHOT_VALID = 0x10000,
SCREENSHOT_INVALID = 0x20000,
};
static const struct mInputPlatformInfo _mGUIKeyInfo = { static const struct mInputPlatformInfo _mGUIKeyInfo = {
.platformName = "gui", .platformName = "gui",
.keyId = (const char*[GUI_INPUT_MAX]) { .keyId = (const char*[GUI_INPUT_MAX]) {
@ -105,39 +110,49 @@ static void _drawBackground(struct GUIBackground* background, void* context) {
static void _drawState(struct GUIBackground* background, void* id) { static void _drawState(struct GUIBackground* background, void* id) {
struct mGUIBackground* gbaBackground = (struct mGUIBackground*) background; struct mGUIBackground* gbaBackground = (struct mGUIBackground*) background;
int stateId = ((int) id) >> 16; unsigned stateId = ((uint32_t) id) >> 16;
if (gbaBackground->p->drawScreenshot) { if (gbaBackground->p->drawScreenshot) {
unsigned w, h; unsigned w, h;
gbaBackground->p->core->desiredVideoDimensions(gbaBackground->p->core, &w, &h); gbaBackground->p->core->desiredVideoDimensions(gbaBackground->p->core, &w, &h);
if (gbaBackground->screenshot && gbaBackground->screenshotId == (int) id) { size_t size = w * h * BYTES_PER_PIXEL;
gbaBackground->p->drawScreenshot(gbaBackground->p, gbaBackground->screenshot, w, h, true); 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; return;
} } else if (gbaBackground->screenshotId != (stateId | SCREENSHOT_INVALID)) {
struct VFile* vf = mCoreGetState(gbaBackground->p->core, stateId, false); struct VFile* vf = mCoreGetState(gbaBackground->p->core, stateId, false);
color_t* pixels = gbaBackground->screenshot; color_t* pixels = gbaBackground->image;
if (!pixels) { if (!pixels) {
pixels = anonymousMemoryMap(w * h * 4); pixels = anonymousMemoryMap(size);
gbaBackground->screenshot = pixels; gbaBackground->image = pixels;
} gbaBackground->imageSize = size;
bool success = false; }
if (vf && isPNG(vf) && pixels) { bool success = false;
png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES); if (vf && isPNG(vf) && pixels) {
png_infop info = png_create_info_struct(png); png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
png_infop end = png_create_info_struct(png); png_infop info = png_create_info_struct(png);
if (png && info && end) { png_infop end = png_create_info_struct(png);
success = PNGReadHeader(png, info); if (png && info && end) {
success = success && PNGReadPixels(png, info, pixels, w, h, w); success = PNGReadHeader(png, info);
success = success && PNGReadFooter(png, end); 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) { if (gbaBackground->p->drawFrame && gbaBackground->screenshotId == (stateId | SCREENSHOT_INVALID)) {
vf->close(vf);
}
if (success) {
gbaBackground->p->drawScreenshot(gbaBackground->p, pixels, w, h, true);
gbaBackground->screenshotId = (int) id;
} else if (gbaBackground->p->drawFrame) {
gbaBackground->p->drawFrame(gbaBackground->p, true); gbaBackground->p->drawFrame(gbaBackground->p, true);
} }
} }
@ -166,10 +181,10 @@ static void _tryAutosave(struct mGUIRunner* runner) {
#ifdef DISABLE_THREADING #ifdef DISABLE_THREADING
mCoreSaveState(runner->core, 0, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA); mCoreSaveState(runner->core, 0, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
#else #else
MutexLock(&runner->autosave.mutex);
if (!runner->autosave.buffer) { if (!runner->autosave.buffer) {
runner->autosave.buffer = VFileMemChunk(NULL, 0); runner->autosave.buffer = VFileMemChunk(NULL, 0);
} }
MutexLock(&runner->autosave.mutex);
runner->autosave.core = runner->core; runner->autosave.core = runner->core;
mCoreSaveStateNamed(runner->core, runner->autosave.buffer, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA); mCoreSaveStateNamed(runner->core, runner->autosave.buffer, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
ConditionWake(&runner->autosave.cond); ConditionWake(&runner->autosave.cond);
@ -313,7 +328,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
.draw = _drawState .draw = _drawState
}, },
.p = runner, .p = runner,
.screenshot = 0, .image = 0,
.screenshotId = 0 .screenshotId = 0
}; };
struct GUIMenu pauseMenu = { struct GUIMenu pauseMenu = {
@ -605,10 +620,10 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
runner->core->reset(runner->core); runner->core->reset(runner->core);
break; break;
case RUNNER_SAVE_STATE: case RUNNER_SAVE_STATE:
mCoreSaveState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA); mCoreSaveState(runner->core, ((uint32_t) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
break; break;
case RUNNER_LOAD_STATE: case RUNNER_LOAD_STATE:
mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); mCoreLoadState(runner->core, ((uint32_t) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
break; break;
case RUNNER_SCREENSHOT: case RUNNER_SCREENSHOT:
mCoreTakeScreenshot(runner->core); mCoreTakeScreenshot(runner->core);
@ -663,10 +678,8 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
mLOG(GUI_RUNNER, DEBUG, "Unloading game..."); mLOG(GUI_RUNNER, DEBUG, "Unloading game...");
runner->core->unloadROM(runner->core); runner->core->unloadROM(runner->core);
drawState.screenshotId = 0; drawState.screenshotId = 0;
if (drawState.screenshot) { if (drawState.image) {
unsigned w, h; mappedMemoryFree(drawState.image, drawState.imageSize);
runner->core->desiredVideoDimensions(runner->core, &w, &h);
mappedMemoryFree(drawState.screenshot, w * h * 4);
} }
if (runner->config.port) { if (runner->config.port) {
@ -726,7 +739,13 @@ THREAD_ENTRY mGUIAutosaveThread(void* context) {
while (autosave->running) { while (autosave->running) {
ConditionWait(&autosave->cond, &autosave->mutex); ConditionWait(&autosave->cond, &autosave->mutex);
if (autosave->running && autosave->core) { if (autosave->running && autosave->core) {
if (!autosave->buffer) {
continue;
}
struct VFile* vf = mCoreGetState(autosave->core, 0, true); struct VFile* vf = mCoreGetState(autosave->core, 0, true);
if (!vf) {
continue;
}
void* mem = autosave->buffer->map(autosave->buffer, autosave->buffer->size(autosave->buffer), MAP_READ); void* mem = autosave->buffer->map(autosave->buffer, autosave->buffer->size(autosave->buffer), MAP_READ);
vf->write(vf, mem, autosave->buffer->size(autosave->buffer)); vf->write(vf, mem, autosave->buffer->size(autosave->buffer));
autosave->buffer->unmap(autosave->buffer, mem, autosave->buffer->size(autosave->buffer)); autosave->buffer->unmap(autosave->buffer, mem, autosave->buffer->size(autosave->buffer));

View File

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

View File

@ -84,4 +84,5 @@ void mGUIRemapKeys(struct GUIParams* params, struct mInputMap* map, const struct
// TODO: Open remap menu // TODO: Open remap menu
} }
} }
GUIMenuItemListDeinit(&menu.items);
} }

View File

@ -446,7 +446,8 @@ static void _GBCoreReset(struct mCore* core) {
bool modelOverride = GBOverrideFind(gbcore->overrides, &override) || (doColorOverride && GBOverrideColorFind(&override)); bool modelOverride = GBOverrideFind(gbcore->overrides, &override) || (doColorOverride && GBOverrideColorFind(&override));
if (modelOverride) { if (modelOverride) {
GBOverrideApply(gb, &override); GBOverrideApply(gb, &override);
} else { }
if (!modelOverride || override.model == GB_MODEL_AUTODETECT) {
const char* modelGB = mCoreConfigGetValue(&core->config, "gb.model"); const char* modelGB = mCoreConfigGetValue(&core->config, "gb.model");
const char* modelSGB = mCoreConfigGetValue(&core->config, "sgb.model"); const char* modelSGB = mCoreConfigGetValue(&core->config, "sgb.model");
const char* modelCGB = mCoreConfigGetValue(&core->config, "cgb.model"); const char* modelCGB = mCoreConfigGetValue(&core->config, "cgb.model");
@ -770,13 +771,13 @@ void* _GBGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
*sizeOut = gb->memory.romSize; *sizeOut = gb->memory.romSize;
return gb->memory.rom; return gb->memory.rom;
case GB_REGION_VRAM: case GB_REGION_VRAM:
*sizeOut = GB_SIZE_WORKING_RAM_BANK0 * (isCgb ? 1 : 2); *sizeOut = GB_SIZE_VRAM_BANK0 * (isCgb ? 1 : 2);
return gb->video.vram; return gb->video.vram;
case GB_REGION_EXTERNAL_RAM: case GB_REGION_EXTERNAL_RAM:
*sizeOut = gb->sramSize; *sizeOut = gb->sramSize;
return gb->memory.sram; return gb->memory.sram;
case GB_REGION_WORKING_RAM_BANK0: case GB_REGION_WORKING_RAM_BANK0:
*sizeOut = GB_SIZE_VRAM * (isCgb ? 8 : 2); *sizeOut = GB_SIZE_WORKING_RAM_BANK0 * (isCgb ? 8 : 2);
return gb->memory.wram; return gb->memory.wram;
case GB_BASE_OAM: case GB_BASE_OAM:
*sizeOut = GB_SIZE_OAM; *sizeOut = GB_SIZE_OAM;

View File

@ -582,13 +582,13 @@ void GBSkipBIOS(struct GB* gb) {
mTimingDeschedule(&gb->timing, &gb->timer.event); mTimingDeschedule(&gb->timing, &gb->timer.event);
mTimingSchedule(&gb->timing, &gb->timer.event, gb->timer.nextDiv); mTimingSchedule(&gb->timing, &gb->timer.event, gb->timer.nextDiv);
GBIOWrite(gb, GB_REG_LCDC, 0x91);
gb->memory.io[GB_REG_BANK] = 0x1;
GBVideoSkipBIOS(&gb->video);
if (gb->biosVf) { if (gb->biosVf) {
GBUnmapBIOS(gb); GBUnmapBIOS(gb);
} }
GBIOWrite(gb, GB_REG_LCDC, 0x91);
gb->memory.io[GB_REG_BANK] = 0x1;
GBVideoSkipBIOS(&gb->video);
} }
void GBMapBIOS(struct GB* gb) { void GBMapBIOS(struct GB* gb) {

View File

@ -119,19 +119,15 @@ static void _writeSGBBits(struct GB* gb, int bits) {
if (bits == gb->currentSgbBits) { if (bits == gb->currentSgbBits) {
return; return;
} }
switch (bits) { if (bits & 2) {
case 0:
case 1:
if (gb->currentSgbBits & 2) {
gb->sgbIncrement = !gb->sgbIncrement;
}
break;
case 3:
if (gb->sgbIncrement) { if (gb->sgbIncrement) {
gb->sgbIncrement = false; gb->sgbIncrement = false;
gb->sgbCurrentController = (gb->sgbCurrentController + 1) & gb->sgbControllers; gb->sgbCurrentController = (gb->sgbCurrentController + 1) & gb->sgbControllers;
} }
break; } else {
if (gb->currentSgbBits & 2) {
gb->sgbIncrement = !gb->sgbIncrement;
}
} }
gb->currentSgbBits = bits; gb->currentSgbBits = bits;
if (gb->sgbBit == 128 && bits == 2) { if (gb->sgbBit == 128 && bits == 2) {
@ -724,6 +720,7 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->audio.enable = GBAudioEnableGetEnable(*gb->audio.nr52); gb->audio.enable = GBAudioEnableGetEnable(*gb->audio.nr52);
if (gb->audio.enable) { if (gb->audio.enable) {
gb->audio.playingCh1 = false;
GBIOWrite(gb, GB_REG_NR10, gb->memory.io[GB_REG_NR10]); 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_NR11, gb->memory.io[GB_REG_NR11]);
GBIOWrite(gb, GB_REG_NR12, gb->memory.io[GB_REG_NR12]); GBIOWrite(gb, GB_REG_NR12, gb->memory.io[GB_REG_NR12]);
@ -731,12 +728,14 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->audio.ch1.control.frequency &= 0xFF; gb->audio.ch1.control.frequency &= 0xFF;
gb->audio.ch1.control.frequency |= GBAudioRegisterControlGetFrequency(gb->memory.io[GB_REG_NR14] << 8); 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.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_NR21, gb->memory.io[GB_REG_NR21]);
GBIOWrite(gb, GB_REG_NR22, gb->memory.io[GB_REG_NR22]); GBIOWrite(gb, GB_REG_NR22, gb->memory.io[GB_REG_NR22]);
GBIOWrite(gb, GB_REG_NR23, gb->memory.io[GB_REG_NR23]); GBIOWrite(gb, GB_REG_NR23, gb->memory.io[GB_REG_NR23]);
gb->audio.ch2.control.frequency &= 0xFF; gb->audio.ch2.control.frequency &= 0xFF;
gb->audio.ch2.control.frequency |= GBAudioRegisterControlGetFrequency(gb->memory.io[GB_REG_NR24] << 8); 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.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_NR30, gb->memory.io[GB_REG_NR30]);
GBIOWrite(gb, GB_REG_NR31, gb->memory.io[GB_REG_NR31]); GBIOWrite(gb, GB_REG_NR31, gb->memory.io[GB_REG_NR31]);
GBIOWrite(gb, GB_REG_NR32, gb->memory.io[GB_REG_NR32]); GBIOWrite(gb, GB_REG_NR32, gb->memory.io[GB_REG_NR32]);
@ -744,6 +743,7 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->audio.ch3.rate &= 0xFF; gb->audio.ch3.rate &= 0xFF;
gb->audio.ch3.rate |= GBAudioRegisterControlGetRate(gb->memory.io[GB_REG_NR34] << 8); 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.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_NR41, gb->memory.io[GB_REG_NR41]);
GBIOWrite(gb, GB_REG_NR42, gb->memory.io[GB_REG_NR42]); GBIOWrite(gb, GB_REG_NR42, gb->memory.io[GB_REG_NR42]);
GBIOWrite(gb, GB_REG_NR43, gb->memory.io[GB_REG_NR43]); GBIOWrite(gb, GB_REG_NR43, gb->memory.io[GB_REG_NR43]);

View File

@ -780,6 +780,9 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer)
case SGB_ATTR_SET: case SGB_ATTR_SET:
if (softwareRenderer->sgbPacket[1] & 0x40) { if (softwareRenderer->sgbPacket[1] & 0x40) {
renderer->sgbRenderMode = 0; renderer->sgbRenderMode = 0;
if (softwareRenderer->sgbBorders) {
_regenerateSGBBorder(softwareRenderer);
}
} }
break; break;
case SGB_PAL_TRN: case SGB_PAL_TRN:

View File

@ -172,6 +172,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->cpu->condition = GBSerializedCpuFlagsGetCondition(flags); gb->cpu->condition = GBSerializedCpuFlagsGetCondition(flags);
gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags); gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags);
gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags); gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags);
gb->cpu->tMultiplier = 2 - gb->doubleSpeed;
gb->cpu->halted = GBSerializedCpuFlagsGetHalted(flags); gb->cpu->halted = GBSerializedCpuFlagsGetHalted(flags);
gb->cpuBlocked = GBSerializedCpuFlagsGetBlocked(flags); gb->cpuBlocked = GBSerializedCpuFlagsGetBlocked(flags);

View File

@ -80,26 +80,60 @@ void GBVideoReset(struct GBVideo* video) {
video->frameskipCounter = 0; video->frameskipCounter = 0;
GBVideoSwitchBank(video, 0); GBVideoSwitchBank(video, 0);
memset(video->vram, 0, GB_SIZE_VRAM);
video->renderer->vram = video->vram; video->renderer->vram = video->vram;
memset(&video->oam, 0, sizeof(video->oam)); memset(&video->oam, 0, sizeof(video->oam));
video->renderer->oam = &video->oam; video->renderer->oam = &video->oam;
memset(&video->palette, 0, sizeof(video->palette)); memset(&video->palette, 0, sizeof(video->palette));
if (video->p->model & GB_MODEL_SGB) { if (video->p->model & GB_MODEL_SGB) {
video->renderer->sgbCharRam = anonymousMemoryMap(SGB_SIZE_CHAR_RAM); if (video->renderer->sgbCharRam) {
video->renderer->sgbMapRam = anonymousMemoryMap(SGB_SIZE_MAP_RAM); memset(video->renderer->sgbCharRam, 0, SGB_SIZE_CHAR_RAM);
video->renderer->sgbPalRam = anonymousMemoryMap(SGB_SIZE_PAL_RAM); } else {
video->renderer->sgbAttributeFiles = anonymousMemoryMap(SGB_SIZE_ATF_RAM); video->renderer->sgbCharRam = anonymousMemoryMap(SGB_SIZE_CHAR_RAM);
video->renderer->sgbAttributes = malloc(90 * 45); }
if (video->renderer->sgbMapRam) {
memset(video->renderer->sgbMapRam, 0, SGB_SIZE_MAP_RAM);
} else {
video->renderer->sgbMapRam = anonymousMemoryMap(SGB_SIZE_MAP_RAM);
}
if (video->renderer->sgbPalRam) {
memset(video->renderer->sgbPalRam, 0, SGB_SIZE_PAL_RAM);
} else {
video->renderer->sgbPalRam = anonymousMemoryMap(SGB_SIZE_PAL_RAM);
}
if (video->renderer->sgbAttributeFiles) {
memset(video->renderer->sgbAttributeFiles, 0, SGB_SIZE_ATF_RAM);
} else {
video->renderer->sgbAttributeFiles = anonymousMemoryMap(SGB_SIZE_ATF_RAM);
}
if (!video->renderer->sgbAttributes) {
video->renderer->sgbAttributes = malloc(90 * 45);
}
memset(video->renderer->sgbAttributes, 0, 90 * 45); memset(video->renderer->sgbAttributes, 0, 90 * 45);
video->sgbCommandHeader = 0; video->sgbCommandHeader = 0;
video->sgbBufferIndex = 0; video->sgbBufferIndex = 0;
} else { } else {
video->renderer->sgbCharRam = NULL; if (video->renderer->sgbCharRam) {
video->renderer->sgbMapRam = NULL; mappedMemoryFree(video->renderer->sgbCharRam, SGB_SIZE_CHAR_RAM);
video->renderer->sgbPalRam = NULL; video->renderer->sgbCharRam = NULL;
video->renderer->sgbAttributes = NULL; }
video->renderer->sgbAttributeFiles = NULL; if (video->renderer->sgbMapRam) {
mappedMemoryFree(video->renderer->sgbMapRam, SGB_SIZE_MAP_RAM);
video->renderer->sgbMapRam = NULL;
}
if (video->renderer->sgbPalRam) {
mappedMemoryFree(video->renderer->sgbPalRam, SGB_SIZE_PAL_RAM);
video->renderer->sgbPalRam = NULL;
}
if (video->renderer->sgbAttributeFiles) {
mappedMemoryFree(video->renderer->sgbAttributeFiles, SGB_SIZE_ATF_RAM);
video->renderer->sgbAttributeFiles = NULL;
}
if (video->renderer->sgbAttributes) {
free(video->renderer->sgbAttributes);
video->renderer->sgbAttributes = NULL;
}
} }
video->palette[0] = video->dmgPalette[0]; video->palette[0] = video->dmgPalette[0];

View File

@ -300,6 +300,7 @@ static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* con
mCoreConfigCopyValue(&core->config, config, "gba.bios"); mCoreConfigCopyValue(&core->config, config, "gba.bios");
mCoreConfigCopyValue(&core->config, config, "gba.forceGbp"); mCoreConfigCopyValue(&core->config, config, "gba.forceGbp");
mCoreConfigCopyValue(&core->config, config, "gba.audioHle"); mCoreConfigCopyValue(&core->config, config, "gba.audioHle");
mCoreConfigCopyValue(&core->config, config, "vbaBugCompat");
#ifndef DISABLE_THREADING #ifndef DISABLE_THREADING
mCoreConfigCopyValue(&core->config, config, "threadedVideo"); mCoreConfigCopyValue(&core->config, config, "threadedVideo");
@ -610,6 +611,10 @@ static void _GBACoreReset(struct mCore* core) {
if (mCoreConfigGetIntValue(&core->config, "gba.forceGbp", &fakeBool)) { if (mCoreConfigGetIntValue(&core->config, "gba.forceGbp", &fakeBool)) {
forceGbp = fakeBool; forceGbp = fakeBool;
} }
bool vbaBugCompat = true;
if (mCoreConfigGetIntValue(&core->config, "vbaBugCompat", &fakeBool)) {
vbaBugCompat = fakeBool;
}
if (!forceGbp) { if (!forceGbp) {
gba->memory.hw.devices &= ~HW_GB_PLAYER_DETECTION; gba->memory.hw.devices &= ~HW_GB_PLAYER_DETECTION;
} }
@ -617,6 +622,9 @@ static void _GBACoreReset(struct mCore* core) {
if (forceGbp) { if (forceGbp) {
gba->memory.hw.devices |= HW_GB_PLAYER_DETECTION; gba->memory.hw.devices |= HW_GB_PLAYER_DETECTION;
} }
if (!vbaBugCompat) {
gba->vbaBugCompat = false;
}
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
if (!gba->biosVf && core->opts.useBios) { if (!gba->biosVf && core->opts.useBios) {

View File

@ -370,6 +370,11 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s
// Bitmap sizes // Bitmap sizes
case 5456: case 5456:
bitmap = true; bitmap = true;
blocks = 124;
break;
case 3520:
bitmap = true;
blocks = 80;
break; break;
default: default:
return; return;
@ -380,9 +385,9 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s
if (bitmap) { if (bitmap) {
size_t x; size_t x;
for (i = 0; i < 40; ++i) { for (i = 0; i < 40; ++i) {
const uint8_t* line = &cdata[(i + 2) * 124]; const uint8_t* line = &cdata[(i + 2) * blocks];
uint8_t* origin = &hw->eReaderDots[EREADER_DOTCODE_STRIDE * i + 200]; uint8_t* origin = &hw->eReaderDots[EREADER_DOTCODE_STRIDE * i + 200];
for (x = 0; x < 124; ++x) { for (x = 0; x < blocks; ++x) {
uint8_t byte = line[x]; uint8_t byte = line[x];
if (x == 123) { if (x == 123) {
byte &= 0xE0; byte &= 0xE0;

View File

@ -8,6 +8,7 @@
#include <mgba/core/cache-set.h> #include <mgba/core/cache-set.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/renderers/cache-set.h>
static void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer); static void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer);
static void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer); static void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer);
@ -267,6 +268,9 @@ uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* render
if (address > REG_BLDY) { if (address > REG_BLDY) {
return value; return value;
} }
if (renderer->cache) {
GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
}
mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value); mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
if (!proxyRenderer->logger->block) { if (!proxyRenderer->logger->block) {

View File

@ -411,6 +411,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
gba->memory.romSize = gba->pristineRomSize; gba->memory.romSize = gba->pristineRomSize;
} }
if (!gba->memory.rom) { if (!gba->memory.rom) {
gba->romVf = NULL;
mLOG(GBA, WARN, "Couldn't map ROM"); mLOG(GBA, WARN, "Couldn't map ROM");
return false; return false;
} }
@ -500,7 +501,11 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
} }
if (gba->romVf) { if (gba->romVf) {
#ifndef FIXED_ROM_BUFFER #ifndef FIXED_ROM_BUFFER
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); if (!gba->isPristine) {
mappedMemoryFree(gba->memory.rom, SIZE_CART0);
} else {
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize);
}
#endif #endif
gba->romVf->close(gba->romVf); gba->romVf->close(gba->romVf);
gba->romVf = NULL; gba->romVf = NULL;
@ -509,7 +514,7 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
gba->memory.rom = newRom; gba->memory.rom = newRom;
gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]; gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1];
gba->memory.romSize = patchedSize; gba->memory.romSize = patchedSize;
gba->memory.romMask = SIZE_CART0 - 1; gba->memory.romMask = toPow2(patchedSize) - 1;
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
} }

View File

@ -226,7 +226,7 @@ static const int _isValidRegister[REG_MAX >> 1] = {
1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -268,7 +268,7 @@ static const int _isRSpecialRegister[REG_MAX >> 1] = {
1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -309,7 +309,7 @@ static const int _isWSpecialRegister[REG_MAX >> 1] = {
1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -763,8 +763,9 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
} }
} }
} }
return 0x3FF ^ input; gba->memory.io[address >> 1] = 0x3FF ^ input;
} }
break;
case REG_SIOCNT: case REG_SIOCNT:
return gba->sio.siocnt; return gba->sio.siocnt;
case REG_RCNT: case REG_RCNT:
@ -973,6 +974,9 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
} }
void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
LOAD_16(gba->memory.io[REG_SOUNDCNT_X >> 1], REG_SOUNDCNT_X, state->io);
GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]);
int i; int i;
for (i = 0; i < REG_MAX; i += 2) { for (i = 0; i < REG_MAX; i += 2) {
if (_isWSpecialRegister[i >> 1]) { if (_isWSpecialRegister[i >> 1]) {
@ -1003,7 +1007,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].nextCount, 0, &state->dma[i].nextCount);
LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].when); 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]; gba->sio.siocnt = gba->memory.io[REG_SIOCNT >> 1];
GBASIOWriteRCNT(&gba->sio, gba->memory.io[REG_RCNT >> 1]); GBASIOWriteRCNT(&gba->sio, gba->memory.io[REG_RCNT >> 1]);

View File

@ -121,6 +121,7 @@ void GBAMemoryReset(struct GBA* gba) {
memset(gba->memory.io, 0, sizeof(gba->memory.io)); memset(gba->memory.io, 0, sizeof(gba->memory.io));
GBAAdjustWaitstates(gba, 0); GBAAdjustWaitstates(gba, 0);
gba->memory.activeRegion = -1;
gba->memory.agbPrintProtect = 0; gba->memory.agbPrintProtect = 0;
gba->memory.agbPrintBase = 0; gba->memory.agbPrintBase = 0;
memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx)); memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx));
@ -260,6 +261,11 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
gba->lastJump = address; gba->lastJump = address;
memory->lastPrefetchedPc = 0; memory->lastPrefetchedPc = 0;
if (newRegion == memory->activeRegion) { 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) { if (newRegion < REGION_CART0 || (address & (SIZE_CART0 - 1)) < memory->romSize) {
return; return;
} }
@ -345,7 +351,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
cpu->memory.activeSeqCycles16 = memory->waitstatesSeq16[memory->activeRegion]; cpu->memory.activeSeqCycles16 = memory->waitstatesSeq16[memory->activeRegion];
cpu->memory.activeNonseqCycles32 = memory->waitstatesNonseq32[memory->activeRegion]; cpu->memory.activeNonseqCycles32 = memory->waitstatesNonseq32[memory->activeRegion];
cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[memory->activeRegion]; cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[memory->activeRegion];
cpu->memory.activeMask &= -(cpu->executionMode == MODE_THUMB ? WORD_SIZE_THUMB : WORD_SIZE_ARM); cpu->memory.activeMask &= -(cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM);
} }
#define LOAD_BAD \ #define LOAD_BAD \
@ -369,7 +375,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
wait += waitstatesRegion[REGION_WORKING_RAM]; wait += waitstatesRegion[REGION_WORKING_RAM];
#define LOAD_WORKING_IRAM LOAD_32(value, address & (SIZE_WORKING_IRAM - 4), memory->iwram); #define LOAD_WORKING_IRAM LOAD_32(value, address & (SIZE_WORKING_IRAM - 4), memory->iwram);
#define LOAD_IO value = GBAIORead(gba, address & OFFSET_MASK & ~2) | (GBAIORead(gba, (address & OFFSET_MASK) | 2) << 16); #define LOAD_IO value = GBAIORead(gba, address & OFFSET_MASK & ~3) | (GBAIORead(gba, (address & OFFSET_MASK & ~1) | 2) << 16);
#define LOAD_PALETTE_RAM \ #define LOAD_PALETTE_RAM \
LOAD_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ LOAD_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \
@ -912,7 +918,11 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
} }
break; break;
case REGION_CART0: case REGION_CART0:
if (memory->hw.devices != HW_NONE && IS_GPIO_REGISTER(address & 0xFFFFFE)) { if (IS_GPIO_REGISTER(address & 0xFFFFFE)) {
if (memory->hw.devices == HW_NONE) {
mLOG(GBA_HW, WARN, "Write to GPIO address %08X on cartridge without GPIO", address);
break;
}
uint32_t reg = address & 0xFFFFFE; uint32_t reg = address & 0xFFFFFE;
GBAHardwareGPIOWrite(&memory->hw, reg, value); GBAHardwareGPIOWrite(&memory->hw, reg, value);
break; break;
@ -926,7 +936,6 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
if ((address & 0x00FFFFFF) >= AGB_PRINT_BASE) { if ((address & 0x00FFFFFF) >= AGB_PRINT_BASE) {
uint32_t agbPrintAddr = address & 0x00FFFFFF; uint32_t agbPrintAddr = address & 0x00FFFFFF;
if (agbPrintAddr == AGB_PRINT_PROTECT) { if (agbPrintAddr == AGB_PRINT_PROTECT) {
bool wasProtected = memory->agbPrintProtect != 0x20;
memory->agbPrintProtect = value; memory->agbPrintProtect = value;
if (!memory->agbPrintBuffer) { if (!memory->agbPrintBuffer) {

View File

@ -34,6 +34,9 @@ static const struct GBACartridgeOverride _overrides[] = {
{ "AC8E", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, { "AC8E", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
{ "AC8P", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, { "AC8P", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
// DigiCommunication Nyo - Datou! Black Gemagema Dan
{ "BDKJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
// Dragon Ball Z - The Legacy of Goku // Dragon Ball Z - The Legacy of Goku
{ "ALGP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, { "ALGP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
@ -49,6 +52,7 @@ static const struct GBACartridgeOverride _overrides[] = {
// Drill Dozer // Drill Dozer
{ "V49J", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false }, { "V49J", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false },
{ "V49E", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false }, { "V49E", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false },
{ "V49P", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false },
// e-Reader // e-Reader
{ "PEAJ", SAVEDATA_FLASH1M, HW_EREADER, IDLE_LOOP_NONE }, { "PEAJ", SAVEDATA_FLASH1M, HW_EREADER, IDLE_LOOP_NONE },
@ -375,8 +379,45 @@ void GBAOverrideApplyDefaults(struct GBA* gba, const struct Configuration* overr
if (cart) { if (cart) {
memcpy(override.id, &cart->id, sizeof(override.id)); memcpy(override.id, &cart->id, sizeof(override.id));
if (!strncmp("pokemon red version", &((const char*) gba->memory.rom)[0x108], 20) && gba->romCrc32 != 0xDD88761C) { static const uint32_t pokemonTable[] = {
// Enable FLASH1M and RTC on Pokémon FireRed ROM hacks // Emerald
0x4881F3F8, // BPEJ
0x8C4D3108, // BPES
0x1F1C08FB, // BPEE
0x34C9DF89, // BPED
0xA3FDCCB1, // BPEF
0xA0AEC80A, // BPEI
// FireRed
0x1A81EEDF, // BPRD
0x3B2056E9, // BPRJ
0x5DC668F6, // BPRF
0x73A72167, // BPRI
0x84EE4776, // BPRE rev 1
0x9F08064E, // BPRS
0xBB640DF7, // BPRJ rev 1
0xDD88761C, // BPRE
// Ruby
0x61641576, // AXVE rev 1
0xAEAC73E6, // AXVE rev 2
0xF0815EE7, // AXVE
};
bool isPokemon = false;
isPokemon = isPokemon || !strncmp("pokemon red version", &((const char*) gba->memory.rom)[0x108], 20);
isPokemon = isPokemon || !strncmp("pokemon emerald version", &((const char*) gba->memory.rom)[0x108], 24);
isPokemon = isPokemon || !strncmp("AXVE", &((const char*) gba->memory.rom)[0xAC], 4);
bool isKnownPokemon = false;
if (isPokemon) {
size_t i;
for (i = 0; !isKnownPokemon && i < sizeof(pokemonTable) / sizeof(*pokemonTable); ++i) {
isKnownPokemon = gba->romCrc32 == pokemonTable[i];
}
}
if (isPokemon && !isKnownPokemon) {
// Enable FLASH1M and RTC on Pokémon ROM hacks
override.savetype = SAVEDATA_FLASH1M; override.savetype = SAVEDATA_FLASH1M;
override.hardware = HW_RTC; override.hardware = HW_RTC;
override.vbaBugCompat = true; override.vbaBugCompat = true;

View File

@ -25,10 +25,10 @@ void GBAVideoCacheInit(struct mCacheSet* cache) {
sysconfig = mTileCacheSystemInfoSetPaletteBPP(sysconfig, 3); // 2^(2^3) = 256 entries sysconfig = mTileCacheSystemInfoSetPaletteBPP(sysconfig, 3); // 2^(2^3) = 256 entries
sysconfig = mTileCacheSystemInfoSetPaletteCount(sysconfig, 0); // 1 palettes sysconfig = mTileCacheSystemInfoSetPaletteCount(sysconfig, 0); // 1 palettes
sysconfig = mTileCacheSystemInfoSetMaxTiles(sysconfig, 2048); sysconfig = mTileCacheSystemInfoSetMaxTiles(sysconfig, 1024);
mTileCacheConfigureSystem(mTileCacheSetGetPointer(&cache->tiles, 1), sysconfig, 0, 0); mTileCacheConfigureSystem(mTileCacheSetGetPointer(&cache->tiles, 1), sysconfig, 0, 0);
mTileCacheConfigure(mTileCacheSetGetPointer(&cache->tiles, 1), config); mTileCacheConfigure(mTileCacheSetGetPointer(&cache->tiles, 1), config);
sysconfig = mTileCacheSystemInfoSetMaxTiles(sysconfig, 1024); sysconfig = mTileCacheSystemInfoSetMaxTiles(sysconfig, 512);
mTileCacheConfigureSystem(mTileCacheSetGetPointer(&cache->tiles, 3), sysconfig, 0x10000, 0x100); mTileCacheConfigureSystem(mTileCacheSetGetPointer(&cache->tiles, 3), sysconfig, 0x10000, 0x100);
mTileCacheConfigure(mTileCacheSetGetPointer(&cache->tiles, 3), config); mTileCacheConfigure(mTileCacheSetGetPointer(&cache->tiles, 3), config);

View File

@ -933,6 +933,7 @@ void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
glRenderer->nextPalette = 0; glRenderer->nextPalette = 0;
glRenderer->paletteDirtyScanlines = GBA_VIDEO_VERTICAL_PIXELS; glRenderer->paletteDirtyScanlines = GBA_VIDEO_VERTICAL_PIXELS;
memset(glRenderer->shadowRegs, 0, sizeof(glRenderer->shadowRegs)); memset(glRenderer->shadowRegs, 0, sizeof(glRenderer->shadowRegs));
glRenderer->shadowRegs[REG_DISPCNT >> 1] = glRenderer->dispcnt;
glRenderer->regsDirty = 0xFFFFFFFFFFFEULL; glRenderer->regsDirty = 0xFFFFFFFFFFFEULL;
int i; int i;
@ -947,6 +948,9 @@ void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
if (renderer->cache) {
mCacheSetWriteVRAM(renderer->cache, address);
}
glRenderer->vramDirty |= 1 << (address >> 12); glRenderer->vramDirty |= 1 << (address >> 12);
} }
@ -958,8 +962,9 @@ void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam)
void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
UNUSED(address); if (renderer->cache) {
UNUSED(value); mCacheSetWritePalette(renderer->cache, address >> 1, mColorFrom555(value));
}
glRenderer->paletteDirty = true; glRenderer->paletteDirty = true;
int r = M_R5(value); int r = M_R5(value);
int g = M_G5(value) << 1; int g = M_G5(value) << 1;
@ -1432,10 +1437,14 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
} }
if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) { if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
glRenderer->bg[2].affine.sx += glRenderer->bg[2].affine.dmx; if (glRenderer->bg[2].enabled == 4) {
glRenderer->bg[2].affine.sy += glRenderer->bg[2].affine.dmy; glRenderer->bg[2].affine.sx += glRenderer->bg[2].affine.dmx;
glRenderer->bg[3].affine.sx += glRenderer->bg[3].affine.dmx; glRenderer->bg[2].affine.sy += glRenderer->bg[2].affine.dmy;
glRenderer->bg[3].affine.sy += glRenderer->bg[3].affine.dmy; }
if (glRenderer->bg[3].enabled == 4) {
glRenderer->bg[3].affine.sx += glRenderer->bg[3].affine.dmx;
glRenderer->bg[3].affine.sy += glRenderer->bg[3].affine.dmy;
}
} }
} }

View File

@ -13,15 +13,21 @@
int32_t y = background->sy + (renderer->start - 1) * background->dy; \ int32_t y = background->sy + (renderer->start - 1) * background->dy; \
int mosaicH = 0; \ int mosaicH = 0; \
int mosaicWait = 0; \ int mosaicWait = 0; \
if (background->mosaic) { \
int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; \
y -= (inY % mosaicV) * background->dmy; \
x -= (inY % mosaicV) * background->dmx; \
mosaicH = GBAMosaicControlGetBgH(renderer->mosaic); \
mosaicWait = renderer->start % (mosaicH + 1); \
} \
int32_t localX; \ int32_t localX; \
int32_t localY; \ int32_t localY; \
if (background->mosaic) { \
int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; \
mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
mosaicWait = (mosaicH - renderer->start + GBA_VIDEO_HORIZONTAL_PIXELS * mosaicH) % mosaicH; \
int32_t startX = renderer->start - (renderer->start % mosaicH); \
--mosaicH; \
localX = -(inY % mosaicV) * background->dmx; \
localY = -(inY % mosaicV) * background->dmy; \
x += localX; \
y += localY; \
localX += background->sx + startX * background->dx; \
localY += background->sy + startX * background->dy; \
} \
\ \
uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \ uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
flags |= FLAG_TARGET_2 * background->target2; \ flags |= FLAG_TARGET_2 * background->target2; \
@ -49,16 +55,14 @@
UNUSED(palette); \ UNUSED(palette); \
PREPARE_OBJWIN; PREPARE_OBJWIN;
#define BACKGROUND_BITMAP_ITERATE(W, H) \ #define BACKGROUND_BITMAP_ITERATE(W, H) \
x += background->dx; \ x += background->dx; \
y += background->dy; \ y += background->dy; \
\ if ((x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) && !mosaicWait) { \
if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \ continue; \
continue; \ } \
} else { \ localX = x; \
localX = x; \ localY = y;
localY = y; \
}
#define MODE_2_COORD_OVERFLOW \ #define MODE_2_COORD_OVERFLOW \
localX = x & (sizeAdjusted - 1); \ localX = x & (sizeAdjusted - 1); \
@ -102,12 +106,20 @@
#define DRAW_BACKGROUND_MODE_2(BLEND, OBJWIN) \ #define DRAW_BACKGROUND_MODE_2(BLEND, OBJWIN) \
if (background->overflow) { \ if (background->overflow) { \
if (mosaicH > 1) { \ if (mosaicH > 1) { \
localX &= sizeAdjusted - 1; \
localY &= sizeAdjusted - 1; \
MODE_2_NO_MOSAIC(); \
MODE_2_LOOP(MODE_2_MOSAIC, MODE_2_COORD_OVERFLOW, BLEND, OBJWIN); \ MODE_2_LOOP(MODE_2_MOSAIC, MODE_2_COORD_OVERFLOW, BLEND, OBJWIN); \
} else { \ } else { \
MODE_2_LOOP(MODE_2_NO_MOSAIC, MODE_2_COORD_OVERFLOW, BLEND, OBJWIN); \ MODE_2_LOOP(MODE_2_NO_MOSAIC, MODE_2_COORD_OVERFLOW, BLEND, OBJWIN); \
} \ } \
} else { \ } else { \
if (mosaicH > 1) { \ if (mosaicH > 1) { \
if (!((x | y) & ~(sizeAdjusted - 1))) { \
localX &= sizeAdjusted - 1; \
localY &= sizeAdjusted - 1; \
MODE_2_NO_MOSAIC(); \
} \
MODE_2_LOOP(MODE_2_MOSAIC, MODE_2_COORD_NO_OVERFLOW, BLEND, OBJWIN); \ MODE_2_LOOP(MODE_2_MOSAIC, MODE_2_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
} else { \ } else { \
MODE_2_LOOP(MODE_2_NO_MOSAIC, MODE_2_COORD_NO_OVERFLOW, BLEND, OBJWIN); \ MODE_2_LOOP(MODE_2_NO_MOSAIC, MODE_2_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
@ -146,6 +158,10 @@ void GBAVideoSoftwareRendererDrawBackgroundMode3(struct GBAVideoSoftwareRenderer
BACKGROUND_BITMAP_INIT; BACKGROUND_BITMAP_INIT;
uint32_t color = renderer->normalPalette[0]; uint32_t color = renderer->normalPalette[0];
if (mosaicWait && localX >= 0 && localY >= 0) {
LOAD_16(color, ((localX >> 8) + (localY >> 8) * GBA_VIDEO_HORIZONTAL_PIXELS) << 1, renderer->d.vram);
color = mColorFrom555(color);
}
int outX; int outX;
uint32_t* pixel; uint32_t* pixel;
@ -185,6 +201,9 @@ void GBAVideoSoftwareRendererDrawBackgroundMode4(struct GBAVideoSoftwareRenderer
if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) { if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
offset = 0xA000; offset = 0xA000;
} }
if (mosaicWait && localX >= 0 && localY >= 0) {
color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * GBA_VIDEO_HORIZONTAL_PIXELS];
}
int outX; int outX;
uint32_t* pixel; uint32_t* pixel;
@ -223,6 +242,10 @@ void GBAVideoSoftwareRendererDrawBackgroundMode5(struct GBAVideoSoftwareRenderer
if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) { if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
offset = 0xA000; offset = 0xA000;
} }
if (mosaicWait && localX >= 0 && localY >= 0) {
LOAD_16(color, offset + (localX >> 8) * 2 + (localY >> 8) * 320, renderer->d.vram);
color = mColorFrom555(color);
}
int outX; int outX;
uint32_t* pixel; uint32_t* pixel;

View File

@ -543,10 +543,14 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
if (!dirty) { if (!dirty) {
if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) { if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) {
softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx; if (softwareRenderer->bg[2].enabled == 4) {
softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy; softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx; softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy; }
if (softwareRenderer->bg[3].enabled == 4) {
softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
}
} }
return; return;
} }
@ -719,16 +723,16 @@ static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* rendere
softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy; softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
if (softwareRenderer->bg[0].enabled > 0) { if (softwareRenderer->bg[0].enabled > 0) {
softwareRenderer->bg[0].enabled = 3; softwareRenderer->bg[0].enabled = 4;
} }
if (softwareRenderer->bg[1].enabled > 0) { if (softwareRenderer->bg[1].enabled > 0) {
softwareRenderer->bg[1].enabled = 3; softwareRenderer->bg[1].enabled = 4;
} }
if (softwareRenderer->bg[2].enabled > 0) { if (softwareRenderer->bg[2].enabled > 0) {
softwareRenderer->bg[2].enabled = 3; softwareRenderer->bg[2].enabled = 4;
} }
if (softwareRenderer->bg[3].enabled > 0) { if (softwareRenderer->bg[3].enabled > 0) {
softwareRenderer->bg[3].enabled = 3; softwareRenderer->bg[3].enabled = 4;
} }
} }
@ -753,9 +757,11 @@ static void _enableBg(struct GBAVideoSoftwareRenderer* renderer, int bg, bool ac
if (!active) { if (!active) {
renderer->bg[bg].enabled = 0; renderer->bg[bg].enabled = 0;
} else if (!wasActive && active) { } else if (!wasActive && active) {
if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) { if (renderer->nextY == 0) {
// TODO: Investigate in more depth how switching background works in different modes // TODO: Investigate in more depth how switching background works in different modes
renderer->bg[bg].enabled = 3; renderer->bg[bg].enabled = 4;
} else if (GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
renderer->bg[bg].enabled = 2;
} else { } else {
renderer->bg[bg].enabled = 1; renderer->bg[bg].enabled = 1;
} }
@ -830,7 +836,7 @@ static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer*
#define TEST_LAYER_ENABLED(X) \ #define TEST_LAYER_ENABLED(X) \
!renderer->d.disableBG[X] && \ !renderer->d.disableBG[X] && \
(renderer->bg[X].enabled == 3 && \ (renderer->bg[X].enabled == 4 && \
(GBAWindowControlIsBg ## X ## Enable(renderer->currentWindow.packed) || \ (GBAWindowControlIsBg ## X ## Enable(renderer->currentWindow.packed) || \
(GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (renderer->objwin.packed))) && \ (GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (renderer->objwin.packed))) && \
renderer->bg[X].priority == priority) renderer->bg[X].priority == priority)
@ -931,25 +937,29 @@ static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
} }
} }
if (GBARegisterDISPCNTGetMode(renderer->dispcnt) != 0) { if (GBARegisterDISPCNTGetMode(renderer->dispcnt) != 0) {
renderer->bg[2].sx += renderer->bg[2].dmx; if (renderer->bg[2].enabled == 4) {
renderer->bg[2].sy += renderer->bg[2].dmy; renderer->bg[2].sx += renderer->bg[2].dmx;
renderer->bg[3].sx += renderer->bg[3].dmx; renderer->bg[2].sy += renderer->bg[2].dmy;
renderer->bg[3].sy += renderer->bg[3].dmy; }
if (renderer->bg[3].enabled == 4) {
renderer->bg[3].sx += renderer->bg[3].dmx;
renderer->bg[3].sy += renderer->bg[3].dmy;
}
} }
if (renderer->bg[0].enabled > 0 && renderer->bg[0].enabled < 3) { if (renderer->bg[0].enabled > 0 && renderer->bg[0].enabled < 4) {
++renderer->bg[0].enabled; ++renderer->bg[0].enabled;
DIRTY_SCANLINE(renderer, y); DIRTY_SCANLINE(renderer, y);
} }
if (renderer->bg[1].enabled > 0 && renderer->bg[1].enabled < 3) { if (renderer->bg[1].enabled > 0 && renderer->bg[1].enabled < 4) {
++renderer->bg[1].enabled; ++renderer->bg[1].enabled;
DIRTY_SCANLINE(renderer, y); DIRTY_SCANLINE(renderer, y);
} }
if (renderer->bg[2].enabled > 0 && renderer->bg[2].enabled < 3) { if (renderer->bg[2].enabled > 0 && renderer->bg[2].enabled < 4) {
++renderer->bg[2].enabled; ++renderer->bg[2].enabled;
DIRTY_SCANLINE(renderer, y); DIRTY_SCANLINE(renderer, y);
} }
if (renderer->bg[3].enabled > 0 && renderer->bg[3].enabled < 3) { if (renderer->bg[3].enabled > 0 && renderer->bg[3].enabled < 4) {
++renderer->bg[3].enabled; ++renderer->bg[3].enabled;
DIRTY_SCANLINE(renderer, y); DIRTY_SCANLINE(renderer, y);
} }

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"); mLOG(GBA_STATE, WARN, "Savestate has unaligned PC and is probably corrupted");
gba->cpu->gprs[ARM_PC] &= ~1; gba->cpu->gprs[ARM_PC] &= ~1;
} }
gba->memory.activeRegion = -1;
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
if (state->biosPrefetch) { if (state->biosPrefetch) {
LOAD_32(gba->memory.biosPrefetch, 0, &state->biosPrefetch); LOAD_32(gba->memory.biosPrefetch, 0, &state->biosPrefetch);

View File

@ -60,7 +60,9 @@ static void _switchMode(struct GBASIO* sio) {
if (sio->activeDriver && sio->activeDriver->unload) { if (sio->activeDriver && sio->activeDriver->unload) {
sio->activeDriver->unload(sio->activeDriver); sio->activeDriver->unload(sio->activeDriver);
} }
mLOG(GBA_SIO, DEBUG, "Switching mode from %s to %s", _modeName(sio->mode), _modeName(newMode)); if (sio->mode != (enum GBASIOMode) -1) {
mLOG(GBA_SIO, DEBUG, "Switching mode from %s to %s", _modeName(sio->mode), _modeName(newMode));
}
sio->mode = newMode; sio->mode = newMode;
sio->activeDriver = _lookupDriver(sio, sio->mode); sio->activeDriver = _lookupDriver(sio, sio->mode);
if (sio->activeDriver && sio->activeDriver->load) { if (sio->activeDriver && sio->activeDriver->load) {

View File

@ -113,6 +113,7 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
node->d.p->siocnt = GBASIOMultiplayerFillSlave(node->d.p->siocnt); node->d.p->siocnt = GBASIOMultiplayerFillSlave(node->d.p->siocnt);
} }
break; break;
case SIO_NORMAL_8:
case SIO_NORMAL_32: case SIO_NORMAL_32:
ATOMIC_ADD(node->p->attachedNormal, 1); ATOMIC_ADD(node->p->attachedNormal, 1);
node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister; node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister;
@ -140,6 +141,7 @@ bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
case SIO_MULTI: case SIO_MULTI:
ATOMIC_SUB(node->p->attachedMulti, 1); ATOMIC_SUB(node->p->attachedMulti, 1);
break; break;
case SIO_NORMAL_8:
case SIO_NORMAL_32: case SIO_NORMAL_32:
ATOMIC_SUB(node->p->attachedNormal, 1); ATOMIC_SUB(node->p->attachedNormal, 1);
break; break;
@ -178,10 +180,14 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value);
enum mLockstepPhase transferActive; enum mLockstepPhase transferActive;
int attached;
ATOMIC_LOAD(transferActive, node->p->d.transferActive); 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 (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); mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id);
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); 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]); ATOMIC_STORE(node->p->d.transferCycles, GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(node->d.p->siocnt)][node->p->d.attached - 1]);
@ -441,11 +447,22 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
struct GBASIOLockstepNode* node = user; struct GBASIOLockstepNode* node = user;
mLockstepLock(&node->p->d); mLockstepLock(&node->p->d);
int32_t cycles = 0; int32_t cycles = cycles = node->nextEvent;
node->nextEvent -= cyclesLate; node->nextEvent -= cyclesLate;
node->eventDiff += cyclesLate; node->eventDiff += cyclesLate;
if (node->p->d.attached < 2) { 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) { } else if (node->nextEvent <= 0) {
if (!node->id) { if (!node->id) {
cycles = _masterUpdate(node); cycles = _masterUpdate(node);
@ -454,8 +471,6 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
cycles += node->p->d.useCycles(&node->p->d, node->id, node->eventDiff); cycles += node->p->d.useCycles(&node->p->d, node->id, node->eventDiff);
} }
node->eventDiff = 0; node->eventDiff = 0;
} else {
cycles = node->nextEvent;
} }
if (cycles > 0) { if (cycles > 0) {
node->nextEvent = 0; node->nextEvent = 0;
@ -519,6 +534,8 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04X", node->id, value); mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04X", node->id, value);
} else if (address == REG_SIODATA32_HI) { } else if (address == REG_SIODATA32_HI) {
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04X", node->id, value); mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04X", node->id, value);
} else if (address == REG_SIODATA8) {
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA8 <- %02X", node->id, value);
} }
mLockstepUnlock(&node->p->d); mLockstepUnlock(&node->p->d);

View File

@ -130,7 +130,10 @@ void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer*
renderer->writeVideoRegister(renderer, REG_DISPCNT, video->p->memory.io[REG_DISPCNT >> 1]); renderer->writeVideoRegister(renderer, REG_DISPCNT, video->p->memory.io[REG_DISPCNT >> 1]);
renderer->writeVideoRegister(renderer, REG_GREENSWP, video->p->memory.io[REG_GREENSWP >> 1]); renderer->writeVideoRegister(renderer, REG_GREENSWP, video->p->memory.io[REG_GREENSWP >> 1]);
int address; int address;
for (address = REG_BG0CNT; address < REG_SOUND1CNT_LO; address += 2) { for (address = REG_BG0CNT; address < 0x56; address += 2) {
if (address == 0x4E) {
continue;
}
renderer->writeVideoRegister(renderer, address, video->p->memory.io[address >> 1]); renderer->writeVideoRegister(renderer, address, video->p->memory.io[address >> 1]);
} }
} }
@ -156,9 +159,6 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
video->p->memory.io[REG_VCOUNT >> 1] = video->vcount; video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) { if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
if (video->frameskipCounter <= 0) {
video->renderer->drawScanline(video->renderer, video->vcount);
}
video->shouldStall = 1; video->shouldStall = 1;
} }
@ -210,6 +210,9 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
// Begin Hblank // Begin Hblank
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
dispstat = GBARegisterDISPSTATFillInHblank(dispstat); dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
video->renderer->drawScanline(video->renderer, video->vcount);
}
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) { if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
GBADMARunHblank(video->p, -cyclesLate); GBADMARunHblank(video->p, -cyclesLate);

View File

@ -845,10 +845,12 @@ int main() {
gfxInit(GSP_BGR8_OES, GSP_BGR8_OES, true); gfxInit(GSP_BGR8_OES, GSP_BGR8_OES, true);
u8 model = 0; u8 model = 0;
cfguInit();
CFGU_GetSystemModel(&model); CFGU_GetSystemModel(&model);
if (model != 3 /* o2DS */) { if (model != 3 /* o2DS */) {
gfxSetWide(true); gfxSetWide(true);
} }
cfguExit();
if (!_initGpu()) { if (!_initGpu()) {
outputTexture[0].data = 0; outputTexture[0].data = 0;

View File

@ -0,0 +1,6 @@
message(FATAL ${CPACK_GENERATOR})
if(CPACK_GENERATOR STREQUAL "DragNDrop")
set(CPACK_COMPONENTS_ALL @BINARY_NAME@ @BINARY_NAME@-qt @BINARY_NAME@-qt-dbg)
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
unset(CPACK_RESOURCE_FILE_LICENSE)
endif()

View File

@ -618,8 +618,8 @@ static void _setupMaps(struct mCore* core) {
i++; i++;
/* Map External RAM */ /* Map External RAM */
if (gb->memory.sram) { if (savedataSize) {
descs[i].ptr = gb->memory.sram; descs[i].ptr = savedata;
descs[i].start = GB_BASE_EXTERNAL_RAM; descs[i].start = GB_BASE_EXTERNAL_RAM;
descs[i].len = savedataSize; descs[i].len = savedataSize;
i++; i++;
@ -690,6 +690,7 @@ bool retro_load_game(const struct retro_game_info* game) {
blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), 32768); blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), 32768);
core->setPeripheral(core, mPERIPH_RUMBLE, &rumble); core->setPeripheral(core, mPERIPH_RUMBLE, &rumble);
core->setPeripheral(core, mPERIPH_ROTATION, &rotation);
savedata = anonymousMemoryMap(SIZE_CART_FLASH1M); savedata = anonymousMemoryMap(SIZE_CART_FLASH1M);
memset(savedata, 0xFF, SIZE_CART_FLASH1M); memset(savedata, 0xFF, SIZE_CART_FLASH1M);
@ -870,7 +871,9 @@ void retro_cheat_set(unsigned index, bool enabled, const char* code) {
} }
} }
#endif #endif
cheatSet->refresh(cheatSet, device); if (cheatSet->refresh) {
cheatSet->refresh(cheatSet, device);
}
} }
unsigned retro_get_region(void) { unsigned retro_get_region(void) {

View File

@ -3,7 +3,7 @@ include("${VITASDK}/share/vita.cmake" REQUIRED)
find_program(OBJCOPY ${cross_prefix}objcopy) find_program(OBJCOPY ${cross_prefix}objcopy)
find_file(NIDDB db.json PATHS ${VITASDK} ${VITASDK}/bin ${VITASDK}/share) find_file(NIDDB db.json PATHS ${VITASDK} ${VITASDK}/bin ${VITASDK}/share)
set(OS_DEFINES IOAPI_NO_64) set(OS_DEFINES IOAPI_NO_64 _GNU_SOURCE)
set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE) set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE)
file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/psp2-*.c) file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/psp2-*.c)

View File

@ -90,7 +90,7 @@ struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode) {
bool _vfsceClose(struct VFile* vf) { bool _vfsceClose(struct VFile* vf) {
struct VFileSce* vfsce = (struct VFileSce*) vf; struct VFileSce* vfsce = (struct VFileSce*) vf;
sceIoSyncByFd(vfsce->fd); sceIoSyncByFd(vfsce->fd, 0);
return sceIoClose(vfsce->fd) >= 0; return sceIoClose(vfsce->fd) >= 0;
} }
@ -128,7 +128,7 @@ static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size) {
sceIoLseek(vfsce->fd, 0, SEEK_SET); sceIoLseek(vfsce->fd, 0, SEEK_SET);
sceIoWrite(vfsce->fd, memory, size); sceIoWrite(vfsce->fd, memory, size);
sceIoLseek(vfsce->fd, cur, SEEK_SET); sceIoLseek(vfsce->fd, cur, SEEK_SET);
sceIoSyncByFd(vfsce->fd); sceIoSyncByFd(vfsce->fd, 0);
mappedMemoryFree(memory, size); mappedMemoryFree(memory, size);
} }
@ -155,7 +155,7 @@ bool _vfsceSync(struct VFile* vf, void* buffer, size_t size) {
sceIoLseek(vfsce->fd, cur, SEEK_SET); sceIoLseek(vfsce->fd, cur, SEEK_SET);
return res == size; return res == size;
} }
return sceIoSyncByFd(vfsce->fd) >= 0; return sceIoSyncByFd(vfsce->fd, 0) >= 0;
} }
struct VDir* VDirOpen(const char* path) { struct VDir* VDirOpen(const char* path) {

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::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); QObject::connect(action, &Action::destroyed, qaction, &QAction::deleteLater);
if (shortcut) { if (shortcut) {
QObject::connect(shortcut, &Shortcut::shortcutChanged, qaction, [qaction](int 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; QLabel* value = new QLabel;
value->setFont(GBAApp::app()->monospaceFont()); value->setFont(GBAApp::app()->monospaceFont());
value->setAlignment(Qt::AlignRight); value->setAlignment(Qt::AlignRight);
value->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
newLayout->addWidget(value); newLayout->addWidget(value);
m_customProperties[id] = value; m_customProperties[id] = value;
int index = customLocation(); int index = customLocation();

View File

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

View File

@ -112,15 +112,19 @@ void AssetView::compositeTile(const void* tBuffer, void* buffer, size_t stride,
} }
} }
QImage AssetView::compositeMap(int map, mMapCacheEntry* mapStatus) { QImage AssetView::compositeMap(int map, QVector<mMapCacheEntry>* mapStatus) {
mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, map); mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, map);
int tilesW = 1 << mMapCacheSystemInfoGetTilesWide(mapCache->sysConfig); int tilesW = 1 << mMapCacheSystemInfoGetTilesWide(mapCache->sysConfig);
int tilesH = 1 << mMapCacheSystemInfoGetTilesHigh(mapCache->sysConfig); int tilesH = 1 << mMapCacheSystemInfoGetTilesHigh(mapCache->sysConfig);
if (mapStatus->size() != tilesW * tilesH) {
mapStatus->resize(tilesW * tilesH);
mapStatus->fill({});
}
QImage rawMap = QImage(QSize(tilesW * 8, tilesH * 8), QImage::Format_ARGB32); QImage rawMap = QImage(QSize(tilesW * 8, tilesH * 8), QImage::Format_ARGB32);
uchar* bgBits = rawMap.bits(); uchar* bgBits = rawMap.bits();
for (int j = 0; j < tilesH; ++j) { for (int j = 0; j < tilesH; ++j) {
for (int i = 0; i < tilesW; ++i) { for (int i = 0; i < tilesW; ++i) {
mMapCacheCleanTile(mapCache, mapStatus, i, j); mMapCacheCleanTile(mapCache, mapStatus->data(), i, j);
} }
for (int i = 0; i < 8; ++i) { for (int i = 0; i < 8; ++i) {
memcpy(static_cast<void*>(&bgBits[tilesW * 32 * (i + j * 8)]), mMapCacheGetRow(mapCache, i + j * 8), tilesW * 32); memcpy(static_cast<void*>(&bgBits[tilesW * 32 * (i + j * 8)]), mMapCacheGetRow(mapCache, i + j * 8), tilesW * 32);
@ -131,6 +135,7 @@ QImage AssetView::compositeMap(int map, mMapCacheEntry* mapStatus) {
QImage AssetView::compositeObj(const ObjInfo& objInfo) { QImage AssetView::compositeObj(const ObjInfo& objInfo) {
mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, objInfo.paletteSet); mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, objInfo.paletteSet);
unsigned maxTiles = mTileCacheSystemInfoGetMaxTiles(tileCache->sysConfig);
const color_t* rawPalette = mTileCacheGetPalette(tileCache, objInfo.paletteId); const color_t* rawPalette = mTileCacheGetPalette(tileCache, objInfo.paletteId);
unsigned colors = 1 << objInfo.bits; unsigned colors = 1 << objInfo.bits;
QVector<QRgb> palette; QVector<QRgb> palette;
@ -142,10 +147,11 @@ QImage AssetView::compositeObj(const ObjInfo& objInfo) {
QImage image = QImage(QSize(objInfo.width * 8, objInfo.height * 8), QImage::Format_Indexed8); QImage image = QImage(QSize(objInfo.width * 8, objInfo.height * 8), QImage::Format_Indexed8);
image.setColorTable(palette); image.setColorTable(palette);
image.fill(0);
uchar* bits = image.bits(); uchar* bits = image.bits();
unsigned t = objInfo.tile; unsigned t = objInfo.tile;
for (unsigned y = 0; y < objInfo.height; ++y) { for (unsigned y = 0; y < objInfo.height && t < maxTiles; ++y) {
for (unsigned x = 0; x < objInfo.width; ++x, ++t) { for (unsigned x = 0; x < objInfo.width && t < maxTiles; ++x, ++t) {
compositeTile(static_cast<const void*>(mTileCacheGetVRAM(tileCache, t)), bits, objInfo.width * 8, x * 8, y * 8, objInfo.bits); compositeTile(static_cast<const void*>(mTileCacheGetVRAM(tileCache, t)), bits, objInfo.width * 8, x * 8, y * 8, objInfo.bits);
} }
t += objInfo.stride - objInfo.width; t += objInfo.stride - objInfo.width;

View File

@ -65,7 +65,7 @@ protected:
}; };
static void compositeTile(const void* tile, void* image, size_t stride, size_t x, size_t y, int depth = 8); static void compositeTile(const void* tile, void* image, size_t stride, size_t x, size_t y, int depth = 8);
QImage compositeMap(int map, mMapCacheEntry*); QImage compositeMap(int map, QVector<mMapCacheEntry>*);
QImage compositeObj(const ObjInfo&); QImage compositeObj(const ObjInfo&);
bool lookupObj(int id, struct ObjInfo*); bool lookupObj(int id, struct ObjInfo*);

View File

@ -22,7 +22,7 @@ endif()
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Qt5 COMPONENTS Core Widgets OpenGL Network Multimedia) find_package(Qt5 COMPONENTS Core Widgets Network Multimedia)
if(NOT BUILD_GL AND NOT BUILD_GLES2) if(NOT BUILD_GL AND NOT BUILD_GLES2)
message(WARNING "OpenGL is recommended to build the Qt port") message(WARNING "OpenGL is recommended to build the Qt port")
@ -165,7 +165,7 @@ set(GB_SRC
PrinterView.cpp) PrinterView.cpp)
set(QT_LIBRARIES) set(QT_LIBRARIES)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5,libqt5opengl5") set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5")
set(AUDIO_SRC) set(AUDIO_SRC)
if(BUILD_SDL) if(BUILD_SDL)
@ -233,6 +233,11 @@ if(APPLE)
set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME})
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.endrift.${BINARY_NAME}-qt) set(MACOSX_BUNDLE_GUI_IDENTIFIER com.endrift.${BINARY_NAME}-qt)
set_source_files_properties(${CMAKE_SOURCE_DIR}/res/mgba.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set_source_files_properties(${CMAKE_SOURCE_DIR}/res/mgba.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
if(DISTBUILD)
set(APPDIR ".")
else()
set(APPDIR "Applications")
endif()
endif() endif()
if(WIN32) if(WIN32)
configure_file(${CMAKE_SOURCE_DIR}/res/mgba.rc.in ${CMAKE_BINARY_DIR}/res/mgba.rc) configure_file(${CMAKE_SOURCE_DIR}/res/mgba.rc.in ${CMAKE_BINARY_DIR}/res/mgba.rc)
@ -241,7 +246,7 @@ if(WIN32)
endif() endif()
if(NOT DEFINED DATADIR) if(NOT DEFINED DATADIR)
if(APPLE) if(APPLE)
set(DATADIR Applications/${PROJECT_NAME}.app/Contents/Resources) set(DATADIR ${APPDIR}/${PROJECT_NAME}.app/Contents/Resources)
elseif(WIN32 AND NOT WIN32_UNIX_PATHS) elseif(WIN32 AND NOT WIN32_UNIX_PATHS)
set(DATADIR ".") set(DATADIR ".")
else() else()
@ -297,11 +302,14 @@ set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CM
if(WIN32) if(WIN32)
set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
if(NOT MSVC)
target_link_libraries(${BINARY_NAME}-qt -municode)
endif()
endif() endif()
list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::Network) list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::Network)
if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY) if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY)
list(APPEND QT_LIBRARIES Qt5::OpenGL ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) list(APPEND QT_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
endif() endif()
if(QT_STATIC) if(QT_STATIC)
find_library(QTPCRE NAMES qtpcre2 qtpcre) find_library(QTPCRE NAMES qtpcre2 qtpcre)
@ -328,11 +336,11 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE)
install(TARGETS ${BINARY_NAME}-qt install(TARGETS ${BINARY_NAME}-qt
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt
BUNDLE DESTINATION Applications COMPONENT ${BINARY_NAME}-qt) BUNDLE DESTINATION ${APPDIR} COMPONENT ${BINARY_NAME}-qt)
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
install(FILES ${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop DESTINATION share/applications COMPONENT ${BINARY_NAME}-qt) install(FILES ${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop DESTINATION share/applications COMPONENT ${BINARY_NAME}-qt)
endif() endif()
if(UNIX) if(UNIX AND NOT (APPLE AND DISTBUILD))
install(FILES ${CMAKE_SOURCE_DIR}/doc/mgba-qt.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-qt) install(FILES ${CMAKE_SOURCE_DIR}/doc/mgba-qt.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-qt)
endif() endif()
if(APPLE OR WIN32) if(APPLE OR WIN32)
@ -365,7 +373,7 @@ if(APPLE)
if(DEFINED CROSS_ROOT) if(DEFINED CROSS_ROOT)
set(DEPLOY_OPTIONS ${DEPLOY_OPTIONS} -R "${CROSS_ROOT}") set(DEPLOY_OPTIONS ${DEPLOY_OPTIONS} -R "${CROSS_ROOT}")
endif() endif()
install(CODE "execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/tools/deploy-mac.py\" -v ${DEPLOY_OPTIONS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/Applications/${PROJECT_NAME}.app\")") install(CODE "execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/tools/deploy-mac.py\" -v ${DEPLOY_OPTIONS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${APPDIR}/${PROJECT_NAME}.app\")")
endif() endif()
elseif(WIN32) elseif(WIN32)
if(CMAKE_MAJOR_VERSION EQUAL 3 AND CMAKE_MINOR_VERSION EQUAL 8) if(CMAKE_MAJOR_VERSION EQUAL 3 AND CMAKE_MINOR_VERSION EQUAL 8)

View File

@ -183,13 +183,16 @@ CoreController::CoreController(mCore* core, QObject* parent)
return; return;
} }
} }
message = QString().vsprintf(format, args); va_list argc;
va_copy(argc, args);
message = QString().vsprintf(format, argc);
va_end(argc);
QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message)); QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message));
} }
message = QString().vsprintf(format, args); message = QString().vsprintf(format, args);
QMetaObject::invokeMethod(controller, "logPosted", Q_ARG(int, level), Q_ARG(int, category), Q_ARG(const QString&, message)); QMetaObject::invokeMethod(controller, "logPosted", Q_ARG(int, level), Q_ARG(int, category), Q_ARG(const QString&, message));
if (level == mLOG_FATAL) { if (level == mLOG_FATAL) {
QMetaObject::invokeMethod(controller, "crashed", Q_ARG(const QString&, QString().vsprintf(format, args))); QMetaObject::invokeMethod(controller, "crashed", Q_ARG(const QString&, message));
} }
}; };
} }
@ -799,7 +802,7 @@ void CoreController::scanCard(const QString& path) {
QFile file(path); QFile file(path);
file.open(QIODevice::ReadOnly); file.open(QIODevice::ReadOnly);
m_eReaderData = file.read(2912); m_eReaderData = file.read(2912);
} else if (image.size() == QSize(989, 44)) { } else if (image.size() == QSize(989, 44) || image.size() == QSize(639, 44)) {
const uchar* bits = image.constBits(); const uchar* bits = image.constBits();
size_t size; size_t size;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))

View File

@ -6,6 +6,7 @@
#include "DebuggerConsole.h" #include "DebuggerConsole.h"
#include "DebuggerConsoleController.h" #include "DebuggerConsoleController.h"
#include "GBAApp.h"
#include <QScrollBar> #include <QScrollBar>
@ -18,6 +19,8 @@ DebuggerConsole::DebuggerConsole(DebuggerConsoleController* controller, QWidget*
m_ui.setupUi(this); m_ui.setupUi(this);
m_ui.prompt->installEventFilter(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(m_ui.prompt, &QLineEdit::returnPressed, this, &DebuggerConsole::postLine);
connect(controller, &DebuggerConsoleController::log, this, &DebuggerConsole::log); 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]); m_ui.prompt->setText(m_history[m_history.size() - m_historyOffset]);
} }
return true; return true;
} }

View File

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

View File

@ -50,7 +50,7 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
m_painter = std::make_unique<PainterGL>(windowHandle(), format); m_painter = std::make_unique<PainterGL>(windowHandle(), format);
m_drawThread.setObjectName("Painter Thread"); m_drawThread.setObjectName("Painter Thread");
m_painter->moveToThread(&m_drawThread); m_painter->setThread(&m_drawThread);
connect(&m_drawThread, &QThread::started, m_painter.get(), &PainterGL::create); connect(&m_drawThread, &QThread::started, m_painter.get(), &PainterGL::create);
connect(m_painter.get(), &PainterGL::started, this, [this] { connect(m_painter.get(), &PainterGL::started, this, [this] {
@ -158,7 +158,6 @@ void DisplayGL::stopDrawing() {
void DisplayGL::pauseDrawing() { void DisplayGL::pauseDrawing() {
if (m_hasStarted) { if (m_hasStarted) {
m_isDrawing = false; m_isDrawing = false;
CoreController::Interrupter interrupter(m_context);
QMetaObject::invokeMethod(m_painter.get(), "pause", Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(m_painter.get(), "pause", Qt::BlockingQueuedConnection);
#ifndef Q_OS_MAC #ifndef Q_OS_MAC
setUpdatesEnabled(true); setUpdatesEnabled(true);
@ -169,7 +168,6 @@ void DisplayGL::pauseDrawing() {
void DisplayGL::unpauseDrawing() { void DisplayGL::unpauseDrawing() {
if (m_hasStarted) { if (m_hasStarted) {
m_isDrawing = true; m_isDrawing = true;
CoreController::Interrupter interrupter(m_context);
QMetaObject::invokeMethod(m_painter.get(), "unpause", Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(m_painter.get(), "unpause", Qt::BlockingQueuedConnection);
#ifndef Q_OS_MAC #ifndef Q_OS_MAC
setUpdatesEnabled(false); setUpdatesEnabled(false);
@ -265,6 +263,8 @@ PainterGL::PainterGL(QWindow* surface, const QSurfaceFormat& format)
for (auto& buf : m_buffers) { for (auto& buf : m_buffers) {
m_free.append(&buf.front()); m_free.append(&buf.front());
} }
connect(&m_drawTimer, &QTimer::timeout, this, &PainterGL::draw);
m_drawTimer.setSingleShot(true);
} }
PainterGL::~PainterGL() { PainterGL::~PainterGL() {
@ -273,6 +273,11 @@ PainterGL::~PainterGL() {
} }
} }
void PainterGL::setThread(QThread* thread) {
moveToThread(thread);
m_drawTimer.moveToThread(thread);
}
void PainterGL::makeCurrent() { void PainterGL::makeCurrent() {
m_gl->makeCurrent(m_surface); m_gl->makeCurrent(m_surface);
#if defined(_WIN32) && defined(USE_EPOXY) #if defined(_WIN32) && defined(USE_EPOXY)
@ -368,6 +373,10 @@ void PainterGL::resizeContext() {
m_interrupter.resume(); m_interrupter.resume();
QSize size = m_context->screenDimensions(); QSize size = m_context->screenDimensions();
if (m_dims == size) {
return;
}
dequeueAll(false);
m_backend->setDimensions(m_backend, size.width(), size.height()); m_backend->setDimensions(m_backend, size.width(), size.height());
} }
@ -425,40 +434,45 @@ void PainterGL::start() {
} }
void PainterGL::draw() { void PainterGL::draw() {
if (!m_active || m_queue.isEmpty()) { if (!m_started || m_queue.isEmpty()) {
return;
}
if (m_lagging >= 1) {
return; return;
} }
mCoreSync* sync = &m_context->thread()->impl->sync; mCoreSync* sync = &m_context->thread()->impl->sync;
if (!mCoreSyncWaitFrameStart(sync)) { if (!mCoreSyncWaitFrameStart(sync)) {
mCoreSyncWaitFrameEnd(sync); mCoreSyncWaitFrameEnd(sync);
++m_lagging; if (!sync->audioWait && !sync->videoFrameWait) {
if ((sync->audioWait || sync->videoFrameWait) && m_delayTimer.elapsed() < 1000 / m_surface->screen()->refreshRate()) { return;
QTimer::singleShot(1, this, &PainterGL::draw); }
if (m_delayTimer.elapsed() >= 1000 / m_surface->screen()->refreshRate()) {
return;
}
if (!m_drawTimer.isActive()) {
m_drawTimer.start(1);
} }
return; return;
} }
dequeue(); dequeue();
if (m_videoProxy) { bool forceRedraw = !m_videoProxy;
// Only block on the next frame if we're trying to run at roughly 60fps via audio
m_videoProxy->setBlocking(sync->audioWait && std::abs(60.f - sync->fpsTarget) < 0.1f);
}
if (!m_delayTimer.isValid()) { if (!m_delayTimer.isValid()) {
m_delayTimer.start(); m_delayTimer.start();
} else { } else {
if (sync->audioWait || sync->videoFrameWait) { 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); QThread::usleep(500);
} }
forceRedraw = sync->videoFrameWait;
}
if (!forceRedraw) {
forceRedraw = m_delayTimer.nsecsElapsed() + 1'000'000 >= 1'000'000'000 / m_surface->screen()->refreshRate();
} }
m_delayTimer.restart();
} }
mCoreSyncWaitFrameEnd(sync); mCoreSyncWaitFrameEnd(sync);
performDraw(); if (forceRedraw) {
m_backend->swap(m_backend); m_delayTimer.restart();
performDraw();
m_backend->swap(m_backend);
}
} }
void PainterGL::forceDraw() { void PainterGL::forceDraw() {
@ -473,9 +487,10 @@ void PainterGL::forceDraw() {
} }
void PainterGL::stop() { void PainterGL::stop() {
m_drawTimer.stop();
m_active = false; m_active = false;
m_started = false; m_started = false;
dequeueAll(); dequeueAll(false);
if (m_context) { if (m_context) {
if (m_videoProxy) { if (m_videoProxy) {
m_videoProxy->detach(m_context.get()); m_videoProxy->detach(m_context.get());
@ -496,28 +511,27 @@ void PainterGL::stop() {
} }
void PainterGL::pause() { void PainterGL::pause() {
m_drawTimer.stop();
m_active = false; m_active = false;
dequeueAll(true);
} }
void PainterGL::unpause() { void PainterGL::unpause() {
m_lagging = 0;
m_active = true; m_active = true;
} }
void PainterGL::performDraw() { void PainterGL::performDraw() {
m_painter.begin(m_window.get());
m_painter.beginNativePainting();
float r = m_surface->devicePixelRatio(); float r = m_surface->devicePixelRatio();
m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r); m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r);
if (m_buffer) { if (m_buffer) {
m_backend->postFrame(m_backend, m_buffer); m_backend->postFrame(m_backend, m_buffer);
} }
m_backend->drawFrame(m_backend); m_backend->drawFrame(m_backend);
m_painter.endNativePainting();
if (m_showOSD && m_messagePainter) { if (m_showOSD && m_messagePainter) {
m_painter.begin(m_window.get());
m_messagePainter->paint(&m_painter); m_messagePainter->paint(&m_painter);
m_painter.end();
} }
m_painter.end();
} }
void PainterGL::enqueue(const uint32_t* backing) { void PainterGL::enqueue(const uint32_t* backing) {
@ -534,7 +548,6 @@ void PainterGL::enqueue(const uint32_t* backing) {
memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL); memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL);
} }
} }
m_lagging = 0;
m_queue.enqueue(buffer); m_queue.enqueue(buffer);
} }
@ -551,23 +564,24 @@ void PainterGL::dequeue() {
m_buffer = buffer; m_buffer = buffer;
} }
void PainterGL::dequeueAll() { void PainterGL::dequeueAll(bool keep) {
QMutexLocker locker(&m_mutex);
uint32_t* buffer = 0; uint32_t* buffer = 0;
m_mutex.lock();
while (!m_queue.isEmpty()) { while (!m_queue.isEmpty()) {
buffer = m_queue.dequeue(); buffer = m_queue.dequeue();
if (buffer) { if (keep) {
if (m_buffer && buffer) {
m_free.append(m_buffer);
m_buffer = buffer;
}
} else if (buffer) {
m_free.append(buffer); m_free.append(buffer);
} }
} }
if (buffer) { if (m_buffer && !keep) {
m_backend->postFrame(m_backend, buffer);
}
if (m_buffer) {
m_free.append(m_buffer); m_free.append(m_buffer);
m_buffer = nullptr; m_buffer = nullptr;
} }
m_mutex.unlock();
} }
void PainterGL::setVideoProxy(std::shared_ptr<VideoProxy> proxy) { void PainterGL::setVideoProxy(std::shared_ptr<VideoProxy> proxy) {

View File

@ -96,11 +96,13 @@ public:
PainterGL(QWindow* surface, const QSurfaceFormat& format); PainterGL(QWindow* surface, const QSurfaceFormat& format);
~PainterGL(); ~PainterGL();
void setThread(QThread*);
void setContext(std::shared_ptr<CoreController>); void setContext(std::shared_ptr<CoreController>);
void setMessagePainter(MessagePainter*); void setMessagePainter(MessagePainter*);
void enqueue(const uint32_t* backing); void enqueue(const uint32_t* backing);
bool supportsShaders() const { return m_supportsShaders; } bool supportsShaders() const { return m_supportsShaders; }
int glTex();
void setVideoProxy(std::shared_ptr<VideoProxy>); void setVideoProxy(std::shared_ptr<VideoProxy>);
void interrupt(); void interrupt();
@ -127,8 +129,6 @@ public slots:
void clearShaders(); void clearShaders();
VideoShader* shaders(); VideoShader* shaders();
int glTex();
signals: signals:
void started(); void started();
@ -136,13 +136,12 @@ private:
void makeCurrent(); void makeCurrent();
void performDraw(); void performDraw();
void dequeue(); void dequeue();
void dequeueAll(); void dequeueAll(bool keep = false);
std::array<std::array<uint32_t, 0x100000>, 3> m_buffers; std::array<std::array<uint32_t, 0x100000>, 3> m_buffers;
QList<uint32_t*> m_free; QList<uint32_t*> m_free;
QQueue<uint32_t*> m_queue; QQueue<uint32_t*> m_queue;
QAtomicInt m_lagging = 0; uint32_t* m_buffer = nullptr;
uint32_t* m_buffer;
QPainter m_painter; QPainter m_painter;
QMutex m_mutex; QMutex m_mutex;
QWindow* m_surface; QWindow* m_surface;
@ -151,6 +150,7 @@ private:
std::unique_ptr<QOpenGLContext> m_gl; std::unique_ptr<QOpenGLContext> m_gl;
bool m_active = false; bool m_active = false;
bool m_started = false; bool m_started = false;
QTimer m_drawTimer;
std::shared_ptr<CoreController> m_context; std::shared_ptr<CoreController> m_context;
CoreController::Interrupter m_interrupter; CoreController::Interrupter m_interrupter;
bool m_supportsShaders; bool m_supportsShaders;
@ -158,6 +158,7 @@ private:
VideoShader m_shader{}; VideoShader m_shader{};
VideoBackend* m_backend = nullptr; VideoBackend* m_backend = nullptr;
QSize m_size; QSize m_size;
QSize m_dims;
MessagePainter* m_messagePainter = nullptr; MessagePainter* m_messagePainter = nullptr;
QElapsedTimer m_delayTimer; QElapsedTimer m_delayTimer;
std::shared_ptr<VideoProxy> m_videoProxy; std::shared_ptr<VideoProxy> m_videoProxy;

View File

@ -245,7 +245,7 @@ void FrameView::updateTilesGBA(bool) {
m_queue.append({ m_queue.append({
{ LayerId::BACKGROUND, bg }, { LayerId::BACKGROUND, bg },
!m_disabled.contains({ LayerId::BACKGROUND, bg }), !m_disabled.contains({ LayerId::BACKGROUND, bg }),
QPixmap::fromImage(compositeMap(bg, m_mapStatus[bg])), QPixmap::fromImage(compositeMap(bg, &m_mapStatus[bg])),
{}, offset, true, false {}, offset, true, false
}); });
if (m_queue.back().image.hasAlpha()) { if (m_queue.back().image.hasAlpha()) {

View File

@ -108,7 +108,7 @@ private:
QSet<LayerId> m_disabled; QSet<LayerId> m_disabled;
QPixmap m_composited; QPixmap m_composited;
QPixmap m_rendered; QPixmap m_rendered;
mMapCacheEntry m_mapStatus[4][128 * 128] = {}; // TODO: Correct size QVector<mMapCacheEntry> m_mapStatus[4];
ColorPicker m_backdropPicker; ColorPicker m_backdropPicker;
QColor m_overrideBackdrop; QColor m_overrideBackdrop;

View File

@ -6,6 +6,7 @@
#include "GBAOverride.h" #include "GBAOverride.h"
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/internal/gba/gba.h>
using namespace QGBA; using namespace QGBA;
@ -13,7 +14,11 @@ void GBAOverride::apply(struct mCore* core) {
if (core->platform(core) != mPLATFORM_GBA) { if (core->platform(core) != mPLATFORM_GBA) {
return; return;
} }
GBAOverrideApply(static_cast<GBA*>(core->board), &override); GBA* gba = static_cast<GBA*>(core->board);
if (!vbaBugCompatSet) {
override.vbaBugCompat = gba->vbaBugCompat;
}
GBAOverrideApply(gba, &override);
} }
void GBAOverride::identify(const struct mCore* core) { void GBAOverride::identify(const struct mCore* core) {

View File

@ -18,6 +18,7 @@ public:
void save(struct Configuration*) const override; void save(struct Configuration*) const override;
struct GBACartridgeOverride override; struct GBACartridgeOverride override;
bool vbaBugCompatSet;
}; };
} }

View File

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

View File

@ -98,19 +98,19 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
image->image.load(":/res/no-cam.png"); image->image.load(":/res/no-cam.png");
} }
#ifdef BUILD_QT_MULTIMEDIA #ifdef BUILD_QT_MULTIMEDIA
if (image->p->m_config->getQtOption("cameraDriver").toInt() == static_cast<int>(CameraDriver::QT_MULTIMEDIA)) { image->p->m_cameraActive = true;
QByteArray camera = image->p->m_config->getQtOption("camera").toByteArray(); QByteArray camera = image->p->m_config->getQtOption("camera").toByteArray();
if (!camera.isNull()) { if (!camera.isNull()) {
QMetaObject::invokeMethod(image->p, "setCamera", Q_ARG(QByteArray, camera)); image->p->m_cameraDevice = camera;
}
QMetaObject::invokeMethod(image->p, "setupCam");
} }
QMetaObject::invokeMethod(image->p, "setupCam");
#endif #endif
}; };
m_image.stopRequestImage = [](mImageSource* context) { m_image.stopRequestImage = [](mImageSource* context) {
InputControllerImage* image = static_cast<InputControllerImage*>(context); InputControllerImage* image = static_cast<InputControllerImage*>(context);
#ifdef BUILD_QT_MULTIMEDIA #ifdef BUILD_QT_MULTIMEDIA
image->p->m_cameraActive = false;
QMetaObject::invokeMethod(image->p, "teardownCam"); QMetaObject::invokeMethod(image->p, "teardownCam");
#endif #endif
}; };
@ -766,10 +766,18 @@ void InputController::setLuminanceValue(uint8_t value) {
void InputController::setupCam() { void InputController::setupCam() {
#ifdef BUILD_QT_MULTIMEDIA #ifdef BUILD_QT_MULTIMEDIA
if (m_config->getQtOption("cameraDriver").toInt() != static_cast<int>(CameraDriver::QT_MULTIMEDIA)) {
return;
}
if (!m_camera) { if (!m_camera) {
m_camera = std::make_unique<QCamera>(); m_camera = std::make_unique<QCamera>(m_cameraDevice);
connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection); connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection);
} }
if (m_camera->status() == QCamera::UnavailableStatus) {
m_camera.reset();
return;
}
m_camera->setCaptureMode(QCamera::CaptureVideo); m_camera->setCaptureMode(QCamera::CaptureVideo);
m_camera->setViewfinder(&m_videoDumper); m_camera->setViewfinder(&m_videoDumper);
m_camera->load(); m_camera->load();
@ -820,20 +828,22 @@ void InputController::prepareCamSettings(QCamera::Status status) {
void InputController::teardownCam() { void InputController::teardownCam() {
#ifdef BUILD_QT_MULTIMEDIA #ifdef BUILD_QT_MULTIMEDIA
if (m_camera) { if (m_camera) {
m_camera->stop(); m_camera->unload();
m_camera.reset();
} }
#endif #endif
} }
void InputController::setCamera(const QByteArray& name) { void InputController::setCamera(const QByteArray& name) {
#ifdef BUILD_QT_MULTIMEDIA #ifdef BUILD_QT_MULTIMEDIA
bool needsRestart = false; if (m_cameraDevice == name) {
if (m_camera) { return;
needsRestart = m_camera->state() == QCamera::ActiveState;
} }
m_camera = std::make_unique<QCamera>(name); m_cameraDevice = name;
connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection); if (m_camera && m_camera->state() == QCamera::ActiveState) {
if (needsRestart) { teardownCam();
}
if (m_cameraActive) {
setupCam(); setupCam();
} }
#endif #endif

View File

@ -158,6 +158,8 @@ private:
} m_image; } m_image;
#ifdef BUILD_QT_MULTIMEDIA #ifdef BUILD_QT_MULTIMEDIA
bool m_cameraActive = false;
QByteArray m_cameraDevice;
std::unique_ptr<QCamera> m_camera; std::unique_ptr<QCamera> m_camera;
VideoDumper m_videoDumper; VideoDumper m_videoDumper;
#endif #endif

View File

@ -27,6 +27,9 @@ QVariant LogConfigModel::data(const QModelIndex& index, int role) const {
} }
int levels; int levels;
if (index.row() == 0) { if (index.row() == 0) {
if (index.column() == 0) {
return QVariant();
}
levels = m_levels; levels = m_levels;
} else { } else {
levels = m_cache[index.row() - 1].levels; levels = m_cache[index.row() - 1].levels;
@ -46,6 +49,9 @@ bool LogConfigModel::setData(const QModelIndex& index, const QVariant& value, in
} }
int levels; int levels;
if (index.row() == 0) { if (index.row() == 0) {
if (index.column() == 0) {
return false;
}
levels = m_levels; levels = m_levels;
} else { } else {
levels = m_cache[index.row() - 1].levels; levels = m_cache[index.row() - 1].levels;
@ -60,7 +66,7 @@ bool LogConfigModel::setData(const QModelIndex& index, const QVariant& value, in
if (value.value<Qt::CheckState>() == Qt::Unchecked) { if (value.value<Qt::CheckState>() == Qt::Unchecked) {
levels &= ~bit; levels &= ~bit;
} else { } else {
levels |= bit; levels |= bit;
} }
} }
if (index.row() == 0) { if (index.row() == 0) {
@ -132,10 +138,10 @@ int LogConfigModel::rowCount(const QModelIndex& parent) const {
} }
Qt::ItemFlags LogConfigModel::flags(const QModelIndex& index) const { Qt::ItemFlags LogConfigModel::flags(const QModelIndex& index) const {
if (!index.isValid()) { if (!index.isValid() || (index.row() == 0 && index.column() == 0)) {
return 0; return 0;
} }
return Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsEnabled; return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
} }
void LogConfigModel::reset() { void LogConfigModel::reset() {

View File

@ -116,12 +116,18 @@ void MapView::selectMap(int map) {
return; return;
} }
m_map = map; m_map = map;
m_mapStatus.fill({});
updateTiles(true); updateTiles(true);
} }
void MapView::selectTile(int x, int y) { void MapView::selectTile(int x, int y) {
CoreController::Interrupter interrupter(m_controller); CoreController::Interrupter interrupter(m_controller);
mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, m_map); mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, m_map);
int tiles = mMapCacheTileCount(mapCache);
if (m_mapStatus.size() != tiles) {
m_mapStatus.resize(tiles);
m_mapStatus.fill({});
}
size_t tileCache = mTileCacheSetIndex(&m_cacheSet->tiles, mapCache->tileCache); size_t tileCache = mTileCacheSetIndex(&m_cacheSet->tiles, mapCache->tileCache);
m_ui.tile->setBoundary(m_boundary, tileCache, tileCache); m_ui.tile->setBoundary(m_boundary, tileCache, tileCache);
uint32_t location = mMapCacheTileId(mapCache, x, y); uint32_t location = mMapCacheTileId(mapCache, x, y);
@ -221,7 +227,7 @@ void MapView::updateTilesGBA(bool) {
m_rawMap = QImage(QSize(width, height), QImage::Format_ARGB32); m_rawMap = QImage(QSize(width, height), QImage::Format_ARGB32);
uchar* bgBits = m_rawMap.bits(); uchar* bgBits = m_rawMap.bits();
for (int j = 0; j < height; ++j) { for (int j = 0; j < height; ++j) {
mBitmapCacheCleanRow(bitmapCache, m_bitmapStatus, j); mBitmapCacheCleanRow(bitmapCache, m_bitmapStatus.data(), j);
memcpy(static_cast<void*>(&bgBits[width * j * 4]), mBitmapCacheGetRow(bitmapCache, j), width * 4); memcpy(static_cast<void*>(&bgBits[width * j * 4]), mBitmapCacheGetRow(bitmapCache, j), width * 4);
} }
m_rawMap = m_rawMap.convertToFormat(QImage::Format_RGB32).rgbSwapped(); m_rawMap = m_rawMap.convertToFormat(QImage::Format_RGB32).rgbSwapped();
@ -239,7 +245,7 @@ void MapView::updateTilesGBA(bool) {
m_ui.bgInfo->setCustomProperty("priority", priority); m_ui.bgInfo->setCustomProperty("priority", priority);
m_ui.bgInfo->setCustomProperty("offset", offset); m_ui.bgInfo->setCustomProperty("offset", offset);
m_ui.bgInfo->setCustomProperty("transform", transform); m_ui.bgInfo->setCustomProperty("transform", transform);
m_rawMap = compositeMap(m_map, m_mapStatus); m_rawMap = compositeMap(m_map, &m_mapStatus);
} }
} }
QPixmap map = QPixmap::fromImage(m_rawMap.convertToFormat(QImage::Format_RGB32)); QPixmap map = QPixmap::fromImage(m_rawMap.convertToFormat(QImage::Format_RGB32));

View File

@ -43,8 +43,8 @@ private:
Ui::MapView m_ui; Ui::MapView m_ui;
std::shared_ptr<CoreController> m_controller; std::shared_ptr<CoreController> m_controller;
mMapCacheEntry m_mapStatus[128 * 128] = {}; // TODO: Correct size QVector<mMapCacheEntry> m_mapStatus;
mBitmapCacheEntry m_bitmapStatus[512 * 2] = {}; // TODO: Correct size QVector<mBitmapCacheEntry> m_bitmapStatus{512 * 2}; // TODO: Correct size
int m_map = 0; int m_map = 0;
QImage m_rawMap; QImage m_rawMap;
int m_boundary; int m_boundary;

View File

@ -131,15 +131,19 @@ void ObjView::updateTilesGBA(bool force) {
m_objInfo = newInfo; m_objInfo = newInfo;
m_tileOffset = newInfo.tile; m_tileOffset = newInfo.tile;
mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, newInfo.paletteSet); mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, newInfo.paletteSet);
unsigned maxTiles = mTileCacheSystemInfoGetMaxTiles(tileCache->sysConfig);
int i = 0; int i = 0;
for (unsigned y = 0; y < newInfo.height; ++y) { for (unsigned y = 0; y < newInfo.height; ++y) {
for (unsigned x = 0; x < newInfo.width; ++x, ++i, ++tile, ++tileBase) { for (unsigned x = 0; x < newInfo.width; ++x, ++i, ++tile, ++tileBase) {
const color_t* data = mTileCacheGetTileIfDirty(tileCache, &m_tileStatus[16 * tileBase], tile, newInfo.paletteId); if (tile < maxTiles) {
if (data) { const color_t* data = mTileCacheGetTileIfDirty(tileCache, &m_tileStatus[16 * tileBase], tile, newInfo.paletteId);
m_ui.tiles->setTile(i, data); if (data) {
} else if (force) { m_ui.tiles->setTile(i, data);
m_ui.tiles->setTile(i, mTileCacheGetTile(tileCache, tile, newInfo.paletteId)); } else if (force) {
m_ui.tiles->setTile(i, mTileCacheGetTile(tileCache, tile, newInfo.paletteId));
}
} else {
m_ui.tiles->clearTile(i);
} }
} }
tile += newInfo.stride - newInfo.width; tile += newInfo.stride - newInfo.width;

View File

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

View File

@ -144,7 +144,11 @@ void OverrideView::updateOverrides() {
gba->override.idleLoop = IDLE_LOOP_NONE; gba->override.idleLoop = IDLE_LOOP_NONE;
gba->override.mirroring = false; gba->override.mirroring = false;
gba->override.vbaBugCompat = false; gba->override.vbaBugCompat = false;
gba->vbaBugCompatSet = false;
if (gba->override.savetype != SAVEDATA_AUTODETECT) {
hasOverride = true;
}
if (!m_ui.hwAutodetect->isChecked()) { if (!m_ui.hwAutodetect->isChecked()) {
hasOverride = true; hasOverride = true;
gba->override.hardware = HW_NONE; gba->override.hardware = HW_NONE;
@ -168,9 +172,10 @@ void OverrideView::updateOverrides() {
hasOverride = true; hasOverride = true;
gba->override.hardware |= HW_GB_PLAYER_DETECTION; gba->override.hardware |= HW_GB_PLAYER_DETECTION;
} }
if (m_ui.vbaBugCompat->isChecked()) { if (m_ui.vbaBugCompat->checkState() != Qt::PartiallyChecked) {
hasOverride = true; hasOverride = true;
gba->override.vbaBugCompat = true; gba->vbaBugCompatSet = true;
gba->override.vbaBugCompat = m_ui.vbaBugCompat->isChecked();
} }
bool ok; bool ok;
@ -270,7 +275,7 @@ void OverrideView::gameStopped() {
m_ui.tabWidget->setEnabled(true); m_ui.tabWidget->setEnabled(true);
m_ui.savetype->setCurrentIndex(0); m_ui.savetype->setCurrentIndex(0);
m_ui.idleLoop->clear(); m_ui.idleLoop->clear();
m_ui.vbaBugCompat->setChecked(false); m_ui.vbaBugCompat->setCheckState(Qt::PartiallyChecked);
m_ui.mbc->setCurrentIndex(0); m_ui.mbc->setCurrentIndex(0);
m_ui.gbModel->setCurrentIndex(0); m_ui.gbModel->setCurrentIndex(0);

View File

@ -143,7 +143,12 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>EEPROM</string> <string>EEPROM 8kB</string>
</property>
</item>
<item>
<property name="text">
<string>EEPROM 512 bytes</string>
</property> </property>
</item> </item>
</widget> </widget>
@ -186,6 +191,12 @@
<property name="text"> <property name="text">
<string>VBA bug compatibility mode</string> <string>VBA bug compatibility mode</string>
</property> </property>
<property name="tristate">
<bool>true</bool>
</property>
<property name="checkState">
<enum>Qt::PartiallyChecked</enum>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>

Some files were not shown because too many files have changed in this diff Show More