Compare commits

...

266 Commits

Author SHA1 Message Date
Vicki Pfau 26b7884bc2 CHANGES: Update for 0.10.5 2025-03-08 19:09:26 -08:00
Vicki Pfau b9b090c566 CHANGES: Fix ordering 2025-02-28 21:44:15 -08:00
Vicki Pfau 5e8c47ecb1 Util: Cap internal buffer size when unzipping files (fixes #3404) 2025-02-28 21:41:34 -08:00
Vicki Pfau 406a202157 GBA Cheats: Let VBA-style codes patch ROM (fixes #3423) 2025-02-28 21:41:34 -08:00
Vicki Pfau 332b1b8bc3 GB: Allow use of CGB-E and AGB-0 BIOS versions (closes #3427) 2025-02-27 02:39:07 -08:00
Vicki Pfau 7cfe792559 res: Add Game Boy/Color metadata to .desktop file 2025-02-23 23:24:23 -08:00
Vicki Pfau e19f35e7e4 ARM Debugger: Fix disassembly of ror r0 barrel shift (fixes #3412) 2025-02-11 05:17:02 -08:00
Vicki Pfau 56b59c767b CMake: Default macOS SDK version to OS version if not found 2025-02-11 05:15:34 -08:00
Vicki Pfau 0670687b86 FFmpeg: Use avcodec_get_supported_config when present 2025-02-11 05:14:03 -08:00
Vicki Pfau 196adf233c GB Core: Fix cloning savedata when backing file is outdated (fixes #3388) 2024-12-31 01:46:12 -08:00
Vicki Pfau dbae6e1eef macOS: Declare camera usage in Info.plist 2024-12-29 17:39:25 -08:00
Vicki Pfau 5e4b91c94f CMake: Fix entitlements plist filename 2024-12-29 17:39:08 -08:00
Vicki Pfau afdf3b0b88 Updater: Fix rewriting folders and files on Windows (fixes #3384) 2024-12-28 22:45:39 -08:00
Vicki Pfau f40ea09127 FFmpeg: Fix failing to record videos with CRF video (fixes #3368) 2024-12-24 18:54:35 -08:00
Vicki Pfau 7e1e7d2253 GBA Core: Fix booting into BIOS when skip BIOS is enabled 2024-12-20 02:16:04 -08:00
Vicki Pfau c9083715e6 GBA Savedata: Demote savegme time offset message to DEBUG level 2024-12-20 02:15:14 -08:00
Vicki Pfau eecdfd0c77 Qt: Skip processing QLocale::C if present 2024-12-20 02:15:09 -08:00
Vicki Pfau 20da804e65 Qt: Fix up how the language names get displayed 2024-12-20 02:15:04 -08:00
Vicki Pfau e4dc41df31 Qt: Rename LatAm Spanish to just "Spanish" 2024-12-20 02:14:58 -08:00
Vicki Pfau 9a1d2442a3 GBA Hardware: Fix loading states unconditionally overwriting GPIO memory 2024-12-11 16:29:44 -08:00
Vicki Pfau 6c2da7b621 CHANGES: Update for 0.10.4 2024-12-07 20:45:16 -08:00
Vicki Pfau 9a38ba4913 Qt: Fix installer updates if a version number is in the filename (fixes #3109) 2024-11-24 17:55:04 -08:00
Vicki Pfau 2f880b17ca CMake: Set both -mmacosx-version-min and CMAKE_OSX_DEPLOYMENT_TARGET 2024-11-24 17:05:09 -08:00
Bo He bbf601da02 associate windows with the owning application 2024-11-24 17:03:46 -08:00
Jan200101 b51e58640f Qt: Fix potential crash on Wayland with OpenGL (fixes #3276) 2024-11-01 02:13:47 -07:00
Vicki Pfau 30897fee62 Qt: Fix saving named states breaking when screenshot states disabled (fixes #3320) 2024-10-20 18:04:56 -07:00
Vicki Pfau c143851916 Core: Fix patch autoloading leaking the file handle 2024-10-08 04:44:35 -07:00
Vicki Pfau 10eb8c57d2 Core: Add sanity check for mDirectorySetOpenSuffix 2024-10-04 17:17:47 -07:00
Vicki Pfau 952c883755 Core: Check for null when autoloading/saving cheats 2024-10-04 17:14:17 -07:00
CasualPokePlayer 7ae9117930 GBA Serialize: Properly restore GPIO register state (fixes #3294) 2024-09-29 01:02:29 -07:00
Vicki Pfau 07ac520a9a 3DS: Fix build 2024-09-21 23:16:37 -07:00
Vicki Pfau f778cf4749 GBA I/O: Fix audio register 8-bit write behavior (fixes #3086) 2024-09-21 23:08:13 -07:00
Vicki Pfau 9271198f64 Qt: Make window corners square on Windows 11 (fixes #3285) 2024-09-21 23:01:16 -07:00
Vicki Pfau 16f7a05b1a Appveyor: Use Windows 11 SDK 2024-09-21 23:00:04 -07:00
Vicki Pfau a66ad9c2c8 Qt: Fix how LatAm Spanish is shown in settings 2024-09-16 02:53:46 -07:00
Vicki Pfau 3ae88949a3 Core: Extdata should not have a size < 0 2024-09-16 02:52:07 -07:00
Vicki Pfau ffe539343e No-Intro: Prevent database from being downgraded 2024-09-16 02:51:04 -07:00
Vicki Pfau 00426ad6d1 GB Serialize: Prevent loading invalid states where LY >= 144 in modes other than 1 2024-07-19 20:14:40 -07:00
Adam Higerd 7d8b029282 Don't try to load non-.sym files as ARMIPS symbols 2024-07-19 19:43:39 -07:00
Vicki Pfau 67b0a27d88 GBA Core: Fix crash after loading debug symbols (fixes #3254) 2024-07-11 14:09:46 -07:00
Vicki Pfau d2a8b43ae0 GBA I/O: Fix HALTCNT access behavior (fixes #2309) 2024-07-09 20:17:22 -07:00
Vicki Pfau ebad7a380b GB I/O: Fix STAT writing IRQ trigger conditions (fixes #2501) 2024-07-07 11:26:36 -07:00
Vicki Pfau a0f6255a0b Switch: Add bilinear filtering option (closes #3111) 2024-07-07 11:24:26 -07:00
Vicki Pfau 154fe6b344 GB: Fix uninitialized save data when loading undersized temporary saves 2024-07-07 11:24:26 -07:00
Vicki Pfau 5ac23cfe4f GBA Core: Fix loading symbols from ELF files if the file doesn't end with .elf 2024-07-07 11:24:26 -07:00
Vicki Pfau 2c2eab2b94 GB, GBA Core: Fix memory leak if reloading debug symbols
Also fix loading an ELF if the current seek is not 0
2024-07-07 11:24:26 -07:00
Vicki Pfau ee2533831d Util: Add THREAD_EXIT macro 2024-07-07 11:24:26 -07:00
Vicki Pfau b205537558 GBA Audio: Fix crash in audio subsampling if timing lockstep breaks 2024-06-25 04:09:36 -07:00
Vicki Pfau e0b0e45821 GBA Audio: Fix crash if audio FIFOs and timers get out of sync 2024-06-25 03:46:31 -07:00
Vicki Pfau 832c0aed27 Windows: Inno Setup fixes
Update minimum Windows version to 7, add DisableDirPage=no
2024-06-25 03:44:41 -07:00
Vicki Pfau f8ece1ed04 Third-Party: Cherry-pick rapidjson buildfix 2024-05-25 01:33:47 -07:00
Vicki Pfau 95c2e2fbe6 GBA SIO: Remove erroneous RCNT setting 2024-05-24 22:28:55 -07:00
Vicki Pfau 40dcea36c0 Updater: Fix build 2024-05-24 20:34:16 -07:00
Vicki Pfau 21d76adf66 Updater: Fix existing directory update logic 2024-05-24 20:32:47 -07:00
Vicki Pfau 8f648b6808 Updater: Fix overwriting files with directories 2024-05-24 20:32:47 -07:00
Vicki Pfau b342ecb76f Updater: Fix MSVC build 2024-05-24 20:32:09 -07:00
Vicki Pfau 63c0bedf44 Qt: Fix LCDC background priority/enable bit being mis-mapped in I/O view 2024-05-12 02:51:23 -07:00
Vicki Pfau ff00fed81c Qt: Better fix for I/O viewer 2024-05-12 02:51:23 -07:00
Vicki Pfau a2493a7bf3 GBA Memory: Let raw access read high MMIO addresses 2024-05-11 21:50:57 -07:00
Vicki Pfau b1c7c6d14a Qt: Fix crash when applying changes to GB I/O registers in I/O view 2024-05-11 21:41:37 -07:00
Vicki Pfau 16a565dc6b Debugger: Actually handle parseLexedExpression returning false 2024-04-24 23:21:02 -07:00
Vicki Pfau 0ca0ea47d4 Qt: Update translation (Chinese (Simplified))
Translation: mGBA/Qt
Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/zh_Hans/
2024-04-08 15:48:23 -07:00
Vicki Pfau 455d424685 CMakeLists: Fix strtof_l detection logic 2024-04-08 02:11:31 -07:00
Vicki Pfau 99bae2dc06 GBA GPIO: Fix gyro read-out start (fixes #3141) 2024-04-05 00:24:28 -07:00
Vicki Pfau 34b1f0b3a4 Core: Add missing defines to flags.h 2024-04-01 16:46:38 -07:00
Vicki Pfau c2907e8286 CMake: Bump version 2024-03-19 00:15:20 -07:00
Vicki Pfau 557a3b67db GB Audio: Fix audio envelope timing resetting too often (fixes #3164) 2024-03-17 20:02:30 -07:00
TuxSH 950e576f09 Fix menu bug that allowed the selection of invalid states when wrap=true 2024-03-17 20:02:13 -07:00
Vicki Pfau 2ab7500e3e Qt: Fix uninitialized variable 2024-03-17 20:02:13 -07:00
Vicki Pfau 5aa7c955f1 Updater: Fix updating appimage across filesystems 2024-03-17 20:01:44 -07:00
Vicki Pfau 32149f0e24 VFS: Fix UB 2024-03-17 19:56:27 -07:00
unknown 1c058b596e Qt GDB Window defaults to loopback address 2024-03-17 19:55:38 -07:00
unknown 3253c5dd32 GBA Debugger defaults to loopback address 2024-03-17 19:55:38 -07:00
Vicki Pfau 858c94e5ee GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes #3110) 2024-01-21 17:45:06 -08:00
Vicki Pfau a2b7b19d89 Vita: Add imc0 and xmc0 mount point support 2024-01-21 03:39:36 -08:00
Vicki Pfau 48350b1fd3 VFS: Modernize VDeviceList implementation 2024-01-21 03:38:44 -08:00
Vicki Pfau 1c61b54208 CHANGES: Update for 0.10.3 2024-01-07 19:17:00 -08:00
Vicki Pfau 15ec8eb795 Res: Update patrons.txt 2024-01-07 19:15:52 -08:00
Vicki Pfau 383dcda998 Res: Update nointro.dat 2024-01-07 19:13:02 -08:00
Vicki Pfau e1a5329f30 Qt: Fix screen freezing on macOS after closing save state window (fixes #2885) 2024-01-07 16:58:30 -08:00
Vicki Pfau 0227d2db39 VFS: Use anonymousMemoryMap for large 7z allocations (fixes #3013) 2023-12-21 22:58:36 -08:00
Vicki Pfau 3f4cb4dbcb GB Audio: Improve "zombie mode" emulation in CGB mode (fixes #2029) 2023-12-20 02:53:21 -08:00
Daniel Simon 960d98a554 Qt: Fix generic icon on Wayland 2023-12-20 02:19:36 -08:00
Vicki Pfau 9a45bf94aa GB Audio: Fix restarting envelope when writing to register (fixes #3067) 2023-12-20 02:18:42 -08:00
Vicki Pfau f8112aee59 mGUI: Persist fast forwarding after closing menu (fixes #2414) 2023-12-18 22:58:55 -08:00
Vicki Pfau 68e5a0aa47 GB: Fix applying a patch that changes the cartridge mapper (fixes #3077) 2023-12-11 22:07:41 -08:00
Vicki Pfau 7be38eaed5 Qt: Do initial macOS resource staging in local app bundle 2023-12-11 22:07:41 -08:00
Vicki Pfau 3c5caf7c53 Qt: Move install in CMake so install scripts get run first 2023-12-11 22:07:41 -08:00
Vicki Pfau f4a5d86f88 Qt: Do codesigning on macOS 2023-12-11 22:07:41 -08:00
Vicki Pfau 3520607ebc Res: Add entitlements list for macOS 2023-12-11 22:06:49 -08:00
Vicki Pfau 4f50fe04fe CMake: Bump minimum macOS to 10.7 when building against SDL2 2023-12-10 23:53:35 -08:00
Vicki Pfau c6afa29215 Revert "SDL: Fix minor leak if debugger isn't used"
This reverts commit ba83c4d9d3.
2023-11-25 22:59:49 -08:00
Vicki Pfau ba83c4d9d3 SDL: Fix minor leak if debugger isn't used 2023-11-24 22:30:52 -08:00
Vicki Pfau 0e3d55c2ac GBA Savedata: Fix crash when resizing flash save games for RTC data 2023-11-24 22:30:51 -08:00
nia ef9c5bd096 Scripting: Check for ENOTRECOVERABLE
It's part of "robust mutexes" and may not necessarily be available
(e.g. on NetBSD 9)
2023-11-24 22:29:08 -08:00
Vicki Pfau 5ecce03fba FFmpeg: Fix deprecation warnings 2023-11-06 23:16:37 -08:00
Vicki Pfau 0b003fe230 GBA Audio: Only read MP2k context addresses if valid 2023-10-29 17:11:01 -07:00
Vicki Pfau a362317f46 CMake: Fix DEFAULT_LTO 2023-10-29 17:08:04 -07:00
leo60228 73ac3dad4f Debugger: Advertise QStartNoAckMode support 2023-10-29 17:08:04 -07:00
leo60228 1d4bf372b8 Debugger: Fix off-by-one breaking StartNoAckMode 2023-10-29 17:08:04 -07:00
Vicki Pfau da8f4f28f3 Wii: Fix build 2023-09-27 20:24:40 -07:00
David Spickett 35cdd5d242 Debugger: Correct PC value when read with 'p' instead of 'g' packet
The 'g' packet is handled by _readGPRs which has a special case for
the PC due to the way the CPU reports the PC value.

This was added by added by a967f9aac4.

The 'p' packet is handled by _readRegister which did not have this
special case for PC. This lead to GDB reporting the correct PC value
but LLDB not, as the latter used 'p' instead.

This meant you saw things like this:
    0x80002a4 <+16>: str    r0, [sp, #0x4]
    0x80002a8 <+20>: bl     0x80001f0      <----------expected to be here.
->  0x80002ac <+24>: b      0x80002b0

Where you expected to be about to bl to another function,
but it looked like you had already done it.

And more obviously, when you first attached to the GDB stub,
the PC was reported as 4 not 0.
2023-09-27 20:24:40 -07:00
Vicki Pfau d2205d3475 Scripting: Enhance error reporting 2023-09-27 20:24:40 -07:00
Vicki Pfau 404e84b045 Scripting: Simplify console implementation 2023-09-27 20:24:40 -07:00
Vicki Pfau 31235a6b2c Scripting: Fix console error log level 2023-09-16 01:37:11 -07:00
Vicki Pfau 0fb8b782be GB Core: Fix exported size information 2023-09-16 01:36:15 -07:00
Vicki Pfau c5dc97a432 GBA: Make sure unloading a ROM frees the AGBPrint buffer 2023-09-16 01:36:10 -07:00
Vicki Pfau 9adb1faef2 Qt: SaveConverter doesn't use the controller 2023-09-16 01:35:34 -07:00
Vicki Pfau 9b98a19a37 Core: Fix potential UAF when loading a ROM in a VDir 2023-09-16 01:35:23 -07:00
Vicki Pfau ddee0af216 GBA Core: Fix Flash 1M memory block descriptor 2023-09-16 01:34:29 -07:00
Vicki Pfau 35bbc74cc2 Vita: Fix camera setting not appearing (fixes #3012) 2023-08-28 13:29:24 -07:00
Vicki Pfau 510d1afa4a GBA Audio: Fix sample position issues when rate changes (fixes #3006) 2023-08-23 14:35:50 -07:00
Vicki Pfau d6291bcc05 Qt: Fix mute settings not being loaded on setting screen (fixes #2990) 2023-08-08 19:34:35 -07:00
shenef e86e9d0c76 Qt: Mark video encoding options as untranslatable 2023-08-05 16:34:56 -07:00
Vicki Pfau cda008eef5 Qt: Re-enable sync for multiplayer windows that aren't connected (fixes #2974) 2023-08-04 23:20:39 -07:00
Vicki Pfau fcb2f11464 GUI: Add missing include 2023-07-23 22:49:59 -07:00
Vicki Pfau eb2233eaaa GBA GPIO: Fix tilt scale and orientation (fixes #2703) 2023-07-23 22:49:55 -07:00
Vicki Pfau 1d37620c8c Updater: Fix overwriting directories with files 2023-07-18 01:43:07 -07:00
Vicki Pfau 1ec45502fb FFmpeg: Fix isampleRate initialization 2023-07-17 21:37:37 -07:00
Vicki Pfau 1dc94c999a Qt: Fix leak if loading a save file fails 2023-07-17 21:36:23 -07:00
Vicki Pfau afb614ad03 ARM: Fake bpkt instruction should take no cycles (fixes #2551) 2023-07-05 22:11:59 -07:00
Vicki Pfau a51cb3f921 GBA SIO: Fix normal mode SI/SO semantics (fixes #2925) 2023-07-03 23:15:47 -07:00
Vicki Pfau 1acb7dfa9e Qt: Add exporting of SAV + RTC GB saves from Save Converter to strip RTC data 2023-07-03 16:10:35 -07:00
Vicki Pfau b06c22a408 GB Audio: Force update channels 1/2 if updating from a register write 2023-07-03 16:10:05 -07:00
Vicki Pfau 2669e0f772 GB Audio: Update channels 1/2 irregularly if silent 2023-07-03 16:10:05 -07:00
Vicki Pfau 87c9d3851e ARM: Remove obsolete force-alignment in `bx pc` (fixes #2964) 2023-06-29 01:29:55 -07:00
Vicki Pfau 0d353a740f GB Audio: Fix channel 1 restarting if sweep applies after stop (fixes #2965) 2023-06-29 01:29:38 -07:00
Vicki Pfau 72379168cb GB: Add missing CGB0 BIOS to model detection 2023-06-26 04:45:59 -07:00
Vicki Pfau 76fe63a254 GBA Audio: Fix initial channel 3 wave RAM (fixes #2947) 2023-06-26 04:45:59 -07:00
Vicki Pfau 24f3d8808f GBA Audio: Fix sample timing drifting when changing sample interval 2023-06-19 22:38:38 -07:00
Vicki Pfau 6c4ca7724f Qt: Add exporting of SAV + RTC GBA saves from Save Converter to strip RTC data 2023-06-18 15:18:34 -07:00
Vicki Pfau 276e911e38 Qt: Reduce minimum size of GB palette color pickers 2023-06-13 16:08:11 -07:00
Vicki Pfau e49fe46c26 Qt: Attempt to shorten Game Boy settings pane 2023-06-13 16:08:08 -07:00
Vicki Pfau 724b79b035 Scripting: Fix build against Lua 5.2 2023-06-04 20:31:04 -07:00
Vicki Pfau c37949fee7 Core: Only attempt to open symbol file if basedir exists 2023-06-04 20:30:47 -07:00
May d99f7b9912 [UI bug] mGBA doesn't update savestate screenshots until you move the cursor over other savestates (#2929) 2023-06-04 20:30:35 -07:00
Vicki Pfau 97162280f9 GB I/O: Read back proper SVBK value after writing 0 (fixes #2921) 2023-05-09 14:51:47 -07:00
Vicki Pfau 84d600cba6 GB SIO: Disabling SIO should cancel pending transfers (fixes #2537) 2023-05-08 04:43:54 -07:00
Vicki Pfau 21cfb15f99 mGUI: Make "bios" name check case-insensitive 2023-05-08 04:43:36 -07:00
Vicki Pfau ff3c68bbd6 GBA BIOS: Fix clobbering registers with word-sized CpuSet 2023-04-28 22:47:11 -07:00
Vicki Pfau 5f762e68b8 GB Audio: Fix channels 1/2 staying muted if restarted after long silence 2023-04-28 19:35:45 -07:00
Vicki Pfau 5a94780871 Debugger: Reject traces with negative trace amounts (fixes #2900) 2023-04-26 00:49:45 -07:00
Vicki Pfau 078ce85774 CMake: Bump version 2023-04-26 00:49:45 -07:00
Vicki Pfau 2fb5545061 CHANGES: Update for 0.10.2 2023-04-23 21:15:24 -07:00
Vicki Pfau c5743e7931 Res: Update no-intro 2023-04-23 21:15:24 -07:00
Vicki Pfau b876218060 Res: Update Patrons for April 2023-04-23 20:25:33 -07:00
Vicki Pfau 76acad90a6 Qt: Only disable swapInterval when fast-forwarding 2023-04-23 20:09:11 -07:00
Vicki Pfau c0e3db2bb4 GB, GBA Audio: Clamp audio buffer size to 8192 2023-04-23 20:09:11 -07:00
Vicki Pfau cbe87be222 GBA Video: Don't repeat yourself 2023-04-18 21:42:40 -07:00
Vicki Pfau e15cca9623 GBA Video: Fix interpolation issues with OpenGL renderer 2023-04-18 21:42:40 -07:00
Vicki Pfau b0441cffd0 CHANGES: Update 2023-04-18 21:40:27 -07:00
Vicki Pfau 2f2287683a Qt: Rip out OpenGL proxy thread 2023-04-18 21:38:56 -07:00
Vicki Pfau 70bbe06bfb Qt: Manually toggle swap interval as needed 2023-04-18 21:37:12 -07:00
Vicki Pfau d79a83321c Vita: Work around broken mktime implementation in Vita SDK (fixes #2876) 2023-04-12 03:35:42 -07:00
Vicki Pfau 8344efdccd Scripting: Return proper callback ID from socket.add 2023-04-12 03:35:33 -07:00
Vicki Pfau 7517cce0af FFmpeg: Fix comment 2023-04-07 01:12:29 -07:00
Vicki Pfau 5a642ae1f1 FFmpeg: Force lower sample rate for codecs not supporting high rates (fixes #2869) 2023-04-07 00:58:28 -07:00
Vicki Pfau 9d7f47413c FFmpeg: Fix buffer size rounding for audio encoding 2023-04-07 00:49:56 -07:00
Vicki Pfau 6806850441 Core: Fix memory leaks in mCacheSet 2023-04-05 05:06:28 -07:00
Vicki Pfau 121376ccb0 Qt: Fix checked state of mute menu option at load (fixes #2701) 2023-03-27 01:10:07 -07:00
Vicki Pfau 7d9d71ef54 GBA Audio: Clear GB audio state when disabled 2023-03-26 23:59:39 -07:00
Vicki Pfau 7da48a7c7f Qt: Swap P1 and other player's save if P1 loaded it first (closes #2750) 2023-03-26 23:59:34 -07:00
Vicki Pfau ff3f2a9d84 Util: Strip loading 16-bit PNGs down to 8-bit 2023-03-25 01:46:49 -07:00
Vicki Pfau 87295370f1 GBA Overrides: Fix saving in PMD:RRT (JP) (fixes #2862) 2023-03-24 23:48:13 -07:00
Vicki Pfau e01b56fbd5 Scripting: Fix scalar hashing on different union layouts, e.g. big endian 2023-03-19 03:50:18 -07:00
Vicki Pfau 5b52f0f277 Qt: Fix OSD on modern macOS (fixes #2736) 2023-03-17 02:38:51 -07:00
Vicki Pfau e7f56bd5c9 GDB: Ugh 2023-03-06 14:57:16 -08:00
Vicki Pfau 40e7d2aa4d GDB: Enable NODELAY on GDB stub connections 2023-03-06 14:51:32 -08:00
Vicki Pfau 603ab67926 Qt: Fix buffer termination issue 2023-03-06 00:08:58 -08:00
Vicki Pfau 5f6244556e Qt: Fix black screen when starting with a game (fixes #2781) 2023-03-05 23:55:54 -08:00
Vicki Pfau 06b1b04c7d GBA Saveata: Fix fumbled check 2023-03-05 14:22:01 -08:00
Vicki Pfau 853b028892 VFS: Improve zip invariant handling 2023-03-02 22:30:26 -08:00
Vicki Pfau 1ed40a1931 Qt: Fix potential directory handle leak 2023-03-02 22:30:26 -08:00
Vicki Pfau b8248327a2 Util: Fix potential socket leak 2023-03-02 22:30:26 -08:00
Vicki Pfau b6e34f084f Qt: Better fps non-zero division check 2023-03-01 23:11:36 -08:00
Vicki Pfau 8b67243780 Qt: Initialize Shortcut::m_direction 2023-03-01 23:05:28 -08:00
Vicki Pfau b06d178c41 GBA SIO: Who wrote this code? Oh, me 2023-03-01 22:53:24 -08:00
Vicki Pfau 43f2eb78d7 Qt: Initialize log-to members 2023-03-01 22:53:24 -08:00
Vicki Pfau 2e4732f1ff Core: Negative log types are invalid 2023-03-01 22:53:24 -08:00
Vicki Pfau 71174659f2 Core: Fix GBK string memory handling in .cht loading 2023-03-01 20:32:18 -08:00
Vicki Pfau d099a0d07e Feature: Fix No-Intro cleanup on initial errors 2023-03-01 20:28:10 -08:00
Vicki Pfau 3357fa6a03 All: Fix handling of strncat bounds 2023-03-01 20:18:21 -08:00
Vicki Pfau 72817df4cf GB: Fix potential double-free of non-pristine ROM memory 2023-03-01 20:18:05 -08:00
Vicki Pfau 9da638f0c0 GBA Savedata: Fix sanity check in Load 2023-03-01 16:17:51 -08:00
Vicki Pfau 1bbbbc745d Core: Add missing va_end 2023-03-01 16:17:29 -08:00
Vicki Pfau 18b798ec41 GB Memory: Fix potential crash when directly accessing invalid SRAM 2023-03-01 16:17:29 -08:00
Vicki Pfau bd805dd720 Qt: Fix crash if loading a shader fails 2023-03-01 16:17:29 -08:00
Vicki Pfau c13e2a822e OpenGL: Fix memory leak in failure path 2023-03-01 16:17:29 -08:00
Vicki Pfau 9132859b6f Qt: Fix full-buffer rewind 2023-03-01 16:00:00 -08:00
Vicki Pfau feb9c4a48b Windows: Fix build clang-cl 2023-02-24 22:07:21 -08:00
Vicki Pfau 081fe27e3a Qt: Automatically change video file extension as appropriate 2023-02-24 03:51:44 -08:00
Vicki Pfau 11d38a8317 CHANGES: Update 2023-02-22 20:22:45 -08:00
Vicki Pfau 766c6625c4 Scripting: Add WSAEWOULDBLOCK to error translation table 2023-02-22 20:22:09 -08:00
Vicki Pfau 1d01a7585c Qt: Fix loading a script leaving sync disabled 2023-02-15 02:30:27 -08:00
Vicki Pfau a473aeff2c Qt: Fix a handful of edge cases with graphics viewers (fixes #2827) 2023-02-14 23:14:02 -08:00
Adam Higerd 3f5fec4e4d hook frame callback in socket connect 2023-02-12 13:29:17 -08:00
Vicki Pfau 8c5ad62dbc OpenGL: Fix null calloc/memcpy 2023-02-11 22:09:30 -08:00
Vicki Pfau 5558e469e0 Qt: Fix modifier key names in shortcut editor (fixes #2817) 2023-02-09 20:02:14 -08:00
Vicki Pfau 1164d5b470 Qt: Disable attempted linking betwen incompatible platforms (fixes #2702) 2023-02-09 00:20:21 -08:00
Vicki Pfau b9a950fee7 Qt: Properly cap number of attached players by platform (fixes #2807) 2023-02-09 00:20:20 -08:00
Vicki Pfau 7e624c6857 GBA SIO: Normal mode transfers with no clock should not finish (fixes #2811) 2023-02-09 00:19:12 -08:00
Vicki Pfau 395fa2da4c GBA SIO: Fix unconnected normal mode SIOCNT SI bit (fixes #2810) 2023-02-09 00:18:34 -08:00
Vicki Pfau 4738e558f2 GBA Timers: Cascading timers don't tick when disabled (fixes #2812) 2023-02-03 01:44:31 -08:00
Vicki Pfau 1f2df26e22 Script: Fix table string key UAF 2023-01-31 21:12:42 -08:00
Vicki Pfau 4f8763ad0b Script: Fix leaking tables passed from Lua 2023-01-31 21:12:42 -08:00
Vicki Pfau 1331788b6e GBA SIO: Fix SIOCNT SI pin value after attaching player 2 (fixes #2805) 2023-01-30 20:50:34 -08:00
Vicki Pfau 7c4a7796f8 GBA Audio: Fix improperly deserializing GB audio registers (fixes #2793) 2023-01-30 00:21:48 -08:00
Vicki Pfau 5183edc3c6 Qt: Stop eating boolean action key events (fixes #2636) 2023-01-29 01:48:33 -08:00
Vicki Pfau 5d1fdf3b24 Qt: Unbind apostrophe from GameShark button by default 2023-01-29 01:36:59 -08:00
Vicki Pfau b00a04099f CHANGES: Wrong bug report 2023-01-28 22:41:06 -08:00
Vicki Pfau d29e782789 Core: Allow sending thread requests to a crashed core (fixes #2785) 2023-01-28 22:39:06 -08:00
Vicki Pfau 17322e50b8 Qt: It's 2023 now 2023-01-28 22:10:15 -08:00
Vicki Pfau 31755ebb19 Qt: Disable sync while running scripts from main thread (fixes #2738) 2023-01-27 19:42:28 -08:00
Vicki Pfau f69853e8ba Qt: Include wayland QPA in AppImage (fixes #2796) 2023-01-26 07:18:42 -08:00
Vicki Pfau df6ae1883c Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes #2794) 2023-01-26 05:57:33 -08:00
Vicki Pfau 21f441953f Scripting: Fix mSCRIPT_TYPE_MS_PCS macro 2023-01-26 03:35:24 -08:00
Vicki Pfau 3415dfa1b7 Qt: Add ig4icd32 crash to OpenGL bug database (see #2136) 2023-01-26 03:35:04 -08:00
Vicki Pfau 71d1f122f9 Libretro: Add back missing audio overkill (fixes #2734) 2023-01-26 03:34:34 -08:00
Vicki Pfau 9c69b86d71 GBA Memory: Make VRAM access stalls only apply to BG RAM 2023-01-16 00:54:56 -08:00
Vicki Pfau b5b4271145 CHANGES: Oops 2023-01-12 15:10:47 -08:00
TheMechasaur a4b63e1db9
Correct year of release date of 0.10.1 to 2023 (#2783) 2023-01-11 21:59:17 -08:00
Vicki Pfau 86061dbc3b CMake: Bump version 2023-01-10 19:53:02 -08:00
Vicki Pfau 456ac2222b CHANGES: Update for 0.10.1 2023-01-10 19:51:20 -08:00
Vicki Pfau a413dda335 Qt: Work around Mesa issue 8035 2023-01-10 18:44:47 -08:00
Vicki Pfau c8d7a7d1a3 Qt: Fix minor leak 2023-01-10 18:43:44 -08:00
Vicki Pfau 9d6d2c9c6b GB Audio: Fix regression in channel updating with NR5x (fixes #2775) 2023-01-06 14:26:27 -08:00
Vicki Pfau f7acda6d64 Qt: Redo stable branch detection heuristic 2022-12-31 18:15:47 -08:00
Vicki Pfau 0da3498ee8 Qt: Fix initializing update revision info 2022-12-31 17:54:41 -08:00
Vicki Pfau d17b1da1b7 GB BIOS: Include timing in degenerate ArcTan2 cases (fixes #2763) 2022-12-27 20:39:30 -08:00
Vicki Pfau 0d52995877 GBA Cheats: Fix issues detecting unencrypted cheats (fixes #2724) 2022-12-22 16:02:22 -08:00
Vicki Pfau 63a019b749 Qt: Start OpenGL bug list with glFlush cross-thread on Windows (fixes #2761) 2022-12-21 22:13:51 -08:00
Vicki Pfau fac26cda3c Qt: glFlush is (thankfully) sufficient here 2022-12-18 21:49:41 -08:00
Vicki Pfau 245f4b961a Qt: Tentative fix for renderer switching crash 2022-12-18 21:49:41 -08:00
Vicki Pfau 3d723d5cf4 GBA Video: Fix #2489 in OpenGL renderer 2022-12-18 17:01:26 -08:00
Vicki Pfau 1a4dc70731 GBA: Clean up BIOS skip detection; add second multiboot entry 2022-12-18 12:53:04 -08:00
CasualPokePlayer 411b7dccb4 Check gba->mbVf for force skipping the BIOS (#2754) 2022-12-17 00:22:40 -08:00
Vicki Pfau 947a1c8f5c Qt: Refactor and fix dequeueAll so keep mode keeps last draw tex too 2022-12-17 00:21:19 -08:00
Vicki Pfau 576f607c81 GB IO: Fix BANK not getting initialized after recent change 2022-12-13 15:18:38 -08:00
Vicki Pfau ab790f4c6d Qt: Fix loading/unloading shaders 2022-12-12 14:31:54 -08:00
Vicki Pfau 94277cffbb Qt: Fix proxy context creation on Nvidia (fixes #2746) 2022-12-12 03:12:26 -08:00
Vicki Pfau 7ba3b40977 GB Audio: Fix updating channels other than 2 when writing NR5x 2022-12-11 19:17:24 -08:00
Vicki Pfau 3dbb90e574 GB Audio: Fix up boot sequence 2022-12-11 19:17:24 -08:00
Vicki Pfau c323ab384c GB Memory: Actually, HDMAs should start when LCD is off (fixes #2662) 2022-12-11 19:17:24 -08:00
Vicki Pfau 411741fba3 GB Audio: Fix channel 3 volume being changed between samples (fixes #1896) 2022-12-07 01:22:33 -08:00
Vicki Pfau 9f057e2719 GB Audio: Fix channels 1/2 not playing when resetting volume (fixes #2614) 2022-11-28 22:39:05 -08:00
Vicki Pfau 0fc6b02691 Qt: Fix build on older Qt 2022-11-28 14:25:04 -08:00
Vicki Pfau 3866d74707 Qt: Improve handling of multiplayer syncing (fixes #2720) 2022-11-28 01:03:10 -08:00
Vicki Pfau aeaa40e373 GB SIO: Further fix bidirectional transfer starting 2022-11-28 01:03:10 -08:00
Vicki Pfau 8e58dcadb8 Qt: Don't re-enable sync if GBA link modes aren't the same (fixes #2044) 2022-11-28 01:03:10 -08:00
Vicki Pfau f632b85338 Qt: Move OpenGL proxy onto its own thread (fixes #2493) 2022-11-28 01:03:10 -08:00
Vicki Pfau 3cd63cb71c CHANGES: Spill chicken 2022-11-12 00:45:48 -08:00
Vicki Pfau 4914954c67 GBA: Fix resetting key IRQ state (fixes #2716) 2022-11-12 00:45:44 -08:00
Vicki Pfau 20c16bc8e7 GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes #2489) 2022-11-07 23:13:58 -08:00
Vicki Pfau 838e4a182e VFS: Fix minizip write returning 0 on success instead of size 2022-10-29 01:42:21 -07:00
Vicki Pfau e3605d291c Updater: Fix mUpdaterGetUpdateForChannel 2022-10-29 01:42:21 -07:00
Vicki Pfau 12f7168e9f macOS: Fix modern build with libepoxy (fixes #2700) 2022-10-20 20:11:37 -07:00
Vicki Pfau 455525e3b6 Qt: Keep track of current pslette preset name (fixes #2680) 2022-10-19 04:17:11 -07:00
Vicki Pfau 3f39bd2536 macOS: Add category to plist (closes #2691) 2022-10-19 04:16:40 -07:00
Vicki Pfau 838122c234 GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes #2694) 2022-10-18 01:39:49 -07:00
Vicki Pfau c6d7c8f601 Qt: Fix e-Reader scanning function reentry (fixes #2693) 2022-10-16 22:15:05 -07:00
Vicki Pfau 9a3ec792dd README: Add MBC30 to the supported mappers list (closes #2686) 2022-10-16 03:29:50 -07:00
Vicki Pfau c4718d6907 Qt: Add missing Sachen MMC2 mapper 2022-10-16 01:31:01 -07:00
Vicki Pfau 64255a716b Qt: Expand criteria for tag branch names (fixes #2679) 2022-10-14 23:37:03 -07:00
Vicki Pfau 277aa36d12 Qt: Manually split filename to avoid overzealous splitting (fixes #2681) 2022-10-14 23:37:01 -07:00
Vicki Pfau 81cb4c3bf6 Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes #2685) 2022-10-14 23:36:57 -07:00
Vicki Pfau df599a1fb6 CMake: Bump verison 2022-10-11 22:24:06 -07:00
142 changed files with 8437 additions and 3373 deletions

View File

@ -12,7 +12,11 @@ install:
- vcpkg --no-dry-run upgrade
- rd /Q /S C:\Tools\vcpkg\buildtrees
before_build:
- cmake . -DCMAKE_PREFIX_PATH=C:\Qt\5.15\msvc2019_64 -DCMAKE_TOOLCHAIN_FILE=C:\Tools\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-release -DCMAKE_CONFIGURATION_TYPES=Release
- cmake . -DCMAKE_PREFIX_PATH=C:\Qt\5.15\msvc2019_64 \
-DCMAKE_TOOLCHAIN_FILE=C:\Tools\vcpkg\scripts\buildsystems\vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=x64-windows-release \
-DCMAKE_CONFIGURATION_TYPES=Release \
-DCMAKE_SYSTEM_VERSION=10.0.22000.1
build:
parallel: true
project: mGBA.sln

146
CHANGES
View File

@ -1,3 +1,139 @@
0.10.5: (2025-03-08)
Other fixes:
- ARM Debugger: Fix disassembly of ror r0 barrel shift (fixes mgba.io/i/3412)
- FFmpeg: Fix failing to record videos with CRF video (fixes mgba.io/i/3368)
- GB Core: Fix cloning savedata when backing file is outdated (fixes mgba.io/i/3388)
- GBA Cheats: Let VBA-style codes patch ROM (fixes mgba.io/i/3423)
- GBA Core: Fix booting into BIOS when skip BIOS is enabled
- GBA Hardware: Fix loading states unconditionally overwriting GPIO memory
- Updater: Fix rewriting folders and files on Windows (fixes mgba.io/i/3384)
- Wii: Fix crash on loading large ZIP files (fixes mgba.io/i/3404)
Misc:
- GB: Allow use of CGB-E and AGB-0 BIOS versions (closes mgba.io/i/3427)
0.10.4: (2024-12-07)
Emulation fixes:
- GB Audio: Fix audio envelope timing resetting too often (fixes mgba.io/i/3164)
- GB I/O: Fix STAT writing IRQ trigger conditions (fixes mgba.io/i/2501)
- GBA GPIO: Fix gyro read-out start (fixes mgba.io/i/3141)
- GBA I/O: Fix HALTCNT access behavior (fixes mgba.io/i/2309)
- GBA I/O: Fix audio register 8-bit write behavior (fixes mgba.io/i/3086)
- GBA Serialize: Properly restore GPIO register state (fixes mgba.io/i/3294)
- GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes mgba.io/i/3110)
Other fixes:
- Core: Fix patch autoloading leaking the file handle
- GB: Fix uninitialized save data when loading undersized temporary saves
- GB, GBA Core: Fix memory leak if reloading debug symbols
- GB Serialize: Prevent loading invalid states where LY >= 144 in modes other than 1
- GBA Audio: Fix crash if audio FIFOs and timers get out of sync
- GBA Audio: Fix crash in audio subsampling if timing lockstep breaks
- GBA Core: Fix loading symbols from ELF files if the file doesn't end with .elf
- GBA Memory: Let raw access read high MMIO addresses
- Qt: Fix crash when applying changes to GB I/O registers in I/O view
- Qt: Fix LCDC background priority/enable bit being mis-mapped in I/O view
- Qt: Fix saving named states breaking when screenshot states disabled (fixes mgba.io/i/3320)
- Qt: Fix potential crash on Wayland with OpenGL (fixes mgba.io/i/3276)
- Qt: Fix installer updates if a version number is in the filename (fixes mgba.io/i/3109)
- Updater: Fix updating appimage across filesystems
Misc:
- Qt: Make window corners square on Windows 11 (fixes mgba.io/i/3285)
- Switch: Add bilinear filtering option (closes mgba.io/i/3111)
- Vita: Add imc0 and xmc0 mount point support
0.10.3: (2024-01-07)
Emulation fixes:
- ARM: Remove obsolete force-alignment in `bx pc` (fixes mgba.io/i/2964)
- ARM: Fake bpkt instruction should take no cycles (fixes mgba.io/i/2551)
- GB Audio: Fix channels 1/2 staying muted if restarted after long silence
- GB Audio: Fix channel 1 restarting if sweep applies after stop (fixes mgba.io/i/2965)
- GB Audio: Fix restarting envelope when writing to register (fixes mgba.io/i/3067)
- GB Audio: Improve "zombie mode" emulation in CGB mode (fixes mgba.io/i/2029)
- GB I/O: Read back proper SVBK value after writing 0 (fixes mgba.io/i/2921)
- GB SIO: Disabling SIO should cancel pending transfers (fixes mgba.io/i/2537)
- GBA Audio: Fix sample timing drifting when changing sample interval
- GBA Audio: Fix initial channel 3 wave RAM (fixes mgba.io/i/2947)
- GBA Audio: Fix sample position issues when rate changes (fixes mgba.io/i/3006)
- GBA GPIO: Fix tilt scale and orientation (fixes mgba.io/i/2703)
- GBA BIOS: Fix clobbering registers with word-sized CpuSet
- GBA SIO: Fix normal mode SI/SO semantics (fixes mgba.io/i/2925)
Other fixes:
- GB: Fix applying a patch that changes the cartridge mapper (fixes mgba.io/i/3077)
- GBA Savedata: Fix crash when resizing flash save games for RTC data
- mGUI: Fix cases where an older save state screenshot would be shown (fixes mgba.io/i/2183)
- Qt: Re-enable sync for multiplayer windows that aren't connected (fixes mgba.io/i/2974)
- Qt: Fix mute settings not being loaded on setting screen (fixes mgba.io/i/2990)
- Qt: Fix screen freezing on macOS after closing save state window (fixes mgba.io/i/2885)
- Vita: Fix camera setting not appearing (fixes mgba.io/i/3012)
Misc:
- mGUI: Persist fast forwarding after closing menu (fixes mgba.io/i/2414)
- Qt: Add exporting of SAV + RTC saves from Save Converter to strip RTC data
- VFS: Use anonymousMemoryMap for large 7z allocations (fixes mgba.io/i/3013)
0.10.2: (2023-04-23)
Emulation fixes:
- GBA Audio: Fix improperly deserializing GB audio registers (fixes mgba.io/i/2793)
- GBA Audio: Clear GB audio state when disabled
- GBA Memory: Make VRAM access stalls only apply to BG RAM
- GBA Overrides: Fix saving in PMD:RRT (JP) (fixes mgba.io/i/2862)
- GBA SIO: Fix SIOCNT SI pin value after attaching player 2 (fixes mgba.io/i/2805)
- GBA SIO: Fix unconnected normal mode SIOCNT SI bit (fixes mgba.io/i/2810)
- GBA SIO: Normal mode transfers with no clock should not finish (fixes mgba.io/i/2811)
- GBA Timers: Cascading timers don't tick when disabled (fixes mgba.io/i/2812)
- GBA Video: Fix interpolation issues with OpenGL renderer
Other fixes:
- Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784)
- FFmpeg: Force lower sample rate for codecs not supporting high rates (fixes mgba.io/i/2869)
- Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794)
- Qt: Disable sync while running scripts from main thread (fixes mgba.io/i/2738)
- Qt: Properly cap number of attached players by platform (fixes mgba.io/i/2807)
- Qt: Disable attempted linking betwen incompatible platforms (fixes mgba.io/i/2702)
- Qt: Fix modifier key names in shortcut editor (fixes mgba.io/i/2817)
- Qt: Fix a handful of edge cases with graphics viewers (fixes mgba.io/i/2827)
- Qt: Fix full-buffer rewind
- Qt: Fix crash if loading a shader fails
- Qt: Fix black screen when starting with a game (fixes mgba.io/i/2781)
- Qt: Fix OSD on modern macOS (fixes mgba.io/i/2736)
- Qt: Fix checked state of mute menu option at load (fixes mgba.io/i/2701)
- Qt: Remove OpenGL proxy thread and override SwapInterval directly instead
- Scripting: Fix receiving packets for client sockets
- Scripting: Fix empty receive calls returning unknown error on Windows
- Scripting: Return proper callback ID from socket.add
- Vita: Work around broken mktime implementation in Vita SDK (fixes mgba.io/i/2876)
Misc:
- Qt: Include wayland QPA in AppImage (fixes mgba.io/i/2796)
- Qt: Stop eating boolean action key events (fixes mgba.io/i/2636)
- Qt: Automatically change video file extension as appropriate
- Qt: Swap P1 and other player's save if P1 loaded it first (closes mgba.io/i/2750)
0.10.1: (2023-01-10)
Emulation fixes:
- GB Audio: Fix channels 1/2 not playing when resetting volume (fixes mgba.io/i/2614)
- GB Audio: Fix channel 3 volume being changed between samples (fixes mgba.io/i/1896)
- GB Audio: Fix up boot sequence
- GB Audio: Fix updating channels other than 2 when writing NR5x
- GB Memory: Actually, HDMAs should start when LCD is off (fixes mgba.io/i/2662)
- GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694)
- GB SIO: Further fix bidirectional transfer starting
- GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716)
- GBA BIOS: Include timing in degenerate ArcTan2 cases (fixes mgba.io/i/2763)
- GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes mgba.io/i/2489)
Other fixes:
- GBA: Fix forceskip BIOS logic for multiboot ROMs (fixes mgba.io/i/2753)
- GBA Cheats: Fix issues detecting unencrypted cheats (fixes mgba.io/i/2724)
- Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681)
- Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693)
- Qt: Don't re-enable sync if GBA link modes aren't the same (fixes mgba.io/i/2044)
- Qt: Improve handling of multiplayer syncing (fixes mgba.io/i/2720)
- Qt: Fix initializing update revision info
- Qt: Redo stable branch detection heuristic (fixes mgba.io/i/2679)
- Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685)
- VFS: Fix minizip write returning 0 on success instead of size
Misc:
- macOS: Add category to plist (closes mgba.io/i/2691)
- macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700)
- Qt: Keep track of current palette preset name (fixes mgba.io/i/2680)
- Qt: Move OpenGL proxy onto its own thread (fixes mgba.io/i/2493)
0.10.0: (2022-10-11)
Features:
- Preliminary Lua scripting support
@ -6,7 +142,7 @@ Features:
- Tool for converting scanned pictures of e-Reader cards to raw dotcode data
- Options for muting when inactive, minimized, or for different players in multiplayer
- Cheat code support in homebrew ports
- Acclerometer and gyro support for controllers on PC
- Accelerometer and gyro support for controllers on PC
- Support for combo "Super Game Boy Color" SGB + GBC ROM hacks
- Improved support for HuC-3 mapper, including RTC
- Support for 64 kiB SRAM saves used in some bootlegs
@ -20,7 +156,7 @@ Emulation fixes:
- ARM7: Fix unsigned multiply timing
- GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378)
- GB: Fix HALT breaking M-cycle alignment (fixes mgba.io/i/250)
- GB Audio: Fix channel 1/2 reseting edge cases (fixes mgba.io/i/1925)
- GB Audio: Fix channel 1/2 resetting edge cases (fixes mgba.io/i/1925)
- GB Audio: Properly apply per-model audio differences
- GB Audio: Revamp channel rendering
- GB Audio: Fix APU re-enable timing glitch
@ -130,7 +266,7 @@ Emulation fixes:
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
- GB Video: Fix memory leak when resetting 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
@ -241,7 +377,7 @@ Emulation fixes:
- GBA BIOS: Implement dummy sound driver calls
- GBA BIOS: Improve HLE BIOS timing
- GBA BIOS: Fix reloading video registers after reset (fixes mgba.io/i/1808)
- GBA BIOS: Make HLE BIOS calls interruptable (fixes mgba.io/i/1711 and mgba.io/i/1823)
- GBA BIOS: Make HLE BIOS calls interruptible (fixes mgba.io/i/1711 and mgba.io/i/1823)
- GBA BIOS: Fix invalid decompression bounds checking
- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320)
- GBA DMA: Fix ordering and timing of overlapping DMAs
@ -257,7 +393,7 @@ Emulation fixes:
- GBA Serialize: Fix alignment check when loading states
- GBA SIO: Fix copying Normal mode transfer values
- GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800)
- GBA SIO: Fix deseralizing SIO registers
- GBA SIO: Fix deserializing SIO registers
- GBA SIO: Fix hanging on starting a second multiplayer window (fixes mgba.io/i/854)
- GBA SIO: Fix Normal mode transfer start timing (fixes mgba.io/i/425)
- GBA Timers: Fix toggling timer cascading while timer is active (fixes mgba.io/i/2043)

View File

@ -175,7 +175,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/core/version.c.in ${CMAKE_CURRENT
source_group("Generated sources" FILES ${CMAKE_CURRENT_BINARY_DIR}/version.c)
# Advanced settings
if(NOT (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_COMPILER_VERSION VERSION_LESS "4.5"))
if(NOT (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "4.5"))
set(DEFAULT_LTO ON)
else()
set(DEFAULT_LTO OFF)
@ -236,10 +236,14 @@ endif()
if(APPLE)
execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK)
if(NOT MACOSX_SDK)
message(WARNING "Could not detect SDK version; defaulting to system version. Is SDKROOT set?")
set(MACOSX_SDK ${CMAKE_SYSTEM_VERSION})
endif()
add_definitions(-D_DARWIN_C_SOURCE)
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_OSX_DEPLOYMENT_TARGET "10.6")
endif()
# Not supported until Xcode 9
if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "9")
@ -336,16 +340,12 @@ find_function(popcount32)
find_function(futimens)
find_function(futimes)
find_function(localtime_r)
if(ANDROID AND ANDROID_NDK_MAJOR GREATER 13)
find_function(localtime_r)
set(HAVE_STRTOF_L ON)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_function(localtime_r)
list(APPEND FUNCTION_DEFINES HAVE_STRTOF_L)
elseif(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
# The strtof_l on Linux not actually exposed nor actually strtof_l
set(HAVE_STRTOF_L OFF)
else()
find_function(localtime_r)
find_function(strtof_l)
endif()
@ -419,6 +419,13 @@ if(BUILD_GL)
elseif(UNIX AND NOT APPLE AND TARGET OpenGL::GL)
set(OPENGL_LIBRARY OpenGL::GL)
endif()
if(OpenGL_GLX_FOUND)
list(APPEND FEATURES GLX)
endif()
if(OpenGL_EGL_FOUND)
list(APPEND FEATURES EGL)
list(APPEND OPENGL_LIBRARY ${OPENGL_egl_LIBRARY})
endif()
endif()
if(BUILD_GL)
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c)
@ -718,8 +725,12 @@ if (USE_LZMA)
endif()
if(USE_EPOXY)
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
list(APPEND FEATURE_DEFINES BUILD_GL BUILD_GLES2 BUILD_GLES3)
if(NOT APPLE OR NOT MACOSX_SDK VERSION_GREATER 10.14)
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c)
list(APPEND FEATURE_DEFINES BUILD_GL)
endif()
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
list(APPEND FEATURE_DEFINES BUILD_GLES2 BUILD_GLES3)
list(APPEND FEATURES EPOXY)
include_directories(AFTER ${EPOXY_INCLUDE_DIRS})
link_directories(${EPOXY_LIBRARY_DIRS})
@ -754,6 +765,7 @@ if(USE_ELF)
endif()
if (USE_DISCORD_RPC)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/discord-rpc discord-rpc EXCLUDE_FROM_ALL)
list(APPEND FEATURES DISCORD_RPC)
include_directories(AFTER ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/discord-rpc/include)

View File

@ -50,6 +50,7 @@ The following mappers are fully supported:
- MBC2
- MBC3
- MBC3+RTC
- MBC30
- MBC5
- MBC5+Rumble
- MBC7

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,3 @@
[testinfo]
skip=10
frames=1

Binary file not shown.

View File

@ -12,6 +12,7 @@
#include <malloc.h>
#define THREAD_ENTRY void
#define THREAD_EXIT(RES) return
typedef ThreadFunc ThreadEntry;
typedef LightLock Mutex;

View File

@ -20,6 +20,7 @@ CXX_GUARD_START
#define THREAD_ENTRY void*
typedef THREAD_ENTRY (*ThreadEntry)(void*);
#define THREAD_EXIT(RES) return RES
typedef pthread_t Thread;
typedef pthread_mutex_t Mutex;

View File

@ -17,6 +17,7 @@ typedef struct {
} Condition;
#define THREAD_ENTRY int
typedef THREAD_ENTRY (*ThreadEntry)(void*);
#define THREAD_EXIT(RES) return RES
static inline int MutexInit(Mutex* mutex) {
Mutex id = sceKernelCreateMutex("mutex", 0, 0, 0);

View File

@ -11,6 +11,7 @@
#include <switch.h>
#define THREAD_ENTRY void
#define THREAD_EXIT(RES) return
typedef ThreadFunc ThreadEntry;
typedef CondVar Condition;

View File

@ -27,6 +27,8 @@
extern "C" {
#endif
struct option;
#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
#ifdef REPLACE_GETOPT

View File

@ -12,6 +12,7 @@
#include <windows.h>
#define THREAD_ENTRY DWORD WINAPI
typedef THREAD_ENTRY ThreadEntry(LPVOID);
#define THREAD_EXIT(RES) return RES
typedef HANDLE Thread;
typedef CRITICAL_SECTION Mutex;

View File

@ -204,6 +204,7 @@ static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress)
err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
#endif
if (err) {
SocketCloseQuiet(sock);
return INVALID_SOCKET;
}

View File

@ -147,6 +147,7 @@ void GBAMemoryInit(struct GBA* gba);
void GBAMemoryDeinit(struct GBA* gba);
void GBAMemoryReset(struct GBA* gba);
void GBAMemoryClearAGBPrint(struct GBA* gba);
uint32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter);
uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter);

View File

@ -97,7 +97,7 @@ CXX_GUARD_START
#define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT)
#define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME)
#define mSCRIPT_TYPE_MS_PS(STRUCT) (&mSTStructPtr_ ## STRUCT)
#define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructConstPtr_ ## STRUCT)
#define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructPtrConst_ ## STRUCT)
#define mSCRIPT_TYPE_MS_WSTR (&mSTStringWrapper)
#define mSCRIPT_TYPE_MS_WLIST (&mSTListWrapper)
#define mSCRIPT_TYPE_MS_W(TYPE) (&mSTWrapper_ ## TYPE)

7
res/entitlements.plist Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.device.camera</key>
<true/>
</dict>
</plist>

View File

@ -32,6 +32,8 @@
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>Only used when Game Boy Camera is selected and a physical camera is set</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
@ -56,5 +58,7 @@
<string>Viewer</string>
</dict>
</array>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
</dict>
</plist>

View File

@ -8,5 +8,6 @@ Name=mGBA
GenericName=Game Boy Advance Emulator
Comment=Nintendo Game Boy Advance Emulator
Categories=Game;Emulator;
MimeType=application/x-gameboy-advance-rom;application/x-agb-rom;application/x-gba-rom;
Keywords=emulator;Nintendo;advance;gba;Game Boy Advance;
MimeType=application/x-gameboy-advance-rom;application/x-agb-rom;application/x-gba-rom;application/x-gameboy-rom;application/x-gameboy-color-rom;
Keywords=emulator;Nintendo;advance;gba;gbc;gb;Game Boy Advance;Game Boy Color; Game Boy;
StartupWMClass=mGBA

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,15 @@
Akatsuki
AVjoyu__Chan
Benedikt Feih
Brandon
Emily A. Bellows
G
EmuDeck
gocha
Jaime J. Denizard
MichaelK__
Miras Absar
Molly Howell
Nic Losby (blurbdust)
Petru-Sebastian Toader
Stevoisiak
Tyler Jenkins
William K. Leung
Zach
Zhongchao Qian

View File

@ -412,6 +412,13 @@ local gameRubyEn = Generation3En:new{
_speciesNameTable=0x1f716c,
}
local gameRubyEnR1 = Generation3En:new{
name="Ruby (USA)",
_party=0x3004360,
_partyCount=0x3004350,
_speciesNameTable=0x1f7184,
}
local gameSapphireEn = Generation3En:new{
name="Sapphire (USA)",
_party=0x3004360,
@ -419,6 +426,13 @@ local gameSapphireEn = Generation3En:new{
_speciesNameTable=0x1f70fc,
}
local gameSapphireEnR1 = Generation3En:new{
name="Sapphire (USA)",
_party=0x3004360,
_partyCount=0x3004350,
_speciesNameTable=0x1f7114,
}
local gameEmeraldEn = Generation3En:new{
name="Emerald (USA)",
_party=0x20244ec,
@ -471,6 +485,10 @@ gameCrc32 = {
[0x7d527d62] = gameYellowEn,
[0x84ee4776] = gameFireRedEnR1,
[0xdaffecec] = gameLeafGreenEnR1,
[0x61641576] = gameRubyEnR1, -- Rev 1
[0xaeac73e6] = gameRubyEnR1, -- Rev 2
[0xbafedae5] = gameSapphireEnR1, -- Rev 1
[0x9cc4410e] = gameSapphireEnR1, -- Rev 2
}
function printPartyStatus(game, buffer)

View File

@ -36,7 +36,7 @@
#define ADDR_MODE_1_ASR ADDR_MODE_1_SHIFT(ASR)
#define ADDR_MODE_1_ROR \
ADDR_MODE_1_SHIFT(ROR) \
if (!info->op3.shifterImm) { \
if ((info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_3) && !info->op3.shifterImm) { \
info->op3.shifterOp = ARM_SHIFT_RRX; \
}

View File

@ -663,7 +663,9 @@ DEFINE_INSTRUCTION_ARM(MRC, ARM_STUB)
// Begin miscellaneous definitions
DEFINE_INSTRUCTION_ARM(BKPT, cpu->irqh.bkpt32(cpu, ((opcode >> 4) & 0xFFF0) | (opcode & 0xF))); // Not strictly in ARMv4T, but here for convenience
DEFINE_INSTRUCTION_ARM(BKPT,
cpu->irqh.bkpt32(cpu, ((opcode >> 4) & 0xFFF0) | (opcode & 0xF));
currentCycles = 0;); // Not strictly in ARMv4T, but here for convenience
DEFINE_INSTRUCTION_ARM(ILL, ARM_ILL) // Illegal opcode
DEFINE_INSTRUCTION_ARM(MSR,

View File

@ -381,7 +381,9 @@ DEFINE_LOAD_STORE_MULTIPLE_THUMB(PUSHR,
cpu->gprs[ARM_SP] = address)
DEFINE_INSTRUCTION_THUMB(ILL, ARM_ILL)
DEFINE_INSTRUCTION_THUMB(BKPT, cpu->irqh.bkpt16(cpu, opcode & 0xFF);)
DEFINE_INSTRUCTION_THUMB(BKPT,
cpu->irqh.bkpt16(cpu, opcode & 0xFF);
currentCycles = 0;) // Not strictly in ARMv4T, but here for convenience
DEFINE_INSTRUCTION_THUMB(B,
int16_t immediate = (opcode & 0x07FF) << 5;
cpu->gprs[ARM_PC] += (((int32_t) immediate) >> 4);
@ -401,11 +403,7 @@ DEFINE_INSTRUCTION_THUMB(BL2,
DEFINE_INSTRUCTION_THUMB(BX,
int rm = (opcode >> 3) & 0xF;
_ARMSetMode(cpu, cpu->gprs[rm] & 0x00000001);
int misalign = 0;
if (rm == ARM_PC) {
misalign = cpu->gprs[rm] & 0x00000002;
}
cpu->gprs[ARM_PC] = (cpu->gprs[rm] & 0xFFFFFFFE) - misalign;
cpu->gprs[ARM_PC] = cpu->gprs[rm] & 0xFFFFFFFE;
if (cpu->executionMode == MODE_THUMB) {
currentCycles += ThumbWritePC(cpu);
} else {

View File

@ -34,12 +34,15 @@ void mCacheSetDeinit(struct mCacheSet* cache) {
for (i = 0; i < mMapCacheSetSize(&cache->maps); ++i) {
mMapCacheDeinit(mMapCacheSetGetPointer(&cache->maps, i));
}
mMapCacheSetDeinit(&cache->maps);
for (i = 0; i < mBitmapCacheSetSize(&cache->bitmaps); ++i) {
mBitmapCacheDeinit(mBitmapCacheSetGetPointer(&cache->bitmaps, i));
}
mBitmapCacheSetDeinit(&cache->bitmaps);
for (i = 0; i < mTileCacheSetSize(&cache->tiles); ++i) {
mTileCacheDeinit(mTileCacheSetGetPointer(&cache->tiles, i));
}
mTileCacheSetDeinit(&cache->tiles);
}
void mCacheSetAssignVRAM(struct mCacheSet* cache, void* vram) {

View File

@ -489,8 +489,10 @@ bool mCheatParseEZFChtFile(struct mCheatDevice* device, struct VFile* vf) {
return false;
}
char* name = gbkToUtf8(&cheat[1], end - cheat - 1);
strncpy(cheatName, name, sizeof(cheatName) - 1);
free(name);
if (name) {
strncpy(cheatName, name, sizeof(cheatName) - 1);
free(name);
}
cheatNameLength = strlen(cheatName);
continue;
}
@ -501,7 +503,10 @@ bool mCheatParseEZFChtFile(struct mCheatDevice* device, struct VFile* vf) {
}
if (strncmp(cheat, "ON", eq - cheat) != 0) {
char* subname = gbkToUtf8(cheat, eq - cheat);
snprintf(&cheatName[cheatNameLength], sizeof(cheatName) - cheatNameLength - 1, ": %s", subname);
if (subname) {
snprintf(&cheatName[cheatNameLength], sizeof(cheatName) - cheatNameLength - 1, ": %s", subname);
free(subname);
}
}
set = device->createSet(device, cheatName);
set->enabled = false;
@ -622,6 +627,9 @@ void mCheatAutosave(struct mCheatDevice* device) {
if (!device->autosave) {
return;
}
if (!device->p->dirs.cheats) {
return;
}
struct VFile* vf = mDirectorySetOpenSuffix(&device->p->dirs, device->p->dirs.cheats, ".cheats", O_WRONLY | O_CREAT | O_TRUNC);
if (!vf) {
return;

View File

@ -169,14 +169,14 @@ void mCoreConfigDeinit(struct mCoreConfig* config) {
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
bool mCoreConfigLoad(struct mCoreConfig* config) {
char path[PATH_MAX];
char path[PATH_MAX + 1];
mCoreConfigDirectory(path, PATH_MAX);
strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
return mCoreConfigLoadPath(config, path);
}
bool mCoreConfigSave(const struct mCoreConfig* config) {
char path[PATH_MAX];
char path[PATH_MAX + 1];
mCoreConfigDirectory(path, PATH_MAX);
strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
return mCoreConfigSavePath(config, path);
@ -304,7 +304,7 @@ void mCoreConfigPortablePath(char* out, size_t outLength) {
CFRelease(suburl);
}
#endif
strncat(out, PATH_SEP "portable.ini", outLength - strlen(out));
strncat(out, PATH_SEP "portable.ini", outLength - strlen(out) - 1);
#endif
}

View File

@ -127,6 +127,7 @@ struct mCore* mCoreFind(const char* path) {
}
bool mCoreLoadFile(struct mCore* core, const char* path) {
core->unloadROM(core);
#ifdef FIXED_ROM_BUFFER
return mCorePreloadFile(core, path);
#else
@ -236,20 +237,35 @@ bool mCoreAutoloadPatch(struct mCore* core) {
if (!core->dirs.patch) {
return false;
}
return core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ups", O_RDONLY)) ||
core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ips", O_RDONLY)) ||
core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY));
struct VFile* vf = NULL;
if (!vf) {
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY);
}
if (!vf) {
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ups", O_RDONLY);
}
if (!vf) {
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ips", O_RDONLY);
}
if (!vf) {
return false;
}
bool result = core->loadPatch(core, vf);
vf->close(vf);
return result;
}
bool mCoreAutoloadCheats(struct mCore* core) {
bool success = true;
bool success = !!core->dirs.cheats;
int cheatAuto;
if (!mCoreConfigGetIntValue(&core->config, "cheatAutoload", &cheatAuto) || cheatAuto) {
if (success && (!mCoreConfigGetIntValue(&core->config, "cheatAutoload", &cheatAuto) || cheatAuto)) {
struct VFile* vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.cheats, ".cheats", O_RDONLY);
if (vf) {
struct mCheatDevice* device = core->cheatDevice(core);
success = mCheatParseFile(device, vf);
vf->close(vf);
} else {
success = false;
}
}
if (!mCoreConfigGetIntValue(&core->config, "cheatAutosave", &cheatAuto) || cheatAuto) {

View File

@ -110,6 +110,9 @@ struct VFile* mDirectorySetOpenPath(struct mDirectorySet* dirs, const char* path
}
struct VFile* mDirectorySetOpenSuffix(struct mDirectorySet* dirs, struct VDir* dir, const char* suffix, int mode) {
if (!dir) {
return NULL;
}
char name[PATH_MAX + 1] = "";
snprintf(name, sizeof(name) - 1, "%s%s", dirs->baseName, suffix);
return dir->openFile(dir, name, mode);

View File

@ -95,6 +95,10 @@
#cmakedefine USE_LIBZIP
#endif
#ifndef USE_LUA
#cmakedefine USE_LUA
#endif
#ifndef USE_LZMA
#cmakedefine USE_LZMA
#endif
@ -121,8 +125,44 @@
// HAVE flags
#ifndef HAVE_CRC32
#cmakedefine HAVE_CRC32
#endif
#ifndef HAVE_LOCALTIME_R
#cmakedefine HAVE_LOCALTIME_R
#endif
#ifndef HAVE_POPCOUNT32
#cmakedefine HAVE_POPCOUNT32
#endif
#ifndef HAVE_PTHREAD_NP_H
#cmakedefine HAVE_PTHREAD_NP_H
#endif
#ifndef HAVE_PTHREAD_SETNAME_NP
#cmakedefine HAVE_PTHREAD_SETNAME_NP
#endif
#ifndef HAVE_STRDUP
#cmakedefine HAVE_STRDUP
#endif
#ifndef HAVE_STRLCPY
#cmakedefine HAVE_STRLCPY
#endif
#ifndef HAVE_STRDUP
#cmakedefine HAVE_STRDUP
#endif
#ifndef HAVE_STRTOF_L
#cmakedefine HAVE_STRTOF_L
#endif
#ifndef HAVE_XLOCALE
#cmakedefine HAVE_XLOCALE
#endif
#endif

View File

@ -50,7 +50,7 @@ const char* mLogCategoryName(int category) {
}
const char* mLogCategoryId(int category) {
if (category < MAX_CATEGORY) {
if (category >= 0 && category < MAX_CATEGORY) {
return _categoryIds[category];
}
return NULL;
@ -88,6 +88,7 @@ void mLogExplicit(struct mLogger* context, int category, enum mLogLevel level, c
if (!context->filter || mLogFilterTest(context->filter, category, level)) {
context->log(context, category, level, format, args);
}
va_end(args);
}
void mLogFilterInit(struct mLogFilter* filter) {

View File

@ -175,7 +175,7 @@ THREAD_ENTRY _rewindThread(void* context) {
rewindContext->ready = false;
}
MutexUnlock(&rewindContext->mutex);
return 0;
THREAD_EXIT(0);
}
#endif

View File

@ -713,33 +713,33 @@ static struct mScriptTextBuffer* _mScriptConsoleCreateBuffer(struct mScriptConso
return buffer;
}
static void mScriptConsoleLog(struct mScriptConsole* console, struct mScriptString* msg) {
static void mScriptConsoleLog(struct mScriptConsole* console, const char* msg) {
if (console->logger) {
mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_INFO, "%s", msg->buffer);
mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_INFO, "%s", msg);
} else {
mLog(_mLOG_CAT_SCRIPT, mLOG_INFO, "%s", msg->buffer);
mLog(_mLOG_CAT_SCRIPT, mLOG_INFO, "%s", msg);
}
}
static void mScriptConsoleWarn(struct mScriptConsole* console, struct mScriptString* msg) {
static void mScriptConsoleWarn(struct mScriptConsole* console, const char* msg) {
if (console->logger) {
mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer);
mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg);
} else {
mLog(_mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer);
mLog(_mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg);
}
}
static void mScriptConsoleError(struct mScriptConsole* console, struct mScriptString* msg) {
static void mScriptConsoleError(struct mScriptConsole* console, const char* msg) {
if (console->logger) {
mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_ERROR, "%s", msg->buffer);
mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_ERROR, "%s", msg);
} else {
mLog(_mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer);
mLog(_mLOG_CAT_SCRIPT, mLOG_ERROR, "%s", msg);
}
}
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, log, mScriptConsoleLog, 1, STR, msg);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, warn, mScriptConsoleWarn, 1, STR, msg);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, error, mScriptConsoleError, 1, STR, msg);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, log, mScriptConsoleLog, 1, CHARP, msg);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, warn, mScriptConsoleWarn, 1, CHARP, msg);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, error, mScriptConsoleError, 1, CHARP, msg);
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptConsole, S(mScriptTextBuffer), createBuffer, _mScriptConsoleCreateBuffer, 1, CHARP, name);
mSCRIPT_DEFINE_STRUCT(mScriptConsole)

View File

@ -131,6 +131,9 @@ bool mStateExtdataDeserialize(struct mStateExtdata* extdata, struct VFile* vf) {
if (vf->seek(vf, header.offset, SEEK_SET) < 0) {
return false;
}
if (header.size <= 0) {
continue;
}
struct mStateExtdataItem item = {
.data = malloc(header.size),
.size = header.size,

View File

@ -58,8 +58,19 @@ static void _waitOnInterrupt(struct mCoreThreadInternal* threadContext) {
}
static void _pokeRequest(struct mCoreThreadInternal* threadContext) {
if (threadContext->state == mTHREAD_RUNNING || threadContext->state == mTHREAD_PAUSED) {
switch (threadContext->state) {
case mTHREAD_RUNNING:
case mTHREAD_PAUSED:
case mTHREAD_CRASHED:
threadContext->state = mTHREAD_REQUEST;
break;
case mTHREAD_INITIALIZED:
case mTHREAD_REQUEST:
case mTHREAD_INTERRUPTED:
case mTHREAD_INTERRUPTING:
case mTHREAD_EXITING:
case mTHREAD_SHUTDOWN:
break;
}
}
@ -439,7 +450,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
}
logger->filter = NULL;
return 0;
THREAD_EXIT(0);
}
bool mCoreThreadStart(struct mCoreThread* threadContext) {

View File

@ -597,7 +597,7 @@ static struct ParseTree* _parseTree(const char** string) {
struct ParseTree* tree = NULL;
if (!error) {
tree = parseTreeCreate();
parseLexedExpression(tree, &lv);
error = !parseLexedExpression(tree, &lv);
}
lexFree(&lv);
LexVectorClear(&lv);
@ -726,10 +726,14 @@ static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector
}
static void _trace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
if (!dv || dv->type != CLIDV_INT_TYPE) {
if (!dv) {
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
return;
}
if (dv->type != CLIDV_INT_TYPE || dv->intValue < 0) {
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
return;
}
debugger->traceRemaining = dv->intValue;
if (debugger->traceVf) {

View File

@ -53,7 +53,11 @@ struct mDebugger* mDebuggerCreate(enum mDebuggerType type, struct mCore* core) {
case DEBUGGER_GDB:
#ifdef USE_GDB_STUB
GDBStubCreate(&debugger->gdb);
GDBStubListen(&debugger->gdb, 2345, 0, GDB_WATCHPOINT_STANDARD_LOGIC);
struct Address localHost = {
.version = IPV4,
.ipv4 = 0x7F000001
};
GDBStubListen(&debugger->gdb, 2345, &localHost, GDB_WATCHPOINT_STANDARD_LOGIC);
break;
#endif
case DEBUGGER_NONE:

View File

@ -354,6 +354,10 @@ static void _writeGPRs(struct GDBStub* stub, const char* message) {
_sendMessage(stub);
}
static int32_t _readPC(struct ARMCore* cpu) {
return cpu->gprs[ARM_PC] - (cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM);
}
static void _readGPRs(struct GDBStub* stub, const char* message) {
struct ARMCore* cpu = stub->d.core->cpu;
UNUSED(message);
@ -367,7 +371,7 @@ static void _readGPRs(struct GDBStub* stub, const char* message) {
}
// Program counter
_int2hex32(cpu->gprs[ARM_PC] - (cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM), &stub->outgoing[i]);
_int2hex32(_readPC(cpu), &stub->outgoing[i]);
i += 8;
// CPU status
@ -417,8 +421,10 @@ static void _readRegister(struct GDBStub* stub, const char* message) {
unsigned i = 0;
uint32_t reg = _readHex(readAddress, &i);
uint32_t value;
if (reg < 0x10) {
if (reg < ARM_PC) {
value = cpu->gprs[reg];
} else if (reg == ARM_PC) {
value = _readPC(cpu);
} else if (reg == 0x19) {
value = cpu->cpsr.packed;
} else {
@ -457,7 +463,7 @@ static void _processQSupportedCommand(struct GDBStub* stub, const char* message)
}
message = end + 1;
}
strncpy(stub->outgoing, "swbreak+;hwbreak+;qXfer:features:read+;qXfer:memory-map:read+", GDB_STUB_MAX_LINE - 4);
strncpy(stub->outgoing, "swbreak+;hwbreak+;qXfer:features:read+;qXfer:memory-map:read+;QStartNoAckMode+", GDB_STUB_MAX_LINE - 4);
}
static void _processQXferCommand(struct GDBStub* stub, const char* params, const char* data) {
@ -542,7 +548,7 @@ static void _processQReadCommand(struct GDBStub* stub, const char* message) {
static void _processQWriteCommand(struct GDBStub* stub, const char* message) {
stub->outgoing[0] = '\0';
if (!strncmp("StartNoAckMode#", message, 16)) {
if (!strncmp("StartNoAckMode#", message, 15)) {
stub->lineAck = GDB_ACK_OFF;
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
}
@ -844,6 +850,7 @@ void GDBStubUpdate(struct GDBStub* stub) {
} else {
goto connectionLost;
}
SocketSetTCPPush(stub->connection, 1);
}
while (true) {
if (stub->shouldBlock) {

View File

@ -34,6 +34,14 @@ CXX_GUARD_START
#define FFMPEG_USE_PACKET_UNREF
#endif
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 0)
#define FFMPEG_USE_NEW_CH_LAYOUT
#endif
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100)
#define FFMPEG_USE_GET_SUPPORTED_CONFIG
#endif
static inline enum AVPixelFormat mColorFormatToFFmpegPixFmt(enum mColorFormat format) {
switch (format) {
#ifndef USE_LIBAV

View File

@ -62,13 +62,13 @@ void FFmpegEncoderInit(struct FFmpegEncoder* encoder) {
encoder->audioCodec = NULL;
encoder->videoCodec = NULL;
encoder->containerFormat = NULL;
encoder->isampleRate = PREFERRED_SAMPLE_RATE;
FFmpegEncoderSetAudio(encoder, "flac", 0);
FFmpegEncoderSetVideo(encoder, "libx264", 0, 0);
FFmpegEncoderSetContainer(encoder, "matroska");
FFmpegEncoderSetDimensions(encoder, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
encoder->iwidth = GBA_VIDEO_HORIZONTAL_PIXELS;
encoder->iheight = GBA_VIDEO_VERTICAL_PIXELS;
encoder->isampleRate = PREFERRED_SAMPLE_RATE;
encoder->frameskip = 1;
encoder->skipResidue = 0;
encoder->loop = false;
@ -134,18 +134,28 @@ bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, un
return false;
}
if (!codec->sample_fmts) {
const enum AVSampleFormat* formats = NULL;
#ifdef FFMPEG_USE_GET_SUPPORTED_CONFIG
if (avcodec_get_supported_config(NULL, codec, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, (const void**) &formats, NULL) < 0) {
return false;
}
#else
formats = codec->sample_fmts;
#endif
if (!formats) {
return false;
}
size_t i;
size_t j;
int priority = INT_MAX;
encoder->sampleFormat = AV_SAMPLE_FMT_NONE;
for (i = 0; codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; ++i) {
for (i = 0; formats[i] != AV_SAMPLE_FMT_NONE; ++i) {
for (j = 0; j < sizeof(priorities) / sizeof(*priorities); ++j) {
if (codec->sample_fmts[i] == priorities[j].format && priority > priorities[j].priority) {
if (formats[i] == priorities[j].format && priority > priorities[j].priority) {
priority = priorities[j].priority;
encoder->sampleFormat = codec->sample_fmts[i];
encoder->sampleFormat = formats[i];
}
}
}
@ -153,20 +163,47 @@ bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, un
return false;
}
encoder->sampleRate = encoder->isampleRate;
if (codec->supported_samplerates) {
for (i = 0; codec->supported_samplerates[i]; ++i) {
if (codec->supported_samplerates[i] < encoder->isampleRate) {
const int* sampleRates = NULL;
#ifdef FFMPEG_USE_GET_SUPPORTED_CONFIG
if (avcodec_get_supported_config(NULL, codec, AV_CODEC_CONFIG_SAMPLE_RATE, 0, (const void**) &sampleRates, NULL) < 0) {
return false;
}
#else
sampleRates = codec->supported_samplerates;
#endif
if (sampleRates) {
bool gotSampleRate = false;
int highestSampleRate = 0;
for (i = 0; sampleRates[i]; ++i) {
if (sampleRates[i] > highestSampleRate) {
highestSampleRate = sampleRates[i];
}
if (sampleRates[i] < encoder->isampleRate) {
continue;
}
if (encoder->sampleRate == encoder->isampleRate || encoder->sampleRate > codec->supported_samplerates[i]) {
encoder->sampleRate = codec->supported_samplerates[i];
if (!gotSampleRate || encoder->sampleRate > sampleRates[i]) {
encoder->sampleRate = sampleRates[i];
gotSampleRate = true;
}
}
if (!gotSampleRate) {
// There are no available sample rates that are higher than the input sample rate
// Let's use the highest available instead
encoder->sampleRate = highestSampleRate;
}
} else if (codec->id == AV_CODEC_ID_FLAC) {
// HACK: FLAC doesn't support > 65535Hz unless it's divisible by 10
if (encoder->sampleRate >= 65535) {
encoder->sampleRate -= encoder->isampleRate % 10;
}
} else if (codec->id == AV_CODEC_ID_VORBIS) {
// HACK: Vorbis doesn't support > 48000Hz but doesn't tell us
if (encoder->sampleRate > 48000) {
encoder->sampleRate = 48000;
}
} else if (codec->id == AV_CODEC_ID_AAC) {
// HACK: AAC doesn't support 32768Hz (it rounds to 32000), but libfaac doesn't tell us that
encoder->sampleRate = 48000;
@ -215,11 +252,19 @@ bool FFmpegEncoderSetVideo(struct FFmpegEncoder* encoder, const char* vcodec, in
size_t j;
int priority = INT_MAX;
encoder->pixFormat = AV_PIX_FMT_NONE;
for (i = 0; codec->pix_fmts[i] != AV_PIX_FMT_NONE; ++i) {
const enum AVPixelFormat* formats;
#ifdef FFMPEG_USE_GET_SUPPORTED_CONFIG
if (avcodec_get_supported_config(NULL, codec, AV_CODEC_CONFIG_PIX_FORMAT, 0, (const void**) &formats, NULL) < 0) {
return false;
}
#else
formats = codec->pix_fmts;
#endif
for (i = 0; formats[i] != AV_PIX_FMT_NONE; ++i) {
for (j = 0; j < sizeof(priorities) / sizeof(*priorities); ++j) {
if (codec->pix_fmts[i] == priorities[j].format && priority > priorities[j].priority) {
if (formats[i] == priorities[j].format && priority > priorities[j].priority) {
priority = priorities[j].priority;
encoder->pixFormat = codec->pix_fmts[i];
encoder->pixFormat = formats[i];
}
}
}
@ -304,8 +349,12 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->audio = encoder->audioStream->codec;
#endif
encoder->audio->bit_rate = encoder->audioBitrate;
#ifdef FFMPEG_USE_NEW_CH_LAYOUT
av_channel_layout_copy(&encoder->audio->ch_layout, &(AVChannelLayout) AV_CHANNEL_LAYOUT_STEREO);
#else
encoder->audio->channels = 2;
encoder->audio->channel_layout = AV_CH_LAYOUT_STEREO;
#endif
encoder->audio->sample_rate = encoder->sampleRate;
encoder->audio->sample_fmt = encoder->sampleFormat;
AVDictionary* opts = 0;
@ -330,7 +379,11 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->audioFrame->nb_samples = encoder->audio->frame_size;
encoder->audioFrame->format = encoder->audio->sample_fmt;
encoder->audioFrame->pts = 0;
#ifdef FFMPEG_USE_NEW_CH_LAYOUT
av_channel_layout_copy(&encoder->audioFrame->ch_layout, &(AVChannelLayout) AV_CHANNEL_LAYOUT_STEREO);
#else
encoder->audioFrame->channel_layout = AV_CH_LAYOUT_STEREO;
#endif
_ffmpegOpenResampleContext(encoder);
av_frame_get_buffer(encoder->audioFrame, 0);
@ -360,7 +413,9 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->videoStream = avformat_new_stream(encoder->context, vcodec);
encoder->video = encoder->videoStream->codec;
#endif
encoder->video->bit_rate = encoder->videoBitrate;
if (encoder->videoBitrate >= 0) {
encoder->video->bit_rate = encoder->videoBitrate;
}
encoder->video->width = encoder->width;
encoder->video->height = encoder->height;
encoder->video->time_base = (AVRational) { encoder->frameCycles * encoder->frameskip, encoder->cycles };
@ -881,7 +936,7 @@ void FFmpegEncoderSetInputSampleRate(struct FFmpegEncoder* encoder, int sampleRa
}
void _ffmpegOpenResampleContext(struct FFmpegEncoder* encoder) {
encoder->audioBufferSize = av_rescale_q(encoder->audioFrame->nb_samples, (AVRational) { 4, encoder->sampleRate }, (AVRational) { 1, encoder->isampleRate });
encoder->audioBufferSize = av_rescale_q(encoder->audioFrame->nb_samples, (AVRational) { 1, encoder->sampleRate }, (AVRational) { 1, encoder->isampleRate }) * 4;
encoder->audioBuffer = av_malloc(encoder->audioBufferSize);
#ifdef USE_LIBAVRESAMPLE
encoder->resampleContext = avresample_alloc_context();
@ -892,9 +947,14 @@ void _ffmpegOpenResampleContext(struct FFmpegEncoder* encoder) {
av_opt_set_int(encoder->resampleContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
av_opt_set_int(encoder->resampleContext, "out_sample_fmt", encoder->sampleFormat, 0);
avresample_open(encoder->resampleContext);
#else
#ifdef FFMPEG_USE_NEW_CH_LAYOUT
swr_alloc_set_opts2(&encoder->resampleContext, &(AVChannelLayout) AV_CHANNEL_LAYOUT_STEREO, encoder->sampleFormat, encoder->sampleRate,
&(AVChannelLayout) AV_CHANNEL_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, encoder->isampleRate, 0, NULL);
#else
encoder->resampleContext = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, encoder->sampleFormat, encoder->sampleRate,
AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, encoder->isampleRate, 0, NULL);
#endif
swr_init(encoder->resampleContext);
#endif
}

View File

@ -9,6 +9,7 @@
#include <mgba/core/core.h>
#include "feature/gui/gui-runner.h"
#include <mgba-util/gui/menu.h>
#include <mgba-util/string.h>
enum mGUICheatAction {
CHEAT_BACK = 0,

View File

@ -31,7 +31,7 @@ static bool _biosNamed(const char* name) {
char ext[PATH_MAX + 1] = {};
separatePath(name, NULL, NULL, ext);
if (strstr(name, "bios")) {
if (strcasestr(name, "bios")) {
return true;
}
if (!strncmp(ext, "bin", PATH_MAX)) {

View File

@ -492,6 +492,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
}
mLOG(GUI_RUNNER, INFO, "Game starting");
runner->fps = 0;
bool fastForward = false;
while (running) {
CircleBufferClear(&runner->fpsBuffer);
runner->totalDelta = 0;
@ -500,7 +501,6 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
runner->lastFpsCheck = 1000000LL * tv.tv_sec + tv.tv_usec;
int frame = 0;
bool fastForward = false;
while (running) {
if (runner->running) {
running = runner->running(runner);
@ -648,6 +648,9 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
runner->core->reset(runner->core);
break;
case RUNNER_SAVE_STATE:
// If we are saving state, then the screenshot stored for the state previously should no longer be considered up-to-date.
// Therefore, mark it as stale so that at draw time we load the new save state's screenshot.
((struct mGUIBackground*) stateSaveMenu.background)->screenshotId |= SCREENSHOT_INVALID;
mCoreSaveState(runner->core, item->data.v.u >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
break;
case RUNNER_LOAD_STATE:
@ -791,5 +794,6 @@ THREAD_ENTRY mGUIAutosaveThread(void* context) {
}
}
MutexUnlock(&autosave->mutex);
THREAD_EXIT(0);
}
#endif

View File

@ -17,7 +17,7 @@ struct NoIntroDB {
};
struct NoIntroDB* NoIntroDBLoad(const char* path) {
struct NoIntroDB* db = malloc(sizeof(*db));
struct NoIntroDB* db = calloc(1, sizeof(*db));
if (sqlite3_open_v2(path, &db->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL)) {
goto error;
@ -60,9 +60,6 @@ struct NoIntroDB* NoIntroDBLoad(const char* path) {
return db;
error:
if (db->crc32) {
sqlite3_finalize(db->crc32);
}
NoIntroDBDestroy(db);
return NULL;
@ -73,6 +70,7 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
sqlite3_stmt* gamedbTable = NULL;
sqlite3_stmt* gamedbDrop = NULL;
sqlite3_stmt* gamedbSelect = NULL;
sqlite3_stmt* gameTable = NULL;
sqlite3_stmt* romTable = NULL;
char* fieldName = NULL;
@ -92,6 +90,11 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
return false;
}
static const char selectGamedb[] = "SELECT * FROM gamedb WHERE name = ? AND version >= ?;";
if (sqlite3_prepare_v2(db->db, selectGamedb, -1, &gamedbSelect, NULL)) {
return false;
}
static const char insertGame[] = "INSERT INTO games (dbid, name) VALUES (?, ?);";
if (sqlite3_prepare_v2(db->db, insertGame, -1, &gameTable, NULL)) {
return false;
@ -162,18 +165,24 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
break;
case ')':
if (currentDb < 0 && dbType && dbVersion) {
sqlite3_clear_bindings(gamedbDrop);
sqlite3_reset(gamedbDrop);
sqlite3_bind_text(gamedbDrop, 1, dbType, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(gamedbDrop, 2, dbVersion, -1, SQLITE_TRANSIENT);
sqlite3_step(gamedbDrop);
sqlite3_clear_bindings(gamedbSelect);
sqlite3_reset(gamedbSelect);
sqlite3_bind_text(gamedbSelect, 1, dbType, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(gamedbSelect, 2, dbVersion, -1, SQLITE_TRANSIENT);
if (sqlite3_step(gamedbSelect) != SQLITE_ROW) {
sqlite3_clear_bindings(gamedbDrop);
sqlite3_reset(gamedbDrop);
sqlite3_bind_text(gamedbDrop, 1, dbType, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(gamedbDrop, 2, dbVersion, -1, SQLITE_TRANSIENT);
sqlite3_step(gamedbDrop);
sqlite3_clear_bindings(gamedbTable);
sqlite3_reset(gamedbTable);
sqlite3_bind_text(gamedbTable, 1, dbType, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(gamedbTable, 2, dbVersion, -1, SQLITE_TRANSIENT);
if (sqlite3_step(gamedbTable) == SQLITE_DONE) {
currentDb = sqlite3_last_insert_rowid(db->db);
sqlite3_clear_bindings(gamedbTable);
sqlite3_reset(gamedbTable);
sqlite3_bind_text(gamedbTable, 1, dbType, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(gamedbTable, 2, dbVersion, -1, SQLITE_TRANSIENT);
if (sqlite3_step(gamedbTable) == SQLITE_DONE) {
currentDb = sqlite3_last_insert_rowid(db->db);
}
}
free((void*) dbType);
free((void*) dbVersion);
@ -273,6 +282,7 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
sqlite3_finalize(gamedbTable);
sqlite3_finalize(gamedbDrop);
sqlite3_finalize(gamedbSelect);
sqlite3_finalize(gameTable);
sqlite3_finalize(romTable);
@ -285,8 +295,12 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
}
void NoIntroDBDestroy(struct NoIntroDB* db) {
sqlite3_finalize(db->crc32);
sqlite3_close(db->db);
if (db->crc32) {
sqlite3_finalize(db->crc32);
}
if (db->db) {
sqlite3_close(db->db);
}
free(db);
}

View File

@ -207,7 +207,7 @@ static THREAD_ENTRY _proxyThread(void* logger) {
}
}
MutexUnlock(&proxyRenderer->mutex);
return 0;
THREAD_EXIT(0);
}
#endif

View File

@ -21,6 +21,9 @@
#include <synchapi.h>
#define mkdir(X, Y) _mkdir(X)
#ifndef S_ISDIR
#define S_ISDIR(MODE) (((MODE) & _S_IFMT) == _S_IFDIR)
#endif
#elif defined(_POSIX_C_SOURCE)
#include <unistd.h>
#endif
@ -31,6 +34,39 @@
FILE* logfile;
bool rmdirRecursive(struct VDir* dir) {
if (!dir) {
return false;
}
bool ok = true;
struct VDirEntry* vde;
while ((vde = dir->listNext(dir))) {
const char* name = vde->name(vde);
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
continue;
}
switch (vde->type(vde)) {
case VFS_DIRECTORY:
fprintf(logfile, "cd %s\n", name);
if (!rmdirRecursive(dir->openDir(dir, name))) {
ok = false;
}
fprintf(logfile, "cd ..\n");
// Fall through
case VFS_FILE:
case VFS_UNKNOWN:
fprintf(logfile, "rm %s\n", name);
if (!dir->deleteFile(dir, name)) {
fprintf(logfile, "error\n");
ok = false;
}
break;
}
}
dir->close(dir);
return ok;
}
bool extractArchive(struct VDir* archive, const char* root, bool prefix) {
char path[PATH_MAX] = {0};
struct VDirEntry* vde;
@ -56,12 +92,32 @@ bool extractArchive(struct VDir* archive, const char* root, bool prefix) {
switch (vde->type(vde)) {
case VFS_DIRECTORY:
fprintf(logfile, "mkdir %s\n", fname);
if (mkdir(path, 0755) < 0 && errno != EEXIST) {
return false;
if (mkdir(path, 0755) < 0) {
bool redo = true;
struct stat st;
if (errno != EEXIST || stat(path, &st) < 0) {
redo = false;
} else if (!S_ISDIR(st.st_mode)) {
#ifdef _WIN32
wchar_t wpath[MAX_PATH + 1];
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);
DeleteFileW(wpath);
#else
unlink(path);
#endif
if (mkdir(path, 0755) < 0) {
redo = false;
}
}
if (!redo) {
fprintf(logfile, "error %i\n", errno);
return false;
}
}
if (!prefix) {
struct VDir* subdir = archive->openDir(archive, fname);
if (!subdir) {
fprintf(logfile, "error\n");
return false;
}
if (!extractArchive(subdir, path, false)) {
@ -76,13 +132,32 @@ bool extractArchive(struct VDir* archive, const char* root, bool prefix) {
vfIn = archive->openFile(archive, vde->name(vde), O_RDONLY);
errno = 0;
vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
if (!vfOut && errno == EACCES) {
if (!vfOut) {
int error = errno;
struct stat st;
if (error == EISDIR || (stat(path, &st) >= 0 && S_ISDIR(st.st_mode))) {
// Windows maps STATUS_FILE_IS_A_DIRECTORY to ERROR_ACCESS_DENIED,
// which then gets mapped to EACCESS, because everything is awful
fprintf(logfile, "rm -r %s\n", path);
if (!rmdirRecursive(VDirOpen(path))) {
return false;
}
#ifdef _WIN32
Sleep(1000);
wchar_t wpath[MAX_PATH + 1];
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);
RemoveDirectoryW(wpath);
#else
sleep(1);
rmdir(path);
#endif
vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
} else if (error == EACCES || error == ETXTBSY) {
#ifdef _WIN32
Sleep(1000);
#else
sleep(1);
#endif
vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
}
}
if (!vfOut) {
vfIn->close(vfIn);
@ -114,7 +189,7 @@ int main(int argc, char* argv[]) {
int ok = 1;
mCoreConfigDirectory(bin, sizeof(bin));
strncat(bin, "/updater.log", sizeof(bin));
strncat(bin, "/updater.log", sizeof(bin) - 1);
logfile = fopen(bin, "w");
mCoreConfigInit(&config, "updater");
@ -200,7 +275,7 @@ int main(int argc, char* argv[]) {
// Cross-dev, need to copy manually
int infd = open(updateArchive, O_RDONLY);
int outfd = -1;
if (infd >= 0) {
if (infd < 0) {
ok = 2;
} else {
outfd = open(bin, O_CREAT | O_WRONLY | O_TRUNC, 0755);
@ -274,7 +349,7 @@ int main(int argc, char* argv[]) {
const char* argv[] = { qbin, NULL };
_execv(bin, argv);
#elif defined(_POSIX_C_SOURCE) || defined(__APPLE__)
const char* argv[] = { bin, NULL };
char* const argv[] = { bin, NULL };
execv(bin, argv);
#endif
}

View File

@ -78,14 +78,7 @@ static void _updateMatch(const char* key, const char* value, void* user) {
return;
}
const char* item = &key[dotLoc + 1];
struct Table* out = user;
struct mUpdate* update = HashTableLookup(out, match->channel);
if (!update) {
update = calloc(1, sizeof(*update));
HashTableInsert(out, match->channel, update);
}
_updateUpdate(update, item, value);
_updateUpdate(match->out, item, value);
}
bool mUpdaterInit(struct mUpdaterContext* context, const char* manifest) {
@ -155,7 +148,7 @@ bool mUpdateLoad(const struct mCoreConfig* config, const char* prefix, struct mU
snprintf(key, sizeof(key), "%s.path", prefix);
update->path = mCoreConfigGetValue(config, key);
snprintf(key, sizeof(key), "%s.size", prefix);
uint32_t size = 0;
unsigned size = 0;
mCoreConfigGetUIntValue(config, key, &size);
if (!update->path && !size) {
return false;

View File

@ -33,10 +33,10 @@ static void _writeDuty(struct GBAudioEnvelope* envelope, uint8_t value);
static bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudioStyle style);
static void _resetSweep(struct GBAudioSweep* sweep);
static bool _resetEnvelope(struct GBAudioEnvelope* sweep);
static bool _resetEnvelope(struct GBAudioEnvelope* sweep, enum GBAudioStyle style);
static void _updateEnvelope(struct GBAudioEnvelope* envelope);
static void _updateEnvelopeDead(struct GBAudioEnvelope* envelope);
static void _updateEnvelopeDead(struct GBAudioEnvelope* envelope, enum GBAudioStyle style);
static bool _updateSweep(struct GBAudioSquareChannel* sweep, bool initial);
static void _updateSquareSample(struct GBAudioSquareChannel* ch);
@ -100,22 +100,24 @@ void GBAudioReset(struct GBAudio* audio) {
audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 };
audio->ch4 = (struct GBAudioNoiseChannel) { .nSamples = 0 };
// TODO: DMG randomness
audio->ch3.wavedata8[0] = 0x00;
audio->ch3.wavedata8[1] = 0xFF;
audio->ch3.wavedata8[2] = 0x00;
audio->ch3.wavedata8[3] = 0xFF;
audio->ch3.wavedata8[4] = 0x00;
audio->ch3.wavedata8[5] = 0xFF;
audio->ch3.wavedata8[6] = 0x00;
audio->ch3.wavedata8[7] = 0xFF;
audio->ch3.wavedata8[8] = 0x00;
audio->ch3.wavedata8[9] = 0xFF;
audio->ch3.wavedata8[10] = 0x00;
audio->ch3.wavedata8[11] = 0xFF;
audio->ch3.wavedata8[12] = 0x00;
audio->ch3.wavedata8[13] = 0xFF;
audio->ch3.wavedata8[14] = 0x00;
audio->ch3.wavedata8[15] = 0xFF;
if (audio->style != GB_AUDIO_GBA) {
audio->ch3.wavedata8[0] = 0x00;
audio->ch3.wavedata8[1] = 0xFF;
audio->ch3.wavedata8[2] = 0x00;
audio->ch3.wavedata8[3] = 0xFF;
audio->ch3.wavedata8[4] = 0x00;
audio->ch3.wavedata8[5] = 0xFF;
audio->ch3.wavedata8[6] = 0x00;
audio->ch3.wavedata8[7] = 0xFF;
audio->ch3.wavedata8[8] = 0x00;
audio->ch3.wavedata8[9] = 0xFF;
audio->ch3.wavedata8[10] = 0x00;
audio->ch3.wavedata8[11] = 0xFF;
audio->ch3.wavedata8[12] = 0x00;
audio->ch3.wavedata8[13] = 0xFF;
audio->ch3.wavedata8[14] = 0x00;
audio->ch3.wavedata8[15] = 0xFF;
}
audio->ch4 = (struct GBAudioNoiseChannel) { .envelope = { .dead = 2 } };
audio->frame = 0;
audio->sampleInterval = SAMPLE_INTERVAL * GB_MAX_SAMPLES;
@ -138,6 +140,9 @@ void GBAudioReset(struct GBAudio* audio) {
}
void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) {
if (samples > BLIP_BUFFER_SIZE / 2) {
samples = BLIP_BUFFER_SIZE / 2;
}
mCoreSyncLockAudio(audio->p->sync);
audio->samples = samples;
blip_clear(audio->left);
@ -187,7 +192,7 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
}
}
if (GBAudioRegisterControlIsRestart(value << 8)) {
audio->playingCh1 = _resetEnvelope(&audio->ch1.envelope);
audio->playingCh1 = _resetEnvelope(&audio->ch1.envelope, audio->style);
audio->ch1.sweep.realFrequency = audio->ch1.control.frequency;
_resetSweep(&audio->ch1.sweep);
if (audio->playingCh1 && audio->ch1.sweep.shift) {
@ -238,7 +243,7 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) {
}
}
if (GBAudioRegisterControlIsRestart(value << 8)) {
audio->playingCh2 = _resetEnvelope(&audio->ch2.envelope);
audio->playingCh2 = _resetEnvelope(&audio->ch2.envelope, audio->style);
if (!audio->ch2.control.length) {
audio->ch2.control.length = 64;
@ -269,6 +274,29 @@ void GBAudioWriteNR31(struct GBAudio* audio, uint8_t value) {
void GBAudioWriteNR32(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x4);
audio->ch3.volume = GBAudioRegisterBankVolumeGetVolumeGB(value);
audio->ch3.sample = audio->ch3.wavedata8[audio->ch3.window >> 1];
if (!(audio->ch3.window & 1)) {
audio->ch3.sample >>= 4;
}
audio->ch3.sample &= 0xF;
int volume;
switch (audio->ch3.volume) {
case 0:
volume = 4;
break;
case 1:
volume = 0;
break;
case 2:
volume = 1;
break;
default:
case 3:
volume = 2;
break;
}
audio->ch3.sample >>= volume;
}
void GBAudioWriteNR33(struct GBAudio* audio, uint8_t value) {
@ -355,7 +383,7 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
}
}
if (GBAudioRegisterNoiseControlIsRestart(value)) {
audio->playingCh4 = _resetEnvelope(&audio->ch4.envelope);
audio->playingCh4 = _resetEnvelope(&audio->ch4.envelope, audio->style);
if (audio->ch4.power) {
audio->ch4.lfsr = 0x7F;
@ -377,13 +405,13 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
}
void GBAudioWriteNR50(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2);
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF);
audio->volumeRight = GBRegisterNR50GetVolumeRight(value);
audio->volumeLeft = GBRegisterNR50GetVolumeLeft(value);
}
void GBAudioWriteNR51(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2);
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF);
audio->ch1Right = GBRegisterNR51GetCh1Right(value);
audio->ch2Right = GBRegisterNR51GetCh2Right(value);
audio->ch3Right = GBRegisterNR51GetCh3Right(value);
@ -476,11 +504,11 @@ void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) {
if (!audio->enable) {
return;
}
if (audio->p && channels != 0xF && timestamp - audio->lastSample > (int) (SAMPLE_INTERVAL * audio->timingFactor)) {
if (audio->p && channels != 0x1F && timestamp - audio->lastSample > (int) (SAMPLE_INTERVAL * audio->timingFactor)) {
GBAudioSample(audio, timestamp);
}
if (audio->playingCh1 && (channels & 0x1)) {
if ((channels & 0x1) && ((audio->playingCh1 && audio->ch1.envelope.dead != 2) || timestamp - audio->ch1.lastUpdate > 0x40000000 || (channels == 0x1))) {
int period = 4 * (2048 - audio->ch1.control.frequency) * audio->timingFactor;
int32_t diff = timestamp - audio->ch1.lastUpdate;
if (diff >= period) {
@ -490,7 +518,7 @@ void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) {
_updateSquareSample(&audio->ch1);
}
}
if (audio->playingCh2 && (channels & 0x2)) {
if ((channels & 0x2) && ((audio->playingCh2 && audio->ch2.envelope.dead != 2) || timestamp - audio->ch2.lastUpdate > 0x40000000 || (channels == 0x2))) {
int period = 4 * (2048 - audio->ch2.control.frequency) * audio->timingFactor;
int32_t diff = timestamp - audio->ch2.lastUpdate;
if (diff >= period) {
@ -621,7 +649,9 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
if (audio->ch1.sweep.enable) {
--audio->ch1.sweep.step;
if (audio->ch1.sweep.step == 0) {
audio->playingCh1 = _updateSweep(&audio->ch1, false);
if (!_updateSweep(&audio->ch1, false)) {
audio->playingCh1 = false;
}
*audio->nr52 &= ~0x0001;
*audio->nr52 |= audio->playingCh1;
}
@ -756,7 +786,7 @@ void GBAudioSample(struct GBAudio* audio, int32_t timestamp) {
for (sample = audio->sampleIndex; timestamp >= interval && sample < GB_MAX_SAMPLES; ++sample, timestamp -= interval) {
int16_t sampleLeft = 0;
int16_t sampleRight = 0;
GBAudioRun(audio, sample * interval + audio->lastSample, 0xF);
GBAudioRun(audio, sample * interval + audio->lastSample, 0x1F);
GBAudioSamplePSG(audio, &sampleLeft, &sampleRight);
sampleLeft = (sampleLeft * audio->masterVolume * 6) >> 7;
sampleRight = (sampleRight * audio->masterVolume * 6) >> 7;
@ -817,12 +847,10 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval * audio->timingFactor - cyclesLate);
}
bool _resetEnvelope(struct GBAudioEnvelope* envelope) {
bool _resetEnvelope(struct GBAudioEnvelope* envelope, enum GBAudioStyle style) {
envelope->currentVolume = envelope->initialVolume;
_updateEnvelopeDead(envelope);
if (!envelope->dead) {
envelope->nextStep = envelope->stepTime;
}
envelope->nextStep = envelope->stepTime;
_updateEnvelopeDead(envelope, style);
return envelope->initialVolume || envelope->direction;
}
@ -854,16 +882,29 @@ void _writeDuty(struct GBAudioEnvelope* envelope, uint8_t value) {
}
bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudioStyle style) {
bool oldDirection = envelope->direction;
envelope->stepTime = GBAudioRegisterSweepGetStepTime(value);
envelope->direction = GBAudioRegisterSweepGetDirection(value);
envelope->initialVolume = GBAudioRegisterSweepGetInitialVolume(value);
if (style == GB_AUDIO_DMG && !envelope->stepTime) {
if (!envelope->stepTime) {
// TODO: Improve "zombie" mode
++envelope->currentVolume;
if (style == GB_AUDIO_DMG) {
++envelope->currentVolume;
} else if (style == GB_AUDIO_CGB) {
if (envelope->direction == oldDirection) {
if (envelope->direction) {
++envelope->currentVolume;
} else {
envelope->currentVolume += 2;
}
} else {
envelope->currentVolume = 0;
}
}
envelope->currentVolume &= 0xF;
}
_updateEnvelopeDead(envelope);
return (envelope->initialVolume || envelope->direction) && envelope->dead != 2;
_updateEnvelopeDead(envelope, style);
return envelope->initialVolume || envelope->direction;
}
static void _updateSquareSample(struct GBAudioSquareChannel* ch) {
@ -898,14 +939,19 @@ static void _updateEnvelope(struct GBAudioEnvelope* envelope) {
}
}
static void _updateEnvelopeDead(struct GBAudioEnvelope* envelope) {
static void _updateEnvelopeDead(struct GBAudioEnvelope* envelope, enum GBAudioStyle style) {
if (!envelope->stepTime) {
envelope->dead = envelope->currentVolume ? 1 : 2;
} else if (!envelope->direction && !envelope->currentVolume) {
envelope->dead = 2;
} else if (envelope->direction && envelope->currentVolume == 0xF) {
envelope->dead = 1;
} else {
} else if (envelope->dead) {
// TODO: Figure out if this happens on DMG/CGB or just AGB
// TODO: Figure out the exact circumstances that lead to reloading the step
if (style == GB_AUDIO_GBA) {
envelope->nextStep = envelope->stepTime;
}
envelope->dead = 0;
}
}
@ -1066,6 +1112,8 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
audio->ch4.lastEvent = currentTime + (when & (cycles - 1)) - cycles;
}
}
audio->ch4.nSamples = 0;
audio->ch4.samples = 0;
}
void GBAudioSerialize(const struct GBAudio* audio, struct GBSerializedState* state) {

View File

@ -607,16 +607,16 @@ static void _GBCoreReset(struct mCore* core) {
switch (gb->model) {
case GB_MODEL_DMG:
case GB_MODEL_MGB: // TODO
strncat(path, PATH_SEP "gb_bios.bin", PATH_MAX - strlen(path));
strncat(path, PATH_SEP "gb_bios.bin", PATH_MAX - strlen(path) - 1);
break;
case GB_MODEL_SGB:
case GB_MODEL_SGB2: // TODO
strncat(path, PATH_SEP "sgb_bios.bin", PATH_MAX - strlen(path));
strncat(path, PATH_SEP "sgb_bios.bin", PATH_MAX - strlen(path) - 1);
break;
case GB_MODEL_CGB:
case GB_MODEL_AGB:
case GB_MODEL_SCGB:
strncat(path, PATH_SEP "gbc_bios.bin", PATH_MAX - strlen(path));
strncat(path, PATH_SEP "gbc_bios.bin", PATH_MAX - strlen(path) - 1);
break;
default:
break;
@ -644,8 +644,10 @@ static void _GBCoreReset(struct mCore* core) {
size_t i;
for (i = 0; i < sizeof(gbcore->memoryBlocks) / sizeof(*gbcore->memoryBlocks); ++i) {
if (gbcore->memoryBlocks[i].id == GB_REGION_CART_BANK0) {
gbcore->memoryBlocks[i].size = gb->memory.romSize;
gbcore->memoryBlocks[i].maxSegment = gb->memory.romSize / GB_SIZE_CART_BANK0;
} else if (gbcore->memoryBlocks[i].id == GB_REGION_EXTERNAL_RAM) {
gbcore->memoryBlocks[i].size = gb->sramSize;
gbcore->memoryBlocks[i].maxSegment = gb->sramSize / GB_SIZE_EXTERNAL_RAM;
} else {
continue;
@ -1048,9 +1050,11 @@ static void _GBCoreDetachDebugger(struct mCore* core) {
}
static void _GBCoreLoadSymbols(struct mCore* core, struct VFile* vf) {
core->symbolTable = mDebuggerSymbolTableCreate();
if (!core->symbolTable) {
core->symbolTable = mDebuggerSymbolTableCreate();
}
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
if (!vf) {
if (!vf && core->dirs.base) {
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".sym", O_RDONLY);
}
#endif
@ -1088,19 +1092,32 @@ static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) {
static size_t _GBCoreSavedataClone(struct mCore* core, void** sram) {
struct GB* gb = core->board;
struct VFile* vf = gb->sramVf;
if (vf) {
*sram = malloc(vf->size(vf));
vf->seek(vf, 0, SEEK_SET);
return vf->read(vf, *sram, vf->size(vf));
size_t sramSize = gb->sramSize;
size_t vfSize = 0;
size_t size = sramSize;
uint8_t* view = NULL;
if (gb->sramVf) {
vfSize = gb->sramVf->size(gb->sramVf);
if (vfSize > size) {
size = vfSize;
}
}
if (gb->sramSize) {
*sram = malloc(gb->sramSize);
memcpy(*sram, gb->memory.sram, gb->sramSize);
return gb->sramSize;
if (!size) {
*sram = NULL;
return 0;
}
*sram = NULL;
return 0;
view = malloc(size);
if (sramSize) {
memcpy(view, gb->memory.sram, gb->sramSize);
}
if (vfSize > sramSize) {
gb->sramVf->seek(gb->sramVf, sramSize, SEEK_SET);
gb->sramVf->read(gb->sramVf, &view[sramSize], vfSize - sramSize);
}
*sram = view;
return size;
}
static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {

View File

@ -34,7 +34,9 @@ static const uint8_t _registeredTrademark[] = {0x3C, 0x42, 0xB9, 0xA5, 0xB9, 0xA
#define SGB2_BIOS_CHECKSUM 0X53D0DD63
#define CGB_BIOS_CHECKSUM 0x41884E46
#define CGB0_BIOS_CHECKSUM 0xE8EF5318
#define CGBE_BIOS_CHECKSUM 0xE95DC95D
#define AGB_BIOS_CHECKSUM 0xFFD6B0F1
#define AGB0_BIOS_CHECKSUM 0x570337EA
mLOG_DEFINE_CATEGORY(GB, "GB", "gb");
@ -260,9 +262,13 @@ void GBResizeSram(struct GB* gb, size_t size) {
}
struct VFile* vf = gb->sramVf;
if (vf) {
// We have a vf
ssize_t vfSize = vf->size(vf);
if (vf == gb->sramRealVf) {
ssize_t vfSize = vf->size(vf);
// This is the real save file, not a masked one
if (vfSize >= 0 && (size_t) vfSize < size) {
// We need to grow the file
// Make sure to copy the footer data, if any
uint8_t extdataBuffer[0x100];
if (vfSize & 0xFF) {
vf->seek(vf, -(vfSize & 0xFF), SEEK_END);
@ -270,6 +276,7 @@ void GBResizeSram(struct GB* gb, size_t size) {
}
if (gb->memory.sram) {
vf->unmap(vf, gb->memory.sram, gb->sramSize);
gb->memory.sram = NULL;
}
vf->truncate(vf, size + (vfSize & 0xFF));
if (vfSize & 0xFF) {
@ -281,24 +288,36 @@ void GBResizeSram(struct GB* gb, size_t size) {
memset(&gb->memory.sram[vfSize], 0xFF, size - vfSize);
}
} else if (size > gb->sramSize || !gb->memory.sram) {
// We aren't growing the file, but we are changing our mapping of it
if (gb->memory.sram) {
vf->unmap(vf, gb->memory.sram, gb->sramSize);
gb->memory.sram = NULL;
}
if (size) {
gb->memory.sram = vf->map(vf, size, MAP_WRITE);
}
}
} else {
// This is a masked save file
if (gb->memory.sram) {
vf->unmap(vf, gb->memory.sram, gb->sramSize);
}
if (vf->size(vf) < gb->sramSize) {
void* sram = vf->map(vf, vf->size(vf), MAP_READ);
struct VFile* newVf = VFileMemChunk(sram, vf->size(vf));
vf->unmap(vf, sram,vf->size(vf));
vf = newVf;
gb->sramVf = newVf;
vf->truncate(vf, size);
if ((vfSize <= 0 && size) || (size_t) vfSize < size) {
// The loaded mask file is too small. Since these can be read-only,
// we need to make a new one of the right size
if (vfSize < 0) {
vfSize = 0;
}
gb->sramVf = VFileMemChunk(NULL, size);
uint8_t* sram = gb->sramVf->map(gb->sramVf, size, MAP_WRITE);
if (vfSize > 0) {
vf->seek(vf, 0, SEEK_SET);
vf->read(vf, sram, vfSize);
}
memset(&sram[vfSize], 0xFF, size - vfSize);
gb->sramVf->unmap(gb->sramVf, sram, size);
vf->close(vf);
vf = gb->sramVf;
}
if (size) {
gb->memory.sram = vf->map(vf, size, MAP_READ);
@ -308,6 +327,8 @@ void GBResizeSram(struct GB* gb, size_t size) {
gb->memory.sram = NULL;
}
} else if (size) {
// There's no vf, so let's make it only memory-backed
// TODO: Investigate just using a VFileMemChunk instead of this hybrid approach
uint8_t* newSram = anonymousMemoryMap(size);
if (gb->memory.sram) {
if (size > gb->sramSize) {
@ -405,7 +426,9 @@ void GBUnloadROM(struct GB* gb) {
if (gb->romVf) {
#ifndef FIXED_ROM_BUFFER
gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize);
if (gb->isPristine && gb->memory.rom) {
gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize);
}
#endif
gb->romVf->close(gb->romVf);
gb->romVf = NULL;
@ -453,6 +476,9 @@ void GBApplyPatch(struct GB* gb, struct Patch* patch) {
if (patchedSize > GB_SIZE_CART_MAX) {
patchedSize = GB_SIZE_CART_MAX;
}
const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
uint8_t type = cart->type;
void* newRom = anonymousMemoryMap(GB_SIZE_CART_MAX);
if (!patch->applyPatch(patch, gb->memory.rom, gb->pristineRomSize, newRom, patchedSize)) {
mappedMemoryFree(newRom, GB_SIZE_CART_MAX);
@ -471,6 +497,12 @@ void GBApplyPatch(struct GB* gb, struct Patch* patch) {
}
gb->memory.rom = newRom;
gb->memory.romSize = patchedSize;
cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
if (cart->type != type) {
gb->memory.mbcType = GB_MBC_AUTODETECT;
GBMBCInit(gb);
}
gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
}
@ -521,7 +553,9 @@ bool GBIsBIOS(struct VFile* vf) {
case SGB2_BIOS_CHECKSUM:
case CGB_BIOS_CHECKSUM:
case CGB0_BIOS_CHECKSUM:
case CGBE_BIOS_CHECKSUM:
case AGB_BIOS_CHECKSUM:
case AGB0_BIOS_CHECKSUM:
return true;
default:
return false;
@ -596,13 +630,13 @@ void GBReset(struct SM83Core* cpu) {
GBVideoReset(&gb->video);
GBTimerReset(&gb->timer);
GBIOReset(gb);
GBAudioReset(&gb->audio);
if (!gb->biosVf && gb->memory.rom) {
GBSkipBIOS(gb);
} else {
mTimingSchedule(&gb->timing, &gb->timer.event, 0);
}
GBAudioReset(&gb->audio);
GBSIOReset(&gb->sio);
cpu->memory.setActiveRegion(cpu, cpu->pc);
@ -744,6 +778,25 @@ void GBSkipBIOS(struct GB* gb) {
GBUnmapBIOS(gb);
}
GBIOWrite(gb, GB_REG_NR52, 0xF1);
GBIOWrite(gb, GB_REG_NR14, 0x3F);
GBIOWrite(gb, GB_REG_NR10, 0x80);
GBIOWrite(gb, GB_REG_NR11, 0xBF);
GBIOWrite(gb, GB_REG_NR12, 0xF3);
GBIOWrite(gb, GB_REG_NR13, 0xF3);
GBIOWrite(gb, GB_REG_NR24, 0x3F);
GBIOWrite(gb, GB_REG_NR21, 0x3F);
GBIOWrite(gb, GB_REG_NR22, 0x00);
GBIOWrite(gb, GB_REG_NR34, 0x3F);
GBIOWrite(gb, GB_REG_NR30, 0x7F);
GBIOWrite(gb, GB_REG_NR31, 0xFF);
GBIOWrite(gb, GB_REG_NR32, 0x9F);
GBIOWrite(gb, GB_REG_NR44, 0x3F);
GBIOWrite(gb, GB_REG_NR41, 0xFF);
GBIOWrite(gb, GB_REG_NR42, 0x00);
GBIOWrite(gb, GB_REG_NR43, 0x00);
GBIOWrite(gb, GB_REG_NR50, 0x77);
GBIOWrite(gb, GB_REG_NR51, 0xF3);
GBIOWrite(gb, GB_REG_LCDC, 0x91);
gb->memory.io[GB_REG_BANK] = 0x1;
GBVideoSkipBIOS(&gb->video);
@ -796,9 +849,12 @@ void GBDetectModel(struct GB* gb) {
gb->model = GB_MODEL_SGB2;
break;
case CGB_BIOS_CHECKSUM:
case CGB0_BIOS_CHECKSUM:
case CGBE_BIOS_CHECKSUM:
gb->model = GB_MODEL_CGB;
break;
case AGB_BIOS_CHECKSUM:
case AGB0_BIOS_CHECKSUM:
gb->model = GB_MODEL_AGB;
break;
default:

View File

@ -162,36 +162,7 @@ void GBIOReset(struct GB* gb) {
GBIOWrite(gb, GB_REG_TMA, 0);
GBIOWrite(gb, GB_REG_TAC, 0);
GBIOWrite(gb, GB_REG_IF, 1);
gb->audio.playingCh1 = false;
gb->audio.playingCh2 = false;
gb->audio.playingCh3 = false;
gb->audio.playingCh4 = false;
GBIOWrite(gb, GB_REG_NR52, 0xF1);
GBIOWrite(gb, GB_REG_NR14, 0x3F);
GBIOWrite(gb, GB_REG_NR10, 0x80);
GBIOWrite(gb, GB_REG_NR11, 0xBF);
GBIOWrite(gb, GB_REG_NR12, 0xF3);
GBIOWrite(gb, GB_REG_NR13, 0xF3);
GBIOWrite(gb, GB_REG_NR24, 0x3F);
GBIOWrite(gb, GB_REG_NR21, 0x3F);
GBIOWrite(gb, GB_REG_NR22, 0x00);
GBIOWrite(gb, GB_REG_NR34, 0x3F);
GBIOWrite(gb, GB_REG_NR30, 0x7F);
GBIOWrite(gb, GB_REG_NR31, 0xFF);
GBIOWrite(gb, GB_REG_NR32, 0x9F);
GBIOWrite(gb, GB_REG_NR44, 0x3F);
GBIOWrite(gb, GB_REG_NR41, 0xFF);
GBIOWrite(gb, GB_REG_NR42, 0x00);
GBIOWrite(gb, GB_REG_NR43, 0x00);
GBIOWrite(gb, GB_REG_NR50, 0x77);
GBIOWrite(gb, GB_REG_NR51, 0xF3);
if (!gb->biosVf) {
GBIOWrite(gb, GB_REG_LCDC, 0x91);
gb->memory.io[GB_REG_BANK] = 1;
} else {
GBIOWrite(gb, GB_REG_LCDC, 0x00);
gb->memory.io[GB_REG_BANK] = 0xFF;
}
GBIOWrite(gb, GB_REG_LCDC, 0x00);
GBIOWrite(gb, GB_REG_SCY, 0x00);
GBIOWrite(gb, GB_REG_SCX, 0x00);
GBIOWrite(gb, GB_REG_LYC, 0x00);
@ -203,6 +174,7 @@ void GBIOReset(struct GB* gb) {
}
GBIOWrite(gb, GB_REG_WY, 0x00);
GBIOWrite(gb, GB_REG_WX, 0x00);
gb->memory.io[GB_REG_BANK] = 0xFF;
if (gb->model & GB_MODEL_CGB) {
GBIOWrite(gb, GB_REG_KEY0, 0);
GBIOWrite(gb, GB_REG_JOYP, 0xFF);
@ -519,7 +491,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
return;
case GB_REG_SVBK:
GBMemorySwitchWramBank(&gb->memory, value);
value = gb->memory.wramCurrentBank;
value &= 7;
break;
default:
goto failed;
@ -754,7 +726,7 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_SCX, state->io[GB_REG_SCX]);
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_WY, state->io[GB_REG_WY]);
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_WX, state->io[GB_REG_WX]);
if (gb->model & GB_MODEL_SGB) {
if (gb->model == GB_MODEL_SGB) {
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_BGP, state->io[GB_REG_BGP]);
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_OBP0, state->io[GB_REG_OBP0]);
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_OBP1, state->io[GB_REG_OBP1]);

View File

@ -14,6 +14,7 @@
#include <mgba/internal/sm83/sm83.h>
#include <mgba-util/memory.h>
#include <mgba-util/vfs.h>
mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory", "gb.memory");
@ -474,13 +475,14 @@ uint8_t GBView8(struct SM83Core* cpu, uint16_t address, int segment) {
if (memory->rtcAccess) {
return memory->rtcRegs[memory->activeRtcReg];
} else if (memory->sramAccess) {
if (segment < 0 && memory->sram) {
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
} else if ((size_t) segment * GB_SIZE_EXTERNAL_RAM < gb->sramSize) {
return memory->sram[(address & (GB_SIZE_EXTERNAL_RAM - 1)) + segment *GB_SIZE_EXTERNAL_RAM];
} else {
return 0xFF;
if (memory->sram) {
if (segment < 0) {
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
} else if ((size_t) segment * GB_SIZE_EXTERNAL_RAM < gb->sramSize) {
return memory->sram[(address & (GB_SIZE_EXTERNAL_RAM - 1)) + segment *GB_SIZE_EXTERNAL_RAM];
}
}
return 0xFF;
} else if (memory->mbcRead) {
return memory->mbcRead(memory, address);
} else if (memory->mbcType == GB_HuC3) {
@ -558,7 +560,7 @@ uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) {
gb->memory.hdmaDest |= 0x8000;
bool wasHdma = gb->memory.isHdma;
gb->memory.isHdma = value & 0x80;
if ((!wasHdma && !gb->memory.isHdma) || (GBRegisterLCDCIsEnable(gb->memory.io[GB_REG_LCDC]) && gb->video.mode == 0)) {
if ((!wasHdma && !gb->memory.isHdma) || gb->video.mode == 0) {
if (gb->memory.isHdma) {
gb->memory.hdmaRemaining = 0x10;
} else {
@ -566,8 +568,6 @@ uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) {
}
gb->cpuBlocked = true;
mTimingSchedule(&gb->timing, &gb->memory.hdmaEvent, 0);
} else if (gb->memory.isHdma && !GBRegisterLCDCIsEnable(gb->memory.io[GB_REG_LCDC])) {
return 0x80 | ((value + 1) & 0x7F);
}
return value & 0x7F;
}
@ -958,6 +958,11 @@ void _pristineCow(struct GB* gb) {
if (gb->memory.rom == gb->memory.romBase) {
gb->memory.romBase = newRom;
}
if (gb->romVf) {
gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->memory.romSize);
gb->romVf->close(gb->romVf);
gb->romVf = NULL;
}
gb->memory.rom = newRom;
GBMBCSwitchBank(gb, gb->memory.currentBank);
gb->isPristine = false;

View File

@ -122,6 +122,13 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
mLOG(GB_STATE, WARN, "Savestate is corrupted: video y is out of range");
error = true;
}
GBSerializedVideoFlags videoFlags = state->video.flags;
if (check16 >= GB_VIDEO_VERTICAL_PIXELS && GBSerializedVideoFlagsGetMode(videoFlags) != 1) {
mLOG(GB_STATE, WARN, "Savestate is corrupted: video y is in vblank but mode is not vblank");
error = true;
}
LOAD_16LE(ucheck16, 0, &state->memory.dmaDest);
if (ucheck16 + state->memory.dmaRemaining > GB_SIZE_OAM) {
mLOG(GB_STATE, WARN, "Savestate is corrupted: DMA destination is out of range");

View File

@ -91,11 +91,13 @@ void GBSIOWriteSB(struct GBSIO* sio, uint8_t sb) {
void GBSIOWriteSC(struct GBSIO* sio, uint8_t sc) {
sio->period = GBSIOCyclesPerTransfer[GBRegisterSCGetClockSpeed(sc)]; // TODO Shift Clock
if (GBRegisterSCIsEnable(sc)) {
mTimingDeschedule(&sio->p->timing, &sio->event);
if (GBRegisterSCIsShiftClock(sc)) {
mTimingDeschedule(&sio->p->timing, &sio->event);
mTimingSchedule(&sio->p->timing, &sio->event, sio->period * (2 - sio->p->doubleSpeed));
sio->remainingBits = 8;
}
} else {
mTimingDeschedule(&sio->p->timing, &sio->event);
}
if (sio->driver) {
sio->driver->writeSC(sio->driver, sc);

View File

@ -157,7 +157,7 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) {
}
}
// Tell the other GBs they can continue up to where we were
node->p->d.addCycles(&node->p->d, node->id, node->eventDiff);
node->p->d.addCycles(&node->p->d, 0, node->eventDiff);
#ifndef NDEBUG
node->phase = node->p->d.transferActive;
#endif
@ -169,26 +169,28 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) {
static uint32_t _slaveUpdate(struct GBSIOLockstepNode* node) {
enum mLockstepPhase transferActive;
int id;
ATOMIC_LOAD(transferActive, node->p->d.transferActive);
ATOMIC_LOAD(id, node->id);
bool signal = false;
switch (transferActive) {
case TRANSFER_IDLE:
node->p->d.addCycles(&node->p->d, node->id, LOCKSTEP_INCREMENT);
node->p->d.addCycles(&node->p->d, id, LOCKSTEP_INCREMENT);
break;
case TRANSFER_STARTING:
case TRANSFER_FINISHING:
break;
case TRANSFER_STARTED:
if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) {
if (node->p->d.unusedCycles(&node->p->d, id) > node->eventDiff) {
break;
}
node->transferFinished = false;
signal = true;
break;
case TRANSFER_FINISHED:
if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) {
if (node->p->d.unusedCycles(&node->p->d, id) > node->eventDiff) {
break;
}
_finishTransfer(node);
@ -199,7 +201,7 @@ static uint32_t _slaveUpdate(struct GBSIOLockstepNode* node) {
node->phase = node->p->d.transferActive;
#endif
if (signal) {
node->p->d.signal(&node->p->d, 1 << node->id);
node->p->d.signal(&node->p->d, 1 << id);
}
return 0;
}
@ -215,11 +217,13 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
int32_t cycles = 0;
node->nextEvent -= cyclesLate;
if (node->nextEvent <= 0) {
if (!node->id) {
int id;
ATOMIC_LOAD(id, node->id);
if (!id) {
cycles = _masterUpdate(node);
} else {
cycles = _slaveUpdate(node);
cycles += node->p->d.useCycles(&node->p->d, node->id, node->eventDiff);
cycles += node->p->d.useCycles(&node->p->d, id, node->eventDiff);
}
node->eventDiff = 0;
} else {
@ -240,7 +244,9 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
static void GBSIOLockstepNodeWriteSB(struct GBSIODriver* driver, uint8_t value) {
struct GBSIOLockstepNode* node = (struct GBSIOLockstepNode*) driver;
node->p->pendingSB[node->id] = value;
int id;
ATOMIC_LOAD(id, node->id);
node->p->pendingSB[id] = value;
}
static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t value) {
@ -252,11 +258,17 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu
mLockstepLock(&node->p->d);
bool claimed = false;
if (ATOMIC_CMPXCHG(node->p->masterClaimed, claimed, true)) {
if (node->id != 0) {
int id;
ATOMIC_LOAD(id, node->id);
if (id != 0) {
unsigned sb;
node->p->players[0]->id = 1;
node->p->players[1] = node->p->players[0];
node->p->players[0] = node->p->players[1];
node->id = 0;
node->p->players[1] = node->p->players[0];
node->p->players[0] = node;
sb = node->p->pendingSB[0];
node->p->pendingSB[0] = node->p->pendingSB[1];
node->p->pendingSB[1] = sb;
}
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING);
ATOMIC_STORE(node->p->d.transferCycles, GBSIOCyclesPerTransfer[(value >> 1) & 1]);

View File

@ -770,7 +770,10 @@ void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
if (!GBRegisterLCDCIsEnable(video->p->memory.io[GB_REG_LCDC]) || video->p->model >= GB_MODEL_CGB) {
return;
}
if (!_statIRQAsserted(oldStat) && video->mode < 3) {
// Writing to STAT on a DMG selects all STAT IRQ types for one cycle.
// However, the signal that the mode 2 IRQ relies on is only high for
// one cycle, which we don't handle yet. TODO: Handle it.
if (!_statIRQAsserted(oldStat) && (video->mode < 2 || GBRegisterSTATIsLYC(video->stat))) {
// TODO: variable for the IRQ line value?
video->p->memory.io[GB_REG_IF] |= (1 << GB_IRQ_LCDSTAT);
GBUpdateIRQs(video->p);

View File

@ -106,6 +106,9 @@ void GBAAudioDeinit(struct GBAAudio* audio) {
}
void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
if (samples > 0x2000) {
samples = 0x2000;
}
mCoreSyncLockAudio(audio->p->sync);
audio->samples = samples;
blip_clear(audio->psg.left);
@ -128,17 +131,24 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA*
mLOG(GBA_AUDIO, GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest);
return;
}
uint32_t source = info->source;
uint32_t magic[2] = {
audio->p->cpu->memory.load32(audio->p->cpu, source - 0x350, NULL),
audio->p->cpu->memory.load32(audio->p->cpu, source - 0x980, NULL)
};
if (audio->mixer) {
if (magic[0] - MP2K_MAGIC <= MP2K_LOCK_MAX) {
audio->mixer->engage(audio->mixer, source - 0x350);
} else if (magic[1] - MP2K_MAGIC <= MP2K_LOCK_MAX) {
audio->mixer->engage(audio->mixer, source - 0x980);
} else {
uint32_t source = info->source;
uint32_t offsets[] = { 0x350, 0x980 };
size_t i;
for (i = 0; i < sizeof(offsets) / sizeof(*offsets); ++i) {
if (source < BASE_WORKING_RAM + offsets[i]) {
continue;
}
if (source >= BASE_IO + offsets[i]) {
continue;
}
uint32_t value = GBALoad32(audio->p->cpu, source - offsets[i], NULL);
if (value - MP2K_MAGIC <= MP2K_LOCK_MAX) {
audio->mixer->engage(audio->mixer, source - offsets[i]);
break;
}
}
if (i == sizeof(offsets) / sizeof(*offsets)) {
audio->externalMixing = false;
}
}
@ -231,16 +241,39 @@ void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) {
}
void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
audio->enable = GBAudioEnableGetEnable(value);
GBAudioWriteNR52(&audio->psg, value);
if (!audio->enable) {
int i;
for (i = REG_SOUND1CNT_LO; i < REG_SOUNDCNT_HI; i += 2) {
audio->p->memory.io[i >> 1] = 0;
}
audio->psg.ch3.size = 0;
audio->psg.ch3.bank = 0;
audio->psg.ch3.volume = 0;
audio->volume = 0;
audio->volumeChA = 0;
audio->volumeChB = 0;
audio->p->memory.io[REG_SOUNDCNT_HI >> 1] &= 0xFF00;
}
}
void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) {
int32_t timestamp = mTimingCurrentTime(&audio->p->timing);
GBAAudioSample(audio, timestamp);
audio->soundbias = value;
int32_t oldSampleInterval = audio->sampleInterval;
audio->sampleInterval = 0x200 >> GBARegisterSOUNDBIASGetResolution(value);
if (oldSampleInterval != audio->sampleInterval && audio->p->stream && audio->p->stream->audioRateChanged) {
audio->p->stream->audioRateChanged(audio->p->stream, GBA_ARM7TDMI_FREQUENCY / audio->sampleInterval);
if (oldSampleInterval != audio->sampleInterval) {
timestamp -= audio->lastSample;
audio->sampleIndex = timestamp >> (9 - GBARegisterSOUNDBIASGetResolution(value));
if (audio->sampleIndex < 0 || audio->sampleIndex >= GBA_MAX_SAMPLES) {
audio->sampleIndex = 0;
}
if (audio->p->stream && audio->p->stream->audioRateChanged) {
audio->p->stream->audioRateChanged(audio->p->stream, GBA_ARM7TDMI_FREQUENCY / audio->sampleInterval);
}
}
}
@ -327,6 +360,9 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
int bits = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias);
until += 1 << (9 - GBARegisterSOUNDBIASGetResolution(audio->soundbias));
until >>= 9 - GBARegisterSOUNDBIASGetResolution(audio->soundbias);
if (UNLIKELY(bits < until)) {
until = bits;
}
int i;
for (i = bits - until; i < bits; ++i) {
channel->samples[i] = channel->internalSample;
@ -506,6 +542,16 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) {
GBAudioPSGDeserialize(&audio->psg, &state->audio.psg, &state->audio.flags);
uint16_t reg;
LOAD_16(reg, REG_SOUND1CNT_X, state->io);
GBAIOWrite(audio->p, REG_SOUND1CNT_X, reg & 0x7FFF);
LOAD_16(reg, REG_SOUND2CNT_HI, state->io);
GBAIOWrite(audio->p, REG_SOUND2CNT_HI, reg & 0x7FFF);
LOAD_16(reg, REG_SOUND3CNT_X, state->io);
GBAIOWrite(audio->p, REG_SOUND3CNT_X, reg & 0x7FFF);
LOAD_16(reg, REG_SOUND4CNT_HI, state->io);
GBAIOWrite(audio->p, REG_SOUND4CNT_HI, reg & 0x7FFF);
LOAD_32(audio->chA.internalSample, 0, &state->audio.internalA);
LOAD_32(audio->chB.internalSample, 0, &state->audio.internalB);
memcpy(audio->chA.samples, state->samples.chA, sizeof(audio->chA.samples));

View File

@ -336,12 +336,14 @@ static int16_t _ArcTan(int32_t i, int32_t* r1, int32_t* r3, uint32_t* cycles) {
static int16_t _ArcTan2(int32_t x, int32_t y, int32_t* r1, uint32_t* cycles) {
if (!y) {
*cycles = 11;
if (x >= 0) {
return 0;
}
return 0x8000;
}
if (!x) {
*cycles = 11;
if (y >= 0) {
return 0x4000;
}

View File

@ -320,18 +320,20 @@ void _gyroReadPins(struct GBACartridgeHardware* hw) {
return;
}
// Write bit on falling edge
bool doOutput = hw->gyroEdge && !(hw->pinState & 2);
if (hw->pinState & 1) {
if (gyro->sample) {
gyro->sample(gyro);
}
int32_t sample = gyro->readGyroZ(gyro);
// Normalize to ~12 bits, focused on 0x6C0
hw->gyroSample = (sample >> 21) + 0x6C0; // Crop off an extra bit so that we can't go negative
// Normalize to ~12 bits, focused on 0x700
hw->gyroSample = (sample >> 21) + 0x700; // Crop off an extra bit so that we can't go negative
doOutput = true;
}
if (hw->gyroEdge && !(hw->pinState & 2)) {
// Write bit on falling edge
if (doOutput) {
unsigned bit = hw->gyroSample >> 15;
hw->gyroSample <<= 1;
_outputPins(hw, bit << 2);
@ -421,8 +423,8 @@ void GBAHardwareTiltWrite(struct GBACartridgeHardware* hw, uint32_t address, uin
int32_t x = rotationSource->readTiltX(rotationSource);
int32_t y = rotationSource->readTiltY(rotationSource);
// Normalize to ~12 bits, focused on 0x3A0
hw->tiltX = (x >> 21) + 0x3A0; // Crop off an extra bit so that we can't go negative
hw->tiltY = (y >> 21) + 0x3A0;
hw->tiltX = 0x3A0 - (x >> 22);
hw->tiltY = 0x3A0 - (y >> 22);
} else {
mLOG(GBA_HW, GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
}
@ -496,6 +498,18 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer
LOAD_16(hw->direction, 0, &state->hw.pinDirection);
hw->devices = state->hw.devices;
if ((hw->devices & (HW_RTC | HW_RUMBLE | HW_LIGHT_SENSOR | HW_GYRO | HW_TILT)) && hw->gpioBase) {
if (hw->readWrite) {
STORE_16(hw->pinState, 0, hw->gpioBase);
STORE_16(hw->direction, 2, hw->gpioBase);
STORE_16(hw->readWrite, 4, hw->gpioBase);
} else {
hw->gpioBase[0] = 0;
hw->gpioBase[1] = 0;
hw->gpioBase[2] = 0;
}
}
LOAD_32(hw->rtc.bytesRemaining, 0, &state->hw.rtcBytesRemaining);
LOAD_32(hw->rtc.transferStep, 0, &state->hw.rtcTransferStep);
LOAD_32(hw->rtc.bitsRead, 0, &state->hw.rtcBitsRead);

View File

@ -126,13 +126,13 @@ static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_
GBACheatSetGameSharkVersion(set, GBA_GS_PARV3);
}
rgsaP = GBACheatGameSharkProbability(op1, op1);
rgsaP = GBACheatGameSharkProbability(op1, op2);
if (rgsaP > maxProbability) {
maxProbability = rgsaP;
GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1_RAW);
}
rparP = GBACheatProActionReplayProbability(op1, op1);
rparP = GBACheatProActionReplayProbability(op1, op2);
if (rparP > maxProbability) {
maxProbability = rparP;
GBACheatSetGameSharkVersion(set, GBA_GS_PARV3_RAW);
@ -180,14 +180,25 @@ bool GBACheatAddVBALine(struct GBACheatSet* cheats, const char* line) {
return false;
}
struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
cheat->address = address;
cheat->operandOffset = 0;
cheat->addressOffset = 0;
cheat->repeat = 1;
cheat->type = CHEAT_ASSIGN;
cheat->width = width;
cheat->operand = value;
if (address < BASE_CART0 || address >= BASE_CART_SRAM) {
struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
memset(cheat, 0, sizeof(*cheat));
cheat->address = address;
cheat->operandOffset = 0;
cheat->addressOffset = 0;
cheat->repeat = 1;
cheat->type = CHEAT_ASSIGN;
cheat->width = width;
cheat->operand = value;
} else {
struct mCheatPatch* patch = mCheatPatchListAppend(&cheats->d.romPatches);
memset(patch, 0, sizeof(*patch));
patch->width = width;
patch->address = address;
patch->segment = 0;
patch->value = value;
patch->check = false;
}
return true;
}

View File

@ -111,7 +111,7 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = {
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (128kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
};
static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = {
@ -684,7 +684,7 @@ static void _GBACoreReset(struct mCore* core) {
if (!found) {
char path[PATH_MAX];
mCoreConfigDirectory(path, PATH_MAX);
strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path) - 1);
bios = VFileOpen(path, O_RDONLY);
if (bios && GBAIsBIOS(bios)) {
found = true;
@ -700,8 +700,8 @@ static void _GBACoreReset(struct mCore* core) {
#endif
ARMReset(core->cpu);
bool forceSkip = gba->romVf && GBAIsMB(gba->romVf);
if (!(forceSkip || core->opts.skipBios) && (gba->romVf || gba->memory.rom) && gba->pristineRomSize >= 0xA0 && gba->biosVf) {
bool forceSkip = gba->mbVf || (core->opts.skipBios && (gba->romVf || gba->memory.rom));
if (!forceSkip && (gba->romVf || gba->memory.rom) && gba->pristineRomSize >= 0xA0 && gba->biosVf) {
uint32_t crc = doCrc32(&gba->memory.rom[1], 0x9C);
if (crc != LOGO_CRC32) {
mLOG(STATUS, WARN, "Invalid logo, skipping BIOS");
@ -709,7 +709,7 @@ static void _GBACoreReset(struct mCore* core) {
}
}
if (forceSkip || (core->opts.skipBios && (gba->romVf || gba->memory.rom))) {
if (forceSkip) {
GBASkipBIOS(core->board);
}
@ -1112,20 +1112,44 @@ static void _GBACoreDetachDebugger(struct mCore* core) {
}
static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) {
struct GBA* gba = core->board;
bool closeAfter = false;
core->symbolTable = mDebuggerSymbolTableCreate();
if (!core->symbolTable) {
core->symbolTable = mDebuggerSymbolTableCreate();
}
off_t seek;
if (vf) {
seek = vf->seek(vf, 0, SEEK_CUR);
vf->seek(vf, 0, SEEK_SET);
}
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
#ifdef USE_ELF
if (!vf) {
if (!vf && core->dirs.base) {
closeAfter = true;
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".elf", O_RDONLY);
}
#endif
if (!vf) {
closeAfter = true;
if (!vf && core->dirs.base) {
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".sym", O_RDONLY);
if (vf) {
mDebuggerLoadARMIPSSymbols(core->symbolTable, vf);
vf->close(vf);
return;
}
}
#endif
if (!vf && gba->mbVf) {
closeAfter = false;
vf = gba->mbVf;
seek = vf->seek(vf, 0, SEEK_CUR);
vf->seek(vf, 0, SEEK_SET);
}
if (!vf && gba->romVf) {
closeAfter = false;
vf = gba->romVf;
seek = vf->seek(vf, 0, SEEK_CUR);
vf->seek(vf, 0, SEEK_SET);
}
if (!vf) {
return;
}
@ -1136,13 +1160,12 @@ static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) {
mCoreLoadELFSymbols(core->symbolTable, elf);
#endif
ELFClose(elf);
} else
#endif
{
mDebuggerLoadARMIPSSymbols(core->symbolTable, vf);
}
#endif
if (closeAfter) {
vf->close(vf);
} else {
vf->seek(vf, seek, SEEK_SET);
}
}

View File

@ -245,9 +245,6 @@ static void _mp2kReload(struct GBAAudioMixer* mixer) {
}
bool _mp2kEngage(struct GBAAudioMixer* mixer, uint32_t address) {
if (address < BASE_WORKING_RAM) {
return false;
}
if (address != mixer->contextAddress) {
mixer->contextAddress = address;
mixer->p->externalMixing = true;

View File

@ -132,6 +132,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
}
void GBAUnloadROM(struct GBA* gba) {
GBAMemoryClearAGBPrint(gba);
if (gba->memory.rom && !gba->isPristine) {
if (gba->yankedRomSize) {
gba->yankedRomSize = 0;
@ -213,6 +214,7 @@ void GBAReset(struct ARMCore* cpu) {
gba->earlyExit = false;
gba->dmaPC = 0;
gba->biosStall = 0;
gba->keysLast = 0x400;
if (gba->yankedRomSize) {
gba->memory.romSize = gba->yankedRomSize;
gba->memory.romMask = toPow2(gba->memory.romSize) - 1;
@ -274,8 +276,10 @@ void GBASkipBIOS(struct GBA* gba) {
if (cpu->gprs[ARM_PC] == BASE_RESET + WORD_SIZE_ARM) {
if (gba->memory.rom) {
cpu->gprs[ARM_PC] = BASE_CART0;
} else {
} else if (gba->memory.wram[0x30]) {
cpu->gprs[ARM_PC] = BASE_WORKING_RAM + 0xC0;
} else {
cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
}
gba->video.vcount = 0x7E;
gba->memory.io[REG_VCOUNT >> 1] = 0x7E;

View File

@ -50,13 +50,13 @@ const uint8_t hleBios[SIZE_BIOS] = {
0x0c, 0x80, 0xbd, 0xe8, 0x30, 0x40, 0x2d, 0xe9, 0x02, 0x46, 0xa0, 0xe1,
0x00, 0xc0, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, 0x01, 0x04, 0x12, 0xe3,
0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a,
0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xbc, 0xe8, 0x04, 0x00, 0x55, 0xe1,
0x08, 0x00, 0xa5, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x14, 0x00, 0x00, 0xea,
0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xb0, 0xe8, 0x04, 0x00, 0x51, 0xe1,
0x08, 0x00, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x14, 0x00, 0x00, 0xea,
0x01, 0xc0, 0xcc, 0xe3, 0x01, 0x50, 0xc5, 0xe3, 0xa4, 0x45, 0x85, 0xe0,
0xb0, 0x30, 0xdc, 0xe1, 0x04, 0x00, 0x55, 0xe1, 0xb2, 0x30, 0xc5, 0xb0,
0xfc, 0xff, 0xff, 0xba, 0x0c, 0x00, 0x00, 0xea, 0x01, 0x03, 0x12, 0xe3,
0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1,
0x08, 0x00, 0xbc, 0xb8, 0x08, 0x00, 0xa5, 0xb8, 0xfb, 0xff, 0xff, 0xba,
0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x51, 0xe1,
0x08, 0x00, 0xb0, 0xb8, 0x08, 0x00, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba,
0x04, 0x00, 0x00, 0xea, 0xa4, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1,
0xb2, 0x30, 0xdc, 0xb0, 0xb2, 0x30, 0xc5, 0xb0, 0xfb, 0xff, 0xff, 0xba,
0x17, 0x3e, 0xa0, 0xe3, 0x30, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9,

View File

@ -209,10 +209,10 @@ tst r2, #0x04000000
beq 1f
@ Word
add r4, r5, r4, lsr #10
ldmia r12!, {r3}
ldmia r0!, {r3}
2:
cmp r5, r4
stmltia r5!, {r3}
cmp r1, r4
stmltia r1!, {r3}
blt 2b
b 3f
@ Halfword
@ -233,9 +233,9 @@ beq 1f
@ Word
add r4, r5, r4, lsr #10
2:
cmp r5, r4
ldmltia r12!, {r3}
stmltia r5!, {r3}
cmp r1, r4
ldmltia r0!, {r3}
stmltia r1!, {r3}
blt 2b
b 3f
@ Halfword

View File

@ -296,8 +296,8 @@ static const int _isWSpecialRegister[REG_INTERNAL_MAX >> 1] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
// Audio
1, 1, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0,
0, 0, 1, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0,
@ -378,17 +378,19 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
break;
case REG_SOUND1CNT_HI:
GBAAudioWriteSOUND1CNT_HI(&gba->audio, value);
value &= 0xFFC0;
break;
case REG_SOUND1CNT_X:
GBAAudioWriteSOUND1CNT_X(&gba->audio, value);
value &= 0x47FF;
value &= 0x4000;
break;
case REG_SOUND2CNT_LO:
GBAAudioWriteSOUND2CNT_LO(&gba->audio, value);
value &= 0xFFC0;
break;
case REG_SOUND2CNT_HI:
GBAAudioWriteSOUND2CNT_HI(&gba->audio, value);
value &= 0x47FF;
value &= 0x4000;
break;
case REG_SOUND3CNT_LO:
GBAAudioWriteSOUND3CNT_LO(&gba->audio, value);
@ -396,16 +398,15 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
break;
case REG_SOUND3CNT_HI:
GBAAudioWriteSOUND3CNT_HI(&gba->audio, value);
value &= 0xE03F;
value &= 0xE000;
break;
case REG_SOUND3CNT_X:
GBAAudioWriteSOUND3CNT_X(&gba->audio, value);
// TODO: The low bits need to not be readable, but still 8-bit writable
value &= 0x47FF;
value &= 0x4000;
break;
case REG_SOUND4CNT_LO:
GBAAudioWriteSOUND4CNT_LO(&gba->audio, value);
value &= 0xFF3F;
value &= 0xFF00;
break;
case REG_SOUND4CNT_HI:
GBAAudioWriteSOUND4CNT_HI(&gba->audio, value);
@ -583,6 +584,21 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
case REG_MAX:
// Some bad interrupt libraries will write to this
break;
case REG_POSTFLG:
if (gba->memory.activeRegion == REGION_BIOS) {
if (gba->memory.io[address >> 1]) {
if (value & 0x8000) {
GBAStop(gba);
} else {
GBAHalt(gba);
}
}
value &= ~0x8000;
} else {
mLOG(GBA_IO, GAME_ERROR, "Write to BIOS-only I/O register: %03X", address);
return;
}
break;
case REG_EXWAITCNT_HI:
// This register sits outside of the normal I/O block, so we need to stash it somewhere unused
address = REG_INTERNAL_EXWAITCNT_HI;
@ -615,19 +631,6 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
}
void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) {
if (address == REG_HALTCNT) {
value &= 0x80;
if (!value) {
GBAHalt(gba);
} else {
GBAStop(gba);
}
return;
}
if (address == REG_POSTFLG) {
gba->memory.io[(address & (SIZE_IO - 1)) >> 1] = value;
return;
}
if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) {
gba->debugString[address - REG_DEBUG_STRING] = value;
return;
@ -635,9 +638,96 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) {
if (address > SIZE_IO) {
return;
}
uint16_t value16 = value << (8 * (address & 1));
value16 |= (gba->memory.io[(address & (SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1)));
GBAIOWrite(gba, address & 0xFFFFFFFE, value16);
uint16_t value16;
switch (address) {
case REG_SOUND1CNT_HI:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR11(&gba->audio.psg, value);
gba->memory.io[REG_SOUND1CNT_HI >> 1] &= 0xFF00;
gba->memory.io[REG_SOUND1CNT_HI >> 1] |= value & 0xC0;
break;
case REG_SOUND1CNT_HI + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR12(&gba->audio.psg, value);
gba->memory.io[REG_SOUND1CNT_HI >> 1] &= 0x00C0;
gba->memory.io[REG_SOUND1CNT_HI >> 1] |= value << 8;
break;
case REG_SOUND1CNT_X:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR13(&gba->audio.psg, value);
break;
case REG_SOUND1CNT_X + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR14(&gba->audio.psg, value);
gba->memory.io[REG_SOUND1CNT_X >> 1] = (value & 0x40) << 8;
break;
case REG_SOUND2CNT_LO:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR21(&gba->audio.psg, value);
gba->memory.io[REG_SOUND2CNT_LO >> 1] &= 0xFF00;
gba->memory.io[REG_SOUND2CNT_LO >> 1] |= value & 0xC0;
break;
case REG_SOUND2CNT_LO + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR22(&gba->audio.psg, value);
gba->memory.io[REG_SOUND2CNT_LO >> 1] &= 0x00C0;
gba->memory.io[REG_SOUND2CNT_LO >> 1] |= value << 8;
break;
case REG_SOUND2CNT_HI:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR23(&gba->audio.psg, value);
break;
case REG_SOUND2CNT_HI + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR24(&gba->audio.psg, value);
gba->memory.io[REG_SOUND2CNT_HI >> 1] = (value & 0x40) << 8;
break;
case REG_SOUND3CNT_HI:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR31(&gba->audio.psg, value);
break;
case REG_SOUND3CNT_HI + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
gba->audio.psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value);
gba->memory.io[REG_SOUND3CNT_HI >> 1] = (value & 0xE0) << 8;
break;
case REG_SOUND3CNT_X:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR33(&gba->audio.psg, value);
break;
case REG_SOUND3CNT_X + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR34(&gba->audio.psg, value);
gba->memory.io[REG_SOUND3CNT_X >> 1] = (value & 0x40) << 8;
break;
case REG_SOUND4CNT_LO:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR41(&gba->audio.psg, value);
break;
case REG_SOUND4CNT_LO + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR42(&gba->audio.psg, value);
gba->memory.io[REG_SOUND4CNT_LO >> 1] = value << 8;
break;
case REG_SOUND4CNT_HI:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR43(&gba->audio.psg, value);
gba->memory.io[REG_SOUND4CNT_HI >> 1] &= 0x4000;
gba->memory.io[REG_SOUND4CNT_HI >> 1] |= value;
break;
case REG_SOUND4CNT_HI + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR44(&gba->audio.psg, value);
gba->memory.io[REG_SOUND4CNT_HI >> 1] &= 0x00FF;
gba->memory.io[REG_SOUND4CNT_HI >> 1] |= (value & 0x40) << 8;
break;
default:
value16 = value << (8 * (address & 1));
value16 |= (gba->memory.io[(address & (SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1)));
GBAIOWrite(gba, address & 0xFFFFFFFE, value16);
break;
}
}
void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {
@ -858,10 +948,6 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
gba->memory.io[REG_JOYSTAT >> 1] &= ~JOYSTAT_RECV;
break;
case REG_POSTFLG:
mLOG(GBA_IO, STUB, "Stub I/O register read: %03x", address);
break;
// Wave RAM can be written and read even if the audio hardware is disabled.
// However, it is not possible to switch between the two banks because it
// isn't possible to write to register SOUND3CNT_LO.
@ -935,6 +1021,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
case REG_IF:
case REG_WAITCNT:
case REG_IME:
case REG_POSTFLG:
// Handled transparently by registers
break;
case 0x066:
@ -949,6 +1036,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
case 0x142:
case 0x15A:
case 0x206:
case 0x302:
mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address);
return 0;
// These registers sit outside of the normal I/O block, so we need to stash them somewhere unused

View File

@ -128,6 +128,21 @@ void GBAMemoryReset(struct GBA* gba) {
GBAAdjustWaitstates(gba, 0);
GBAAdjustEWRAMWaitstates(gba, 0x0D00);
GBAMemoryClearAGBPrint(gba);
gba->memory.prefetch = false;
gba->memory.lastPrefetchedPc = 0;
if (!gba->memory.wram || !gba->memory.iwram) {
GBAMemoryDeinit(gba);
mLOG(GBA_MEM, FATAL, "Could not map memory");
}
GBADMAReset(gba);
memset(&gba->memory.matrix, 0, sizeof(gba->memory.matrix));
}
void GBAMemoryClearAGBPrint(struct GBA* gba) {
gba->memory.activeRegion = -1;
gba->memory.agbPrintProtect = 0;
gba->memory.agbPrintBase = 0;
@ -140,17 +155,6 @@ void GBAMemoryReset(struct GBA* gba) {
mappedMemoryFree(gba->memory.agbPrintBufferBackup, SIZE_AGB_PRINT);
gba->memory.agbPrintBufferBackup = NULL;
}
gba->memory.prefetch = false;
gba->memory.lastPrefetchedPc = 0;
if (!gba->memory.wram || !gba->memory.iwram) {
GBAMemoryDeinit(gba);
mLOG(GBA_MEM, FATAL, "Could not map memory");
}
GBADMAReset(gba);
memset(&gba->memory.matrix, 0, sizeof(gba->memory.matrix));
}
static void _analyzeForIdleLoop(struct GBA* gba, struct ARMCore* cpu, uint32_t address) {
@ -397,7 +401,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \
} \
++wait; \
if (gba->video.shouldStall) { \
if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { \
wait += GBAMemoryStallVRAM(gba, wait, 1); \
}
@ -557,7 +561,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
} else {
LOAD_16(value, address & 0x0001FFFE, gba->video.vram);
}
if (gba->video.shouldStall) {
if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) {
wait += GBAMemoryStallVRAM(gba, wait, 0);
}
break;
@ -777,7 +781,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
} \
} \
++wait; \
if (gba->video.shouldStall) { \
if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { \
wait += GBAMemoryStallVRAM(gba, wait, 1); \
}
@ -904,7 +908,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
}
}
if (gba->video.shouldStall) {
if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) {
wait += GBAMemoryStallVRAM(gba, wait, 0);
}
break;
@ -1114,10 +1118,8 @@ uint32_t GBAView32(struct ARMCore* cpu, uint32_t address) {
value = GBALoad32(cpu, address, 0);
break;
case REGION_IO:
if ((address & OFFSET_MASK) < REG_MAX) {
value = gba->memory.io[(address & OFFSET_MASK) >> 1];
value |= gba->memory.io[((address & OFFSET_MASK) >> 1) + 1] << 16;
}
value = GBAView16(cpu, address);
value |= GBAView16(cpu, address + 2) << 16;
break;
case REGION_CART_SRAM:
value = GBALoad8(cpu, address, 0);
@ -1155,7 +1157,10 @@ uint16_t GBAView16(struct ARMCore* cpu, uint32_t address) {
value = GBALoad16(cpu, address, 0);
break;
case REGION_IO:
if ((address & OFFSET_MASK) < REG_MAX) {
if ((address & OFFSET_MASK) < REG_MAX || (address & OFFSET_MASK) == REG_POSTFLG) {
value = gba->memory.io[(address & OFFSET_MASK) >> 1];
} else if ((address & OFFSET_MASK) == REG_EXWAITCNT_LO || (address & OFFSET_MASK) == REG_EXWAITCNT_HI) {
address += REG_INTERNAL_EXWAITCNT_LO - REG_EXWAITCNT_LO;
value = gba->memory.io[(address & OFFSET_MASK) >> 1];
}
break;

View File

@ -123,10 +123,8 @@ static const struct GBACartridgeOverride _overrides[] = {
{ "BPEF", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false },
// Pokemon Mystery Dungeon
{ "B24J", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
{ "B24E", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
{ "B24P", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
{ "B24U", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
// Pokemon FireRed
{ "BPRJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },

View File

@ -233,6 +233,29 @@ static const char* const _interpolate =
" aff[2] = transform[start + 2].zw;\n"
" mat[3] = transform[start + 3].xy;\n"
" aff[3] = transform[start + 3].zw;\n"
"}\n"
"ivec2 affineInterpolate() {\n"
" ivec2 mat[4];\n"
" ivec2 offset[4];\n"
" vec2 incoord = texCoord;\n"
" if (mosaic.x > 1) {\n"
" incoord.x = float(MOSAIC(incoord.x, mosaic.x));\n"
" }\n"
" if (mosaic.y > 1) {\n"
" incoord.y = float(MOSAIC(incoord.y, mosaic.y));\n"
" }\n"
" loadAffine(int(incoord.y), mat, offset);\n"
" float y = fract(incoord.y);\n"
" float start = 2. / 3.;\n"
" if (int(incoord.y) - range.x < 4) {\n"
" y = incoord.y - float(range.x);\n"
" start -= 1.;\n"
" }\n"
" float lin = start + y / 3.;\n"
" vec2 mixedTransform = interpolate(mat, lin);\n"
" vec2 mixedOffset = interpolate(offset, lin);\n"
" return ivec2(mixedTransform * incoord.x + mixedOffset);\n"
"}\n";
static const char* const _renderMode2 =
@ -250,8 +273,7 @@ static const char* const _renderMode2 =
"OUT(0) out vec4 color;\n"
"int fetchTile(ivec2 coord);\n"
"vec2 interpolate(ivec2 arr[4], float x);\n"
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
"ivec2 affineInterpolate();\n"
"int renderTile(ivec2 coord) {\n"
" int map = (coord.x >> 11) + (((coord.y >> 7) & 0x7F0) << size);\n"
@ -278,26 +300,7 @@ static const char* const _renderMode2 =
"}\n"
"void main() {\n"
" ivec2 mat[4];\n"
" ivec2 offset[4];\n"
" vec2 incoord = texCoord;\n"
" if (mosaic.x > 1) {\n"
" incoord.x = float(MOSAIC(incoord.x, mosaic.x));\n"
" }\n"
" if (mosaic.y > 1) {\n"
" incoord.y = float(MOSAIC(incoord.y, mosaic.y));\n"
" }\n"
" loadAffine(int(incoord.y), mat, offset);\n"
" float y = fract(incoord.y);\n"
" float start = 0.75;\n"
" if (int(incoord.y) - range.x < 4) {\n"
" y = incoord.y - float(range.x);\n"
" start = 0.;\n"
" }\n"
" float lin = start + y * 0.25;\n"
" vec2 mixedTransform = interpolate(mat, lin);\n"
" vec2 mixedOffset = interpolate(offset, lin);\n"
" int paletteEntry = fetchTile(ivec2(mixedTransform * incoord.x + mixedOffset));\n"
" int paletteEntry = fetchTile(affineInterpolate());\n"
" color = texelFetch(palette, ivec2(paletteEntry, int(texCoord.y)), 0);\n"
"}";
@ -325,30 +328,10 @@ static const char* const _renderMode35 =
"uniform ivec2 mosaic;\n"
"OUT(0) out vec4 color;\n"
"vec2 interpolate(ivec2 arr[4], float x);\n"
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
"ivec2 affineInterpolate();\n"
"void main() {\n"
" ivec2 mat[4];\n"
" ivec2 offset[4];\n"
" vec2 incoord = texCoord;\n"
" if (mosaic.x > 1) {\n"
" incoord.x = float(MOSAIC(incoord.x, mosaic.x));\n"
" }\n"
" if (mosaic.y > 1) {\n"
" incoord.y = float(MOSAIC(incoord.y, mosaic.y));\n"
" }\n"
" loadAffine(int(incoord.y), mat, offset);\n"
" float y = fract(incoord.y);\n"
" float start = 0.75;\n"
" if (int(incoord.y) - range.x < 4) {\n"
" y = incoord.y - float(range.x);\n"
" start = 0.;\n"
" }\n"
" float lin = start + y * 0.25;\n"
" vec2 mixedTransform = interpolate(mat, lin);\n"
" vec2 mixedOffset = interpolate(offset, lin);\n"
" ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n"
" ivec2 coord = affineInterpolate();\n"
" if (coord.x < 0 || coord.x >= (size.x << 8)) {\n"
" discard;\n"
" }\n"
@ -386,30 +369,10 @@ static const char* const _renderMode4 =
"uniform ivec2 mosaic;\n"
"OUT(0) out vec4 color;\n"
"vec2 interpolate(ivec2 arr[4], float x);\n"
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
"ivec2 affineInterpolate();\n"
"void main() {\n"
" ivec2 mat[4];\n"
" ivec2 offset[4];\n"
" vec2 incoord = texCoord;\n"
" if (mosaic.x > 1) {\n"
" incoord.x = float(MOSAIC(incoord.x, mosaic.x));\n"
" }\n"
" if (mosaic.y > 1) {\n"
" incoord.y = float(MOSAIC(incoord.y, mosaic.y));\n"
" }\n"
" loadAffine(int(incoord.y), mat, offset);\n"
" float y = fract(incoord.y);\n"
" float start = 0.75;\n"
" if (int(incoord.y) - range.x < 4) {\n"
" y = incoord.y - float(range.x);\n"
" start = 0.;\n"
" }\n"
" float lin = start + y * 0.25;\n"
" vec2 mixedTransform = interpolate(mat, lin);\n"
" vec2 mixedOffset = interpolate(offset, lin);\n"
" ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n"
" ivec2 coord = affineInterpolate();\n"
" if (coord.x < 0 || coord.x >= (size.x << 8)) {\n"
" discard;\n"
" }\n"
@ -664,10 +627,12 @@ static const char* const _finalize =
" if (((topFlags.y & 13) == 5 || topFlags.w > 0) && (bottomFlags.y & 2) == 2) {\n"
" topPixel.rgb *= float(topFlags.z) / 16.;\n"
" topPixel.rgb += bottomPixel.rgb * float(windowFlags.y) / 16.;\n"
" } else if ((topFlags.y & 13) == 9) {\n"
" topPixel.rgb += (1. - topPixel.rgb) * float(windowFlags.z) / 16.;\n"
" } else if ((topFlags.y & 13) == 13) {\n"
" topPixel.rgb -= topPixel.rgb * float(windowFlags.z) / 16.;\n"
" } else if (topFlags.w == 0) { \n"
" if ((topFlags.y & 13) == 9) {\n"
" topPixel.rgb += (1. - topPixel.rgb) * float(windowFlags.z) / 16.;\n"
" } else if ((topFlags.y & 13) == 13) {\n"
" topPixel.rgb -= topPixel.rgb * float(windowFlags.z) / 16.;\n"
" }\n"
" }\n"
" color = topPixel;\n"
"}";

View File

@ -169,10 +169,10 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
(renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT || (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || objwinSlowPath) {
int target2 = renderer->target2Bd;
target2 |= renderer->bg[0].target2;
target2 |= renderer->bg[1].target2;
target2 |= renderer->bg[2].target2;
target2 |= renderer->bg[3].target2;
target2 |= renderer->bg[0].target2 && renderer->bg[0].enabled;
target2 |= renderer->bg[1].target2 && renderer->bg[1].enabled;
target2 |= renderer->bg[2].target2 && renderer->bg[2].enabled;
target2 |= renderer->bg[3].target2 && renderer->bg[3].enabled;
if (target2) {
renderer->forceTarget1 = true;
flags |= FLAG_REBLEND;

View File

@ -13,6 +13,10 @@
#include <mgba-util/memory.h>
#include <mgba-util/vfs.h>
#ifdef PSP2
#include <psp2/rtc.h>
#endif
#include <errno.h>
#include <fcntl.h>
@ -184,7 +188,7 @@ size_t GBASavedataSize(const struct GBASavedata* savedata) {
bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in) {
if (savedata->data) {
if (!in && savedata->type != SAVEDATA_FORCE_NONE) {
if (!in || savedata->type == SAVEDATA_FORCE_NONE) {
return false;
}
ssize_t size = GBASavedataSize(savedata);
@ -598,14 +602,23 @@ void GBASavedataRTCWrite(struct GBASavedata* savedata) {
size_t size = GBASavedataSize(savedata);
savedata->vf->seek(savedata->vf, size & ~0xFF, SEEK_SET);
int bank = 0;
if ((savedata->vf->size(savedata->vf) & 0xFF) != sizeof(buffer)) {
// Writing past the end of the file can invalidate the file mapping
if (savedata->type == SAVEDATA_FLASH1M) {
bank = savedata->currentBank == &savedata->data[0x10000];
}
savedata->vf->unmap(savedata->vf, savedata->data, size);
savedata->data = NULL;
}
savedata->vf->write(savedata->vf, &buffer, sizeof(buffer));
if (!savedata->data) {
savedata->data = savedata->vf->map(savedata->vf, size, MAP_WRITE);
if (savedata->type == SAVEDATA_FLASH1M) {
savedata->currentBank = &savedata->data[bank << 16];
} else if (savedata->type == SAVEDATA_FLASH512) {
savedata->currentBank = savedata->data;
}
}
}
@ -637,6 +650,9 @@ void GBASavedataRTCRead(struct GBASavedata* savedata) {
}
LOAD_64LE(savedata->gpio->rtc.lastLatch, 0, &buffer.lastLatch);
time_t rtcTime;
#ifndef PSP2
struct tm date;
date.tm_year = _unBCD(savedata->gpio->rtc.time[0]) + 100;
date.tm_mon = _unBCD(savedata->gpio->rtc.time[1]) - 1;
@ -645,8 +661,40 @@ void GBASavedataRTCRead(struct GBASavedata* savedata) {
date.tm_min = _unBCD(savedata->gpio->rtc.time[5]);
date.tm_sec = _unBCD(savedata->gpio->rtc.time[6]);
date.tm_isdst = -1;
rtcTime = mktime(&date);
#else
struct SceDateTime date;
date.year = _unBCD(savedata->gpio->rtc.time[0]) + 2000;
date.month = _unBCD(savedata->gpio->rtc.time[1]);
date.day = _unBCD(savedata->gpio->rtc.time[2]);
date.hour = _unBCD(savedata->gpio->rtc.time[4]);
date.minute = _unBCD(savedata->gpio->rtc.time[5]);
date.second = _unBCD(savedata->gpio->rtc.time[6]);
date.microsecond = 0;
savedata->gpio->rtc.offset = savedata->gpio->rtc.lastLatch - mktime(&date);
struct SceRtcTick tick;
int res;
res = sceRtcConvertDateTimeToTick(&date, &tick);
if (res < 0) {
mLOG(GBA_SAVE, ERROR, "sceRtcConvertDateTimeToTick %lx", res);
}
res = sceRtcConvertLocalTimeToUtc(&tick, &tick);
if (res < 0) {
mLOG(GBA_SAVE, ERROR, "sceRtcConvertUtcToLocalTime %lx", res);
}
res = sceRtcConvertTickToDateTime(&tick, &date);
if (res < 0) {
mLOG(GBA_SAVE, ERROR, "sceRtcConvertTickToDateTime %lx", res);
}
res = sceRtcConvertDateTimeToTime_t(&date, &rtcTime);
if (res < 0) {
mLOG(GBA_SAVE, ERROR, "sceRtcConvertDateTimeToTime_t %lx", res);
}
#endif
savedata->gpio->rtc.offset = savedata->gpio->rtc.lastLatch - rtcTime;
mLOG(GBA_SAVE, DEBUG, "Savegame time offset set to %li", savedata->gpio->rtc.offset);
}
void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state) {

View File

@ -105,17 +105,32 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
switch (node->mode) {
case SIO_MULTI:
node->d.writeRegister = GBASIOLockstepNodeMultiWriteRegister;
node->d.p->rcnt |= 3;
ATOMIC_ADD(node->p->attachedMulti, 1);
node->d.p->siocnt = GBASIOMultiplayerSetReady(node->d.p->siocnt, node->p->attachedMulti == node->p->d.attached);
if (node->id) {
node->d.p->rcnt |= 4;
node->d.p->siocnt = GBASIOMultiplayerFillSlave(node->d.p->siocnt);
int try;
for (try = 0; try < 3; ++try) {
uint16_t masterSiocnt;
ATOMIC_LOAD(masterSiocnt, node->p->players[0]->d.p->siocnt);
if (ATOMIC_CMPXCHG(node->p->players[0]->d.p->siocnt, masterSiocnt, GBASIOMultiplayerClearSlave(masterSiocnt))) {
break;
}
}
} else {
node->d.p->rcnt &= ~4;
node->d.p->siocnt = GBASIOMultiplayerClearSlave(node->d.p->siocnt);
}
break;
case SIO_NORMAL_8:
case SIO_NORMAL_32:
ATOMIC_ADD(node->p->attachedNormal, 1);
if (ATOMIC_ADD(node->p->attachedNormal, 1) > node->id + 1 && node->id > 0) {
node->d.p->siocnt = GBASIONormalSetSi(node->d.p->siocnt, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt));
} else {
node->d.p->siocnt = GBASIONormalClearSi(node->d.p->siocnt);
}
node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister;
break;
default:
@ -197,8 +212,6 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver
mTimingDeschedule(&driver->p->p->timing, &node->event);
}
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
} else {
value &= ~0x0080;
}
}
value &= 0xFF83;
@ -447,7 +460,7 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
struct GBASIOLockstepNode* node = user;
mLockstepLock(&node->p->d);
int32_t cycles = cycles = node->nextEvent;
int32_t cycles = node->nextEvent;
node->nextEvent -= cyclesLate;
node->eventDiff += cyclesLate;
if (node->p->d.attached < 2) {
@ -495,11 +508,28 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
if (address == REG_SIOCNT) {
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value);
int attached;
ATOMIC_LOAD(attached, node->p->attachedNormal);
value &= 0xFF8B;
if (!node->id) {
if (node->id > 0) {
value = GBASIONormalSetSi(value, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt));
} else {
value = GBASIONormalClearSi(value);
}
if (value & 0x0080) {
enum mLockstepPhase transferActive;
ATOMIC_LOAD(transferActive, node->p->d.transferActive);
if (node->id < 3 && attached > node->id + 1 && transferActive == TRANSFER_IDLE) {
int try;
for (try = 0; try < 3; ++try) {
GBASIONormal nextSiocnct;
ATOMIC_LOAD(nextSiocnct, node->p->players[node->id + 1]->d.p->siocnt);
if (ATOMIC_CMPXCHG(node->p->players[node->id + 1]->d.p->siocnt, nextSiocnct, GBASIONormalSetSi(nextSiocnct, GBASIONormalGetIdleSo(value)))) {
break;
}
}
}
if ((value & 0x0081) == 0x0081) {
if (!node->id) {
// Frequency
int32_t cycles;
@ -512,9 +542,6 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
cycles *= 4;
}
enum mLockstepPhase transferActive;
ATOMIC_LOAD(transferActive, node->p->d.transferActive);
if (transferActive == TRANSFER_IDLE) {
mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id);
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING);
@ -529,7 +556,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
value &= ~0x0080;
}
} else {
// TODO
}
}
} else if (address == REG_SIODATA32_LO) {

View File

@ -34,7 +34,7 @@ static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
if (timerId < 3) {
struct GBATimer* nextTimer = &gba->timers[timerId + 1];
if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
if (GBATimerFlagsIsCountUp(nextTimer->flags) && GBATimerFlagsIsEnable(nextTimer->flags)) {
++gba->memory.io[REG_TMCNT_LO(timerId + 1) >> 1];
if (!gba->memory.io[REG_TMCNT_LO(timerId + 1) >> 1] && GBATimerFlagsIsEnable(nextTimer->flags)) {
GBATimerUpdate(gba, timerId + 1, cyclesLate);

View File

@ -702,7 +702,7 @@ static int32_t _readTiltY(struct mRotationSource* source) {
static int32_t _readGyroZ(struct mRotationSource* source) {
struct m3DSRotationSource* rotation = (struct m3DSRotationSource*) source;
return rotation->gyro.y << 18L; // Yes, y
return rotation->gyro.y << 17L; // Yes, y
}
static void _startRequestImage(struct mImageSource* source, unsigned w, unsigned h, int colorFormats) {

View File

@ -617,6 +617,39 @@ void retro_run(void) {
core->desiredVideoDimensions(core, &width, &height);
videoCallback(outputBuffer, width, height, BYTES_PER_PIXEL * 256);
#ifdef M_CORE_GBA
if (core->platform(core) == mPLATFORM_GBA) {
blip_t *audioChannelLeft = core->getAudioChannel(core, 0);
blip_t *audioChannelRight = core->getAudioChannel(core, 1);
int samplesAvail = blip_samples_avail(audioChannelLeft);
if (samplesAvail > 0) {
/* Update 'running average' of number of
* samples per frame.
* Note that this is not a true running
* average, but just a leaky-integrator/
* exponential moving average, used because
* it is simple and fast (i.e. requires no
* window of samples). */
audioSamplesPerFrameAvg = (SAMPLES_PER_FRAME_MOVING_AVG_ALPHA * (float)samplesAvail) +
((1.0f - SAMPLES_PER_FRAME_MOVING_AVG_ALPHA) * audioSamplesPerFrameAvg);
size_t samplesToRead = (size_t)(audioSamplesPerFrameAvg);
/* Resize audio output buffer, if required */
if (audioSampleBufferSize < (samplesToRead * 2)) {
audioSampleBufferSize = (samplesToRead * 2);
audioSampleBuffer = realloc(audioSampleBuffer, audioSampleBufferSize * sizeof(int16_t));
}
int produced = blip_read_samples(audioChannelLeft, audioSampleBuffer, samplesToRead, true);
blip_read_samples(audioChannelRight, audioSampleBuffer + 1, samplesToRead, true);
if (produced > 0) {
if (audioLowPassEnabled) {
_audioLowPassFilter(audioSampleBuffer, produced);
}
audioCallback(audioSampleBuffer, (size_t)produced);
}
}
}
#endif
if (rumbleCallback) {
if (rumbleUp) {
rumbleCallback(0, RETRO_RUMBLE_STRONG, rumbleUp * 0xFFFF / (rumbleUp + rumbleDown));
@ -1363,7 +1396,7 @@ static void _updateRotation(struct mRotationSource* source) {
tiltY = sensorGetCallback(0, RETRO_SENSOR_ACCELEROMETER_Y) * 2e8f;
}
if (gyroEnabled) {
gyroZ = sensorGetCallback(0, RETRO_SENSOR_GYROSCOPE_Z) * -1.1e9f;
gyroZ = sensorGetCallback(0, RETRO_SENSOR_GYROSCOPE_Z) * -5.5e8f;
}
}

View File

@ -1010,8 +1010,11 @@ bool mGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
}
}
u = mGLES2UniformListSize(&uniformVector);
struct mGLES2Uniform* uniformBlock = calloc(u, sizeof(*uniformBlock));
memcpy(uniformBlock, mGLES2UniformListGetPointer(&uniformVector, 0), sizeof(*uniformBlock) * u);
struct mGLES2Uniform* uniformBlock;
if (u) {
uniformBlock = calloc(u, sizeof(*uniformBlock));
memcpy(uniformBlock, mGLES2UniformListGetPointer(&uniformVector, 0), sizeof(*uniformBlock) * u);
}
mGLES2UniformListDeinit(&uniformVector);
mGLES2ShaderInit(&shaderBlock[n], vssrc, fssrc, width, height, scaling, uniformBlock, u);
@ -1048,6 +1051,7 @@ bool mGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
for (n = 0; n < inShaders; ++n) {
mGLES2ShaderDeinit(&shaderBlock[n]);
}
free(shaderBlock);
}
}
}

View File

@ -28,6 +28,7 @@ set(OS_LIB -lvita2d -l${M_LIBRARY}
-lScePgf_stub
-lScePhotoExport_stub
-lScePower_stub
-lSceRtc_stub
-lSceSysmodule_stub
-lSceTouch_stub)
set(OS_LIB ${OS_LIB} PARENT_SCOPE)

View File

@ -229,7 +229,7 @@ int main() {
},
{ .id = 0 }
},
.nConfigExtra = 2,
.nConfigExtra = 3,
.setup = mPSP2Setup,
.teardown = mPSP2Teardown,
.gameLoaded = mPSP2LoadROM,

View File

@ -129,7 +129,7 @@ static THREAD_ENTRY _audioThread(void* context) {
sceAudioOutOutput(audioPort, buffer);
}
sceAudioOutReleasePort(audioPort);
return 0;
THREAD_EXIT(0);
}
static void _sampleRotation(struct mRotationSource* source) {
@ -149,7 +149,7 @@ static int32_t _readTiltY(struct mRotationSource* source) {
static int32_t _readGyroZ(struct mRotationSource* source) {
struct mSceRotationSource* rotation = (struct mSceRotationSource*) source;
return rotation->state.gyro.z * -0x10000000;
return rotation->state.gyro.z * -0x8000000;
}
static void _setRumble(struct mRumble* source, int enable) {

View File

@ -284,7 +284,10 @@ struct VDirSceDevList {
static const char* _devs[] = {
"ux0:",
"ur0:",
"uma0:"
"uma0:",
"imc0:",
"xmc0:",
NULL
};
struct VDir* VDeviceList() {
@ -322,7 +325,7 @@ static void _vdlsceRewind(struct VDir* vd) {
static struct VDirEntry* _vdlsceListNext(struct VDir* vd) {
struct VDirSceDevList* vdl = (struct VDirSceDevList*) vd;
while (vdl->vde.index < 3) {
while (vdl->vde.index < 0 || _devs[vdl->vde.index]) {
++vdl->vde.index;
vdl->vde.name = _devs[vdl->vde.index];
SceUID dir = sceIoDopen(vdl->vde.name);

View File

@ -74,7 +74,7 @@ AboutScreen::AboutScreen(QWidget* parent)
{
QString copyright = m_ui.copyright->text();
copyright.replace("{year}", QLatin1String("2022"));
copyright.replace("{year}", QLatin1String("2023"));
m_ui.copyright->setText(copyright);
}
}

View File

@ -7,6 +7,7 @@
#include <QDir>
#include <QFileInfo>
#include <QRegularExpression>
#include "ApplicationUpdatePrompt.h"
#include "ConfigController.h"
@ -71,9 +72,10 @@ QStringList ApplicationUpdater::listChannels() {
}
QString ApplicationUpdater::currentChannel() {
QLatin1String version(projectVersion);
QLatin1String branch(gitBranch);
if (branch == QLatin1String("heads/") + version) {
QString version(projectVersion);
QString branch(gitBranch);
QRegularExpression stable("^(?:(?:refs/)?(?:tags|heads)/)?[0-9]+\\.[0-9]+\\.[0-9]+$");
if (branch.contains(stable) || (branch == "(unknown)" && version.contains(stable))) {
return QLatin1String("stable");
} else {
return QLatin1String("dev");
@ -137,7 +139,15 @@ QUrl ApplicationUpdater::parseManifest(const QByteArray& manifest) {
QString ApplicationUpdater::destination() const {
QFileInfo path(updateInfo().url.path());
QDir dir(ConfigController::configDir());
return dir.filePath(QLatin1String("update.") + path.completeSuffix());
// QFileInfo::completeSuffix will eat all .'s in the filename...including
// ones in the version string, turning mGBA-1.0.0-win32.7z into
// 0.0-win32.7z instead of the intended .7z
// As a result, so we have to split out the complete suffix manually.
QString suffix(path.suffix());
if (path.completeBaseName().endsWith(".tar")) {
suffix = "tar." + suffix;
}
return dir.filePath(QLatin1String("update.") + suffix);
}
const char* ApplicationUpdater::platform() {
@ -166,7 +176,8 @@ const char* ApplicationUpdater::platform() {
}
ApplicationUpdater::UpdateInfo::UpdateInfo(const QString& prefix, const mUpdate* update)
: size(update->size)
: rev(-1)
, size(update->size)
, url(prefix + update->path)
{
if (update->rev > 0) {

View File

@ -44,20 +44,21 @@ if(APPLE)
endif()
if(Qt6Widgets_VERSION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.14")
set(MIN_VER 10.14)
elseif(Qt5Widgets_VERSION MATCHES "^5.15")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.13")
set(MIN_VER 10.13)
elseif(Qt5Widgets_VERSION MATCHES "^5.1[234]")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.12")
set(MIN_VER 10.12)
elseif(Qt5Widgets_VERSION MATCHES "^5.11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.11")
set(MIN_VER 10.11)
elseif(Qt5Widgets_VERSION MATCHES "^5.(9|10)")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.10")
set(MIN_VER 10.10)
elseif(Qt5Widgets_VERSION MATCHES "^5.8")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.9")
set(MIN_VER 10.9)
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.8")
set(MIN_VER 10.8)
endif()
set(CMAKE_OSX_DEPLOYMENT_TARGET ${MIN_VER})
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
@ -118,6 +119,7 @@ set(SOURCE_FILES
MessagePainter.cpp
MultiplayerController.cpp
ObjView.cpp
OpenGLBug.cpp
OverrideView.cpp
PaletteView.cpp
PlacementControl.cpp
@ -300,7 +302,7 @@ if(WIN32)
endif()
if(NOT DEFINED DATADIR)
if(APPLE)
set(DATADIR ${APPDIR}/${PROJECT_NAME}.app/Contents/Resources)
set(DATADIR ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.app/Contents/Resources)
elseif(WIN32 AND NOT WIN32_UNIX_PATHS)
set(DATADIR ".")
else()
@ -384,6 +386,7 @@ set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CM
if(WIN32)
set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
target_link_libraries(${BINARY_NAME}-qt dwmapi)
if(NOT MSVC)
target_link_libraries(${BINARY_NAME}-qt -municode)
endif()
@ -399,7 +402,7 @@ if(QT_STATIC)
if(CMAKE_CROSSCOMPILING)
set(QWINDOWS_DEPS ${QT}EventDispatcherSupport ${QT}FontDatabaseSupport ${QT}ThemeSupport ${QT}WindowsUIAutomationSupport)
endif()
list(APPEND QT_LIBRARIES ${QT}::QWindowsIntegrationPlugin ${QWINDOWS_DEPS} amstrmid dwmapi uxtheme imm32 -static-libgcc -static-libstdc++)
list(APPEND QT_LIBRARIES ${QT}::QWindowsIntegrationPlugin ${QWINDOWS_DEPS} amstrmid uxtheme imm32 -static-libgcc -static-libstdc++)
set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};version;winmm;ssl;crypto;ws2_32;iphlpapi;crypt32;userenv;netapi32;wtsapi32")
set_target_properties(${QT}::Gui PROPERTIES INTERFACE_LINK_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
elseif(APPLE)
@ -413,15 +416,12 @@ if(QT_STATIC)
list(APPEND QT_LIBRARIES "-framework AVFoundation" "-framework CoreMedia" "-framework SystemConfiguration" "-framework Security")
set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE}")
elseif(UNIX)
list(APPEND QT_LIBRARIES ${QT}::FontDatabaseSupport ${QT}::XcbQpa)
list(APPEND QT_LIBRARIES ${QT}::FontDatabaseSupport ${QT}::XcbQpa ${QT}::QWaylandIntegrationPlugin)
endif()
endif()
target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE)
install(TARGETS ${BINARY_NAME}-qt
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt
BUNDLE DESTINATION ${APPDIR} COMPONENT ${BINARY_NAME}-qt)
if(UNIX AND NOT APPLE)
install(FILES ${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop DESTINATION share/applications RENAME io.mgba.${PROJECT_NAME}.desktop COMPONENT ${BINARY_NAME}-qt)
endif()
@ -447,6 +447,10 @@ if(APPLE)
file(GLOB_RECURSE PLUGINS \"${BUNDLE_PATH}/Contents/PlugIns/*${CMAKE_SHARED_LIBRARY_SUFFIX}\")
fixup_bundle(\"${BUNDLE_PATH}\" \"${PLUGINS}\" \"\")
" COMPONENT ${BINARY_NAME}-qt)
if(CODESIGN_IDENTITY)
install(CODE "execute_process(COMMAND codesign -s \"${CODESIGN_IDENTITY}\" -vf -o runtime --timestamp --entitlements \"${CMAKE_SOURCE_DIR}/res/entitlements.plist\" \"${BUNDLE_PATH}\")"
COMPONENT ${BINARY_NAME}-qt)
endif()
else()
set(DEPLOY_OPTIONS -p platforms/libqcocoa.dylib,audio/libqtaudio_coreaudio.dylib,mediaservice/libqavfcamera.dylib)
if(NOT CMAKE_INSTALL_NAME_TOOL EQUAL "install_name_tool")
@ -458,6 +462,9 @@ if(APPLE)
if(DEFINED CROSS_ROOT)
set(DEPLOY_OPTIONS ${DEPLOY_OPTIONS} -R "${CROSS_ROOT}")
endif()
if($ENV{CODESIGN_IDENTITY})
set(DEPLOY_OPTIONS ${DEPLOY_OPTIONS} -s "$ENV{CODESIGN_IDENTITY}" -E "${CMAKE_SOURCE_DIR}/res/entitlements.plist")
endif()
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()
elseif(WIN32)
@ -485,3 +492,7 @@ if(DISTBUILD AND NOT APPLE)
add_custom_command(TARGET ${BINARY_NAME}-qt POST_BUILD COMMAND "${STRIP}" "$<TARGET_FILE:${BINARY_NAME}-qt>")
endif()
endif()
install(TARGETS ${BINARY_NAME}-qt
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt
BUNDLE DESTINATION ${APPDIR} COMPONENT ${BINARY_NAME}-qt)

View File

@ -289,13 +289,6 @@ void CoreController::loadConfig(ConfigController* config) {
mCoreConfigCopyValue(&m_threadContext.core->config, config->config(), "mute");
m_preload = config->getOption("preload").toInt();
int playerId = m_multiplayer->playerId(this) + 1;
QVariant savePlayerId = config->getOption("savePlayerId");
if (m_multiplayer->attached() < 2 && savePlayerId.canConvert<int>()) {
playerId = savePlayerId.toInt();
}
mCoreConfigSetOverrideIntValue(&m_threadContext.core->config, "savePlayerId", playerId);
QSize sizeBefore = screenDimensions();
m_activeBuffer.resize(256 * 224 * sizeof(color_t));
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer.data()), sizeBefore.width());
@ -518,9 +511,6 @@ void CoreController::setRewinding(bool rewind) {
}
void CoreController::rewind(int states) {
if (!states) {
return;
}
if (!m_threadContext.core->opts.rewindEnable) {
emit statusPosted(tr("Rewinding not currently enabled"));
}
@ -712,7 +702,7 @@ void CoreController::saveState(const QString& path, int flags) {
vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
vf->close(vf);
}
vf = VFileDevice::open(controller->m_statePath, O_WRONLY | O_CREAT | O_TRUNC);
vf = VFileDevice::open(controller->m_statePath, O_RDWR | O_CREAT | O_TRUNC);
if (!vf) {
return;
}
@ -785,10 +775,14 @@ void CoreController::loadSave(const QString& path, bool temporary) {
return;
}
bool ok;
if (temporary) {
m_threadContext.core->loadTemporarySave(m_threadContext.core, vf);
ok = m_threadContext.core->loadTemporarySave(m_threadContext.core, vf);
} else {
m_threadContext.core->loadSave(m_threadContext.core, vf);
ok = m_threadContext.core->loadSave(m_threadContext.core, vf);
}
if (!ok) {
vf->close(vf);
}
});
if (hasStarted()) {
@ -798,10 +792,14 @@ void CoreController::loadSave(const QString& path, bool temporary) {
void CoreController::loadSave(VFile* vf, bool temporary) {
m_resetActions.append([this, vf, temporary]() {
bool ok;
if (temporary) {
m_threadContext.core->loadTemporarySave(m_threadContext.core, vf);
ok = m_threadContext.core->loadTemporarySave(m_threadContext.core, vf);
} else {
m_threadContext.core->loadSave(m_threadContext.core, vf);
ok = m_threadContext.core->loadSave(m_threadContext.core, vf);
}
if (!ok) {
vf->close(vf);
}
});
if (hasStarted()) {
@ -914,7 +912,10 @@ void CoreController::scanCard(const QString& path) {
if (!file.open(QIODevice::ReadOnly)) {
return;
}
m_eReaderData = file.read(2912);
QByteArray eReaderData = file.read(2912);
if (eReaderData.isEmpty()) {
return;
}
file.seek(0);
QStringList lines;
@ -936,6 +937,7 @@ void CoreController::scanCard(const QString& path) {
}
}
scanCards(lines);
m_eReaderData = eReaderData;
} else if (image.size() == QSize(989, 44) || image.size() == QSize(639, 44)) {
const uchar* bits = image.constBits();
size_t size;
@ -1215,7 +1217,12 @@ void CoreController::updatePlayerSave() {
int savePlayerId = 0;
mCoreConfigGetIntValue(&m_threadContext.core->config, "savePlayerId", &savePlayerId);
if (savePlayerId == 0 || m_multiplayer->attached() > 1) {
savePlayerId = m_multiplayer->playerId(this) + 1;
if (savePlayerId == m_multiplayer->playerId(this) + 1) {
// Player 1 is using our save, so let's use theirs, at least for now.
savePlayerId = 1;
} else {
savePlayerId = m_multiplayer->playerId(this) + 1;
}
}
QString saveSuffix;

View File

@ -16,6 +16,7 @@
#endif
#include <mgba/core/core.h>
#include <mgba-util/string.h>
#include <mgba-util/vfs.h>
using namespace QGBA;
@ -163,7 +164,7 @@ CoreController* CoreManager::loadBIOS(int platform, const QString& path) {
mCoreConfigSetOverrideIntValue(&core->config, "skipBios", 0);
QByteArray bytes(info.baseName().toUtf8());
strncpy(core->dirs.baseName, bytes.constData(), sizeof(core->dirs.baseName));
strlcpy(core->dirs.baseName, bytes.constData(), sizeof(core->dirs.baseName));
bytes = info.dir().canonicalPath().toUtf8();
mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData()));

View File

@ -25,7 +25,7 @@ protected:
private:
Ui::DebuggerConsole m_ui;
int m_historyOffset;
int m_historyOffset = 0;
DebuggerConsoleController* m_consoleController;
};

View File

@ -111,10 +111,11 @@ void QGBA::Display::configure(ConfigController* config) {
filter(opts->resampleVideo);
config->updateOption("showOSD");
config->updateOption("showFrameCounter");
config->updateOption("videoSync");
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (opts->shader) {
if (opts->shader && supportsShaders()) {
struct VDir* shader = VDirOpen(opts->shader);
if (shader && supportsShaders()) {
if (shader) {
setShaders(shader);
shader->close(shader);
}

View File

@ -72,6 +72,7 @@ public slots:
virtual void showOSDMessages(bool enable);
virtual void showFrameCounter(bool enable);
virtual void filter(bool filter);
virtual void swapInterval(int interval) = 0;
virtual void framePosted() = 0;
virtual void setShaders(struct VDir*) = 0;
virtual void clearShaders() = 0;

View File

@ -38,12 +38,29 @@ using QOpenGLFunctions_Baseline = QOpenGLFunctions_3_2_Core;
#endif
#endif
#ifdef _WIN32
#include <windows.h>
#elif defined(Q_OS_MAC)
#include <OpenGL/OpenGL.h>
#endif
#ifdef USE_GLX
#define GLX_GLXEXT_PROTOTYPES
typedef struct _XDisplay Display;
#include <GL/glx.h>
#include <GL/glxext.h>
#endif
#ifdef USE_EGL
#include <EGL/egl.h>
#endif
#ifdef _WIN32
#define OVERHEAD_NSEC 1000000
#else
#define OVERHEAD_NSEC 300000
#endif
#include "OpenGLBug.h"
using namespace QGBA;
QHash<QSurfaceFormat, bool> DisplayGL::s_supports;
@ -63,6 +80,10 @@ mGLWidget::mGLWidget(QWidget* parent)
connect(&m_refresh, &QTimer::timeout, this, static_cast<void (QWidget::*)()>(&QWidget::update));
}
mGLWidget::~mGLWidget() {
// This is needed for unique_ptr<QOpenGLPaintDevice> to work
}
void mGLWidget::initializeGL() {
m_vao = std::make_unique<QOpenGLVertexArrayObject>();
m_vao->create();
@ -92,6 +113,8 @@ void mGLWidget::initializeGL() {
m_vaoDone = false;
m_tex = 0;
m_paintDev = std::make_unique<QOpenGLPaintDevice>();
}
bool mGLWidget::finalizeVAO() {
@ -143,6 +166,23 @@ void mGLWidget::paintGL() {
} else {
m_refresh.start(17);
}
if (m_showOSD && m_messagePainter) {
qreal r = window()->devicePixelRatio();
m_paintDev->setDevicePixelRatio(r);
m_paintDev->setSize(size() * r);
QPainter painter(m_paintDev.get());
m_messagePainter->paint(&painter);
painter.end();
}
}
void mGLWidget::setMessagePainter(MessagePainter* messagePainter) {
m_messagePainter = messagePainter;
}
void mGLWidget::setShowOSD(bool showOSD) {
m_showOSD = showOSD;
}
DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
@ -150,6 +190,8 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
{
setAttribute(Qt::WA_NativeWindow);
window()->windowHandle()->setFormat(format);
windowHandle()->setSurfaceType(QSurface::OpenGLSurface);
windowHandle()->destroy();
windowHandle()->create();
#ifdef USE_SHARE_WIDGET
@ -163,6 +205,7 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
m_gl = new mGLWidget;
m_gl->setAttribute(Qt::WA_NativeWindow);
m_gl->setFormat(format);
m_gl->setMessagePainter(messagePainter());
QBoxLayout* layout = new QVBoxLayout;
layout->addWidget(m_gl);
layout->setContentsMargins(0, 0, 0, 0);
@ -226,7 +269,7 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
messagePainter()->resize(size(), devicePixelRatio());
#endif
CoreController::Interrupter interrupter(controller);
CoreController::Interrupter interrupter(m_context);
QMetaObject::invokeMethod(m_painter.get(), "start");
if (!m_gl) {
if (shouldDisableUpdates()) {
@ -348,6 +391,9 @@ void DisplayGL::interframeBlending(bool enable) {
void DisplayGL::showOSDMessages(bool enable) {
Display::showOSDMessages(enable);
if (m_gl) {
m_gl->setShowOSD(enable);
}
QMetaObject::invokeMethod(m_painter.get(), "showOSD", Q_ARG(bool, enable));
}
@ -361,6 +407,10 @@ void DisplayGL::filter(bool filter) {
QMetaObject::invokeMethod(m_painter.get(), "filter", Q_ARG(bool, filter));
}
void DisplayGL::swapInterval(int interval) {
QMetaObject::invokeMethod(m_painter.get(), "swapInterval", Q_ARG(int, interval));
}
void DisplayGL::framePosted() {
m_painter->enqueue(m_context->drawContext());
QMetaObject::invokeMethod(m_painter.get(), "draw");
@ -477,7 +527,9 @@ void PainterGL::create() {
mGLES2Context* gl2Backend;
#endif
m_paintDev = std::make_unique<QOpenGLPaintDevice>();
if (!m_widget) {
m_paintDev = std::make_unique<QOpenGLPaintDevice>();
}
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (m_supportsShaders) {
@ -503,10 +555,10 @@ void PainterGL::create() {
painter->makeCurrent();
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(painter->m_backend);
if (painter->m_widget && painter->supportsShaders()) {
QOpenGLFunctions_Baseline* fn = painter->m_gl->versionFunctions<QOpenGLFunctions_Baseline>();
QOpenGLFunctions* fn = painter->m_gl->functions();
fn->glFinish();
mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(painter->m_backend);
painter->m_widget->setTex(painter->m_finalTex[painter->m_finalTexIdx]);
painter->m_finalTexIdx ^= 1;
gl2Backend->finalShader.tex = painter->m_finalTex[painter->m_finalTexIdx];
@ -540,6 +592,9 @@ void PainterGL::create() {
m_backend->filter = false;
m_backend->lockAspectRatio = false;
m_backend->interframeBlending = false;
m_gl->doneCurrent();
emit created();
}
void PainterGL::destroy() {
@ -591,8 +646,10 @@ void PainterGL::setMessagePainter(MessagePainter* messagePainter) {
void PainterGL::resize(const QSize& size) {
qreal r = m_window->devicePixelRatio();
m_size = size;
m_paintDev->setSize(m_size * r);
m_paintDev->setDevicePixelRatio(r);
if (m_paintDev) {
m_paintDev->setSize(m_size * r);
m_paintDev->setDevicePixelRatio(r);
}
if (m_started && !m_active) {
forceDraw();
}
@ -627,8 +684,46 @@ void PainterGL::filter(bool filter) {
}
}
void PainterGL::swapInterval(int interval) {
if (!m_started) {
return;
}
m_swapInterval = interval;
#ifdef Q_OS_WIN
wglSwapIntervalEXT(interval);
#elif defined(Q_OS_MAC)
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
#else
#ifdef USE_GLX
if (QGuiApplication::platformName() == "xcb") {
::Display* display = glXGetCurrentDisplay();
GLXDrawable drawable = glXGetCurrentDrawable();
glXSwapIntervalEXT(display, drawable, interval);
}
#endif
#ifdef USE_EGL
if (QGuiApplication::platformName().contains("egl")) {
EGLDisplay display = eglGetCurrentDisplay();
eglSwapInterval(display, interval);
}
#endif
#endif
}
#ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS
#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
#endif
void PainterGL::start() {
makeCurrent();
#if defined(BUILD_GLES3) && !defined(Q_OS_MAC)
if (glContextHasBug(OpenGLBug::GLTHREAD_BLOCKS_SWAP)) {
// Suggested on Discord as a way to strongly hint that glthread should be disabled
// See https://gitlab.freedesktop.org/mesa/mesa/-/issues/8035
QOpenGLFunctions* fn = m_gl->functions();
fn->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
#endif
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (m_supportsShaders && m_shader.passes) {
@ -640,6 +735,7 @@ void PainterGL::start() {
m_buffer = nullptr;
m_active = true;
m_started = true;
swapInterval(1);
emit started();
}
@ -670,12 +766,16 @@ void PainterGL::draw() {
}
return;
}
int wantSwap = sync->audioWait || sync->videoFrameWait;
if (m_swapInterval != wantSwap) {
swapInterval(wantSwap);
}
dequeue();
bool forceRedraw = !m_videoProxy;
bool forceRedraw = true;
if (!m_delayTimer.isValid()) {
m_delayTimer.start();
} else {
if (sync->audioWait || sync->videoFrameWait) {
if (wantSwap) {
while (m_delayTimer.nsecsElapsed() + OVERHEAD_NSEC < 1000000000 / sync->fpsTarget) {
QThread::usleep(500);
}
@ -725,13 +825,13 @@ void PainterGL::doStop() {
m_videoProxy->processData();
}
}
m_backend->clear(m_backend);
m_backend->swap(m_backend);
if (m_videoProxy) {
m_videoProxy->reset();
m_videoProxy->moveToThread(m_window->thread());
m_videoProxy.reset();
}
m_backend->clear(m_backend);
m_backend->swap(m_backend);
}
void PainterGL::pause() {
@ -751,7 +851,7 @@ void PainterGL::performDraw() {
m_backend->postFrame(m_backend, m_buffer);
}
m_backend->drawFrame(m_backend);
if (m_showOSD && m_messagePainter) {
if (m_showOSD && m_messagePainter && m_paintDev && !glContextHasBug(OpenGLBug::IG4ICD_CRASH)) {
m_painter.begin(m_paintDev.get());
m_messagePainter->paint(&m_painter);
m_painter.end();
@ -777,20 +877,18 @@ void PainterGL::enqueue(const uint32_t* backing) {
void PainterGL::dequeue() {
QMutexLocker locker(&m_mutex);
if (m_queue.isEmpty()) {
return;
if (!m_queue.isEmpty()) {
uint32_t* buffer = m_queue.dequeue();
if (m_buffer) {
m_free.append(m_buffer);
}
m_buffer = buffer;
}
uint32_t* buffer = m_queue.dequeue();
if (m_buffer) {
m_free.append(m_buffer);
m_buffer = nullptr;
}
m_buffer = buffer;
}
void PainterGL::dequeueAll(bool keep) {
QMutexLocker locker(&m_mutex);
uint32_t* buffer = 0;
uint32_t* buffer = nullptr;
while (!m_queue.isEmpty()) {
buffer = m_queue.dequeue();
if (keep) {
@ -821,12 +919,21 @@ void PainterGL::setShaders(struct VDir* dir) {
return;
}
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (!m_started) {
makeCurrent();
}
if (m_shader.passes) {
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
mGLES2ShaderFree(&m_shader);
}
mGLES2ShaderLoad(&m_shader, dir);
mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses);
if (mGLES2ShaderLoad(&m_shader, dir)) {
mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses);
}
if (!m_started) {
m_gl->doneCurrent();
}
#endif
}
@ -835,10 +942,18 @@ void PainterGL::clearShaders() {
return;
}
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (!m_started) {
makeCurrent();
}
if (m_shader.passes) {
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
mGLES2ShaderFree(&m_shader);
}
if (!m_started) {
m_gl->doneCurrent();
}
#endif
}
@ -861,4 +976,12 @@ int PainterGL::glTex() {
#endif
}
QOpenGLContext* PainterGL::shareContext() {
if (m_widget) {
return m_widget->context();
} else {
return m_gl.get();
}
}
#endif

View File

@ -51,9 +51,12 @@ Q_OBJECT
public:
mGLWidget(QWidget* parent = nullptr);
~mGLWidget();
void setTex(GLuint tex) { m_tex = tex; }
void setVBO(GLuint vbo) { m_vbo = vbo; }
void setMessagePainter(MessagePainter*);
void setShowOSD(bool showOSD);
bool finalizeVAO();
void reset();
@ -72,6 +75,9 @@ private:
QTimer m_refresh;
int m_refreshResidue = 0;
std::unique_ptr<QOpenGLPaintDevice> m_paintDev;
MessagePainter* m_messagePainter = nullptr;
bool m_showOSD = false;
};
class PainterGL;
@ -102,6 +108,7 @@ public slots:
void showOSDMessages(bool enable) override;
void showFrameCounter(bool enable) override;
void filter(bool filter) override;
void swapInterval(int interval) override;
void framePosted() override;
void setShaders(struct VDir*) override;
void clearShaders() override;
@ -143,6 +150,8 @@ public:
bool supportsShaders() const { return m_supportsShaders; }
int glTex();
QOpenGLContext* shareContext();
void setVideoProxy(std::shared_ptr<VideoProxy>);
void interrupt();
@ -162,6 +171,7 @@ public slots:
void showOSD(bool enable);
void showFrameCounter(bool enable);
void filter(bool filter);
void swapInterval(int interval);
void resizeContext();
void setShaders(struct VDir*);
@ -169,7 +179,9 @@ public slots:
VideoShader* shaders();
signals:
void created();
void started();
void texSwapped();
private slots:
void doStop();
@ -184,6 +196,7 @@ private:
QList<uint32_t*> m_free;
QQueue<uint32_t*> m_queue;
uint32_t* m_buffer = nullptr;
QPainter m_painter;
QMutex m_mutex;
QWindow* m_window;
@ -209,6 +222,7 @@ private:
MessagePainter* m_messagePainter = nullptr;
QElapsedTimer m_delayTimer;
std::shared_ptr<VideoProxy> m_videoProxy;
int m_swapInterval = -1;
};
}

View File

@ -31,6 +31,7 @@ public slots:
void lockAspectRatio(bool lock) override;
void lockIntegerScaling(bool lock) override;
void interframeBlending(bool enable) override;
void swapInterval(int) override {};
void filter(bool filter) override;
void framePosted() override;
void setShaders(struct VDir*) override {}

View File

@ -316,7 +316,7 @@ void GBAApp::cleanupAfterUpdate() {
void GBAApp::restartForUpdate() {
QFileInfo updaterPath(m_updater.updateInfo().url.path());
QDir configDir(ConfigController::configDir());
if (updaterPath.completeSuffix() == "exe") {
if (updaterPath.suffix() == "exe") {
m_invokeOnExit = configDir.filePath(QLatin1String("update.exe"));
} else {
QFile updater(":/updater");

View File

@ -44,7 +44,7 @@ GDBWindow::GDBWindow(GDBController* controller, QWidget* parent)
connect(m_portEdit, &QLineEdit::textChanged, this, &GDBWindow::portChanged);
settingsGrid->addWidget(m_portEdit, 0, 1, Qt::AlignLeft);
m_bindAddressEdit = new QLineEdit("0.0.0.0");
m_bindAddressEdit = new QLineEdit("127.0.0.1");
m_bindAddressEdit->setMaxLength(15);
connect(m_bindAddressEdit, &QLineEdit::textChanged, this, &GDBWindow::bindAddressChanged);
settingsGrid->addWidget(m_bindAddressEdit, 1, 1, Qt::AlignLeft);

View File

@ -39,6 +39,7 @@ static const QList<GBMemoryBankControllerType> s_mbcList{
GB_UNL_BBD,
GB_UNL_HITEK,
GB_UNL_SACHEN_MMC1,
GB_UNL_SACHEN_MMC2,
};
static QMap<GBModel, QString> s_gbModelNames;

View File

@ -1323,7 +1323,7 @@ const QList<IOViewer::RegisterDescription>& IOViewer::registerDescriptions(mPlat
});
// 0xFF40: LCDC
regGB.append({
{ tr("Background enable/priority"), 1 },
{ tr("Background enable/priority"), 0 },
{ tr("Enable sprites"), 1 },
{ tr("Double-height sprites"), 2 },
{ tr("Background tile map"), 3, 1, {
@ -1689,7 +1689,15 @@ void IOViewer::bitFlipped() {
void IOViewer::writeback() {
{
CoreController::Interrupter interrupter(m_controller);
GBAIOWrite(static_cast<GBA*>(m_controller->thread()->core->board), m_register, m_value);
mCore* core = m_controller->thread()->core;
switch (m_width) {
case 0:
core->busWrite8(core, m_base + m_register, m_value);
break;
case 1:
core->busWrite16(core, m_base + m_register, m_value);
break;
}
}
updateRegister();
}

View File

@ -8,6 +8,7 @@
#include "GamepadAxisEvent.h"
#include "GamepadButtonEvent.h"
#include "ShortcutController.h"
#include "utils.h"
#include <QCoreApplication>
#include <QFontMetrics>
@ -33,35 +34,7 @@ void KeyEditor::setValue(int key) {
if (key < 0) {
setText(tr("---"));
} else {
QKeySequence seq(key);
switch (key) {
#ifndef Q_OS_MAC
case Qt::Key_Shift:
setText(QCoreApplication::translate("QShortcut", "Shift"));
break;
case Qt::Key_Control:
setText(QCoreApplication::translate("QShortcut", "Control"));
break;
case Qt::Key_Alt:
setText(QCoreApplication::translate("QShortcut", "Alt"));
break;
case Qt::Key_Meta:
setText(QCoreApplication::translate("QShortcut", "Meta"));
break;
#endif
case Qt::Key_Super_L:
setText(tr("Super (L)"));
break;
case Qt::Key_Super_R:
setText(tr("Super (R)"));
break;
case Qt::Key_Menu:
setText(tr("Menu"));
break;
default:
setText(QKeySequence(key).toString(QKeySequence::NativeText));
break;
}
setText(keyName(key));
}
}
emit valueChanged(key);

View File

@ -79,8 +79,8 @@ public slots:
private:
mLogFilter m_filter;
bool m_logToFile;
bool m_logToStdout;
bool m_logToFile = false;
bool m_logToStdout = false;
std::unique_ptr<QFile> m_logFile;
std::unique_ptr<QTextStream> m_logStream;

View File

@ -42,7 +42,7 @@ MapView::MapView(std::shared_ptr<CoreController> controller, QWidget* parent)
#ifdef M_CORE_GBA
case mPLATFORM_GBA:
m_boundary = 2048;
m_ui.tile->setMaxTile(3096);
m_ui.tile->setMaxTile(3072);
m_addressBase = BASE_VRAM;
m_addressWidth = 8;
m_ui.bgInfo->addCustomProperty("priority", tr("Priority"));
@ -119,6 +119,9 @@ void MapView::selectMap(int map) {
}
m_map = map;
m_mapStatus.fill({});
// Different maps can have different max palette counts; set it to
// 0 immediately to avoid tile lookups with state palette IDs break
m_ui.tile->setPalette(0);
updateTiles(true);
}
@ -184,11 +187,18 @@ void MapView::updateTilesGBA(bool) {
frame = GBARegisterDISPCNTGetFrameSelect(io[REG_DISPCNT >> 1]);
}
}
m_boundary = 1024;
m_ui.tile->setMaxTile(1536);
priority = GBARegisterBGCNTGetPriority(io[(REG_BG0CNT >> 1) + m_map]);
if (mode == 0 || (mode == 1 && m_map != 2)) {
offset = QString("%1, %2")
.arg(io[(REG_BG0HOFS >> 1) + (m_map << 1)])
.arg(io[(REG_BG0VOFS >> 1) + (m_map << 1)]);
if (!GBARegisterBGCNTIs256Color(io[(REG_BG0CNT >> 1) + m_map])) {
m_boundary = 2048;
m_ui.tile->setMaxTile(3072);
}
} else if ((mode > 0 && m_map == 2) || (mode == 2 && m_map == 3)) {
int32_t refX = io[(REG_BG2X_LO >> 1) + ((m_map - 2) << 2)];
refX |= io[(REG_BG2X_HI >> 1) + ((m_map - 2) << 2)] << 16;

View File

@ -14,24 +14,46 @@
#include <mgba/internal/gb/gb.h>
#endif
#include <algorithm>
using namespace QGBA;
#ifdef M_CORE_GB
MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node)
MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* gbNode)
: controller(coreController)
, gbNode(node)
{
node.gb = gbNode;
}
#endif
#ifdef M_CORE_GBA
MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node)
MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* gbaNode)
: controller(coreController)
, gbaNode(node)
{
node.gba = gbaNode;
}
#endif
int MultiplayerController::Player::id() const {
switch (controller->platform()) {
#ifdef M_CORE_GBA
case mPLATFORM_GBA:
return node.gba->id;
#endif
#ifdef M_CORE_GB
case mPLATFORM_GB:
return node.gb->id;
#endif
case mPLATFORM_NONE:
break;
}
return -1;
}
bool MultiplayerController::Player::operator<(const MultiplayerController::Player& other) const {
return id() < other.id();
}
MultiplayerController::MultiplayerController() {
mLockstepInit(&m_lockstep);
m_lockstep.context = this;
@ -65,6 +87,7 @@ MultiplayerController::MultiplayerController() {
player->awake = 0;
slept = true;
}
player->controller->setSync(true);
return slept;
};
m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
@ -72,44 +95,55 @@ MultiplayerController::MultiplayerController() {
abort();
}
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
if (!id) {
for (int i = 1; i < controller->m_players.count(); ++i) {
Player* player = &controller->m_players[i];
Player* player = controller->player(id);
switch (player->controller->platform()) {
#ifdef M_CORE_GBA
if (player->controller->platform() == mPLATFORM_GBA && player->gbaNode->d.p->mode != controller->m_players[0].gbaNode->d.p->mode) {
player->controller->setSync(true);
continue;
}
#endif
player->controller->setSync(false);
player->cyclesPosted += cycles;
if (player->awake < 1) {
switch (player->controller->platform()) {
#ifdef M_CORE_GBA
case mPLATFORM_GBA:
player->gbaNode->nextEvent += player->cyclesPosted;
break;
#endif
#ifdef M_CORE_GB
case mPLATFORM_GB:
player->gbNode->nextEvent += player->cyclesPosted;
break;
#endif
default:
break;
case mPLATFORM_GBA:
if (!id) {
for (int i = 1; i < controller->m_players.count(); ++i) {
player = controller->player(i);
if (player->node.gba->d.p->mode > SIO_MULTI) {
player->controller->setSync(true);
continue;
}
player->controller->setSync(false);
player->cyclesPosted += cycles;
if (player->awake < 1) {
player->node.gba->nextEvent += player->cyclesPosted;
}
mCoreThreadStopWaiting(player->controller->thread());
player->awake = 1;
}
} else {
player->controller->setSync(true);
player->cyclesPosted += cycles;
}
} else {
controller->m_players[id].controller->setSync(true);
controller->m_players[id].cyclesPosted += cycles;
break;
#endif
#ifdef M_CORE_GB
case mPLATFORM_GB:
if (!id) {
player = controller->player(1);
player->controller->setSync(false);
player->cyclesPosted += cycles;
if (player->awake < 1) {
player->node.gb->nextEvent += player->cyclesPosted;
}
mCoreThreadStopWaiting(player->controller->thread());
player->awake = 1;
} else {
player->controller->setSync(true);
player->cyclesPosted += cycles;
}
break;
#endif
default:
break;
}
};
m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
Player* player = &controller->m_players[id];
Player* player = controller->player(id);
player->cyclesPosted -= cycles;
if (player->cyclesPosted <= 0) {
mCoreThreadWaitFromThread(player->controller->thread());
@ -118,21 +152,21 @@ MultiplayerController::MultiplayerController() {
cycles = player->cyclesPosted;
return cycles;
};
m_lockstep.unusedCycles= [](mLockstep* lockstep, int id) {
m_lockstep.unusedCycles = [](mLockstep* lockstep, int id) {
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
Player* player = &controller->m_players[id];
Player* player = controller->player(id);
auto cycles = player->cyclesPosted;
return cycles;
};
m_lockstep.unload = [](mLockstep* lockstep, int id) {
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
if (id) {
Player* player = &controller->m_players[id];
Player* player = controller->player(id);
player->controller->setSync(true);
player->cyclesPosted = 0;
// release master GBA if it is waiting for this GBA
player = &controller->m_players[0];
player = controller->player(0);
player->waitMask &= ~(1 << id);
if (!player->waitMask && player->awake < 1) {
mCoreThreadStopWaiting(player->controller->thread());
@ -140,7 +174,7 @@ MultiplayerController::MultiplayerController() {
}
} else {
for (int i = 1; i < controller->m_players.count(); ++i) {
Player* player = &controller->m_players[i];
Player* player = controller->player(i);
player->controller->setSync(true);
switch (player->controller->platform()) {
#ifdef M_CORE_GBA
@ -160,12 +194,12 @@ MultiplayerController::MultiplayerController() {
switch (player->controller->platform()) {
#ifdef M_CORE_GBA
case mPLATFORM_GBA:
player->gbaNode->nextEvent += player->cyclesPosted;
player->node.gba->nextEvent += player->cyclesPosted;
break;
#endif
#ifdef M_CORE_GB
case mPLATFORM_GB:
player->gbNode->nextEvent += player->cyclesPosted;
player->node.gb->nextEvent += player->cyclesPosted;
break;
#endif
default:
@ -184,10 +218,6 @@ MultiplayerController::~MultiplayerController() {
}
bool MultiplayerController::attachGame(CoreController* controller) {
if (m_lockstep.attached == MAX_GBAS) {
return false;
}
if (m_lockstep.attached == 0) {
switch (controller->platform()) {
#ifdef M_CORE_GBA
@ -203,6 +233,9 @@ bool MultiplayerController::attachGame(CoreController* controller) {
default:
return false;
}
m_platform = controller->platform();
} else if (controller->platform() != m_platform) {
return false;
}
mCoreThread* thread = controller->thread();
@ -213,6 +246,10 @@ bool MultiplayerController::attachGame(CoreController* controller) {
switch (controller->platform()) {
#ifdef M_CORE_GBA
case mPLATFORM_GBA: {
if (m_lockstep.attached >= MAX_GBAS) {
return false;
}
GBA* gba = static_cast<GBA*>(thread->core->board);
GBASIOLockstepNode* node = new GBASIOLockstepNode;
@ -229,6 +266,10 @@ bool MultiplayerController::attachGame(CoreController* controller) {
#endif
#ifdef M_CORE_GB
case mPLATFORM_GB: {
if (m_lockstep.attached >= 2) {
return false;
}
GB* gb = static_cast<GB*>(thread->core->board);
GBSIOLockstepNode* node = new GBSIOLockstepNode;
@ -315,3 +356,29 @@ int MultiplayerController::attached() {
num = m_lockstep.attached;
return num;
}
MultiplayerController::Player* MultiplayerController::player(int id) {
Player* player = &m_players[id];
switch (player->controller->platform()) {
#ifdef M_CORE_GBA
case mPLATFORM_GBA:
if (player->node.gba->id != id) {
std::sort(m_players.begin(), m_players.end());
player = &m_players[id];
}
break;
#endif
#ifdef M_CORE_GB
case mPLATFORM_GB:
if (player->node.gb->id != id) {
std::swap(m_players[0], m_players[1]);
player = &m_players[id];
}
break;
#endif
case mPLATFORM_NONE:
break;
}
return player;
}

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