Merge branch 'master' into port/psp2
jump to
@@ -27,8 +27,11 @@ - Configurable display driver, between software and OpenGL
- Undo-able savestate loading and saving - Controller profiles now store shortcut settings - Default controller profiles for several common controllers - - Libretro now supports BIOS and rumble + - Libretro now supports BIOS, rumble and solar sensor - Implement BIOS call Stop, for sleep mode + - Automatically load patches, if found + - Improved video synchronization + - Configurable audio output sample rate Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit@@ -64,6 +67,11 @@ - Qt: Fix window being too tall after exiting fullscreen
- Qt: Fix a missing va_end call in the log handler lambda within the GameController constructor - GBA Cheats: Fix Pro Action Replay and GameShark issues when used together - Qt: Fix analog buttons not getting unmapped + - GBA Video: Prevent tiles < 512 from being used in modes 3 - 5 + - Qt: Fix passing command line options + - Qt: Fix crashes on Windows by using using QMetaObject to do cross-thread calls + - GBA Video: Fix timing on first scanline + - GBA: Ensure cycles never go negative Misc: - Qt: Handle saving input settings better - Debugger: Free watchpoints in addition to breakpoints@@ -109,6 +117,12 @@ - GBA: Savedata is now synced shortly after data finishes being written
- GBA Input: Allow axes and buttons to be mapped to the same key - GBA BIOS: Stub out SoundBias - Qt: Gamepads can now have both buttons and analog axes mapped to the same key + - Qt: Increase usability of key mapper + - Qt: Show checkmark for window sizes + - Qt: Set window path to loaded ROM + - GBA Memory: Run multiple DMAs in a tight loop if they all occur before present + - GBA Audio: Process multiple audio events at once, if necessary + - GBA: Process multiple timer events at once, if necessary 0.2.1: (2015-05-13) Bugfixes:
@@ -21,6 +21,7 @@ set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool")
set(BUILD_STATIC OFF CACHE BOOL "Build a static library") set(BUILD_SHARED ON CACHE BOOL "Build a shared library") set(BUILD_GL ON CACHE STRING "Build with OpenGL") +set(BUILD_GLES2 OFF CACHE STRING "Build with OpenGL|ES 2") file(GLOB ARM_SRC ${CMAKE_SOURCE_DIR}/src/arm/*.c) file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c) file(GLOB GBA_CHEATS_SRC ${CMAKE_SOURCE_DIR}/src/gba/cheats/*.c)@@ -44,13 +45,17 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type (e.g. Release or Debug)" FORCE) endif() +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBDIR}") + +include(GNUInstallDirs) + if (NOT DEFINED LIBDIR) set(LIBDIR "lib") endif() -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBDIR}") - -include(GNUInstallDirs) +if (NOT DEFINED MANDIR) + set(MANDIR ${CMAKE_INSTALL_MANDIR}) +endif() # Function definitions include(FindPkgConfig)@@ -126,7 +131,14 @@ endif()
if(BUILD_GL) find_package(OpenGL QUIET) if(NOT OPENGL_FOUND) - set(BUILD_GL OFF) + set(BUILD_GL OFF CACHE BOOL "OpenGL not found" FORCE) + endif() +endif() +if(BUILD_GLES2 AND NOT BUILD_RASPI) + find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h) + find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM) + if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY) + set(BUILD_GLES2 OFF CACHE BOOL "OpenGL|ES 2 not found" FORCE) endif() endif() find_feature(USE_FFMPEG "libavcodec;libavformat;libavresample;libavutil;libswscale")@@ -175,6 +187,10 @@ if(BUILD_BBB OR BUILD_RASPI OR BUILD_PANDORA)
if(NOT BUILD_EGL) add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5) endif() +endif() + +if(BUILD_RASPI) + set(BUILD_GL OFF CACHE BOOL "OpenGL not supported" FORCE) endif() if(BUILD_PANDORA)@@ -371,6 +387,7 @@ endif()
if(BUILD_SHARED) add_library(${BINARY_NAME} SHARED ${SRC}) + set_target_properties(${BINARY_NAME} PROPERTIES SOVERSION ${LIB_VERSION_ABI}) if(BUILD_STATIC) add_library(${BINARY_NAME}-static STATIC ${SRC}) set_target_properties(${BINARY_NAME}-static PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES}")@@ -384,7 +401,7 @@
add_dependencies(${BINARY_NAME} version-info) target_link_libraries(${BINARY_NAME} ${DEBUGGER_LIB} ${DEPENDENCY_LIB} ${OS_LIB}) -install(TARGETS ${BINARY_NAME} DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME}) +install(TARGETS ${BINARY_NAME} LIBRARY DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME} NAMELINK_SKIP ARCHIVE DESTINATION ${LIBDIR} RUNTIME DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME}) if(UNIX AND NOT APPLE) install(FILES ${CMAKE_SOURCE_DIR}/res/mgba-16.png DESTINATION share/icons/hicolor/16x16/apps RENAME mgba.png COMPONENT lib${BINARY_NAME}) install(FILES ${CMAKE_SOURCE_DIR}/res/mgba-24.png DESTINATION share/icons/hicolor/24x24/apps RENAME mgba.png COMPONENT lib${BINARY_NAME})@@ -400,6 +417,10 @@ set_target_properties(${BINARY_NAME} PROPERTIES VERSION ${LIB_VERSION_STRING} SOVERSION ${LIB_VERSION_ABI} COMPILE_DEFINITIONS "${FEATURE_DEFINES}")
if(BUILD_GL) add_definitions(-DBUILD_GL) +endif() + +if(BUILD_GLES2) + add_definitions(-DBUILD_GLES2) endif() if(BUILD_LIBRETRO)
@@ -30,13 +30,12 @@ * Hardware acceleration
### PS Vita (port/psp2) * Add menu -* Add audio +* Fix audio * Make it faster * Threaded renderer shim * Hardware acceleration ### Wii (port/wii) * Add menu -* Add audio * Thread support * Clean up video detection
@@ -126,8 +126,6 @@
- OBJ window for modes 3, 4 and 5 ([Bug #5](http://mgba.io/b/5)) - Mosaic for transformed OBJs ([Bug #9](http://mgba.io/b/9)) - BIOS call RegisterRamReset is partially stubbed out ([Bug #141](http://mgba.io/b/141)) -- Game Pak prefetch ([Bug #195](http://mgba.io/b/195)) -- BIOS call Stop, for entering sleep mode ([Bug #199](http://mgba.io/b/199)) <a name="flashdetect">[2]</a> Flash memory size detection does not work in some cases. These can be configured at runtime, but filing a bug is recommended if such a case is encountered.
@@ -0,0 +1,118 @@
+.\" Copyright (c) 2015 Anthony J. Bentley <anthony@anjbe.name> +.\" +.\" This Source Code Form is subject to the terms of the Mozilla Public +.\" License, v. 2.0. If a copy of the MPL was not distributed with this +.\" file, you can obtain one at https://mozilla.org/MPL/2.0/. +.Dd July 29, 2015 +.Dt MGBA-QT 6 +.Os +.Sh NAME +.Nm mgba-qt +.Nd Game Boy Advance emulator +.Sh SYNOPSIS +.Nm mgba-qt +.Op Fl b Ar biosfile +.Op Fl l Ar loglevel +.Op Fl p Ar patchfile +.Op Fl s Ar n +.Ar file +.Sh DESCRIPTION +.Nm +is a Game Boy Advance emulator. +The options are as follows: +.Bl -tag -width Ds +.It Fl b Ar biosfile , Fl -bios Ar biosfile +Specify a BIOS file to use during boot. +If this flag is omitted, +.Nm +will use the BIOS specified in the configuration file, +or a high\(hylevel emulated BIOS if none is specified. +.It Fl l Ar loglevel +Log messages during emulation. +.Ar loglevel +is a bitmask defining which types of messages to log: +.Bl -bullet -compact +.It +1 \(en fatal errors +.It +2 \(en errors +.It +4 \(en warnings +.It +8 \(en informative messages +.It +16 \(en debugging messages +.It +32 \(en stub messages for unimplemented features +.It +256 \(en in\(hygame errors +.It +512 \(en software interrupts +.It +1024 \(en emulator status messages +.It +2048 \(en serial I/O messages +.El +The default is to log warnings, errors, fatal errors, and status messages. +.It Fl p Ar patchfile , Fl -patch Ar patchfile +Specify a patch file in BPS, IPS, or UPS format. +.It Fl s Ar n , Fl -frameskip Ar n +Skip every +.Ar n +frames. +.El +.Sh CONTROLS +The default controls are as follows: +.Bl -hang -width "Frame advance" -compact +.It A +.Cm x +.It B +.Cm z +.It L +.Cm a +.It R +.Cm s +.It Start +.Aq Cm Enter +.It Select +.Aq Cm Backspace +.It Load state +.Cm F1 Ns \(en Ns Cm F9 +.It Save state +.Ao Cm Shift Ac Ns \(hy Ns Cm F1 Ns \(en Ns Cm F9 +.It Frame advance +.Ao Cm Ctrl Ac Ns \(hy Ns Cm n +.El +.Sh FILES +.Bl -tag -width ~/.config/mgba/config.ini -compact +.It Pa ~/.config/mgba/config.ini +Default +.Xr mgba 6 +configuration file. +.It Pa ~/.config/mgba/qt.ini +Default +.Nm mgba-qt +configuration file. +.It Pa portable.ini +If this file exists in the current directory, +.Nm +will read +.Pa config.ini +and +.Pa qt.ini +from the current directory instead of +.Pa ~/.config/mgba . +.El +.Sh AUTHORS +.An Jeffrey Pfau Aq Mt jeffrey@endrift.com +.Sh HOMEPAGE +.Bl -bullet +.It +.Lk https://mgba.io/ "mGBA homepage" +.It +.Lk https://github.com/mgba-emu/mgba "Development repository" +.It +.Lk https://github.com/mgba-emu/mgba/issues "Bug tracker" +.It +.Lk https://forums.mgba.io/ "Message board" +.El
@@ -0,0 +1,259 @@
+.\" Copyright (c) 2015 Anthony J. Bentley <anthony@anjbe.name> +.\" +.\" This Source Code Form is subject to the terms of the Mozilla Public +.\" License, v. 2.0. If a copy of the MPL was not distributed with this +.\" file, you can obtain one at https://mozilla.org/MPL/2.0/. +.Dd July 29, 2015 +.Dt MGBA 6 +.Os +.Sh NAME +.Nm mgba +.Nd Game Boy Advance emulator +.Sh SYNOPSIS +.Nm mgba +.Op Fl 123456dfg +.Op Fl b Ar biosfile +.Op Fl c Ar cheatfile +.Op Fl l Ar loglevel +.Op Fl p Ar patchfile +.Op Fl s Ar n +.Op Fl v Ar moviefile +.Ar file +.Sh DESCRIPTION +.Nm +is a Game Boy Advance emulator. +The options are as follows: +.Bl -tag -width Ds +.It Fl 1 +Scale the window 1\(mu. +.It Fl 2 +Scale the window 2\(mu. +.It Fl 3 +Scale the window 3\(mu. +.It Fl 4 +Scale the window 4\(mu. +.It Fl 5 +Scale the window 5\(mu. +.It Fl 6 +Scale the window 6\(mu. +.It Fl b Ar biosfile , Fl -bios Ar biosfile +Specify a BIOS file to use during boot. +If this flag is omitted, +.Nm +will use the BIOS specified in the configuration file, +or a high\(hylevel emulated BIOS if none is specified. +.It Fl c Ar cheatfile , Fl -cheats Ar cheatfile +Apply cheat codes from +.Ar cheatfile . +.It Fl d +Start emulating via the command\(hyline debugger. +.It Fl f +Start the emulator full\(hyscreen. +.It Fl g +Start a +.Xr gdb 1 +session. +By default the session starts on port 2345. +.It Fl l Ar loglevel +Log messages during emulation to +.Dv stdout . +.Ar loglevel +is a bitmask defining which types of messages to log: +.Bl -bullet -compact +.It +1 \(en fatal errors +.It +2 \(en errors +.It +4 \(en warnings +.It +8 \(en informative messages +.It +16 \(en debugging messages +.It +32 \(en stub messages for unimplemented features +.It +256 \(en in\(hygame errors +.It +512 \(en software interrupts +.It +1024 \(en emulator status messages +.It +2048 \(en serial I/O messages +.El +The default is to log warnings, errors, fatal errors, and status messages. +.It Fl p Ar patchfile , Fl -patch Ar patchfile +Specify a patch file in BPS, IPS, or UPS format. +.It Fl s Ar n , Fl -frameskip Ar n +Skip every +.Ar n +frames. +.It Fl v Ar moviefile , Fl -movie Ar moviefile +Play back a movie of recording input from +.Ar moviefile . +.El +.Sh CONTROLS +The default controls are as follows: +.Bl -hang -width "Frame advance" -compact +.It A +.Cm x +.It B +.Cm z +.It L +.Cm a +.It R +.Cm s +.It Start +.Aq Cm Enter +.It Select +.Aq Cm Backspace +.It Load state +.Cm F1 Ns \(en Ns Cm F9 +.It Save state +.Ao Cm Shift Ac Ns \(hy Ns Cm F1 Ns \(en Ns Cm F9 +.It Frame advance +.Ao Cm Ctrl Ac Ns \(hy Ns Cm n +.El +.Sh DEBUGGER +When +.Nm +is run with the +.Fl d +option, the command\(hyline debugger is enabled. +It supports the following commands: +.Pp +.Bl -tag -compact -width 1 +.It Cm b Ns Oo Cm reak Oc Ar address +.It Cm b Ns Oo Cm reak Oc Ns Cm /a Ar address +.It Cm b Ns Oo Cm reak Oc Ns Cm /t Ar address +Set a breakpoint \(en ARM +.Pq Ql /a , +Thumb +.Pq Ql /t , +or the current CPU mode \(en at +.Ar address . +.It Cm c Ns Op Cm ontinue +Continue execution. +.It Cm d Ns Oo Cm elete Oc Ar address +Delete a breakpoint at +.Ar address . +.It Cm dis Ns Oo Cm asm Oc Op Ar address Op Ar count +.It Cm dis Ns Oo Cm asm Oc Ns Cm /a Op Ar address Op Ar count +.It Cm dis Ns Oo Cm asm Oc Ns Cm /t Op Ar address Op Ar count +.It Cm dis Ns Oo Cm assemble Oc Op Ar address Op Ar count +.It Cm dis Ns Oo Cm assemble Oc Ns Cm /a Op Ar address Op Ar count +.It Cm dis Ns Oo Cm assemble Oc Ns Cm /t Op Ar address Op Ar count +Disassemble +.Ar count +instructions starting at +.Ar address , +as ARM +.Pq Ql /a , +Thumb +.Pq Ql /t , +or the current CPU mode. +If +.Ar count +is not specified, only disassemble the instruction at +.Ar address . +If +.Ar address +is not specified, only disassemble the current address. +.It Cm h Ns Op Cm elp +Print help. +.It Cm i Ns Op Cm nfo +.It Cm status +Print the current contents of general\(hypurpose registers and the current +program state register, and disassemble the current instruction. +.It Cm n Ns Op Cm ext +Execute the next instruction. +.It Cm p Ns Oo Cm rint Oc Ar value ... +.It Cm p Ns Oo Cm rint Oc Ns Cm /t Ar value ... +.It Cm p Ns Oo Cm rint Oc Ns Cm /x Ar value ... +Print one or more +.Ar value Ns s +as binary +.Pq Ql /t , +hexadecimal +.Pq Ql /x , +or decimal. +.It Cm q Ns Op Cm uit +Quit the emulator. +.It Cm reset +Reset the emulation. +.It Cm r/1 Ar address +.It Cm r/2 Ar address +.It Cm r/4 Ar address +Read a byte +.Pq Ql /1 , +halfword +.Pq Ql /2 , +or word +.Pq Ql /4 +from +.Ar address . +.It Cm w Ns Oo Cm atch Oc Ar address +Set a watchpoint at +.Ar address . +.It Cm w/1 Ar address data +.It Cm w/2 Ar address data +.It Cm w/4 Ar address data +Write +.Ar data +as a byte +.Pq Ql /1 , +halfword +.Pq Ql /2 , +or word +.Pq Ql /4 +to +.Ar address . +.It Cm w/r Ar register data +Write +.Ar data +as a word to +.Ar register . +.It Cm x/1 Ar address Op Ar count +.It Cm x/2 Ar address Op Ar count +.It Cm x/4 Ar address Op Ar count +Examine +.Ar count +bytes +.Pq Ql /1 , +halfwords +.Pq Ql /2 , +or words +.Pq Ql /4 +from +.Ar address . +If +.Ar count +is not specified, examine 16 bytes, 8 halfwords, or 4 words. +.El +.Sh FILES +.Bl -tag -width ~/.config/mgba/config.ini -compact +.It Pa ~/.config/mgba/config.ini +Default +.Nm +configuration file. +.It Pa portable.ini +If this file exists in the current directory, +.Nm +will read +.Pa config.ini +from the current directory instead of +.Pa ~/.config/mgba . +.El +.Sh AUTHORS +.An Jeffrey Pfau Aq Mt jeffrey@endrift.com +.Sh HOMEPAGE +.Bl -bullet +.It +.Lk https://mgba.io/ "mGBA homepage" +.It +.Lk https://github.com/mgba-emu/mgba "Development repository" +.It +.Lk https://github.com/mgba-emu/mgba/issues "Bug tracker" +.It +.Lk https://forums.mgba.io/ "Message board" +.El
@@ -7,130 +7,266 @@ <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="239.9995" y1="0" x2="239.9995" y2="480.0005">
<stop offset="0" style="stop-color:#7A65F5"/> <stop offset="1" style="stop-color:#302575"/> </linearGradient> -<path fill="url(#SVGID_1_)" d="M480,450c0,16.5684-13.4316,30-30,30H30c-16.5684,0-30-13.4316-30-30V30C0,13.4316,13.4316,0,30,0 - h420c16.5684,0,30,13.4316,30,30V450z"/> -<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="239.9995" y1="-479" x2="239.9994" y2="1159.0004"> - <stop offset="0" style="stop-color:#7A65F5"/> - <stop offset="1" style="stop-color:#302575"/> +<path fill="url(#SVGID_1_)" d="M480,450c0,16.568-13.432,30-30,30H30c-16.568,0-30-13.432-30-30V30C0,13.432,13.432,0,30,0h420 + c16.568,0,30,13.432,30,30V450z"/> +<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="239.9995" y1="-479.0015" x2="239.9994" y2="1158.9995"> + <stop offset="0" style="stop-color:#7762F5"/> + <stop offset="1" style="stop-color:#372E75"/> </linearGradient> -<path fill="url(#SVGID_2_)" d="M30,473c-12.6821,0-23-10.3174-23-23V30C7,17.3179,17.3179,7,30,7h420c12.6826,0,23,10.3179,23,23 - v420c0,12.6826-10.3174,23-23,23H30z"/> -<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="118.3335" y1="291.667" x2="118.3335" y2="47.9991"> - <stop offset="0" style="stop-color:#7A65F5"/> +<path fill="url(#SVGID_2_)" d="M30,473c-12.682,0-23-10.317-23-23V30C7,17.318,17.318,7,30,7h420c12.683,0,23,10.318,23,23v420 + c0,12.683-10.317,23-23,23H30z"/> +<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="118.3335" y1="291.667" x2="118.3335" y2="47.9995"> + <stop offset="0" style="stop-color:#7466CF"/> <stop offset="1" style="stop-color:#302575"/> </linearGradient> -<path fill="url(#SVGID_3_)" d="M118.3335,122.333c-46.7603,0-84.667,37.9067-84.667,84.667c0,46.7607,37.9067,84.667,84.667,84.667 - s84.667-37.9062,84.667-84.667C203.0005,160.2397,165.0938,122.333,118.3335,122.333z M118.334,278.7344 - c-39.6172,0-71.7339-32.1172-71.7339-71.7344c0-39.6177,32.1167-71.7339,71.7339-71.7339S190.0679,167.3823,190.0679,207 - C190.0679,246.6172,157.9512,278.7344,118.334,278.7344z"/> +<path fill="url(#SVGID_3_)" d="M118.333,122.333c-46.76,0-84.667,37.907-84.667,84.667c0,46.761,37.907,84.667,84.667,84.667 + S203,253.761,203,207C203,160.24,165.094,122.333,118.333,122.333z M118.334,278.734C78.717,278.734,46.6,246.617,46.6,207 + c0-39.618,32.117-71.734,71.734-71.734s71.734,32.116,71.734,71.734C190.068,246.617,157.951,278.734,118.334,278.734z"/> <g> - <radialGradient id="SVGID_4_" cx="118.8335" cy="138.167" r="103.2725" fx="165.1467" fy="138.167" gradientTransform="matrix(-4.371139e-08 1 -1.42 -6.206884e-08 315.0264 19.3335)" gradientUnits="userSpaceOnUse"> + <radialGradient id="SVGID_4_" cx="118.8335" cy="138.167" r="103.2721" fx="165.1465" fy="138.167" gradientTransform="matrix(-4.371139e-08 1 -1.42 -6.206886e-08 315.0265 19.3335)" gradientUnits="userSpaceOnUse"> <stop offset="0" style="stop-color:#9C9CB3"/> <stop offset="1" style="stop-color:#333045"/> </radialGradient> - <path fill="url(#SVGID_4_)" d="M172.1973,207c0-9.667-3.0029-17.333-3.0029-17.333H135.667v-33.5283 - c0,0-7.667-3.0029-17.3335-3.0029s-17.333,3.0029-17.333,3.0029v33.5283H67.4727c0,0-3.0029,7.666-3.0029,17.333 - c0,9.666,3.0029,17.333,3.0029,17.333h33.5278v33.5273c0,0,7.6665,3.0039,17.333,3.0039s17.3335-3.0039,17.3335-3.0039V224.333 - h33.5273C169.1943,224.333,172.1973,216.666,172.1973,207z"/> - <path fill="#5C567D" d="M118.3335,258.8643c-6.9185,0-12.833-1.625-15.333-2.4287V222.333h-34.103 - c-0.8027-2.5-2.4277-8.4146-2.4277-15.333s1.6245-12.833,2.4277-15.333h34.103v-34.1035c2.5-0.8027,8.4146-2.4277,15.333-2.4277 - c6.9453,0,12.8408,1.6226,15.3335,2.4258v34.1055h34.1025c0.8032,2.5,2.4277,8.4146,2.4277,15.333 - c0,6.9448-1.6226,12.8403-2.4258,15.333H133.667v34.1025C131.167,257.2393,125.252,258.8643,118.3335,258.8643z"/> - <radialGradient id="SVGID_5_" cx="118.333" cy="220.397" r="69.7522" gradientUnits="userSpaceOnUse"> + <path fill="url(#SVGID_4_)" d="M172.197,207c0-9.667-3.003-17.333-3.003-17.333h-33.527v-33.528c0,0-7.667-3.003-17.333-3.003 + S101,156.139,101,156.139v33.528H67.473c0,0-3.003,7.666-3.003,17.333c0,9.666,3.003,17.333,3.003,17.333H101v33.527 + c0,0,7.667,3.004,17.333,3.004s17.333-3.004,17.333-3.004v-33.527h33.527C169.194,224.333,172.197,216.666,172.197,207z"/> + <path fill="#5C567D" d="M118.333,258.864c-6.918,0-12.833-1.625-15.333-2.429v-34.103H68.897c-0.803-2.5-2.428-8.415-2.428-15.333 + s1.625-12.833,2.428-15.333H103v-34.104c2.5-0.803,8.415-2.428,15.333-2.428c6.945,0,12.841,1.623,15.333,2.426v34.105h34.103 + c0.803,2.5,2.428,8.415,2.428,15.333c0,6.945-1.623,12.84-2.426,15.333h-34.104v34.103 + C131.167,257.239,125.252,258.864,118.333,258.864z"/> + <radialGradient id="SVGID_5_" cx="118.333" cy="220.397" r="69.7523" gradientUnits="userSpaceOnUse"> <stop offset="0" style="stop-color:#706F8A"/> <stop offset="0.9951" style="stop-color:#2D2842"/> </radialGradient> - <circle fill="url(#SVGID_5_)" cx="118.333" cy="207" r="21.2749"/> + <circle fill="url(#SVGID_5_)" cx="118.333" cy="207" r="21.275"/> </g> <g> <g> - <linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="198.9673" y1="490" x2="198.9673" y2="384.981"> + <linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="198.9673" y1="490" x2="198.9673" y2="384.9809"> <stop offset="0" style="stop-color:#7A65F5"/> <stop offset="1" style="stop-color:#302575"/> </linearGradient> - <path fill="url(#SVGID_6_)" d="M198.9678,387.0303c-18.8418,0-34.1162,15.2734-34.1162,34.1162 - c0,18.8418,15.2744,34.1152,34.1162,34.1152c18.8408,0,34.1152-15.2734,34.1152-34.1152 - C233.083,402.3037,217.8086,387.0303,198.9678,387.0303z M198.9678,445.7871c-13.6089,0-24.6401-11.0332-24.6401-24.6406 - c0-13.6094,11.0312-24.6406,24.6401-24.6406c13.6074,0,24.6396,11.0312,24.6396,24.6406 - C223.6074,434.7539,212.5752,445.7871,198.9678,445.7871z"/> + <path fill="url(#SVGID_6_)" d="M198.968,387.03c-18.842,0-34.116,15.273-34.116,34.116c0,18.842,15.274,34.115,34.116,34.115 + c18.841,0,34.115-15.273,34.115-34.115C233.083,402.304,217.809,387.03,198.968,387.03z M198.968,445.787 + c-13.609,0-24.64-11.033-24.64-24.641c0-13.609,11.031-24.641,24.64-24.641c13.607,0,24.64,11.031,24.64,24.641 + C223.607,434.754,212.575,445.787,198.968,445.787z"/> <g> <linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="198.9673" y1="437.4414" x2="198.9673" y2="404.8516"> <stop offset="0" style="stop-color:#333045"/> <stop offset="1" style="stop-color:#9C9CB3"/> </linearGradient> - <circle fill="url(#SVGID_7_)" cx="198.9673" cy="421.1465" r="16.2949"/> - <circle fill="#5C567D" cx="198.9673" cy="421.1465" r="15.0737"/> + <circle fill="url(#SVGID_7_)" cx="198.967" cy="421.146" r="16.295"/> + <circle fill="#5C567D" cx="198.967" cy="421.146" r="15.074"/> </g> </g> <g> - <linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="281.0312" y1="490" x2="281.0312" y2="384.981"> + <linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="281.0312" y1="490" x2="281.0312" y2="384.9809"> <stop offset="0" style="stop-color:#7A65F5"/> <stop offset="1" style="stop-color:#302575"/> </linearGradient> - <path fill="url(#SVGID_8_)" d="M281.0322,387.0303c-18.8418,0-34.1162,15.2734-34.1162,34.1162 - c0,18.8418,15.2744,34.1152,34.1162,34.1152c18.8408,0,34.1152-15.2734,34.1152-34.1152 - C315.1475,402.3037,299.873,387.0303,281.0322,387.0303z M281.0322,445.7871c-13.6084,0-24.6396-11.0332-24.6396-24.6406 - c0-13.6094,11.0312-24.6406,24.6396-24.6406c13.6074,0,24.6396,11.0312,24.6396,24.6406 - C305.6719,434.7539,294.6396,445.7871,281.0322,445.7871z"/> + <path fill="url(#SVGID_8_)" d="M281.032,387.03c-18.842,0-34.116,15.273-34.116,34.116c0,18.842,15.274,34.115,34.116,34.115 + c18.841,0,34.115-15.273,34.115-34.115C315.147,402.304,299.873,387.03,281.032,387.03z M281.032,445.787 + c-13.608,0-24.64-11.033-24.64-24.641c0-13.609,11.031-24.641,24.64-24.641c13.607,0,24.64,11.031,24.64,24.641 + C305.672,434.754,294.64,445.787,281.032,445.787z"/> <g> <linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="281.0322" y1="437.4414" x2="281.0322" y2="404.8516"> <stop offset="0" style="stop-color:#333045"/> <stop offset="1" style="stop-color:#9C9CB3"/> </linearGradient> - <circle fill="url(#SVGID_9_)" cx="281.0322" cy="421.1465" r="16.2949"/> - <circle fill="#5C567D" cx="281.0317" cy="421.1465" r="15.0737"/> + <circle fill="url(#SVGID_9_)" cx="281.032" cy="421.146" r="16.295"/> + <circle fill="#5C567D" cx="281.032" cy="421.146" r="15.074"/> </g> </g> </g> -<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="356.1787" y1="280.2178" x2="356.1787" y2="134.9952"> - <stop offset="0" style="stop-color:#7A65F5"/> +<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="356.1787" y1="280.2178" x2="356.1787" y2="134.9951"> + <stop offset="0" style="stop-color:#7466CF"/> <stop offset="1" style="stop-color:#302575"/> </linearGradient> -<path fill="url(#SVGID_10_)" d="M437.9521,179.5503c-7.0088-23.9434-32.0986-37.6724-56.043-30.6646l-76.8369,22.4912 - c-23.9453,7.0073-37.6748,32.0991-30.666,56.043c7.0088,23.9443,32.0996,37.6719,56.0449,30.6621l76.8369-22.4902 - C431.2314,228.585,444.96,203.4946,437.9521,179.5503z M426.3311,209.6025c-4.6377,8.4756-12.2979,14.6382-21.5703,17.3516 - l-76.8379,22.4902c-3.3301,0.9746-6.7559,1.4688-10.1816,1.4688c-0.001,0-0.001,0-0.002,0 - c-15.9443-0.001-30.2109-10.7012-34.6953-26.0215c-2.7148-9.2729-1.6553-19.0479,2.9824-27.5244 - c4.6387-8.4761,12.2998-14.6387,21.5732-17.3525l76.8379-22.4912c3.3281-0.9741,6.7539-1.4683,10.1807-1.4683 - c15.9434,0,30.2109,10.7012,34.6963,26.0234C432.0283,191.3516,430.9688,201.1265,426.3311,209.6025z"/> +<path fill="url(#SVGID_10_)" d="M437.952,179.55c-7.009-23.943-32.099-37.672-56.043-30.665l-76.837,22.491 + c-23.945,7.007-37.675,32.099-30.666,56.043c7.009,23.944,32.1,37.672,56.045,30.662l76.837-22.49 + C431.231,228.585,444.96,203.495,437.952,179.55z M426.331,209.603c-4.638,8.476-12.298,14.638-21.57,17.352l-76.838,22.49 + c-3.33,0.975-6.756,1.469-10.182,1.469c-0.001,0-0.001,0-0.001,0c-15.945-0.001-30.212-10.701-34.696-26.021 + c-2.715-9.273-1.655-19.048,2.982-27.524c4.639-8.476,12.3-14.639,21.573-17.353l76.838-22.491 + c3.328-0.974,6.754-1.468,10.181-1.468c15.943,0,30.211,10.701,34.696,26.023C432.028,191.352,430.969,201.126,426.331,209.603z"/> <g> <linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="395.9062" y1="217.8188" x2="395.9062" y2="166.6519"> <stop offset="0" style="stop-color:#333045"/> <stop offset="1" style="stop-color:#9C9CB3"/> </linearGradient> - <circle fill="url(#SVGID_11_)" cx="395.9062" cy="192.2358" r="25.584"/> - <circle fill="#5C567D" cx="395.9072" cy="192.2358" r="23.666"/> + <circle fill="url(#SVGID_11_)" cx="395.906" cy="192.236" r="25.584"/> + <circle fill="#5C567D" cx="395.907" cy="192.236" r="23.666"/> </g> <g> <linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="318.9785" y1="239.6406" x2="318.9785" y2="188.4722"> <stop offset="0" style="stop-color:#333045"/> <stop offset="1" style="stop-color:#9C9CB3"/> </linearGradient> - <circle fill="url(#SVGID_12_)" cx="318.979" cy="214.0571" r="25.5835"/> - <circle fill="#5C567D" cx="318.98" cy="214.0571" r="23.6665"/> + <circle fill="url(#SVGID_12_)" cx="318.979" cy="214.057" r="25.583"/> + <circle fill="#5C567D" cx="318.98" cy="214.057" r="23.667"/> </g> <g> - <linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="-15.0425" y1="77.9346" x2="55.6245" y2="-5.3989"> + <linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="-15.0435" y1="77.9331" x2="55.6236" y2="-5.4006"> <stop offset="0" style="stop-color:#333045"/> <stop offset="1" style="stop-color:#9C9CB3"/> </linearGradient> - <path fill="url(#SVGID_13_)" d="M33.6665,0H61.5c2.9697,1.0112,2.3306,7,2.3306,7l0.0581,8.4844 - C63.8887,31.3535,48.9922,50,27.9922,50H7.0068c0,0-5.2197,2.312-7.0068-8.0029V30C0,11.5,12.333,0,33.6665,0z"/> - <path fill="#5C567D" d="M9.7783,49.1743c-0.2793-0.251-1.166-1.2764-1.7744-4.5308V33.0029c0-16.5234,10.813-26,29.6665-26h26.0854 - c0.1318,0.709,0.1821,1.7549,0.0996,2.5908L63.835,9.8032l0.0576,8.7114c0,14.3774-13.6406,30.4883-31.8965,30.4883H10.1646 - L9.7783,49.1743z"/> + <path fill="url(#SVGID_13_)" d="M33.667,0H61.5c2.97,1.011,2.331,7,2.331,7l0.058,8.484C63.889,31.354,48.992,50,27.992,50H7.007 + c0,0-5.22,2.312-7.007-8.003V30C0,11.5,12.333,0,33.667,0z"/> + <path fill="#5C567D" d="M9.778,49.174c-0.279-0.251-1.166-1.276-1.774-4.531V33.003c0-16.523,10.813-26,29.667-26h26.085 + c0.132,0.709,0.182,1.755,0.1,2.591l-0.021,0.209l0.058,8.711c0,14.377-13.641,30.488-31.896,30.488H10.165L9.778,49.174z"/> </g> <g> - <linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="401.0615" y1="77.9336" x2="471.7285" y2="-5.3999" gradientTransform="matrix(-1 0 0 1 896.1055 0)"> + <linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="401.0605" y1="77.9331" x2="471.7277" y2="-5.4007" gradientTransform="matrix(-1 0 0 1 896.1055 0)"> <stop offset="0" style="stop-color:#333045"/> <stop offset="1" style="stop-color:#9C9CB3"/> </linearGradient> - <path fill="url(#SVGID_14_)" d="M446.334,0h-27.833c-2.9697,1.0112-2.3311,7-2.3311,7l-0.0576,8.4844 - C416.1123,31.3535,431.0088,50,452.0088,50h20.9854c0,0,5.2197,2.312,7.0068-8.0029V30C480.001,11.5,467.668,0,446.334,0z"/> - <path fill="#5C567D" d="M470.2227,49.1743c0.2793-0.251,1.166-1.2764,1.7744-4.5308V33.0029c0-16.5234-10.8135-26-29.667-26 - h-26.085c-0.1318,0.709-0.1826,1.7549-0.0996,2.5908l0.0205,0.2095l-0.0576,8.7114c0,14.3774,13.6406,30.4883,31.8965,30.4883 - h21.8311L470.2227,49.1743z"/> + <path fill="url(#SVGID_14_)" d="M446.334,0h-27.833c-2.97,1.011-2.331,7-2.331,7l-0.058,8.484 + c0,15.869,14.896,34.516,35.896,34.516h20.985c0,0,5.22,2.312,7.007-8.003V30C480.001,11.5,467.668,0,446.334,0z"/> + <path fill="#5C567D" d="M470.223,49.174c0.279-0.251,1.166-1.276,1.774-4.531V33.003c0-16.523-10.813-26-29.667-26h-26.085 + c-0.132,0.709-0.183,1.755-0.1,2.591l0.021,0.209l-0.058,8.711c0,14.377,13.641,30.488,31.896,30.488h21.831L470.223,49.174z"/> +</g> +<g opacity="0.5"> + <path fill="#9C9CB3" stroke="#9C9CB3" stroke-miterlimit="10" d="M312.197,226v-21.475h8.057c1.641,0,2.956,0.217,3.947,0.652 + s1.768,1.104,2.33,2.007c0.561,0.903,0.842,1.848,0.842,2.834c0,0.918-0.249,1.782-0.747,2.593s-1.25,1.465-2.256,1.963 + c1.299,0.381,2.297,1.03,2.995,1.948s1.048,2.002,1.048,3.252c0,1.006-0.212,1.941-0.638,2.805c-0.424,0.864-0.949,1.531-1.574,2 + s-1.409,0.823-2.352,1.062S321.753,226,320.386,226H312.197z M315.039,213.549h4.644c1.26,0,2.163-0.083,2.71-0.249 + c0.723-0.215,1.268-0.571,1.633-1.069c0.367-0.498,0.55-1.123,0.55-1.875c0-0.713-0.171-1.34-0.513-1.882s-0.83-0.913-1.465-1.113 + s-1.724-0.3-3.267-0.3h-4.292V213.549z M315.039,223.466h5.347c0.918,0,1.562-0.034,1.934-0.103 + c0.654-0.117,1.201-0.312,1.641-0.586s0.801-0.671,1.084-1.194s0.425-1.125,0.425-1.809c0-0.801-0.205-1.497-0.615-2.087 + s-0.979-1.006-1.707-1.245s-1.774-0.359-3.142-0.359h-4.966V223.466z"/> +</g> +<g opacity="0.5"> + <path fill="#9C9CB3" stroke="#9C9CB3" stroke-miterlimit="10" d="M385.956,203l8.247-21.475h3.062L406.054,203h-3.237l-2.505-6.504 + h-8.979L388.974,203H385.956z M392.152,194.182h7.28l-2.241-5.947c-0.684-1.807-1.191-3.291-1.523-4.453 + c-0.273,1.377-0.659,2.744-1.157,4.102L392.152,194.182z"/> +</g> +<g> + <path fill="#333045" d="M312.197,224v-21.475h8.057c1.641,0,2.956,0.217,3.947,0.652s1.768,1.104,2.33,2.007 + c0.561,0.903,0.842,1.848,0.842,2.834c0,0.918-0.249,1.782-0.747,2.593s-1.25,1.465-2.256,1.963 + c1.299,0.381,2.297,1.03,2.995,1.948s1.048,2.002,1.048,3.252c0,1.006-0.212,1.941-0.638,2.805c-0.424,0.864-0.949,1.531-1.574,2 + s-1.409,0.823-2.352,1.062S321.753,224,320.386,224H312.197z M315.039,211.549h4.644c1.26,0,2.163-0.083,2.71-0.249 + c0.723-0.215,1.268-0.571,1.633-1.069c0.367-0.498,0.55-1.123,0.55-1.875c0-0.713-0.171-1.34-0.513-1.882s-0.83-0.913-1.465-1.113 + s-1.724-0.3-3.267-0.3h-4.292V211.549z M315.039,221.466h5.347c0.918,0,1.562-0.034,1.934-0.103 + c0.654-0.117,1.201-0.312,1.641-0.586s0.801-0.671,1.084-1.194s0.425-1.125,0.425-1.809c0-0.801-0.205-1.497-0.615-2.087 + s-0.979-1.006-1.707-1.245s-1.774-0.359-3.142-0.359h-4.966V221.466z"/> +</g> +<g> + <path fill="#333045" d="M385.956,201l8.247-21.475h3.062L406.054,201h-3.237l-2.505-6.504h-8.979L388.974,201H385.956z + M392.152,192.182h7.28l-2.241-5.947c-0.684-1.807-1.191-3.291-1.523-4.453c-0.273,1.377-0.659,2.744-1.157,4.102L392.152,192.182z + "/> +</g> +<g opacity="0.5"> + <g> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M320.35,425.264l2.658-0.258 + c0.16,0.893,0.484,1.547,0.974,1.967c0.489,0.418,1.148,0.627,1.979,0.627c0.88,0,1.543-0.186,1.988-0.559 + c0.446-0.373,0.669-0.807,0.669-1.307c0-0.318-0.094-0.592-0.281-0.816s-0.515-0.42-0.982-0.586 + c-0.32-0.111-1.049-0.307-2.188-0.59c-1.464-0.363-2.491-0.809-3.082-1.338c-0.831-0.744-1.246-1.652-1.246-2.723 + c0-0.689,0.195-1.334,0.586-1.934s0.954-1.057,1.689-1.371c0.734-0.312,1.622-0.471,2.662-0.471c1.698,0,2.977,0.373,3.834,1.117 + c0.858,0.744,1.31,1.738,1.353,2.98l-2.731,0.121c-0.117-0.695-0.368-1.195-0.753-1.5c-0.384-0.305-0.961-0.457-1.729-0.457 + c-0.794,0-1.416,0.162-1.864,0.488c-0.29,0.209-0.435,0.49-0.435,0.84c0,0.32,0.136,0.594,0.406,0.822 + c0.345,0.289,1.182,0.59,2.511,0.904s2.312,0.639,2.948,0.973c0.637,0.336,1.135,0.795,1.495,1.375 + c0.359,0.582,0.54,1.301,0.54,2.156c0,0.775-0.216,1.5-0.646,2.178c-0.431,0.676-1.04,1.18-1.827,1.508 + c-0.787,0.33-1.769,0.494-2.943,0.494c-1.711,0-3.024-0.395-3.941-1.186C321.076,427.93,320.528,426.777,320.35,425.264z"/> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M336.694,429.666v-11.24h-4.015v-2.289h10.751 + v2.289h-4.005v11.24H336.694z"/> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M355.99,429.666h-2.972l-1.182-3.072h-5.407 + l-1.117,3.072h-2.897l5.27-13.529h2.889L355.99,429.666z M350.961,424.314l-1.864-5.021l-1.827,5.021H350.961z"/> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M357.449,429.666v-13.529h5.749 + c1.445,0,2.496,0.123,3.151,0.365c0.655,0.244,1.18,0.676,1.573,1.297s0.591,1.332,0.591,2.131c0,1.016-0.299,1.854-0.896,2.516 + s-1.488,1.078-2.676,1.25c0.591,0.346,1.078,0.723,1.463,1.135c0.384,0.412,0.902,1.145,1.555,2.197l1.652,2.639h-3.268 + l-1.975-2.943c-0.701-1.053-1.182-1.715-1.439-1.988c-0.259-0.273-0.532-0.463-0.821-0.562c-0.289-0.102-0.748-0.152-1.375-0.152 + h-0.554v5.646H357.449z M360.181,421.859h2.021c1.311,0,2.129-0.055,2.455-0.166s0.581-0.301,0.766-0.572s0.277-0.609,0.277-1.016 + c0-0.455-0.122-0.822-0.364-1.102c-0.243-0.281-0.587-0.457-1.029-0.531c-0.222-0.031-0.886-0.047-1.993-0.047h-2.132V421.859z"/> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M374.134,429.666v-11.24h-4.015v-2.289h10.751 + v2.289h-4.005v11.24H374.134z"/> + </g> +</g> +<g opacity="0.5"> + <g> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M87.35,425.264l2.658-0.258 + c0.16,0.893,0.484,1.547,0.974,1.967c0.489,0.418,1.148,0.627,1.979,0.627c0.88,0,1.543-0.186,1.988-0.559 + c0.446-0.373,0.669-0.807,0.669-1.307c0-0.318-0.094-0.592-0.281-0.816s-0.515-0.42-0.982-0.586 + c-0.32-0.111-1.049-0.307-2.188-0.59c-1.464-0.363-2.491-0.809-3.082-1.338c-0.831-0.744-1.246-1.652-1.246-2.723 + c0-0.689,0.195-1.334,0.586-1.934s0.954-1.057,1.689-1.371c0.734-0.312,1.622-0.471,2.662-0.471c1.698,0,2.977,0.373,3.834,1.117 + c0.858,0.744,1.31,1.738,1.353,2.98l-2.731,0.121c-0.117-0.695-0.368-1.195-0.753-1.5c-0.384-0.305-0.961-0.457-1.729-0.457 + c-0.794,0-1.416,0.162-1.864,0.488c-0.29,0.209-0.435,0.49-0.435,0.84c0,0.32,0.136,0.594,0.406,0.822 + c0.345,0.289,1.182,0.59,2.511,0.904s2.312,0.639,2.948,0.973c0.637,0.336,1.135,0.795,1.495,1.375 + c0.359,0.582,0.54,1.301,0.54,2.156c0,0.775-0.216,1.5-0.646,2.178c-0.431,0.676-1.04,1.18-1.827,1.508 + c-0.787,0.33-1.769,0.494-2.943,0.494c-1.711,0-3.024-0.395-3.941-1.186C88.076,427.93,87.528,426.777,87.35,425.264z"/> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M100.648,429.666v-13.529h10.031v2.289h-7.3v3 + h6.792v2.279h-6.792v3.682h7.559v2.279H100.648z"/> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M113.328,429.666v-13.418h2.731v11.139h6.792 + v2.279H113.328z"/> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M124.799,429.666v-13.529h10.031v2.289h-7.3v3 + h6.792v2.279h-6.792v3.682h7.559v2.279H124.799z"/> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M146.062,424.691l2.648,0.84 + c-0.406,1.477-1.081,2.574-2.025,3.291s-2.143,1.074-3.595,1.074c-1.796,0-3.272-0.613-4.43-1.84 + c-1.156-1.229-1.734-2.906-1.734-5.035c0-2.252,0.581-4,1.744-5.246c1.162-1.246,2.691-1.869,4.586-1.869 + c1.655,0,3,0.49,4.033,1.469c0.615,0.578,1.076,1.408,1.384,2.49l-2.703,0.646c-0.16-0.701-0.494-1.256-1.002-1.66 + c-0.507-0.406-1.124-0.609-1.85-0.609c-1.003,0-1.817,0.359-2.441,1.08c-0.624,0.719-0.937,1.885-0.937,3.496 + c0,1.711,0.308,2.93,0.923,3.654c0.615,0.727,1.415,1.09,2.399,1.09c0.726,0,1.351-0.23,1.873-0.691 + C145.459,426.408,145.834,425.684,146.062,424.691z"/> + <path fill="#1B0942" stroke="#1B0942" stroke-width="2" stroke-miterlimit="10" d="M154.101,429.666v-11.24h-4.015v-2.289h10.751 + v2.289h-4.005v11.24H154.101z"/> + </g> +</g> +<g> + <g> + <path fill="#7A65F5" d="M320.35,423.264l2.658-0.258c0.16,0.893,0.484,1.547,0.974,1.967c0.489,0.418,1.148,0.627,1.979,0.627 + c0.88,0,1.543-0.186,1.988-0.559c0.446-0.373,0.669-0.807,0.669-1.307c0-0.318-0.094-0.592-0.281-0.816s-0.515-0.42-0.982-0.586 + c-0.32-0.111-1.049-0.307-2.188-0.59c-1.464-0.363-2.491-0.809-3.082-1.338c-0.831-0.744-1.246-1.652-1.246-2.723 + c0-0.689,0.195-1.334,0.586-1.934s0.954-1.057,1.689-1.371c0.734-0.312,1.622-0.471,2.662-0.471c1.698,0,2.977,0.373,3.834,1.117 + c0.858,0.744,1.31,1.738,1.353,2.98l-2.731,0.121c-0.117-0.695-0.368-1.195-0.753-1.5c-0.384-0.305-0.961-0.457-1.729-0.457 + c-0.794,0-1.416,0.162-1.864,0.488c-0.29,0.209-0.435,0.49-0.435,0.84c0,0.32,0.136,0.594,0.406,0.822 + c0.345,0.289,1.182,0.59,2.511,0.904s2.312,0.639,2.948,0.973c0.637,0.336,1.135,0.795,1.495,1.375 + c0.359,0.582,0.54,1.301,0.54,2.156c0,0.775-0.216,1.5-0.646,2.178c-0.431,0.676-1.04,1.18-1.827,1.508 + c-0.787,0.33-1.769,0.494-2.943,0.494c-1.711,0-3.024-0.395-3.941-1.186C321.076,425.93,320.528,424.777,320.35,423.264z"/> + <path fill="#7A65F5" d="M336.694,427.666v-11.24h-4.015v-2.289h10.751v2.289h-4.005v11.24H336.694z"/> + <path fill="#7A65F5" d="M355.99,427.666h-2.972l-1.182-3.072h-5.407l-1.117,3.072h-2.897l5.27-13.529h2.889L355.99,427.666z + M350.961,422.314l-1.864-5.021l-1.827,5.021H350.961z"/> + <path fill="#7A65F5" d="M357.449,427.666v-13.529h5.749c1.445,0,2.496,0.123,3.151,0.365c0.655,0.244,1.18,0.676,1.573,1.297 + s0.591,1.332,0.591,2.131c0,1.016-0.299,1.854-0.896,2.516s-1.488,1.078-2.676,1.25c0.591,0.346,1.078,0.723,1.463,1.135 + c0.384,0.412,0.902,1.145,1.555,2.197l1.652,2.639h-3.268l-1.975-2.943c-0.701-1.053-1.182-1.715-1.439-1.988 + c-0.259-0.273-0.532-0.463-0.821-0.562c-0.289-0.102-0.748-0.152-1.375-0.152h-0.554v5.646H357.449z M360.181,419.859h2.021 + c1.311,0,2.129-0.055,2.455-0.166s0.581-0.301,0.766-0.572s0.277-0.609,0.277-1.016c0-0.455-0.122-0.822-0.364-1.102 + c-0.243-0.281-0.587-0.457-1.029-0.531c-0.222-0.031-0.886-0.047-1.993-0.047h-2.132V419.859z"/> + <path fill="#7A65F5" d="M374.134,427.666v-11.24h-4.015v-2.289h10.751v2.289h-4.005v11.24H374.134z"/> + </g> +</g> +<g> + <g> + <path fill="#7A65F5" d="M87.35,423.264l2.658-0.258c0.16,0.893,0.484,1.547,0.974,1.967c0.489,0.418,1.148,0.627,1.979,0.627 + c0.88,0,1.543-0.186,1.988-0.559c0.446-0.373,0.669-0.807,0.669-1.307c0-0.318-0.094-0.592-0.281-0.816s-0.515-0.42-0.982-0.586 + c-0.32-0.111-1.049-0.307-2.188-0.59c-1.464-0.363-2.491-0.809-3.082-1.338c-0.831-0.744-1.246-1.652-1.246-2.723 + c0-0.689,0.195-1.334,0.586-1.934s0.954-1.057,1.689-1.371c0.734-0.312,1.622-0.471,2.662-0.471c1.698,0,2.977,0.373,3.834,1.117 + c0.858,0.744,1.31,1.738,1.353,2.98l-2.731,0.121c-0.117-0.695-0.368-1.195-0.753-1.5c-0.384-0.305-0.961-0.457-1.729-0.457 + c-0.794,0-1.416,0.162-1.864,0.488c-0.29,0.209-0.435,0.49-0.435,0.84c0,0.32,0.136,0.594,0.406,0.822 + c0.345,0.289,1.182,0.59,2.511,0.904s2.312,0.639,2.948,0.973c0.637,0.336,1.135,0.795,1.495,1.375 + c0.359,0.582,0.54,1.301,0.54,2.156c0,0.775-0.216,1.5-0.646,2.178c-0.431,0.676-1.04,1.18-1.827,1.508 + c-0.787,0.33-1.769,0.494-2.943,0.494c-1.711,0-3.024-0.395-3.941-1.186C88.076,425.93,87.528,424.777,87.35,423.264z"/> + <path fill="#7A65F5" d="M100.648,427.666v-13.529h10.031v2.289h-7.3v3h6.792v2.279h-6.792v3.682h7.559v2.279H100.648z"/> + <path fill="#7A65F5" d="M113.328,427.666v-13.418h2.731v11.139h6.792v2.279H113.328z"/> + <path fill="#7A65F5" d="M124.799,427.666v-13.529h10.031v2.289h-7.3v3h6.792v2.279h-6.792v3.682h7.559v2.279H124.799z"/> + <path fill="#7A65F5" d="M146.062,422.691l2.648,0.84c-0.406,1.477-1.081,2.574-2.025,3.291s-2.143,1.074-3.595,1.074 + c-1.796,0-3.272-0.613-4.43-1.84c-1.156-1.229-1.734-2.906-1.734-5.035c0-2.252,0.581-4,1.744-5.246 + c1.162-1.246,2.691-1.869,4.586-1.869c1.655,0,3,0.49,4.033,1.469c0.615,0.578,1.076,1.408,1.384,2.49l-2.703,0.646 + c-0.16-0.701-0.494-1.256-1.002-1.66c-0.507-0.406-1.124-0.609-1.85-0.609c-1.003,0-1.817,0.359-2.441,1.08 + c-0.624,0.719-0.937,1.885-0.937,3.496c0,1.711,0.308,2.93,0.923,3.654c0.615,0.727,1.415,1.09,2.399,1.09 + c0.726,0,1.351-0.23,1.873-0.691C145.459,424.408,145.834,423.684,146.062,422.691z"/> + <path fill="#7A65F5" d="M154.101,427.666v-11.24h-4.015v-2.289h10.751v2.289h-4.005v11.24H154.101z"/> + </g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> </g> </svg>
@@ -18,7 +18,9 @@ static const char* ERROR_OVERFLOW = "Arguments overflow";
static struct CLIDebugger* _activeDebugger; +#ifndef NDEBUG static void _breakInto(struct CLIDebugger*, struct CLIDebugVector*); +#endif static void _continue(struct CLIDebugger*, struct CLIDebugVector*); static void _disassemble(struct CLIDebugger*, struct CLIDebugVector*); static void _disassembleArm(struct CLIDebugger*, struct CLIDebugVector*);@@ -56,6 +58,8 @@ { "b", _setBreakpoint, CLIDVParse, "Set a breakpoint" },
{ "b/a", _setBreakpointARM, CLIDVParse, "Set a software breakpoint as ARM" }, { "b/t", _setBreakpointThumb, CLIDVParse, "Set a software breakpoint as Thumb" }, { "break", _setBreakpoint, CLIDVParse, "Set a breakpoint" }, + { "break/a", _setBreakpointARM, CLIDVParse, "Set a software breakpoint as ARM" }, + { "break/t", _setBreakpointThumb, CLIDVParse, "Set a software breakpoint as Thumb" }, { "c", _continue, 0, "Continue execution" }, { "continue", _continue, 0, "Continue execution" }, { "d", _clearBreakpoint, CLIDVParse, "Delete a breakpoint" },@@ -97,7 +101,9 @@ { "w/r", _writeRegister, CLIDVParse, "Write a register" },
{ "x/1", _dumpByte, CLIDVParse, "Examine bytes at a specified offset" }, { "x/2", _dumpHalfword, CLIDVParse, "Examine halfwords at a specified offset" }, { "x/4", _dumpWord, CLIDVParse, "Examine words at a specified offset" }, +#ifndef NDEBUG { "!", _breakInto, 0, "Break into attached debugger (for developers)" }, +#endif { 0, 0, 0, 0 } };@@ -112,6 +118,7 @@ psr.f ? 'F' : '-',
psr.t ? 'T' : '-'); } +#ifndef NDEBUG static void _handleDeath(int sig) { UNUSED(sig); printf("No debugger attached!\n");@@ -133,6 +140,7 @@ kill(getpid(), SIGTRAP);
#endif sigaction(SIGTRAP, &osa, 0); } +#endif static void _continue(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { UNUSED(dv);
@@ -167,7 +167,7 @@
int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) { audio->nextEvent -= cycles; audio->eventDiff += cycles; - if (audio->nextEvent <= 0) { + while (audio->nextEvent <= 0) { audio->nextEvent = INT_MAX; if (audio->enable) { if (audio->playingCh1 && !audio->ch1.envelope.dead) {
@@ -94,6 +94,7 @@ gba->idleDetectionStep = 0;
gba->idleDetectionFailures = 0; gba->realisticTiming = true; + gba->hardCrash = true; gba->performingDMA = false; }@@ -183,6 +184,11 @@ struct GBA* gba = (struct GBA*) cpu->master;
int32_t cycles = cpu->nextEvent; int32_t nextEvent = INT_MAX; int32_t testEvent; +#ifndef NDEBUG + if (cycles < 0) { + GBALog(gba, GBA_LOG_FATAL, "Negative cycles passed: %i", cycles); + } +#endif gba->bus = cpu->prefetch[1]; if (cpu->executionMode == MODE_THUMB) {@@ -238,7 +244,7 @@ timer = &gba->timers[0];
if (timer->enable) { timer->nextEvent -= cycles; timer->lastEvent -= cycles; - if (timer->nextEvent <= 0) { + while (timer->nextEvent <= 0) { timer->lastEvent = timer->nextEvent; timer->nextEvent += timer->overflowInterval; gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload;
@@ -124,6 +124,7 @@ int32_t cachedRegisters[16];
bool taintedRegisters[16]; bool realisticTiming; + bool hardCrash; }; struct GBACartridge {
@@ -9,6 +9,8 @@ #include "gba/io.h"
#include "gba/serialize.h" #include "util/hash.h" +const int GBA_LUX_LEVELS[10] = { 5, 11, 18, 27, 42, 62, 84, 109, 139, 183 }; + static void _readPins(struct GBACartridgeHardware* hw); static void _outputPins(struct GBACartridgeHardware* hw, unsigned pins);@@ -610,6 +612,7 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASerializedState* state) {
hw->readWrite = state->hw.readWrite; hw->pinState = state->hw.pinState; hw->direction = state->hw.pinDirection; + hw->devices = state->hw.devices; hw->rtc = state->hw.rtc; hw->gyroSample = state->hw.gyroSample; hw->gyroEdge = state->hw.gyroEdge;@@ -622,4 +625,7 @@ hw->lightEdge = state->hw.lightEdge;
hw->gbpInputsPosted = state->hw.gbpInputsPosted; hw->gbpTxPosition = state->hw.gbpTxPosition; hw->gbpNextEvent = state->hw.gbpNextEvent; + if (hw->devices & HW_GB_PLAYER) { + GBASIOSetDriver(&hw->p->sio, &hw->gbpDriver.d, SIO_NORMAL_32); + } }
@@ -433,8 +433,9 @@ }
void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) { struct GBAInputMapImpl* impl = _guaranteeMap(map, type); - TableEnumerate(&impl->axes, _unbindAxis, &description->highDirection); - TableEnumerate(&impl->axes, _unbindAxis, &description->lowDirection); + struct GBAAxis d2 = *description; + TableEnumerate(&impl->axes, _unbindAxis, &d2.highDirection); + TableEnumerate(&impl->axes, _unbindAxis, &d2.lowDirection); struct GBAAxis* dup = malloc(sizeof(struct GBAAxis)); *dup = *description; TableInsert(&impl->axes, axis, dup);
@@ -79,6 +79,8 @@
int32_t (*readGyroZ)(struct GBARotationSource*); }; +extern const int GBA_LUX_LEVELS[10]; + struct GBALuminanceSource { void (*sample)(struct GBALuminanceSource*);
@@ -278,9 +278,11 @@ default:
memory->activeRegion = -1; cpu->memory.activeRegion = _deadbeef; cpu->memory.activeMask = 0; - if (!gba->yankedRomSize) { - GBALog(gba, GBA_LOG_FATAL, "Jumped to invalid address"); + enum GBALogLevel errorLevel = GBA_LOG_FATAL; + if (gba->yankedRomSize || !gba->hardCrash) { + errorLevel = GBA_LOG_GAME_ERROR; } + GBALog(gba, errorLevel, "Jumped to invalid address: %08X", address); return; } cpu->memory.activeSeqCycles32 = memory->waitstatesSeq32[memory->activeRegion];@@ -634,8 +636,12 @@
#define STORE_VRAM \ if ((address & 0x0001FFFF) < SIZE_VRAM) { \ STORE_32(value, address & 0x0001FFFC, gba->video.renderer->vram); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \ } else { \ STORE_32(value, address & 0x00017FFC, gba->video.renderer->vram); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \ } \ wait += waitstatesRegion[REGION_VRAM];@@ -728,8 +734,10 @@ break;
case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { STORE_16(value, address & 0x0001FFFE, gba->video.renderer->vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); } else { STORE_16(value, address & 0x00017FFE, gba->video.renderer->vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); } break; case REGION_OAM:@@ -794,8 +802,8 @@ // TODO: check BG mode
GBALog(gba, GBA_LOG_GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address); break; } - ((int8_t*) gba->video.renderer->vram)[address & 0x1FFFE] = value; - ((int8_t*) gba->video.renderer->vram)[(address & 0x1FFFE) | 1] = value; + gba->video.renderer->vram[(address & 0x1FFFE) >> 1] = ((uint8_t) value) | (value << 8); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); break; case REGION_OAM: GBALog(gba, GBA_LOG_GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address);@@ -1397,7 +1405,7 @@ return INT_MAX;
} memory->nextDMA -= cycles; memory->eventDiff += cycles; - if (memory->nextDMA <= 0) { + while (memory->nextDMA <= 0) { struct GBADMA* dma = &memory->dma[memory->activeDMA]; GBAMemoryServiceDMA(gba, memory->activeDMA, dma); GBAMemoryUpdateDMAs(gba, memory->eventDiff);
@@ -116,6 +116,9 @@ int32_t x = GBAObjAttributesBGetX(sprite->b) << 23;
x >>= 23; uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1]; unsigned charBase = GBAObjAttributesCGetTile(sprite->c) * 0x20; + if (GBARegisterDISPCNTGetMode(renderer->dispcnt) >= 3 && GBAObjAttributesCGetTile(sprite->c) < 512) { + return 0; + } int variant = renderer->target1Obj && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT) { int target2 = renderer->target2Bd << 4;
@@ -13,6 +13,7 @@
static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer); static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer); static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer); +static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address); static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam); static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);@@ -46,6 +47,7 @@ renderer->d.init = GBAVideoSoftwareRendererInit;
renderer->d.reset = GBAVideoSoftwareRendererReset; renderer->d.deinit = GBAVideoSoftwareRendererDeinit; renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister; + renderer->d.writeVRAM = GBAVideoSoftwareRendererWriteVRAM; renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM; renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette; renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;@@ -325,6 +327,11 @@ default:
GBALog(0, GBA_LOG_GAME_ERROR, "Invalid video register: 0x%03X", address); } return value; +} + +static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { + UNUSED(renderer); + UNUSED(address); } static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
@@ -87,6 +87,10 @@ if (state->cpu.cycles < 0) {
GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: CPU cycles are negative"); error = true; } + if (state->cpu.nextEvent < 0) { + GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: Next event is negative"); + error = true; + } if (state->video.eventDiff < 0) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: video eventDiff is negative"); error = true;@@ -97,6 +101,10 @@ error = true;
} if (state->timers[0].overflowInterval < 0 || state->timers[1].overflowInterval < 0 || state->timers[2].overflowInterval < 0 || state->timers[3].overflowInterval < 0) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: overflowInterval is negative"); + error = true; + } + if (state->timers[0].nextEvent < 0 || state->timers[1].nextEvent < 0 || state->timers[2].nextEvent < 0 || state->timers[3].nextEvent < 0) { + GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: timer nextEvent is negative"); error = true; } if (state->audio.eventDiff < 0) {
@@ -247,6 +247,7 @@ unsigned audioBuffers;
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) { opts->audioBuffers = audioBuffers; } + _lookupUIntValue(config, "sampleRate", &opts->sampleRate); int fakeBool; if (_lookupIntValue(config, "useBios", &fakeBool)) {@@ -305,6 +306,7 @@ ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval); ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget); ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers); + ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate); ConfigurationSetIntValue(&config->defaultsTable, 0, "audioSync", opts->audioSync); ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync); ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);
@@ -29,6 +29,7 @@ int rewindBufferCapacity;
int rewindBufferInterval; float fpsTarget; size_t audioBuffers; + unsigned sampleRate; int fullscreen; int width;
@@ -419,6 +419,16 @@ }
threadContext->save = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "sram", ".sav", O_CREAT | O_RDWR); + if (!threadContext->patch) { + threadContext->patch = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "patch", ".ups", O_RDONLY); + } + if (!threadContext->patch) { + threadContext->patch = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "patch", ".ips", O_RDONLY); + } + if (!threadContext->patch) { + threadContext->patch = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "patch", ".bps", O_RDONLY); + } + MutexInit(&threadContext->stateMutex); ConditionInit(&threadContext->stateCond);
@@ -17,6 +17,7 @@ static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer);
static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer); static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer); static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); +static void GBAVideoDummyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address); static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); static void GBAVideoDummyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam); static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);@@ -47,6 +48,7 @@ .init = GBAVideoDummyRendererInit,
.reset = GBAVideoDummyRendererReset, .deinit = GBAVideoDummyRendererDeinit, .writeVideoRegister = GBAVideoDummyRendererWriteVideoRegister, + .writeVRAM = GBAVideoDummyRendererWriteVRAM, .writePalette = GBAVideoDummyRendererWritePalette, .writeOAM = GBAVideoDummyRendererWriteOAM, .drawScanline = GBAVideoDummyRendererDrawScanline,@@ -65,7 +67,7 @@
video->lastHblank = 0; video->nextHblank = VIDEO_HDRAW_LENGTH; video->nextEvent = video->nextHblank; - video->eventDiff = video->nextEvent; + video->eventDiff = 0; video->nextHblankIRQ = 0; video->nextVblankIRQ = 0;@@ -220,6 +222,12 @@ static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
UNUSED(renderer); UNUSED(address); return value; +} + +static void GBAVideoDummyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { + UNUSED(renderer); + UNUSED(address); + // Nothing to do } static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
@@ -161,6 +161,7 @@ void (*reset)(struct GBAVideoRenderer* renderer);
void (*deinit)(struct GBAVideoRenderer* renderer); uint16_t (*writeVideoRegister)(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); + void (*writeVRAM)(struct GBAVideoRenderer* renderer, uint32_t address); void (*writePalette)(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); void (*writeOAM)(struct GBAVideoRenderer* renderer, uint32_t oam); void (*drawScanline)(struct GBAVideoRenderer* renderer, int y);
@@ -8,6 +8,7 @@
#include "util/common.h" #include "gba/gba.h" +#include "gba/interface.h" #include "gba/renderers/video-software.h" #include "gba/serialize.h" #include "gba/supervisor/overrides.h"@@ -17,6 +18,8 @@ #include "util/vfs.h"
#define SAMPLES 1024 #define RUMBLE_PWM 35 + +#define SOLAR_SENSOR_LEVEL "mgba_solar_sensor_level" static retro_environment_t environCallback; static retro_video_refresh_t videoCallback;@@ -31,6 +34,8 @@
static void _postAudioBuffer(struct GBAAVStream*, struct GBAAudio* audio); static void _postVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer); static void _setRumble(struct GBARumble* rumble, int enable); +static uint8_t _readLux(struct GBALuminanceSource* lux); +static void _updateLux(struct GBALuminanceSource* lux); static struct GBA gba; static struct ARMCore cpu;@@ -44,6 +49,8 @@ static struct GBAAVStream stream;
static int rumbleLevel; static struct CircleBuffer rumbleHistory; static struct GBARumble rumble; +static struct GBALuminanceSource lux; +static int luxLevel; unsigned retro_api_version(void) { return RETRO_API_VERSION;@@ -51,6 +58,13 @@ }
void retro_set_environment(retro_environment_t env) { environCallback = env; + + struct retro_variable vars[] = { + { SOLAR_SENSOR_LEVEL, "Solar sensor level; 0|1|2|3|4|5|6|7|8|9|10" }, + { 0, 0 } + }; + + environCallback(RETRO_ENVIRONMENT_SET_VARIABLES, vars); } void retro_set_video_refresh(retro_video_refresh_t video) {@@ -115,7 +129,9 @@ { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" } + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "Brighten Solar Sensor" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "Darken Solar Sensor" } }; environCallback(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &inputDescriptors);@@ -130,6 +146,11 @@ } else {
rumbleCallback = 0; } + luxLevel = 0; + lux.readLuminance = _readLux; + lux.sample = _updateLux; + _updateLux(&lux); + struct retro_log_callback log; if (environCallback(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log)) { logCallback = log.log;@@ -151,6 +172,7 @@ gba.idleOptimization = IDLE_LOOP_REMOVE; // TODO: Settings
if (rumbleCallback) { gba.rumble = &rumble; } + gba.luminanceSource = &lux; rom = 0; const char* sysDir = 0;@@ -201,6 +223,26 @@ keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) << 7;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) << 8; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) << 9; + static bool wasAdjustingLux = false; + if (wasAdjustingLux) { + wasAdjustingLux = inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3) || + inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3); + } else { + if (inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3)) { + ++luxLevel; + if (luxLevel > 10) { + luxLevel = 10; + } + wasAdjustingLux = true; + } else if (inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3)) { + --luxLevel; + if (luxLevel < 0) { + luxLevel = 0; + } + wasAdjustingLux = true; + } + } + int frameCount = gba.video.frameCounter; while (gba.video.frameCounter == frameCount) { ARMRunLoop(&cpu);@@ -401,3 +443,40 @@ }
CircleBufferWrite8(&rumbleHistory, enable); rumbleCallback(0, RETRO_RUMBLE_STRONG, rumbleLevel * 0xFFFF / RUMBLE_PWM); } + +static void _updateLux(struct GBALuminanceSource* lux) { + UNUSED(lux); + struct retro_variable var = { + .key = SOLAR_SENSOR_LEVEL, + .value = 0 + }; + + bool updated = false; + if (!environCallback(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) || !updated) { + return; + } + if (!environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || !var.value) { + return; + } + + char* end; + int newLuxLevel = strtol(var.value, &end, 10); + if (!*end) { + if (newLuxLevel > 10) { + luxLevel = 10; + } else if (newLuxLevel < 0) { + luxLevel = 0; + } else { + luxLevel = newLuxLevel; + } + } +} + +static uint8_t _readLux(struct GBALuminanceSource* lux) { + UNUSED(lux); + int value = 0x16; + if (luxLevel > 0) { + value += GBA_LUX_LEVELS[luxLevel - 1]; + } + return 0xFF - value; +}
@@ -0,0 +1,136 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "gles2.h" + +#include "gba/video.h" + +static const char* const _vertexShader = + "attribute vec4 position;\n" + "varying vec2 texCoord;\n" + + "void main() {\n" + " gl_Position = position;\n" + " texCoord = (position.st + vec2(1.0, -1.0)) * vec2(0.46875, -0.3125);\n" + "}"; + +static const char* const _fragmentShader = + "varying vec2 texCoord;\n" + "uniform sampler2D tex;\n" + + "void main() {\n" + " vec4 color = texture2D(tex, texCoord);\n" + " color.a = 1.;\n" + " gl_FragColor = color;" + "}"; + +static const GLfloat _vertices[] = { + -1.f, -1.f, + -1.f, 1.f, + 1.f, 1.f, + 1.f, -1.f, +}; + +static void GBAGLES2ContextInit(struct VideoBackend* v, WHandle handle) { + UNUSED(handle); + struct GBAGLES2Context* context = (struct GBAGLES2Context*) v; + glGenTextures(1, &context->tex); + glBindTexture(GL_TEXTURE_2D, context->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + +#ifdef COLOR_16_BIT +#ifdef COLOR_5_6_5 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0); +#endif +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); +#endif + + glShaderSource(context->fragmentShader, 1, (const GLchar**) &_fragmentShader, 0); + glShaderSource(context->vertexShader, 1, (const GLchar**) &_vertexShader, 0); + glAttachShader(context->program, context->vertexShader); + glAttachShader(context->program, context->fragmentShader); + char log[1024]; + glCompileShader(context->fragmentShader); + glCompileShader(context->vertexShader); + glGetShaderInfoLog(context->fragmentShader, 1024, 0, log); + glGetShaderInfoLog(context->vertexShader, 1024, 0, log); + glLinkProgram(context->program); + glGetProgramInfoLog(context->program, 1024, 0, log); + printf("%s\n", log); + context->texLocation = glGetUniformLocation(context->program, "tex"); + context->positionLocation = glGetAttribLocation(context->program, "position"); + glClearColor(0.f, 0.f, 0.f, 1.f); +} + +static void GBAGLES2ContextDeinit(struct VideoBackend* v) { + struct GBAGLES2Context* context = (struct GBAGLES2Context*) v; + glDeleteTextures(1, &context->tex); +} + +static void GBAGLES2ContextResized(struct VideoBackend* v, int w, int h) { + int drawW = w; + int drawH = h; + if (v->lockAspectRatio) { + if (w * 2 > h * 3) { + drawW = h * 3 / 2; + } else if (w * 2 < h * 3) { + drawH = w * 2 / 3; + } + } + glViewport(0, 0, 240, 160); + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH); +} + +static void GBAGLES2ContextClear(struct VideoBackend* v) { + UNUSED(v); + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT); +} + +void GBAGLES2ContextDrawFrame(struct VideoBackend* v) { + struct GBAGLES2Context* context = (struct GBAGLES2Context*) v; + glUseProgram(context->program); + glUniform1i(context->texLocation, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, context->tex); + glVertexAttribPointer(context->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); + glEnableVertexAttribArray(context->positionLocation); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glUseProgram(0); +} + +void GBAGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) { + struct GBAGLES2Context* context = (struct GBAGLES2Context*) v; + glBindTexture(GL_TEXTURE_2D, context->tex); +#ifdef COLOR_16_BIT +#ifdef COLOR_5_6_5 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame); +#endif +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame); +#endif +} + +void GBAGLES2ContextCreate(struct GBAGLES2Context* context) { + context->d.init = GBAGLES2ContextInit; + context->d.deinit = GBAGLES2ContextDeinit; + context->d.resized = GBAGLES2ContextResized; + context->d.swap = 0; + context->d.clear = GBAGLES2ContextClear; + context->d.postFrame = GBAGLES2ContextPostFrame; + context->d.drawFrame = GBAGLES2ContextDrawFrame; + context->d.setMessage = 0; + context->d.clearMessage = 0; +}
@@ -0,0 +1,27 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef GLES2_H +#define GLES2_H + +#include <GLES2/gl2.h> + +#include "platform/video-backend.h" + +struct GBAGLES2Context { + struct VideoBackend d; + + GLuint tex; + GLuint fragmentShader; + GLuint vertexShader; + GLuint program; + GLuint bufferObject; + GLuint texLocation; + GLuint positionLocation; +}; + +void GBAGLES2ContextCreate(struct GBAGLES2Context*); + +#endif
@@ -186,7 +186,7 @@ }
} } GBASyncWaitFrameEnd(&context->sync); - if (*frames == duration) { + if (duration > 0 && *frames == duration) { _GBAPerfShutdown(0); } if (_dispatchExiting) {
@@ -10,7 +10,7 @@ #include "util/common.h"
#include <pthread.h> #include <sys/time.h> -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__OpenBSD__) #include <pthread_np.h> #endif@@ -79,7 +79,7 @@
static inline int ThreadSetName(const char* name) { #ifdef __APPLE__ return pthread_setname_np(name); -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__OpenBSD__) pthread_set_name_np(pthread_self(), name); return 0; #else
@@ -0,0 +1,36 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "AboutScreen.h" + +#include "util/version.h" + +#include <QPixmap> + +using namespace QGBA; + +AboutScreen::AboutScreen(QWidget* parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + + QPixmap logo(":/res/mgba-1024.png"); + logo = logo.scaled(m_ui.logo->minimumSize() * devicePixelRatio(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + logo.setDevicePixelRatio(devicePixelRatio()); + m_ui.logo->setPixmap(logo); + + m_ui.projectName->setText(QLatin1String(projectName)); + m_ui.projectVersion->setText(QLatin1String(projectVersion)); + QString gitInfo = m_ui.gitInfo->text(); + gitInfo.replace("{gitBranch}", QLatin1String(gitBranch)); + gitInfo.replace("{gitCommit}", QLatin1String(gitCommit)); + m_ui.gitInfo->setText(gitInfo); + QString description = m_ui.description->text(); + description.replace("{projectName}", QLatin1String(projectName)); + m_ui.description->setText(description); + QString extraLinks = m_ui.extraLinks->text(); + extraLinks.replace("{gitBranch}", QLatin1String(gitBranch)); + m_ui.extraLinks->setText(extraLinks); +}
@@ -0,0 +1,27 @@
+/* Copyright (c) 2013-2014 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_ABOUT_SCREEN +#define QGBA_ABOUT_SCREEN + +#include <QDialog> + +#include "ui_AboutScreen.h" + +namespace QGBA { + +class AboutScreen : public QDialog { +Q_OBJECT + +public: + AboutScreen(QWidget* parent = nullptr); + +private: + Ui::AboutScreen m_ui; +}; + +} + +#endif
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AboutScreen</class> + <widget class="QWidget" name="AboutScreen"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>565</width> + <height>268</height> + </rect> + </property> + <property name="windowTitle"> + <string>About</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="sizeConstraint"> + <enum>QLayout::SetFixedSize</enum> + </property> + <item row="0" column="1"> + <widget class="QLabel" name="projectName"> + <property name="font"> + <font> + <pointsize>36</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>{projectName}</string> + </property> + <property name="alignment"> + <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> + </property> + </widget> + </item> + <item row="6" column="0" colspan="4"> + <widget class="QLabel" name="copyright"> + <property name="font"> + <font> + <pointsize>10</pointsize> + </font> + </property> + <property name="text"> + <string>© 2013 – 2015 Jeffrey Pfau — Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="projectVersion"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>{projectVersion}</string> + </property> + <property name="alignment"> + <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="0" column="0" rowspan="6"> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="leftMargin"> + <number>8</number> + </property> + <property name="rightMargin"> + <number>16</number> + </property> + <property name="bottomMargin"> + <number>12</number> + </property> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="logo"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>256</width> + <height>192</height> + </size> + </property> + <property name="text"> + <string>{logo}</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="description"> + <property name="text"> + <string>{projectName} is an open-source Game Boy Advance emulator</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLabel" name="extraLinks"> + <property name="text"> + <string><a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> • <a href="https://github.com/mgba-emu/mgba/blob/{gitBranch}/LICENSE">License</a></string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="gitInfo"> + <property name="font"> + <font> + <pointsize>10</pointsize> + </font> + </property> + <property name="text"> + <string>Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt></string> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui>
@@ -38,10 +38,10 @@ return new AudioProcessorQt();
#endif default: -#ifdef BUILD_QT_MULTIMEDIA - return new AudioProcessorQt(); -#else +#ifdef BUILD_SDL return new AudioProcessorSDL(); +#else + return new AudioProcessorQt(); #endif } }
@@ -31,6 +31,7 @@ AudioProcessor(QObject* parent = nullptr);
virtual void setInput(GBAThread* input); int getBufferSamples() const { return m_samples; } + virtual unsigned sampleRate() const = 0; public slots: virtual void start() = 0;@@ -39,7 +40,7 @@
virtual void setBufferSamples(int samples) = 0; virtual void inputParametersChanged() = 0; - virtual unsigned sampleRate() const = 0; + virtual void requestSampleRate(unsigned) = 0; protected: GBAThread* input() { return m_context; }
@@ -20,6 +20,7 @@ AudioProcessorQt::AudioProcessorQt(QObject* parent)
: AudioProcessor(parent) , m_audioOutput(nullptr) , m_device(nullptr) + , m_sampleRate(44100) { }@@ -45,7 +46,7 @@ }
if (!m_audioOutput) { QAudioFormat format; - format.setSampleRate(44100); + format.setSampleRate(m_sampleRate); format.setChannelCount(2); format.setSampleSize(16); format.setCodec("audio/pcm");@@ -81,6 +82,15 @@
void AudioProcessorQt::inputParametersChanged() { if (m_device) { m_device->setFormat(m_audioOutput->format()); + } +} + +void AudioProcessorQt::requestSampleRate(unsigned rate) { + m_sampleRate = rate; + if (m_device) { + QAudioFormat format(m_audioOutput->format()); + format.setSampleRate(rate); + m_device->setFormat(format); } }
@@ -20,6 +20,7 @@ public:
AudioProcessorQt(QObject* parent = nullptr); virtual void setInput(GBAThread* input); + virtual unsigned sampleRate() const override; public slots: virtual void start();@@ -28,11 +29,12 @@
virtual void setBufferSamples(int samples); virtual void inputParametersChanged(); - virtual unsigned sampleRate() const override; + virtual void requestSampleRate(unsigned) override; private: QAudioOutput* m_audioOutput; AudioDevice* m_device; + unsigned m_sampleRate; }; }
@@ -15,7 +15,7 @@ using namespace QGBA;
AudioProcessorSDL::AudioProcessorSDL(QObject* parent) : AudioProcessor(parent) - , m_audio() + , m_audio{ 2048, 44100 } { }@@ -55,6 +55,18 @@
void AudioProcessorSDL::inputParametersChanged() { } +void AudioProcessorSDL::requestSampleRate(unsigned rate) { + m_audio.sampleRate = rate; + if (m_audio.thread) { + GBASDLDeinitAudio(&m_audio); + GBASDLInitAudio(&m_audio, input()); + } +} + unsigned AudioProcessorSDL::sampleRate() const { - return m_audio.obtainedSpec.freq; + if (m_audio.thread) { + return m_audio.obtainedSpec.freq; + } else { + return 0; + } }
@@ -22,6 +22,8 @@ public:
AudioProcessorSDL(QObject* parent = nullptr); ~AudioProcessorSDL(); + virtual unsigned sampleRate() const override; + public slots: virtual void start(); virtual void pause();@@ -29,7 +31,7 @@
virtual void setBufferSamples(int samples); virtual void inputParametersChanged(); - virtual unsigned sampleRate() const override; + virtual void requestSampleRate(unsigned) override; private: GBASDLAudio m_audio;
@@ -28,17 +28,31 @@
set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) -find_package(Qt5Multimedia) +if(NOT WIN32 OR NOT BUILD_SDL) + find_package(Qt5Multimedia) +endif() find_package(Qt5OpenGL) find_package(Qt5Widgets) -if(NOT Qt5OpenGL_FOUND OR NOT Qt5Widgets_FOUND OR NOT OPENGL_FOUND) +if(NOT BUILD_GL AND NOT BUILD_GLES2) + message(WARNING "OpenGL is required to build the Qt port") + set(BUILD_QT OFF PARENT_SCOPE) + return() +endif() + +if(NOT Qt5OpenGL_FOUND OR NOT Qt5Widgets_FOUND) message(WARNING "Cannot find Qt modules") set(BUILD_QT OFF PARENT_SCOPE) return() endif() +if(BUILD_GL) list(APPEND PLATFORM_SRC ${PLATFORM_SRC} ${CMAKE_SOURCE_DIR}/src/platform/opengl/gl.c) +endif() + +if(BUILD_GLES2) +list(APPEND PLATFORM_SRC ${PLATFORM_SRC} ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c) +endif() get_target_property(QT_TYPE Qt5::Core TYPE) if(QT_TYPE STREQUAL STATIC_LIBRARY)@@ -47,6 +61,7 @@ add_definitions(-DQT_STATIC)
endif() set(SOURCE_FILES + AboutScreen.cpp AudioProcessor.cpp CheatsModel.cpp CheatsView.cpp@@ -83,6 +98,7 @@ VFileDevice.cpp
VideoView.cpp) qt5_wrap_ui(UI_FILES + AboutScreen.ui CheatsView.ui GIFView.ui LoadSaveState.ui@@ -142,7 +158,7 @@ add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/mgba.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_FILES} ${AUDIO_SRC} ${RESOURCES})
set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES}") list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::OpenGL) -target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES}) +target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE) install(TARGETS ${BINARY_NAME}-qt@@ -153,6 +169,9 @@ find_program(DESKTOP_FILE_INSTALL desktop-file-install)
if(DESKTOP_FILE_INSTALL) install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/share/applications/\")") endif() +endif() +if(UNIX) + install(FILES ${CMAKE_SOURCE_DIR}/doc/mgba-qt.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-qt) endif() if(APPLE OR WIN32) set_target_properties(${BINARY_NAME}-qt PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
@@ -99,19 +99,29 @@ m_controller->threadContinue();
} void CheatsView::enterCheat(std::function<bool(GBACheatSet*, const char*)> callback) { - GBACheatSet* set; + GBACheatSet* set = nullptr; QModelIndexList selection = m_ui.cheatList->selectionModel()->selectedIndexes(); - if (selection.count() != 1) { - return; + QModelIndex index; + if (selection.count() == 0) { + set = new GBACheatSet; + GBACheatSetInit(set, nullptr); + } else if (selection.count() == 1) { + index = selection[0]; + set = m_model.itemAt(index); } - set = m_model.itemAt(selection[0]); + if (!set) { return; } m_controller->threadInterrupt(); + if (selection.count() == 0) { + m_model.addSet(set); + index = m_model.index(m_model.rowCount() - 1, 0, QModelIndex()); + m_ui.cheatList->selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + } QStringList cheats = m_ui.codeEntry->toPlainText().split('\n', QString::SkipEmptyParts); for (const QString& string : cheats) { - m_model.beginAppendRow(selection[0]); + m_model.beginAppendRow(index); callback(set, string.toUtf8().constData()); m_model.endAppendRow(); }
@@ -107,7 +107,7 @@
m_opts.audioSync = GameController::AUDIO_SYNC; m_opts.videoSync = GameController::VIDEO_SYNC; m_opts.fpsTarget = 60; - m_opts.audioBuffers = 2048; + m_opts.audioBuffers = 1536; m_opts.volume = GBA_AUDIO_VOLUME_MAX; m_opts.logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS; m_opts.rewindEnable = false;@@ -115,8 +115,8 @@ m_opts.rewindBufferInterval = 0;
m_opts.rewindBufferCapacity = 0; m_opts.useBios = true; m_opts.suspendScreensaver = true; + GBAConfigLoad(&m_config); GBAConfigLoadDefaults(&m_config, &m_opts); - GBAConfigLoad(&m_config); GBAConfigMap(&m_config, &m_opts); }@@ -126,7 +126,11 @@ GBAConfigFreeOpts(&m_opts);
} bool ConfigController::parseArguments(GBAArguments* args, int argc, char* argv[]) { - return ::parseArguments(args, &m_config, argc, argv, 0); + if (::parseArguments(args, &m_config, argc, argv, 0)) { + GBAConfigMap(&m_config, &m_opts); + return true; + } + return false; } ConfigOption* ConfigController::addOption(const char* key) {
@@ -22,7 +22,7 @@ #endif
Display* Display::create(QWidget* parent) { #ifdef BUILD_GL - QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer)); + QGLFormat format(QGLFormat(QGL::Rgba | QGL::SingleBuffer)); format.setSwapInterval(1); #endif@@ -72,6 +72,9 @@ }
void Display::showMessage(const QString& message) { m_messagePainter.showMessage(message); + if (!isDrawing()) { + forceDraw(); + } } void Display::mouseMoveEvent(QMouseEvent*) {
@@ -33,6 +33,8 @@
bool isAspectRatioLocked() const { return m_lockAspectRatio; } bool isFiltered() const { return m_filter; } + virtual bool isDrawing() const = 0; + signals: void showCursor(); void hideCursor();
@@ -16,6 +16,7 @@ using namespace QGBA;
DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent) : Display(parent) + , m_isDrawing(false) , m_gl(new EmptyGLWidget(format, this)) , m_painter(new PainterGL(m_gl)) , m_drawThread(nullptr)@@ -33,6 +34,7 @@ void DisplayGL::startDrawing(GBAThread* thread) {
if (m_drawThread) { return; } + m_isDrawing = true; m_painter->setContext(thread); m_painter->setMessagePainter(messagePainter()); m_context = thread;@@ -55,6 +57,7 @@ }
void DisplayGL::stopDrawing() { if (m_drawThread) { + m_isDrawing = false; if (GBAThreadIsActive(m_context)) { GBAThreadInterrupt(m_context); }@@ -69,6 +72,7 @@ }
void DisplayGL::pauseDrawing() { if (m_drawThread) { + m_isDrawing = false; if (GBAThreadIsActive(m_context)) { GBAThreadInterrupt(m_context); }@@ -81,6 +85,7 @@ }
void DisplayGL::unpauseDrawing() { if (m_drawThread) { + m_isDrawing = true; if (GBAThreadIsActive(m_context)) { GBAThreadInterrupt(m_context); }@@ -113,7 +118,8 @@ }
void DisplayGL::framePosted(const uint32_t* buffer) { if (m_drawThread && buffer) { - QMetaObject::invokeMethod(m_painter, "setBacking", Q_ARG(const uint32_t*, buffer)); + m_painter->enqueue(buffer); + QMetaObject::invokeMethod(m_painter, "draw"); } }@@ -135,7 +141,11 @@ , m_active(false)
, m_context(nullptr) , m_messagePainter(nullptr) { +#ifdef BUILD_GL GBAGLContextCreate(&m_backend); +#elif defined(BUILD_GLES2) + GBAGLES2ContextCreate(&m_backend); +#endif m_backend.d.swap = [](VideoBackend* v) { PainterGL* painter = static_cast<PainterGL*>(v->user); painter->m_gl->swapBuffers();@@ -143,6 +153,19 @@ };
m_backend.d.user = this; m_backend.d.filter = false; m_backend.d.lockAspectRatio = false; + + for (int i = 0; i < 2; ++i) { + m_free.append(new uint32_t[256 * 256]); + } +} + +PainterGL::~PainterGL() { + while (!m_queue.isEmpty()) { + delete[] m_queue.dequeue(); + } + for (auto item : m_free) { + delete[] item; + } } void PainterGL::setContext(GBAThread* context) {@@ -151,15 +174,6 @@ }
void PainterGL::setMessagePainter(MessagePainter* messagePainter) { m_messagePainter = messagePainter; -} - -void PainterGL::setBacking(const uint32_t* backing) { - m_gl->makeCurrent(); - m_backend.d.postFrame(&m_backend.d, backing); - if (m_active) { - draw(); - } - m_gl->doneCurrent(); } void PainterGL::resize(const QSize& size) {@@ -191,7 +205,11 @@ m_active = true;
} void PainterGL::draw() { - if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip)) { + if (m_queue.isEmpty()) { + return; + } + if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip) || !m_queue.isEmpty()) { + dequeue(); m_painter.begin(m_gl->context()->device()); performDraw(); m_painter.end();@@ -199,6 +217,9 @@ GBASyncWaitFrameEnd(&m_context->sync);
m_backend.d.swap(&m_backend.d); } else { GBASyncWaitFrameEnd(&m_context->sync); + } + if (!m_queue.isEmpty()) { + QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection); } }@@ -212,6 +233,7 @@
void PainterGL::stop() { m_active = false; m_gl->makeCurrent(); + dequeueAll(); m_backend.d.clear(&m_backend.d); m_backend.d.swap(&m_backend.d); m_backend.d.deinit(&m_backend.d);@@ -222,9 +244,6 @@ }
void PainterGL::pause() { m_active = false; - // Make sure both buffers are filled - forceDraw(); - forceDraw(); } void PainterGL::unpause() {@@ -241,3 +260,41 @@ if (m_messagePainter) {
m_messagePainter->paint(&m_painter); } } + +void PainterGL::enqueue(const uint32_t* backing) { + m_mutex.lock(); + uint32_t* buffer; + if (m_free.isEmpty()) { + buffer = m_queue.dequeue(); + } else { + buffer = m_free.takeLast(); + } + memcpy(buffer, backing, 256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL); + m_queue.enqueue(buffer); + m_mutex.unlock(); +} + +void PainterGL::dequeue() { + m_mutex.lock(); + if (m_queue.isEmpty()) { + m_mutex.unlock(); + return; + } + uint32_t* buffer = m_queue.dequeue(); + m_backend.d.postFrame(&m_backend.d, buffer); + m_free.append(buffer); + m_mutex.unlock(); +} + +void PainterGL::dequeueAll() { + uint32_t* buffer = 0; + m_mutex.lock(); + while (!m_queue.isEmpty()) { + buffer = m_queue.dequeue(); + m_free.append(buffer); + } + if (buffer) { + m_backend.d.postFrame(&m_backend.d, buffer); + } + m_mutex.unlock(); +}
@@ -9,12 +9,18 @@
#include "Display.h" #include <QGLWidget> +#include <QList> #include <QMouseEvent> +#include <QQueue> #include <QThread> #include <QTimer> extern "C" { +#ifdef BUILD_GL #include "platform/opengl/gl.h" +#elif defined(BUILD_GLES2) +#include "platform/opengl/gles2.h" +#endif } struct GBAThread;@@ -39,6 +45,8 @@ public:
DisplayGL(const QGLFormat& format, QWidget* parent = nullptr); ~DisplayGL(); + bool isDrawing() const override { return m_isDrawing; } + public slots: void startDrawing(GBAThread* context) override; void stopDrawing() override;@@ -56,6 +64,7 @@
private: void resizePainter(); + bool m_isDrawing; QGLWidget* m_gl; PainterGL* m_painter; QThread* m_drawThread;@@ -67,12 +76,13 @@ Q_OBJECT
public: PainterGL(QGLWidget* parent); + ~PainterGL(); void setContext(GBAThread*); void setMessagePainter(MessagePainter*); + void enqueue(const uint32_t* backing); public slots: - void setBacking(const uint32_t*); void forceDraw(); void draw(); void start();@@ -85,12 +95,21 @@ void filter(bool filter);
private: void performDraw(); + void dequeue(); + void dequeueAll(); + QList<uint32_t*> m_free; + QQueue<uint32_t*> m_queue; QPainter m_painter; + QMutex m_mutex; QGLWidget* m_gl; bool m_active; GBAThread* m_context; +#ifdef BUILD_GL GBAGLContext m_backend; +#elif defined(BUILD_GLES2) + GBAGLES2Context m_backend; +#endif QSize m_size; MessagePainter* m_messagePainter; };
@@ -15,11 +15,13 @@ using namespace QGBA;
DisplayQt::DisplayQt(QWidget* parent) : Display(parent) + , m_isDrawing(false) , m_backing(nullptr) { } void DisplayQt::startDrawing(GBAThread*) { + m_isDrawing = true; } void DisplayQt::lockAspectRatio(bool lock) {
@@ -21,11 +21,13 @@
public: DisplayQt(QWidget* parent = nullptr); + bool isDrawing() const override { return m_isDrawing; } + public slots: void startDrawing(GBAThread* context) override; - void stopDrawing() override {} - void pauseDrawing() override {} - void unpauseDrawing() override {} + void stopDrawing() override { m_isDrawing = false; } + void pauseDrawing() override { m_isDrawing = false; } + void unpauseDrawing() override { m_isDrawing = true; } void forceDraw() override { update(); } void lockAspectRatio(bool lock) override; void filter(bool filter) override;@@ -35,6 +37,7 @@ protected:
virtual void paintEvent(QPaintEvent*) override; private: + bool m_isDrawing; QImage m_backing; };
@@ -15,6 +15,7 @@ #include <QFileOpenEvent>
#include <QIcon> extern "C" { +#include "gba/supervisor/thread.h" #include "platform/commandline.h" #include "util/socket.h" }@@ -33,10 +34,13 @@ #ifdef BUILD_SDL
SDL_Init(SDL_INIT_NOPARACHUTE); #endif +#ifndef Q_OS_MAC setWindowIcon(QIcon(":/res/mgba-1024.png")); +#endif SocketSubsystemInit(); qRegisterMetaType<const uint32_t*>("const uint32_t*"); + qRegisterMetaType<GBAThread*>("GBAThread*"); QApplication::setApplicationName(projectName); QApplication::setApplicationVersion(projectVersion);@@ -45,31 +49,30 @@ if (!m_configController.getQtOption("displayDriver").isNull()) {
Display::setDriver(static_cast<Display::Driver>(m_configController.getQtOption("displayDriver").toInt())); } + GBAArguments args; + bool loaded = m_configController.parseArguments(&args, argc, argv); + if (loaded && args.showHelp) { + usage(argv[0], 0); + ::exit(0); + return; + } + + AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt())); Window* w = new Window(&m_configController); connect(w, &Window::destroyed, [this]() { m_windows[0] = nullptr; }); m_windows[0] = w; -#ifndef Q_OS_MAC - w->show(); -#endif - - GBAArguments args; - if (m_configController.parseArguments(&args, argc, argv)) { + if (loaded) { w->argumentsPassed(&args); } else { w->loadConfig(); } freeArguments(&args); - - AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt())); - w->controller()->reloadAudioDriver(); + w->show(); w->controller()->setMultiplayerController(&m_multiplayer); -#ifdef Q_OS_MAC - w->show(); -#endif } bool GBAApp::event(QEvent* event) {@@ -91,14 +94,9 @@ m_windows[windowId] = nullptr;
}); m_windows[windowId] = w; w->setAttribute(Qt::WA_DeleteOnClose); -#ifndef Q_OS_MAC - w->show(); -#endif w->loadConfig(); + w->show(); w->controller()->setMultiplayerController(&m_multiplayer); -#ifdef Q_OS_MAC - w->show(); -#endif return w; }
@@ -18,9 +18,9 @@
using namespace QGBA; const qreal GBAKeyEditor::DPAD_CENTER_X = 0.247; -const qreal GBAKeyEditor::DPAD_CENTER_Y = 0.431; -const qreal GBAKeyEditor::DPAD_WIDTH = 0.1; -const qreal GBAKeyEditor::DPAD_HEIGHT = 0.1; +const qreal GBAKeyEditor::DPAD_CENTER_Y = 0.432; +const qreal GBAKeyEditor::DPAD_WIDTH = 0.12; +const qreal GBAKeyEditor::DPAD_HEIGHT = 0.12; GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& profile, QWidget* parent) : QWidget(parent)@@ -148,8 +148,8 @@ setLocation(m_keyDL, DPAD_CENTER_X - DPAD_WIDTH, DPAD_CENTER_Y);
setLocation(m_keyDR, DPAD_CENTER_X + DPAD_WIDTH, DPAD_CENTER_Y); setLocation(m_keySelect, 0.415, 0.93); setLocation(m_keyStart, 0.585, 0.93); - setLocation(m_keyA, 0.826, 0.451); - setLocation(m_keyB, 0.667, 0.490); + setLocation(m_keyA, 0.826, 0.475); + setLocation(m_keyB, 0.667, 0.514); setLocation(m_keyL, 0.1, 0.1); setLocation(m_keyR, 0.9, 0.1);
@@ -18,7 +18,7 @@
using namespace QGBA; GDBWindow::GDBWindow(GDBController* controller, QWidget* parent) - : QWidget(parent) + : QDialog(parent) , m_gdbController(controller) { setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint);
@@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef QGBA_GDB_WINDOW #define QGBA_GDB_WINDOW -#include <QWidget> +#include <QDialog> class QLineEdit; class QPushButton;@@ -15,7 +15,7 @@ namespace QGBA {
class GDBController; -class GDBWindow : public QWidget { +class GDBWindow : public QDialog { Q_OBJECT public:
@@ -29,11 +29,10 @@
using namespace QGBA; using namespace std; -const int GameController::LUX_LEVELS[10] = { 5, 11, 18, 27, 42, 62, 84, 109, 139, 183 }; - GameController::GameController(QObject* parent) : QObject(parent) - , m_drawContext(new uint32_t[256 * 256]) + , m_drawContext(new uint32_t[256 * VIDEO_HORIZONTAL_PIXELS]) + , m_frontBuffer(new uint32_t[256 * 256]) , m_threadContext() , m_activeKeys(0) , m_inactiveKeys(0)@@ -49,6 +48,8 @@ , m_turbo(false)
, m_turboForced(false) , m_turboSpeed(-1) , m_wasPaused(false) + , m_audioChannels{ true, true, true, true, true, true } + , m_videoLayers{ true, true, true, true, true } , m_inputController(nullptr) , m_multiplayer(nullptr) , m_stateSlot(1)@@ -86,12 +87,25 @@ setLuminanceLevel(0);
m_threadContext.startCallback = [](GBAThread* context) { GameController* controller = static_cast<GameController*>(context->userData); - controller->m_audioProcessor->setInput(context); + if (controller->m_audioProcessor) { + controller->m_audioProcessor->setInput(context); + } context->gba->luminanceSource = &controller->m_lux; GBARTCGenericSourceInit(&controller->m_rtc, context->gba); context->gba->rtcSource = &controller->m_rtc.d; context->gba->rumble = controller->m_inputController->rumble(); context->gba->rotationSource = controller->m_inputController->rotationSource(); + context->gba->audio.forceDisableCh[0] = !controller->m_audioChannels[0]; + context->gba->audio.forceDisableCh[1] = !controller->m_audioChannels[1]; + context->gba->audio.forceDisableCh[2] = !controller->m_audioChannels[2]; + context->gba->audio.forceDisableCh[3] = !controller->m_audioChannels[3]; + context->gba->audio.forceDisableChA = !controller->m_audioChannels[4]; + context->gba->audio.forceDisableChB = !controller->m_audioChannels[5]; + context->gba->video.renderer->disableBG[0] = !controller->m_videoLayers[0]; + context->gba->video.renderer->disableBG[1] = !controller->m_videoLayers[1]; + context->gba->video.renderer->disableBG[2] = !controller->m_videoLayers[2]; + context->gba->video.renderer->disableBG[3] = !controller->m_videoLayers[3]; + context->gba->video.renderer->disableOBJ = !controller->m_videoLayers[4]; controller->m_fpsTarget = context->fpsTarget; if (GBALoadState(context, context->stateDir, 0)) {@@ -100,24 +114,25 @@ if (vf) {
vf->truncate(vf, 0); } } - controller->gameStarted(context); + QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(GBAThread*, context)); }; m_threadContext.cleanCallback = [](GBAThread* context) { GameController* controller = static_cast<GameController*>(context->userData); - controller->gameStopped(context); + QMetaObject::invokeMethod(controller, "gameStopped", Q_ARG(GBAThread*, context)); }; m_threadContext.frameCallback = [](GBAThread* context) { GameController* controller = static_cast<GameController*>(context->userData); - if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) { - GBAThreadPauseFromThread(context); - controller->gamePaused(&controller->m_threadContext); - } if (GBASyncDrawingFrame(&controller->m_threadContext.sync)) { - controller->frameAvailable(controller->m_drawContext); + memcpy(controller->m_frontBuffer, controller->m_drawContext, 256 * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL); + QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, controller->m_frontBuffer)); } else { - controller->frameAvailable(nullptr); + QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, nullptr)); + } + if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) { + GBAThreadPauseFromThread(context); + QMetaObject::invokeMethod(controller, "gamePaused", Q_ARG(GBAThread*, context)); } };@@ -146,7 +161,7 @@ va_list argc;
va_copy(argc, args); int immediate = va_arg(argc, int); va_end(argc); - controller->unimplementedBiosCall(immediate); + QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate)); } else if (level == GBA_LOG_STATUS) { // Slot 0 is reserved for suspend points if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) {@@ -174,9 +189,9 @@ return;
} QString message(QString().vsprintf(format, args)); if (level == GBA_LOG_STATUS) { - controller->statusPosted(message); + QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message)); } - controller->postLog(level, message); + QMetaObject::invokeMethod(controller, "postLog", Q_ARG(int, level), Q_ARG(const QString&, message)); }; connect(&m_rewindTimer, &QTimer::timeout, [this]() {@@ -205,6 +220,7 @@ closeGame();
GBACheatDeviceDestroy(&m_cheatDevice); delete m_renderer; delete[] m_drawContext; + delete[] m_frontBuffer; delete m_backupLoadState; }@@ -328,6 +344,7 @@ m_threadContext.patch = VFileDevice::open(m_patch, O_RDONLY);
} m_inputController->recalibrateAxes(); + memset(m_drawContext, 0xF8, 1024 * VIDEO_HORIZONTAL_PIXELS); if (!GBAThreadStart(&m_threadContext)) { m_gameOpen = false;@@ -453,8 +470,7 @@ if (!m_gameOpen || m_rewindTimer.isActive() || paused == GBAThreadIsPaused(&m_threadContext)) {
return; } if (paused) { - GBAThreadPause(&m_threadContext); - emit gamePaused(&m_threadContext); + m_pauseAfterFrame.testAndSetRelaxed(false, true); } else { GBAThreadUnpause(&m_threadContext); emit gameUnpaused(&m_threadContext);@@ -519,9 +535,9 @@ if (!m_gameOpen || m_rewindTimer.isActive()) {
return; } m_wasPaused = isPaused(); - bool signalsBlocked = blockSignals(true); - setPaused(true); - blockSignals(signalsBlocked); + if (!GBAThreadIsPaused(&m_threadContext)) { + GBAThreadPause(&m_threadContext); + } m_rewindTimer.start(); }@@ -574,10 +590,64 @@ updateKeys();
} void GameController::setAudioBufferSamples(int samples) { - threadInterrupt(); - redoSamples(samples); - threadContinue(); - QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples)); + if (m_audioProcessor) { + threadInterrupt(); + redoSamples(samples); + threadContinue(); + QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples)); + } +} + +void GameController::setAudioSampleRate(unsigned rate) { + if (m_audioProcessor) { + threadInterrupt(); + redoSamples(m_audioProcessor->getBufferSamples()); + threadContinue(); + QMetaObject::invokeMethod(m_audioProcessor, "requestSampleRate", Q_ARG(unsigned, rate)); + } +} + +void GameController::setAudioChannelEnabled(int channel, bool enable) { + if (channel > 5 || channel < 0) { + return; + } + m_audioChannels[channel] = enable; + if (m_gameOpen) { + switch (channel) { + case 0: + case 1: + case 2: + case 3: + m_threadContext.gba->audio.forceDisableCh[channel] = !enable; + break; + case 4: + m_threadContext.gba->audio.forceDisableChA = !enable; + break; + case 5: + m_threadContext.gba->audio.forceDisableChB = !enable; + break; + } + } +} + +void GameController::setVideoLayerEnabled(int layer, bool enable) { + if (layer > 4 || layer < 0) { + return; + } + m_videoLayers[layer] = enable; + if (m_gameOpen) { + switch (layer) { + case 0: + case 1: + case 2: + case 3: + m_threadContext.gba->video.renderer->disableBG[layer] = !enable; + break; + case 4: + m_threadContext.gba->video.renderer->disableOBJ = !enable; + break; + } + } } void GameController::setFPSTarget(float fps) {@@ -587,7 +657,9 @@ m_threadContext.fpsTarget = fps;
if (m_turbo && m_turboSpeed > 0) { m_threadContext.fpsTarget *= m_turboSpeed; } - redoSamples(m_audioProcessor->getBufferSamples()); + if (m_audioProcessor) { + redoSamples(m_audioProcessor->getBufferSamples()); + } threadContinue(); }@@ -598,9 +670,14 @@ threadContinue();
} void GameController::setUseBIOS(bool use) { - threadInterrupt(); + if (use == m_useBios) { + return; + } m_useBios = use; - threadContinue(); + if (m_gameOpen) { + closeGame(); + openGame(); + } } void GameController::loadState(int slot) {@@ -744,7 +821,9 @@ m_threadContext.fpsTarget = m_fpsTarget * m_turboSpeed;
m_threadContext.sync.audioWait = true; m_threadContext.sync.videoFrameWait = false; } - redoSamples(m_audioProcessor->getBufferSamples()); + if (m_audioProcessor) { + redoSamples(m_audioProcessor->getBufferSamples()); + } threadContinue(); }@@ -773,11 +852,21 @@ }
#endif void GameController::reloadAudioDriver() { - QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection); - int samples = m_audioProcessor->getBufferSamples(); - delete m_audioProcessor; + int samples = 0; + unsigned sampleRate = 0; + if (m_audioProcessor) { + QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection); + samples = m_audioProcessor->getBufferSamples(); + sampleRate = m_audioProcessor->sampleRate(); + delete m_audioProcessor; + } m_audioProcessor = AudioProcessor::create(); - m_audioProcessor->setBufferSamples(samples); + if (samples) { + m_audioProcessor->setBufferSamples(samples); + } + if (sampleRate) { + m_audioProcessor->requestSampleRate(sampleRate); + } m_audioProcessor->moveToThread(m_audioThread); connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start())); connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));@@ -794,7 +883,7 @@ m_luxValue = value;
value = std::max<int>(value - 0x16, 0); m_luxLevel = 10; for (int i = 0; i < 10; ++i) { - if (value < LUX_LEVELS[i]) { + if (value < GBA_LUX_LEVELS[i]) { m_luxLevel = i; break; }@@ -806,7 +895,7 @@ void GameController::setLuminanceLevel(int level) {
int value = 0x16; level = std::max(0, std::min(10, level)); if (level > 0) { - value += LUX_LEVELS[level - 1]; + value += GBA_LUX_LEVELS[level - 1]; } setLuminanceValue(value); }
@@ -72,6 +72,8 @@ void clearOverride() { m_threadContext.hasOverride = false; }
void setOptions(const GBAOptions*); + int stateSlot() const { return m_stateSlot; } + #ifdef USE_GDB_STUB ARMDebugger* debugger(); void setDebugger(ARMDebugger*);@@ -117,6 +119,9 @@ void keyPressed(int key);
void keyReleased(int key); void clearKeys(); void setAudioBufferSamples(int samples); + void setAudioSampleRate(unsigned rate); + void setAudioChannelEnabled(int channel, bool enable = true); + void setVideoLayerEnabled(int layer, bool enable = true); void setFPSTarget(float fps); void loadState(int slot = 0); void saveState(int slot = 0);@@ -163,6 +168,7 @@ void redoSamples(int samples);
void enableTurbo(); uint32_t* m_drawContext; + uint32_t* m_frontBuffer; GBAThread m_threadContext; GBAVideoSoftwareRenderer* m_renderer; GBACheatDevice m_cheatDevice;@@ -193,6 +199,9 @@ float m_turboSpeed;
QTimer m_rewindTimer; bool m_wasPaused; + bool m_audioChannels[6]; + bool m_videoLayers[5]; + int m_stateSlot; GBASerializedState* m_backupLoadState; QByteArray m_backupSaveState;@@ -206,8 +215,6 @@ uint8_t value;
} m_lux; uint8_t m_luxValue; int m_luxLevel; - - static const int LUX_LEVELS[10]; GBARTCGenericSource m_rtc; };
@@ -208,9 +208,11 @@ }
GBARumble* InputController::rumble() { #ifdef BUILD_SDL +#if SDL_VERSION_ATLEAST(2, 0, 0) if (m_playerAttached) { return &m_sdlPlayer.rumble.d; } +#endif #endif return nullptr; }@@ -497,19 +499,29 @@ bool InputController::hasPendingEvent(GBAKey key) const {
return m_pendingEvents.contains(key); } -#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0) void InputController::suspendScreensaver() { +#ifdef BUILD_SDL +#if SDL_VERSION_ATLEAST(2, 0, 0) GBASDLSuspendScreensaver(&s_sdlEvents); +#endif +#endif } void InputController::resumeScreensaver() { +#ifdef BUILD_SDL +#if SDL_VERSION_ATLEAST(2, 0, 0) GBASDLResumeScreensaver(&s_sdlEvents); +#endif +#endif } void InputController::setScreensaverSuspendable(bool suspendable) { +#ifdef BUILD_SDL +#if SDL_VERSION_ATLEAST(2, 0, 0) GBASDLSetScreensaverSuspendable(&s_sdlEvents, suspendable); +#endif +#endif } -#endif void InputController::stealFocus(QWidget* focus) { m_focusParent = focus;
@@ -87,12 +87,10 @@
public slots: void testGamepad(int type); -#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0) // TODO: Move these to somewhere that makes sense void suspendScreensaver(); void resumeScreensaver(); void setScreensaverSuspendable(bool); -#endif private: void postPendingEvent(GBAKey);
@@ -23,9 +23,10 @@
LoadSaveState::LoadSaveState(GameController* controller, QWidget* parent) : QWidget(parent) , m_controller(controller) - , m_currentFocus(0) + , m_currentFocus(controller->stateSlot() - 1) , m_mode(LoadSave::LOAD) { + setAttribute(Qt::WA_TranslucentBackground); m_ui.setupUi(this); m_slots[0] = m_ui.state1;@@ -43,6 +44,13 @@ for (i = 0; i < NUM_SLOTS; ++i) {
loadState(i + 1); m_slots[i]->installEventFilter(this); connect(m_slots[i], &QAbstractButton::clicked, this, [this, i]() { triggerState(i + 1); }); + } + + if (m_currentFocus >= 9) { + m_currentFocus = 0; + } + if (m_currentFocus < 0) { + m_currentFocus = 0; } QAction* escape = new QAction(this);@@ -201,6 +209,5 @@
void LoadSaveState::paintEvent(QPaintEvent*) { QPainter painter(this); QRect full(QPoint(), size()); - painter.drawPixmap(full, m_currentImage); painter.fillRect(full, QColor(0, 0, 0, 128)); }
@@ -15,7 +15,7 @@
using namespace QGBA; OverrideView::OverrideView(GameController* controller, ConfigController* config, QWidget* parent) - : QWidget(parent) + : QDialog(parent) , m_controller(controller) , m_config(config) {
@@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef QGBA_OVERRIDE_VIEW #define QGBA_OVERRIDE_VIEW -#include <QWidget> +#include <QDialog> #include "ui_OverrideView.h"@@ -21,7 +21,7 @@
class ConfigController; class GameController; -class OverrideView : public QWidget { +class OverrideView : public QDialog { Q_OBJECT public:
@@ -13,7 +13,7 @@
SavestateButton::SavestateButton(QWidget* parent) : QAbstractButton(parent) { - // Nothing to do + setAttribute(Qt::WA_TranslucentBackground); } void SavestateButton::paintEvent(QPaintEvent*) {
@@ -12,7 +12,7 @@
using namespace QGBA; SensorView::SensorView(GameController* controller, InputController* input, QWidget* parent) - : QWidget(parent) + : QDialog(parent) , m_controller(controller) , m_input(input) , m_rotation(input->rotationSource())
@@ -7,7 +7,7 @@ #ifndef QGBA_SENSOR_VIEW
#define QGBA_SENSOR_VIEW #include <QTimer> -#include <QWidget> +#include <QDialog> #include <functional>@@ -22,7 +22,7 @@ class GameController;
class GamepadAxisEvent; class InputController; -class SensorView : public QWidget { +class SensorView : public QDialog { Q_OBJECT public:
@@ -13,8 +13,8 @@
using namespace QGBA; SettingsView::SettingsView(ConfigController* controller, QWidget* parent) - : QWidget(parent) - , m_controller(controller) + : QDialog(parent) + , m_controller(controller) { m_ui.setupUi(this);@@ -22,6 +22,7 @@ loadSetting("bios", m_ui.bios);
loadSetting("useBios", m_ui.useBios); loadSetting("skipBios", m_ui.skipBios); loadSetting("audioBuffers", m_ui.audioBufferSize); + loadSetting("sampleRate", m_ui.sampleRate); loadSetting("videoSync", m_ui.videoSync); loadSetting("audioSync", m_ui.audioSync); loadSetting("frameskip", m_ui.frameskip);@@ -102,6 +103,7 @@ saveSetting("bios", m_ui.bios);
saveSetting("useBios", m_ui.useBios); saveSetting("skipBios", m_ui.skipBios); saveSetting("audioBuffers", m_ui.audioBufferSize); + saveSetting("sampleRate", m_ui.sampleRate); saveSetting("videoSync", m_ui.videoSync); saveSetting("audioSync", m_ui.audioSync); saveSetting("frameskip", m_ui.frameskip);
@@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef QGBA_SETTINGS_VIEW #define QGBA_SETTINGS_VIEW -#include <QWidget> +#include <QDialog> #include "ui_SettingsView.h"@@ -14,7 +14,7 @@ namespace QGBA {
class ConfigController; -class SettingsView : public QWidget { +class SettingsView : public QDialog { Q_OBJECT public:
@@ -62,10 +62,10 @@ <property name="editable">
<bool>true</bool> </property> <property name="currentText"> - <string>2048</string> + <string>1536</string> </property> <property name="currentIndex"> - <number>2</number> + <number>3</number> </property> <item> <property name="text">@@ -74,7 +74,17 @@ </property>
</item> <item> <property name="text"> + <string>768</string> + </property> + </item> + <item> + <property name="text"> <string>1024</string> + </property> + </item> + <item> + <property name="text"> + <string>1536</string> </property> </item> <item>@@ -84,6 +94,11 @@ </property>
</item> <item> <property name="text"> + <string>3072</string> + </property> + </item> + <item> + <property name="text"> <string>4096</string> </property> </item>@@ -99,13 +114,20 @@ </item>
</layout> </item> <item row="2" column="0"> + <widget class="QLabel" name="label_19"> + <property name="text"> + <string>Sample rate:</string> + </property> + </widget> + </item> + <item row="3" column="0"> <widget class="QLabel" name="label_17"> <property name="text"> <string>Volume:</string> </property> </widget> </item> - <item row="2" column="1"> + <item row="3" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_6"> <item> <widget class="QSlider" name="volume">@@ -132,28 +154,38 @@ </widget>
</item> </layout> </item> - <item row="3" column="0" colspan="2"> + <item row="4" column="0" colspan="2"> <widget class="Line" name="line"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> - <item row="4" column="0"> + <item row="5" column="0"> <widget class="QLabel" name="label_10"> <property name="text"> <string>Display driver:</string> </property> </widget> </item> - <item row="5" column="0"> + <item row="5" column="1"> + <widget class="QComboBox" name="displayDriver"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="6" column="0"> <widget class="QLabel" name="label_9"> <property name="text"> <string>Frameskip:</string> </property> </widget> </item> - <item row="5" column="1"> + <item row="6" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_16"> <item> <widget class="QLabel" name="label_12">@@ -174,14 +206,14 @@ </widget>
</item> </layout> </item> - <item row="6" column="0"> + <item row="7" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> <string>FPS target:</string> </property> </widget> </item> - <item row="6" column="1"> + <item row="7" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QDoubleSpinBox" name="fpsTarget">@@ -205,21 +237,21 @@ </widget>
</item> </layout> </item> - <item row="7" column="0" colspan="2"> + <item row="8" column="0" colspan="2"> <widget class="Line" name="line_4"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> - <item row="8" column="0"> + <item row="9" column="0"> <widget class="QLabel" name="label_2"> <property name="text"> <string>Sync:</string> </property> </widget> </item> - <item row="8" column="1"> + <item row="9" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_10"> <item> <widget class="QCheckBox" name="videoSync">@@ -237,29 +269,63 @@ </widget>
</item> </layout> </item> - <item row="9" column="1"> + <item row="10" column="1"> <widget class="QCheckBox" name="lockAspectRatio"> <property name="text"> <string>Lock aspect ratio</string> </property> </widget> </item> - <item row="10" column="1"> + <item row="11" column="1"> <widget class="QCheckBox" name="resampleVideo"> <property name="text"> <string>Resample video</string> </property> </widget> </item> - <item row="4" column="1"> - <widget class="QComboBox" name="displayDriver"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> + <item row="2" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_14"> + <item> + <widget class="QComboBox" name="sampleRate"> + <property name="editable"> + <bool>true</bool> + </property> + <property name="currentText"> + <string>44100</string> + </property> + <property name="currentIndex"> + <number>2</number> + </property> + <item> + <property name="text"> + <string>22050</string> + </property> + </item> + <item> + <property name="text"> + <string>32000</string> + </property> + </item> + <item> + <property name="text"> + <string>44100</string> + </property> + </item> + <item> + <property name="text"> + <string>48000</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QLabel" name="label_20"> + <property name="text"> + <string>Hz</string> + </property> + </widget> + </item> + </layout> </item> </layout> </item>
@@ -103,6 +103,8 @@ void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
const QKeySequence& shortcut, const QString& visibleName, const QString& name); void addMenu(QMenu* menu, QMenu* parent = nullptr); + QAction* getAction(const QString& name); + QKeySequence shortcutAt(const QModelIndex& index) const; bool isMenuAt(const QModelIndex& index) const;
@@ -13,6 +13,7 @@ #include <QMimeData>
#include <QPainter> #include <QStackedLayout> +#include "AboutScreen.h" #include "CheatsView.h" #include "ConfigController.h" #include "Display.h"@@ -40,8 +41,8 @@ }
using namespace QGBA; -#ifdef __WIN32 -// This is a macro everywhere except MinGW, it seems +#if defined(__WIN32) || defined(__OpenBSD__) +// This is a macro everywhere except MinGW and OpenBSD, it seems using std::isnan; #endif@@ -99,6 +100,14 @@ connect(m_controller, SIGNAL(gameStopped(GBAThread*)), this, SLOT(gameStopped()));
connect(m_controller, SIGNAL(gameStopped(GBAThread*)), &m_inputController, SLOT(resumeScreensaver())); connect(m_controller, SIGNAL(stateLoaded(GBAThread*)), m_display, SLOT(forceDraw())); connect(m_controller, SIGNAL(rewound(GBAThread*)), m_display, SLOT(forceDraw())); + connect(m_controller, &GameController::gamePaused, [this]() { + QImage currentImage(reinterpret_cast<const uchar*>(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS, + VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGBX8888); + QPixmap pixmap; + pixmap.convertFromImage(currentImage); + m_screenWidget->setPixmap(pixmap); + m_screenWidget->setLockAspectRatio(3, 2); + }); connect(m_controller, SIGNAL(gamePaused(GBAThread*)), m_display, SLOT(pauseDrawing())); #ifndef Q_OS_MAC connect(m_controller, SIGNAL(gamePaused(GBAThread*)), menuBar(), SLOT(show()));@@ -126,15 +135,16 @@ connect(this, SIGNAL(shutdown()), m_display, SLOT(stopDrawing()));
connect(this, SIGNAL(shutdown()), m_controller, SLOT(closeGame())); connect(this, SIGNAL(shutdown()), m_logView, SLOT(hide())); connect(this, SIGNAL(audioBufferSamplesChanged(int)), m_controller, SLOT(setAudioBufferSamples(int))); + connect(this, SIGNAL(sampleRateChanged(unsigned)), m_controller, SLOT(setAudioSampleRate(unsigned))); connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float))); connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS())); connect(m_display, &Display::hideCursor, [this]() { if (static_cast<QStackedLayout*>(m_screenWidget->layout())->currentWidget() == m_display) { - setCursor(Qt::BlankCursor); + m_screenWidget->setCursor(Qt::BlankCursor); } }); connect(m_display, &Display::showCursor, [this]() { - unsetCursor(); + m_screenWidget->unsetCursor(); }); connect(&m_inputController, SIGNAL(profileLoaded(const QString&)), m_shortcutController, SLOT(loadProfile(const QString&)));@@ -171,6 +181,7 @@ }
void Window::resizeFrame(int width, int height) { QSize newSize(width, height); + m_screenWidget->setSizeHint(newSize); newSize -= m_screenWidget->size(); newSize += size(); resize(newSize);@@ -193,12 +204,17 @@ if (opts->bios) {
m_controller->loadBIOS(opts->bios); } + // TODO: Move these to ConfigController if (opts->fpsTarget) { emit fpsTargetChanged(opts->fpsTarget); } if (opts->audioBuffers) { emit audioBufferSamplesChanged(opts->audioBuffers); + } + + if (opts->sampleRate) { + emit sampleRateChanged(opts->sampleRate); } if (opts->width && opts->height) {@@ -344,6 +360,11 @@ MemoryView* memoryWindow = new MemoryView(m_controller);
openView(memoryWindow); } +void Window::openAboutScreen() { + AboutScreen* about = new AboutScreen(); + openView(about); +} + #ifdef BUILD_SDL void Window::openGamepadWindow() { const char* profile = m_inputController.profileForType(SDL_BINDING_BUTTON);@@ -386,9 +407,7 @@ if (!m_gdbController) {
m_gdbController = new GDBController(m_controller, this); } GDBWindow* window = new GDBWindow(m_gdbController); - connect(this, SIGNAL(shutdown()), window, SLOT(close())); - window->setAttribute(Qt::WA_DeleteOnClose); - window->show(); + openView(window); } #endif@@ -420,19 +439,43 @@ m_controller->keyReleased(key);
event->accept(); } -void Window::resizeEvent(QResizeEvent*) { +void Window::resizeEvent(QResizeEvent* event) { if (!isFullScreen()) { m_config->setOption("height", m_screenWidget->height()); m_config->setOption("width", m_screenWidget->width()); } + + int factor = 0; + if (event->size().width() % VIDEO_HORIZONTAL_PIXELS == 0 && event->size().height() % VIDEO_VERTICAL_PIXELS == 0 && + event->size().width() / VIDEO_HORIZONTAL_PIXELS == event->size().height() / VIDEO_VERTICAL_PIXELS) { + factor = event->size().width() / VIDEO_HORIZONTAL_PIXELS; + } + for (QMap<int, QAction*>::iterator iter = m_frameSizes.begin(); iter != m_frameSizes.end(); ++iter) { + bool enableSignals = iter.value()->blockSignals(true); + if (iter.key() == factor) { + iter.value()->setChecked(true); + } else { + iter.value()->setChecked(false); + } + iter.value()->blockSignals(enableSignals); + } + m_config->setOption("fullscreen", isFullScreen()); } +void Window::showEvent(QShowEvent* event) { + resizeFrame(m_screenWidget->sizeHint().width(), m_screenWidget->sizeHint().height()); +} + void Window::closeEvent(QCloseEvent* event) { emit shutdown(); m_config->setQtOption("windowPos", pos()); saveConfig(); QMainWindow::closeEvent(event); +} + +void Window::focusInEvent(QFocusEvent*) { + m_display->forceDraw(); } void Window::focusOutEvent(QFocusEvent*) {@@ -475,7 +518,6 @@ if (isFullScreen()) {
return; } showFullScreen(); - setCursor(Qt::BlankCursor); #ifndef Q_OS_MAC if (m_controller->isLoaded() && !m_controller->isPaused()) { menuBar()->hide();@@ -487,7 +529,7 @@ void Window::exitFullScreen() {
if (!isFullScreen()) { return; } - unsetCursor(); + m_screenWidget->unsetCursor(); menuBar()->show(); showNormal(); }@@ -515,6 +557,7 @@ foreach (QAction* action, m_gameActions) {
action->setDisabled(false); } if (context->fname) { + setWindowFilePath(context->fname); appendMRU(context->fname); } updateTitle();@@ -534,10 +577,12 @@ void Window::gameStopped() {
foreach (QAction* action, m_gameActions) { action->setDisabled(true); } + setWindowFilePath(QString()); updateTitle(); detachWidget(m_display); m_screenWidget->setLockAspectRatio(m_logo.width(), m_logo.height()); m_screenWidget->setPixmap(m_logo); + m_screenWidget->unsetCursor(); m_fpsTimer.stop(); }@@ -640,7 +685,7 @@ m_stateWindow = new LoadSaveState(m_controller);
connect(this, SIGNAL(shutdown()), m_stateWindow, SLOT(close())); connect(m_controller, SIGNAL(gameStopped(GBAThread*)), m_stateWindow, SLOT(close())); connect(m_stateWindow, &LoadSaveState::closed, [this]() { - m_screenWidget->layout()->removeWidget(m_stateWindow); + detachWidget(m_stateWindow); m_stateWindow = nullptr; QMetaObject::invokeMethod(this, "setFocus", Qt::QueuedConnection); });@@ -753,6 +798,14 @@ });
addControlledAction(fileMenu, multiWindow, "multiWindow"); #ifndef Q_OS_MAC + fileMenu->addSeparator(); +#endif + + QAction* about = new QAction(tr("About"), fileMenu); + connect(about, SIGNAL(triggered()), this, SLOT(openAboutScreen())); + fileMenu->addAction(about); + +#ifndef Q_OS_MAC addControlledAction(fileMenu, fileMenu->addAction(tr("E&xit"), this, SLOT(close()), QKeySequence::Quit), "quit"); #endif@@ -782,13 +835,6 @@ pause->setShortcut(tr("Ctrl+P"));
connect(pause, SIGNAL(triggered(bool)), m_controller, SLOT(setPaused(bool))); connect(m_controller, &GameController::gamePaused, [this, pause]() { pause->setChecked(true); - - QImage currentImage(reinterpret_cast<const uchar*>(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS, - VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGB32); - QPixmap pixmap; - pixmap.convertFromImage(currentImage.rgbSwapped()); - m_screenWidget->setPixmap(pixmap); - m_screenWidget->setLockAspectRatio(3, 2); }); connect(m_controller, &GameController::gameUnpaused, [pause]() { pause->setChecked(false); }); m_gameActions.append(pause);@@ -897,10 +943,15 @@ QMenu* frameMenu = avMenu->addMenu(tr("Frame size"));
m_shortcutController->addMenu(frameMenu, avMenu); for (int i = 1; i <= 6; ++i) { QAction* setSize = new QAction(tr("%1x").arg(QString::number(i)), avMenu); - connect(setSize, &QAction::triggered, [this, i]() { + setSize->setCheckable(true); + connect(setSize, &QAction::triggered, [this, i, setSize]() { showNormal(); resizeFrame(VIDEO_HORIZONTAL_PIXELS * i, VIDEO_VERTICAL_PIXELS * i); + bool enableSignals = setSize->blockSignals(true); + setSize->setChecked(true); + setSize->blockSignals(enableSignals); }); + m_frameSizes[i] = setSize; addControlledAction(frameMenu, setSize, QString("frame%1x").arg(QString::number(i))); } QKeySequence fullscreenKeys;@@ -937,20 +988,6 @@ m_config->updateOption("frameskip");
avMenu->addSeparator(); - QMenu* buffersMenu = avMenu->addMenu(tr("Audio buffer &size")); - ConfigOption* buffers = m_config->addOption("audioBuffers"); - buffers->connect([this](const QVariant& value) { - emit audioBufferSamplesChanged(value.toInt()); - }, this); - buffers->addValue(tr("512"), 512, buffersMenu); - buffers->addValue(tr("768"), 768, buffersMenu); - buffers->addValue(tr("1024"), 1024, buffersMenu); - buffers->addValue(tr("2048"), 2048, buffersMenu); - buffers->addValue(tr("4096"), 4096, buffersMenu); - m_config->updateOption("audioBuffers"); - - avMenu->addSeparator(); - QMenu* target = avMenu->addMenu(tr("FPS target")); ConfigOption* fpsTargetOption = m_config->addOption("fpsTarget"); fpsTargetOption->connect([this](const QVariant& value) {@@ -997,16 +1034,14 @@ for (int i = 0; i < 4; ++i) {
QAction* enableBg = new QAction(tr("Background %0").arg(i), videoLayers); enableBg->setCheckable(true); enableBg->setChecked(true); - connect(enableBg, &QAction::triggered, [this, i](bool enable) { m_controller->thread()->gba->video.renderer->disableBG[i] = !enable; }); - m_gameActions.append(enableBg); + connect(enableBg, &QAction::triggered, [this, i](bool enable) { m_controller->setVideoLayerEnabled(i, enable); }); addControlledAction(videoLayers, enableBg, QString("enableBG%0").arg(i)); } QAction* enableObj = new QAction(tr("OBJ (sprites)"), videoLayers); enableObj->setCheckable(true); enableObj->setChecked(true); - connect(enableObj, &QAction::triggered, [this](bool enable) { m_controller->thread()->gba->video.renderer->disableOBJ = !enable; }); - m_gameActions.append(enableObj); + connect(enableObj, &QAction::triggered, [this](bool enable) { m_controller->setVideoLayerEnabled(4, enable); }); addControlledAction(videoLayers, enableObj, "enableOBJ"); QMenu* audioChannels = avMenu->addMenu(tr("Audio channels"));@@ -1015,23 +1050,20 @@ for (int i = 0; i < 4; ++i) {
QAction* enableCh = new QAction(tr("Channel %0").arg(i + 1), audioChannels); enableCh->setCheckable(true); enableCh->setChecked(true); - connect(enableCh, &QAction::triggered, [this, i](bool enable) { m_controller->thread()->gba->audio.forceDisableCh[i] = !enable; }); - m_gameActions.append(enableCh); + connect(enableCh, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(i, enable); }); addControlledAction(audioChannels, enableCh, QString("enableCh%0").arg(i + 1)); } QAction* enableChA = new QAction(tr("Channel A"), audioChannels); enableChA->setCheckable(true); enableChA->setChecked(true); - connect(enableChA, &QAction::triggered, [this, i](bool enable) { m_controller->thread()->gba->audio.forceDisableChA = !enable; }); - m_gameActions.append(enableChA); + connect(enableChA, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(4, enable); }); addControlledAction(audioChannels, enableChA, QString("enableChA")); QAction* enableChB = new QAction(tr("Channel B"), audioChannels); enableChB->setCheckable(true); enableChB->setChecked(true); - connect(enableChB, &QAction::triggered, [this, i](bool enable) { m_controller->thread()->gba->audio.forceDisableChB = !enable; }); - m_gameActions.append(enableChB); + connect(enableChB, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(5, enable); }); addControlledAction(audioChannels, enableChB, QString("enableChB")); QMenu* toolsMenu = menubar->addMenu(tr("&Tools"));@@ -1091,6 +1123,21 @@ skipBios->connect([this](const QVariant& value) {
m_controller->setSkipBIOS(value.toBool()); }, this); + ConfigOption* useBios = m_config->addOption("useBios"); + useBios->connect([this](const QVariant& value) { + m_controller->setUseBIOS(value.toBool()); + }, this); + + ConfigOption* buffers = m_config->addOption("audioBuffers"); + buffers->connect([this](const QVariant& value) { + emit audioBufferSamplesChanged(value.toInt()); + }, this); + + ConfigOption* sampleRate = m_config->addOption("sampleRate"); + sampleRate->connect([this](const QVariant& value) { + emit sampleRateChanged(value.toUInt()); + }, this); + ConfigOption* volume = m_config->addOption("volume"); volume->connect([this](const QVariant& value) { m_controller->setVolume(value.toInt());@@ -1133,7 +1180,7 @@ }
void Window::attachWidget(QWidget* widget) { m_screenWidget->layout()->addWidget(widget); - unsetCursor(); + m_screenWidget->unsetCursor(); static_cast<QStackedLayout*>(m_screenWidget->layout())->setCurrentWidget(widget); }@@ -1215,10 +1262,10 @@ painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.fillRect(QRect(QPoint(), size()), Qt::black); QSize s = size(); QSize ds = s; - if (s.width() * m_aspectHeight > s.height() * m_aspectWidth) { - ds.setWidth(s.height() * m_aspectWidth / m_aspectHeight); - } else if (s.width() * m_aspectHeight < s.height() * m_aspectWidth) { - ds.setHeight(s.width() * m_aspectHeight / m_aspectWidth); + if (ds.width() * m_aspectHeight > ds.height() * m_aspectWidth) { + ds.setWidth(ds.height() * m_aspectWidth / m_aspectHeight); + } else if (ds.width() * m_aspectHeight < ds.height() * m_aspectWidth) { + ds.setHeight(ds.width() * m_aspectHeight / m_aspectWidth); } QPoint origin = QPoint((s.width() - ds.width()) / 2, (s.height() - ds.height()) / 2); QRect full(origin, ds);
@@ -54,6 +54,7 @@ signals:
void startDrawing(GBAThread*); void shutdown(); void audioBufferSamplesChanged(int samples); + void sampleRateChanged(unsigned samples); void fpsTargetChanged(float target); public slots:@@ -81,6 +82,8 @@ void openCheatsWindow();
void openPaletteWindow(); void openMemoryWindow(); + + void openAboutScreen(); #ifdef BUILD_SDL void openGamepadWindow();@@ -102,7 +105,9 @@ protected:
virtual void keyPressEvent(QKeyEvent* event) override; virtual void keyReleaseEvent(QKeyEvent* event) override; virtual void resizeEvent(QResizeEvent*) override; + virtual void showEvent(QShowEvent*) override; virtual void closeEvent(QCloseEvent*) override; + virtual void focusInEvent(QFocusEvent*) override; virtual void focusOutEvent(QFocusEvent*) override; virtual void dragEnterEvent(QDragEnterEvent*) override; virtual void dropEvent(QDropEvent*) override;@@ -144,6 +149,7 @@
GameController* m_controller; Display* m_display; QList<QAction*> m_gameActions; + QMap<int, QAction*> m_frameSizes; LogController m_log; LogView* m_logView; LoadSaveState* m_stateWindow;
@@ -10,7 +10,9 @@ #ifdef QT_STATIC
#include <QtPlugin> #ifdef _WIN32 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); +#ifdef BUILD_QT_MULTIMEDIA Q_IMPORT_PLUGIN(QWindowsAudioPlugin); +#endif #endif #endif
@@ -51,13 +51,16 @@ set(MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/main.c)
if(BUILD_RASPI) add_definitions(-DBUILD_RASPI) - set(EGL_MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/egl-sdl.c) - set(EGL_LIBRARY "-lEGL -lGLESv2 -lbcm_host") + list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c) + list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gles2-sdl.c) + set(OPENGLES2_LIBRARY "-lEGL -lGLESv2 -lbcm_host") + set(BUILD_GLES2 ON CACHE BOOL "Using OpenGL|ES 2" FORCE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fgnu89-inline") - add_executable(${BINARY_NAME}-rpi ${PLATFORM_SRC} ${MAIN_SRC} ${EGL_MAIN_SRC}) + add_executable(${BINARY_NAME}-rpi ${PLATFORM_SRC} ${MAIN_SRC}) set_target_properties(${BINARY_NAME}-rpi PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES}") - target_link_libraries(${BINARY_NAME}-rpi ${BINARY_NAME} ${PLATFORM_LIBRARY} ${EGL_LIBRARY}) + target_link_libraries(${BINARY_NAME}-rpi ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGLES2_LIBRARY}) install(TARGETS ${BINARY_NAME}-rpi DESTINATION bin COMPONENT ${BINARY_NAME}-rpi) + unset(OPENGLES2_INCLUDE_DIR} CACHE) # Clear NOTFOUND endif() if(BUILD_PANDORA)@@ -66,13 +69,21 @@ else()
list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sw-sdl.c) if(BUILD_GL) list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-sdl.c) - list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gl.c) + list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c) include_directories(${OPENGL_INCLUDE_DIR}) + endif() + if(BUILD_GLES2) + list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gles2-sdl.c) + list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c) + include_directories(${OPENGLES2_INCLUDE_DIR}) endif() endif() add_executable(${BINARY_NAME}-sdl WIN32 ${PLATFORM_SRC} ${MAIN_SRC}) set_target_properties(${BINARY_NAME}-sdl PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES}") -target_link_libraries(${BINARY_NAME}-sdl ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY}) +target_link_libraries(${BINARY_NAME}-sdl ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) set_target_properties(${BINARY_NAME}-sdl PROPERTIES OUTPUT_NAME ${BINARY_NAME}) install(TARGETS ${BINARY_NAME}-sdl DESTINATION bin COMPONENT ${BINARY_NAME}-sdl) +if(UNIX) + install(FILES ${CMAKE_SOURCE_DIR}/doc/mgba.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-sdl) +endif()
@@ -1,166 +0,0 @@
-/* Copyright (c) 2013-2014 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "main.h" - -static const char* _vertexShader = - "attribute vec4 position;\n" - "varying vec2 texCoord;\n" - - "void main() {\n" - " gl_Position = position;\n" - " texCoord = (position.st + vec2(1.0, -1.0)) * vec2(0.46875, -0.3125);\n" - "}"; - -static const char* _fragmentShader = - "varying vec2 texCoord;\n" - "uniform sampler2D tex;\n" - - "void main() {\n" - " vec4 color = texture2D(tex, texCoord);\n" - " color.a = 1.;\n" - " gl_FragColor = color;" - "}"; - -static const GLfloat _vertices[] = { - -1.f, -1.f, - -1.f, 1.f, - 1.f, 1.f, - 1.f, -1.f, -}; - -bool GBASDLInit(struct SDLSoftwareRenderer* renderer) { - bcm_host_init(); - renderer->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - int major, minor; - if (EGL_FALSE == eglInitialize(renderer->display, &major, &minor)) { - printf("Failed to initialize EGL"); - return false; - } - - if (EGL_FALSE == eglBindAPI(EGL_OPENGL_ES_API)) { - printf("Failed to get GLES API"); - return false; - } - - const EGLint requestConfig[] = { - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 5, - EGL_BLUE_SIZE, 5, - EGL_ALPHA_SIZE, 1, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_NONE - }; - - EGLConfig config; - EGLint numConfigs; - - if (EGL_FALSE == eglChooseConfig(renderer->display, requestConfig, &config, 1, &numConfigs)) { - printf("Failed to choose EGL config\n"); - return false; - } - - const EGLint contextAttributes[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - - int dispWidth = 240, dispHeight = 160, adjWidth; - renderer->context = eglCreateContext(renderer->display, config, EGL_NO_CONTEXT, contextAttributes); - graphics_get_display_size(0, &dispWidth, &dispHeight); - adjWidth = dispHeight / 2 * 3; - - DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0); - DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0); - - VC_RECT_T destRect = { - .x = (dispWidth - adjWidth) / 2, - .y = 0, - .width = adjWidth, - .height = dispHeight - }; - - VC_RECT_T srcRect = { - .x = 0, - .y = 0, - .width = 240 << 16, - .height = 160 << 16 - }; - - DISPMANX_ELEMENT_HANDLE_T element = vc_dispmanx_element_add(update, display, 0, &destRect, 0, &srcRect, DISPMANX_PROTECTION_NONE, 0, 0, 0); - vc_dispmanx_update_submit_sync(update); - - renderer->window.element = element; - renderer->window.width = dispWidth; - renderer->window.height = dispHeight; - - renderer->surface = eglCreateWindowSurface(renderer->display, config, &renderer->window, 0); - if (EGL_FALSE == eglMakeCurrent(renderer->display, renderer->surface, renderer->surface, renderer->context)) { - return false; - } - - renderer->d.outputBuffer = memalign(16, 256 * 256 * 4); - renderer->d.outputBufferStride = 256; - glGenTextures(1, &renderer->tex); - glBindTexture(GL_TEXTURE_2D, renderer->tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - renderer->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - renderer->vertexShader = glCreateShader(GL_VERTEX_SHADER); - renderer->program = glCreateProgram(); - - glShaderSource(renderer->fragmentShader, 1, (const GLchar**) &_fragmentShader, 0); - glShaderSource(renderer->vertexShader, 1, (const GLchar**) &_vertexShader, 0); - glAttachShader(renderer->program, renderer->vertexShader); - glAttachShader(renderer->program, renderer->fragmentShader); - char log[1024]; - glCompileShader(renderer->fragmentShader); - glCompileShader(renderer->vertexShader); - glGetShaderInfoLog(renderer->fragmentShader, 1024, 0, log); - glGetShaderInfoLog(renderer->vertexShader, 1024, 0, log); - glLinkProgram(renderer->program); - glGetProgramInfoLog(renderer->program, 1024, 0, log); - printf("%s\n", log); - renderer->texLocation = glGetUniformLocation(renderer->program, "tex"); - renderer->positionLocation = glGetAttribLocation(renderer->program, "position"); - glClearColor(1.f, 0.f, 0.f, 1.f); -} - -void GBASDLRunloop(struct GBAThread* context, struct SDLSoftwareRenderer* renderer) { - SDL_Event event; - - while (context->state < THREAD_EXITING) { - while (SDL_PollEvent(&event)) { - GBASDLHandleEvent(context, &renderer->player, &event); - } - - if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) { - glViewport(0, 0, 240, 160); - glClear(GL_COLOR_BUFFER_BIT); - glUseProgram(renderer->program); - glUniform1i(renderer->texLocation, 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, renderer->tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer); - glVertexAttribPointer(renderer->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); - glEnableVertexAttribArray(renderer->positionLocation); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glUseProgram(0); - eglSwapBuffers(renderer->display, renderer->surface); - } - GBASyncWaitFrameEnd(&context->sync); - } -} - -void GBASDLDeinit(struct SDLSoftwareRenderer* renderer) { - eglMakeCurrent(renderer->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroySurface(renderer->display, renderer->surface); - eglDestroyContext(renderer->display, renderer->context); - eglTerminate(renderer->display); - bcm_host_deinit(); -}
@@ -0,0 +1,47 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "main.h" + +void GBASDLGLCommonSwap(struct VideoBackend* context) { + struct SDLSoftwareRenderer* renderer = (struct SDLSoftwareRenderer*) context->user; +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_GL_SwapWindow(renderer->window); +#else + UNUSED(renderer); + SDL_GL_SwapBuffers(); +#endif +} + +void GBASDLGLCommonInit(struct SDLSoftwareRenderer* renderer) { +#ifndef COLOR_16_BIT + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); +#else + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); +#ifdef COLOR_5_6_5 + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); +#else + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); +#endif + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); +#endif + +#if SDL_VERSION_ATLEAST(2, 0, 0) + renderer->window = SDL_CreateWindow(projectName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->player.fullscreen)); + renderer->glCtx = SDL_GL_CreateContext(renderer->window); + SDL_GL_SetSwapInterval(1); + SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); + renderer->player.window = renderer->window; +#else + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); +#ifdef COLOR_16_BIT + SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_OPENGL); +#else + SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_OPENGL); +#endif +#endif +}
@@ -0,0 +1,13 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef SDL_GL_COMMON_H +#define SDL_GL_COMMON_H +#include "main.h" + +void GBASDLGLCommonSwap(struct VideoBackend* context); +void GBASDLGLCommonInit(struct SDLSoftwareRenderer* renderer); + +#endif
@@ -5,17 +5,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "main.h" +#include "gl-common.h" + #include "gba/supervisor/thread.h" #include "platform/opengl/gl.h" - -static void _sdlSwap(struct VideoBackend* context) { - struct SDLSoftwareRenderer* renderer = (struct SDLSoftwareRenderer*) context->user; -#if SDL_VERSION_ATLEAST(2, 0, 0) - SDL_GL_SwapWindow(renderer->window); -#else - SDL_GL_SwapBuffers(); -#endif -} static void _doViewport(int w, int h, struct VideoBackend* v) { v->resized(v, w, h);@@ -35,34 +28,7 @@ renderer->runloop = GBASDLGLRunloop;
} bool GBASDLGLInit(struct SDLSoftwareRenderer* renderer) { -#ifndef COLOR_16_BIT - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); -#else - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); -#ifdef COLOR_5_6_5 - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); -#else - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); -#endif - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); -#endif - -#if SDL_VERSION_ATLEAST(2, 0, 0) - renderer->window = SDL_CreateWindow(projectName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->player.fullscreen)); - renderer->glCtx = SDL_GL_CreateContext(renderer->window); - SDL_GL_SetSwapInterval(1); - SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); - renderer->player.window = renderer->window; -#else - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); -#ifdef COLOR_16_BIT - SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_OPENGL); -#else - SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_OPENGL); -#endif -#endif + GBASDLGLCommonInit(renderer); renderer->d.outputBuffer = malloc(256 * 256 * BYTES_PER_PIXEL); renderer->d.outputBufferStride = 256;@@ -71,7 +37,7 @@ GBAGLContextCreate(&renderer->gl);
renderer->gl.d.user = renderer; renderer->gl.d.lockAspectRatio = renderer->lockAspectRatio; renderer->gl.d.filter = renderer->filter; - renderer->gl.d.swap = _sdlSwap; + renderer->gl.d.swap = GBASDLGLCommonSwap; renderer->gl.d.init(&renderer->gl.d, 0); _doViewport(renderer->viewportWidth, renderer->viewportHeight, &renderer->gl.d);
@@ -0,0 +1,144 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "main.h" + +#include "gl-common.h" + +#include <malloc.h> + +static bool GBASDLGLES2Init(struct SDLSoftwareRenderer* renderer); +static void GBASDLGLES2Runloop(struct GBAThread* context, struct SDLSoftwareRenderer* renderer); +static void GBASDLGLES2Deinit(struct SDLSoftwareRenderer* renderer); + +void GBASDLGLES2Create(struct SDLSoftwareRenderer* renderer) { + renderer->init = GBASDLGLES2Init; + renderer->deinit = GBASDLGLES2Deinit; + renderer->runloop = GBASDLGLES2Runloop; +} + +bool GBASDLGLES2Init(struct SDLSoftwareRenderer* renderer) { +#ifdef BUILD_RASPI + bcm_host_init(); + renderer->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + int major, minor; + if (EGL_FALSE == eglInitialize(renderer->display, &major, &minor)) { + printf("Failed to initialize EGL"); + return false; + } + + if (EGL_FALSE == eglBindAPI(EGL_OPENGL_ES_API)) { + printf("Failed to get GLES API"); + return false; + } + + const EGLint requestConfig[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 5, + EGL_BLUE_SIZE, 5, + EGL_ALPHA_SIZE, 1, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + EGLConfig config; + EGLint numConfigs; + + if (EGL_FALSE == eglChooseConfig(renderer->display, requestConfig, &config, 1, &numConfigs)) { + printf("Failed to choose EGL config\n"); + return false; + } + + const EGLint contextAttributes[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + int dispWidth = 240, dispHeight = 160, adjWidth; + renderer->context = eglCreateContext(renderer->display, config, EGL_NO_CONTEXT, contextAttributes); + graphics_get_display_size(0, &dispWidth, &dispHeight); + adjWidth = dispHeight / 2 * 3; + + DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0); + DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0); + + VC_RECT_T destRect = { + .x = (dispWidth - adjWidth) / 2, + .y = 0, + .width = adjWidth, + .height = dispHeight + }; + + VC_RECT_T srcRect = { + .x = 0, + .y = 0, + .width = 240 << 16, + .height = 160 << 16 + }; + + DISPMANX_ELEMENT_HANDLE_T element = vc_dispmanx_element_add(update, display, 0, &destRect, 0, &srcRect, DISPMANX_PROTECTION_NONE, 0, 0, 0); + vc_dispmanx_update_submit_sync(update); + + renderer->window.element = element; + renderer->window.width = dispWidth; + renderer->window.height = dispHeight; + + renderer->surface = eglCreateWindowSurface(renderer->display, config, &renderer->window, 0); + if (EGL_FALSE == eglMakeCurrent(renderer->display, renderer->surface, renderer->surface, renderer->context)) { + return false; + } +#else + GBASDLGLCommonInit(renderer); +#endif + + renderer->d.outputBuffer = memalign(16, 256 * 256 * 4); + renderer->d.outputBufferStride = 256; + + GBAGLES2ContextCreate(&renderer->gl); + renderer->gl.d.user = renderer; + renderer->gl.d.lockAspectRatio = renderer->lockAspectRatio; + renderer->gl.d.filter = renderer->filter; + renderer->gl.d.swap = GBASDLGLCommonSwap; + renderer->gl.d.init(&renderer->gl.d, 0); + return true; +} + +void GBASDLGLES2Runloop(struct GBAThread* context, struct SDLSoftwareRenderer* renderer) { + SDL_Event event; + struct VideoBackend* v = &renderer->gl.d; + + while (context->state < THREAD_EXITING) { + while (SDL_PollEvent(&event)) { + GBASDLHandleEvent(context, &renderer->player, &event); + } + + if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) { + v->postFrame(v, renderer->d.outputBuffer); + } + v->drawFrame(v); + GBASyncWaitFrameEnd(&context->sync); +#ifdef BUILD_RASPI + eglSwapBuffers(renderer->display, renderer->surface); +#else + v->swap(v); +#endif + } +} + +void GBASDLGLES2Deinit(struct SDLSoftwareRenderer* renderer) { + if (renderer->gl.d.deinit) { + renderer->gl.d.deinit(&renderer->gl.d); + } +#ifdef BUILD_RASPI + eglMakeCurrent(renderer->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(renderer->display, renderer->surface); + eglDestroyContext(renderer->display, renderer->context); + eglTerminate(renderer->display); + bcm_host_deinit(); +#elif SDL_VERSION_ATLEAST(2, 0, 0) + SDL_GL_DeleteContext(renderer->glCtx); +#endif + free(renderer->d.outputBuffer); +}
@@ -86,6 +86,8 @@ renderer.filter = opts.resampleVideo;
#ifdef BUILD_GL GBASDLGLCreate(&renderer); +#elif defined(BUILD_GLES2) + GBASDLGLES2Create(&renderer); #else GBASDLSWCreate(&renderer); #endif@@ -110,6 +112,10 @@
bool didFail = false; renderer.audio.samples = context.audioBuffers; + renderer.audio.sampleRate = 44100; + if (opts.sampleRate) { + renderer.audio.sampleRate = opts.sampleRate; + } if (!GBASDLInitAudio(&renderer.audio, &context)) { didFail = true; }
@@ -20,11 +20,14 @@ #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #include <SDL/SDL.h> -#include <GLES2/gl2.h> #include <EGL/egl.h> #include <bcm_host.h> #pragma GCC diagnostic pop +#endif + +#ifdef BUILD_GLES2 +#include "platform/opengl/gles2.h" #endif #ifdef USE_PIXMAN@@ -57,6 +60,8 @@ bool filter;
#ifdef BUILD_GL struct GBAGLContext gl; +#elif BUILD_GLES2 + struct GBAGLES2Context gl; #endif #ifdef USE_PIXMAN@@ -69,13 +74,6 @@ EGLDisplay display;
EGLSurface surface; EGLContext context; EGL_DISPMANX_WINDOW_T window; - GLuint tex; - GLuint fragmentShader; - GLuint vertexShader; - GLuint program; - GLuint bufferObject; - GLuint texLocation; - GLuint positionLocation; #endif #ifdef BUILD_PANDORA@@ -89,5 +87,9 @@ void GBASDLSWCreate(struct SDLSoftwareRenderer* renderer);
#ifdef BUILD_GL void GBASDLGLCreate(struct SDLSoftwareRenderer* renderer); +#endif + +#ifdef BUILD_GLES2 +void GBASDLGLES2Create(struct SDLSoftwareRenderer* renderer); #endif #endif
@@ -22,7 +22,7 @@ GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system: %s", SDL_GetError());
return false; } - context->desiredSpec.freq = 44100; + context->desiredSpec.freq = context->sampleRate; context->desiredSpec.format = AUDIO_S16SYS; context->desiredSpec.channels = 2; context->desiredSpec.samples = context->samples;
@@ -15,6 +15,7 @@
struct GBASDLAudio { // Input size_t samples; + unsigned sampleRate; // State SDL_AudioSpec desiredSpec;
@@ -22,7 +22,7 @@ #define GUI_MOD KMOD_CTRL
#endif #define GYRO_STEPS 100 -#define RUMBLE_PWM 35 +#define RUMBLE_PWM 20 #if SDL_VERSION_ATLEAST(2, 0, 0) static void _GBASDLSetRumble(struct GBARumble* rumble, int enable);
@@ -0,0 +1,5 @@
+mgba (0.3.0-1) UNRELEASED; urgency=low + + * Initial release (closes: Bug#787470). + + -- Sérgio Benjamim <sergio_br2@yahoo.com.br> Mon, 17 Aug 2015 18:40:00 -0300
@@ -0,0 +1,78 @@
+Source: mgba +Section: otherosfs +Priority: extra +Maintainer: Sérgio Benjamim <sergio_br2@yahoo.com.br> +Build-Depends: cmake (>= 2.8.11), + debhelper (>= 9), + libavcodec-dev, + libavformat-dev, + libavresample-dev, + libavutil-dev, + libedit-dev, + libmagickwand-dev, + libpng-dev, + libqt5opengl5-dev, + libsdl2-dev, + libswscale-dev, + libzip-dev, + pkg-config, + qtbase5-dev, + qtmultimedia5-dev, + zlib1g-dev +Standards-Version: 3.9.6 +Homepage: http://mgba.io/ + +Package: libmgba +Architecture: any +Multi-Arch: same +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: Game Boy Advance emulator (common library for mGBA) + mGBA is a new emulator for running Game Boy Advance games. It aims to be faster + and more accurate than many existing Game Boy Advance emulators, as well as + adding features that other emulators lack. + . + This package provides the common library for mGBA. + . + Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is + not affiliated with or endorsed by any of the companies mentioned. + +Package: mgba-qt +Architecture: any +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: Game Boy Advance emulator (Qt frontend for mGBA) + mGBA is a new emulator for running Game Boy Advance games. It aims to be faster + and more accurate than many existing Game Boy Advance emulators, as well as + adding features that other emulators lack. + . + This package provides the Qt GUI frontend for mGBA. + . + Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is + not affiliated with or endorsed by any of the companies mentioned. + +Package: mgba-sdl +Architecture: any +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: Game Boy Advance emulator (SDL frontend for mGBA) + mGBA is a new emulator for running Game Boy Advance games. It aims to be faster + and more accurate than many existing Game Boy Advance emulators, as well as + adding features that other emulators lack. + . + This package provides the SDL UI console for mGBA. + . + Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is + not affiliated with or endorsed by any of the companies mentioned. + +Package: libretro-mgba +Architecture: any +Multi-Arch: same +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: Libretro wrapper for mGBA + This wrapper makes mGBA API compatible with libretro, thus allowing its use + with libretro frontends, such as RetroArch. + . + mGBA is a new emulator for running Game Boy Advance games. It aims to be faster + and more accurate than many existing Game Boy Advance emulators, as well as + adding features that other emulators lack. + . + Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is + not affiliated with or endorsed by any of the companies mentioned.
@@ -0,0 +1,472 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: mGBA +Upstream-Contact: Jeffrey Pfau (aka endrift) <jeffrey@endrift.com> +Source: https://github.com/mgba-emu/mgba +Comment: This package was debianized by + Sergio Benjamim (sergio-br2) <sergio_br2@yahoo.com.br> on Mon, 01 Jun 2015 18:40:00 -0300 + + +Files: * +Copyright: 2013-2015 Jeffrey Pfau +License: MPL-2.0 + +Files: src/third-party/blip_buf/* +Copyright: 2003-2009 Shay Green +License: LGPL-2.1+ + +Files: src/third-party/inih/* +Copyright: 2009 Brush Technology. All rights reserved. +License: BSD-3-clause-Brush-Technology + +Files: src/third-party/lzma/* +Copyright: 2008-2015 Igor Pavlov +License: public-domain + These files have been put in the public domain by their author + +Files: src/platform/libretro/libretro.h +Copyright: 2010-2015 The RetroArch Team +License: Expat + +Files: debian/* +Copyright: 2015 Sergio Benjamim (sergio-br2) <sergio_br2@yahoo.com.br> +License: MPL-2.0 + + +License: MPL-2.0 + Mozilla Public License Version 2.0 + ================================== + . + 1. Definitions + -------------- + . + 1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + . + 1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + . + 1.3. "Contribution" + means Covered Software of a particular Contributor. + . + 1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + . + 1.5. "Incompatible With Secondary Licenses" + means + . + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + . + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + . + 1.6. "Executable Form" + means any form of the work other than Source Code Form. + . + 1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + . + 1.8. "License" + means this document. + . + 1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + . + 1.10. "Modifications" + means any of the following: + . + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + . + (b) any new file in Source Code Form that contains any Covered + Software. + . + 1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + . + 1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + . + 1.13. "Source Code Form" + means the form of the work preferred for making modifications. + . + 1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + . + 2. License Grants and Conditions + -------------------------------- + . + 2.1. Grants + . + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + . + (a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + . + (b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + . + 2.2. Effective Date + . + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + . + 2.3. Limitations on Grant Scope + . + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + . + (a) for any code that a Contributor has removed from Covered Software; + or + . + (b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + . + (c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + . + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + . + 2.4. Subsequent Licenses + . + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + . + 2.5. Representation + . + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights + to grant the rights to its Contributions conveyed by this License. + . + 2.6. Fair Use + . + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + . + 2.7. Conditions + . + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted + in Section 2.1. + . + 3. Responsibilities + ------------------- + . + 3.1. Distribution of Source Form + . + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + . + 3.2. Distribution of Executable Form + . + If You distribute Covered Software in Executable Form then: + . + (a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + . + (b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + . + 3.3. Distribution of a Larger Work + . + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + . + 3.4. Notices + . + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, + or limitations of liability) contained within the Source Code Form of + the Covered Software, except that You may alter any license notices to + the extent required to remedy known factual inaccuracies. + . + 3.5. Application of Additional Terms + . + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + . + 4. Inability to Comply Due to Statute or Regulation + --------------------------------------------------- + . + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Software due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description must + be placed in a text file included with all distributions of the Covered + Software under this License. Except to the extent prohibited by statute + or regulation, such description must be sufficiently detailed for a + recipient of ordinary skill to be able to understand it. + . + 5. Termination + -------------- + . + 5.1. The rights granted under this License will terminate automatically + if You fail to comply with any of its terms. However, if You become + compliant, then the rights granted under this License from a particular + Contributor are reinstated (a) provisionally, unless and until such + Contributor explicitly and finally terminates Your grants, and (b) on an + ongoing basis, if such Contributor fails to notify You of the + non-compliance by some reasonable means prior to 60 days after You have + come back into compliance. Moreover, Your grants from a particular + Contributor are reinstated on an ongoing basis if such Contributor + notifies You of the non-compliance by some reasonable means, this is the + first time You have received notice of non-compliance with this License + from such Contributor, and You become compliant prior to 30 days after + Your receipt of the notice. + . + 5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + . + 5.3. In the event of termination under Sections 5.1 or 5.2 above, all + end user license agreements (excluding distributors and resellers) which + have been validly granted by You or Your distributors under this License + prior to termination shall survive termination. + . + ************************************************************************ + * * + * 6. Disclaimer of Warranty * + * ------------------------- * + * * + * Covered Software is provided under this License on an "as is" * + * basis, without warranty of any kind, either expressed, implied, or * + * statutory, including, without limitation, warranties that the * + * Covered Software is free of defects, merchantable, fit for a * + * particular purpose or non-infringing. The entire risk as to the * + * quality and performance of the Covered Software is with You. * + * Should any Covered Software prove defective in any respect, You * + * (not any Contributor) assume the cost of any necessary servicing, * + * repair, or correction. This disclaimer of warranty constitutes an * + * essential part of this License. No use of any Covered Software is * + * authorized under this License except under this disclaimer. * + * * + ************************************************************************ + . + ************************************************************************ + * * + * 7. Limitation of Liability * + * -------------------------- * + * * + * Under no circumstances and under no legal theory, whether tort * + * (including negligence), contract, or otherwise, shall any * + * Contributor, or anyone who distributes Covered Software as * + * permitted above, be liable to You for any direct, indirect, * + * special, incidental, or consequential damages of any character * + * including, without limitation, damages for lost profits, loss of * + * goodwill, work stoppage, computer failure or malfunction, or any * + * and all other commercial damages or losses, even if such party * + * shall have been informed of the possibility of such damages. This * + * limitation of liability shall not apply to liability for death or * + * personal injury resulting from such party's negligence to the * + * extent applicable law prohibits such limitation. Some * + * jurisdictions do not allow the exclusion or limitation of * + * incidental or consequential damages, so this exclusion and * + * limitation may not apply to You. * + * * + ************************************************************************ + . + 8. Litigation + ------------- + . + Any litigation relating to this License may be brought only in the + courts of a jurisdiction where the defendant maintains its principal + place of business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. + Nothing in this Section shall prevent a party's ability to bring + cross-claims or counter-claims. + . + 9. Miscellaneous + ---------------- + . + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides + that the language of a contract shall be construed against the drafter + shall not be used to construe this License against a Contributor. + . + 10. Versions of the License + --------------------------- + . + 10.1. New Versions + . + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + . + 10.2. Effect of New Versions + . + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + . + 10.3. Modified Versions + . + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + . + 10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses + . + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + . + Exhibit A - Source Code Form License Notice + ------------------------------------------- + . + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + . + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to look + for such a notice. + . + You may add additional accurate notices of copyright ownership. + . + Exhibit B - "Incompatible With Secondary Licenses" Notice + --------------------------------------------------------- + . + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + + +License: BSD-3-clause-Brush-Technology + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Brush Technology nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + +License: LGPL-2.1+ + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + . + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + . + On Debian systems, the full text of the GNU Lesser General Public + License version 2.1 can be found in the file + `/usr/share/common-licenses/LGPL-2.1'.
@@ -0,0 +1,1 @@
+obj/mgba_libretro.so usr/lib/@DEB_HOST_MULTIARCH@/libretro
@@ -0,0 +1,3 @@
+usr/bin/mgba-qt +res/mgba-qt.desktop usr/share/applications +usr/share/icons
@@ -0,0 +1,27 @@
+#!/usr/bin/make -f + +# Copyright (C) 2015 Sergio Benjamim + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) +ARCH=$(shell dpkg-architecture -qDEB_HOST_ARCH) + +ifeq ($(ARCH),armhf) + ARM=-DBUILD_GL=OFF -DBUILD_GLES2=ON +endif + +%: + dh $@ --buildsystem=cmake --builddirectory=obj --parallel + +override_dh_auto_configure: + dh_auto_configure -- -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_SKIP_RPATH=ON -DBUILD_LIBRETRO=ON $(ARM) + sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' \ + debian/libretro-mgba.install.in > debian/libretro-mgba.install + sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' \ + debian/libmgba.install.in > debian/libmgba.install + +override_dh_installchangelogs: + dh_installchangelogs -k CHANGES
@@ -0,0 +1,3 @@
+version=3 +opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/mgba-$1\.tar\.gz/ \ + https://github.com/mgba-emu/mgba/tags .*/v?(\d\S*)\.tar\.gz
@@ -56,8 +56,8 @@ sed -i~ "s/,,*/,/g" deb-temp/DEBIAN/control
sed -i~ "s/,$//g" deb-temp/DEBIAN/control sed -i~ "/^[^:]*: $/d" deb-temp/DEBIAN/control rm deb-temp/DEBIAN/control~ - chown -R 0:0 deb-temp - chmod 600 deb-temp/DEBIAN/md5sums + chmod 644 deb-temp/DEBIAN/md5sums + chown -R root:root deb-temp dpkg-deb -b deb-temp $DEB rm -rf deb-temp shift