Merge branch 'master' into port/psp2

This commit is contained in:
Jeffrey Pfau 2015-08-13 23:26:29 -07:00
commit abcc83f68c
96 changed files with 2606 additions and 502 deletions

16
CHANGES
View File

@ -27,8 +27,11 @@ Features:
- 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 @@ Bugfixes:
- 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 @@ Misc:
- 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:

View File

@ -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")
@ -177,6 +189,10 @@ if(BUILD_BBB OR BUILD_RASPI OR BUILD_PANDORA)
endif()
endif()
if(BUILD_RASPI)
set(BUILD_GL OFF CACHE BOOL "OpenGL not supported" FORCE)
endif()
if(BUILD_PANDORA)
add_definitions(-DBUILD_PANDORA)
endif()
@ -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 @@ endif()
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})
@ -402,6 +419,10 @@ if(BUILD_GL)
add_definitions(-DBUILD_GL)
endif()
if(BUILD_GLES2)
add_definitions(-DBUILD_GLES2)
endif()
if(BUILD_LIBRETRO)
file(GLOB RETRO_SRC ${CMAKE_SOURCE_DIR}/src/platform/libretro/*.c)
add_library(${BINARY_NAME}_libretro SHARED ${CORE_SRC} ${RETRO_SRC})

View File

@ -30,13 +30,12 @@ The ports are vaguely usable, but by no means should be considered stable.
### 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

View File

@ -126,8 +126,6 @@ Footnotes
- 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.

118
doc/mgba-qt.6 Normal file
View File

@ -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

259
doc/mgba.6 Normal file
View File

@ -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

Binary file not shown.

View File

@ -7,130 +7,266 @@
<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"/>
<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.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_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"/>
<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>

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 KiB

After

Width:  |  Height:  |  Size: 518 KiB

View File

@ -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 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
{ "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 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
{ "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 @@ static inline void _printPSR(union PSR psr) {
psr.t ? 'T' : '-');
}
#ifndef NDEBUG
static void _handleDeath(int sig) {
UNUSED(sig);
printf("No debugger attached!\n");
@ -133,6 +140,7 @@ static void _breakInto(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
#endif
sigaction(SIGTRAP, &osa, 0);
}
#endif
static void _continue(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
UNUSED(dv);

View File

@ -167,7 +167,7 @@ void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
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) {

View File

@ -94,6 +94,7 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) {
gba->idleDetectionFailures = 0;
gba->realisticTiming = true;
gba->hardCrash = true;
gba->performingDMA = false;
}
@ -183,6 +184,11 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
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 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
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;

View File

@ -124,6 +124,7 @@ struct GBA {
bool taintedRegisters[16];
bool realisticTiming;
bool hardCrash;
};
struct GBACartridge {

View File

@ -9,6 +9,8 @@
#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 GBASer
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 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer
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);
}
}

View File

@ -433,8 +433,9 @@ int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, in
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);

View File

@ -79,6 +79,8 @@ struct GBARotationSource {
int32_t (*readGyroZ)(struct GBARotationSource*);
};
extern const int GBA_LUX_LEVELS[10];
struct GBALuminanceSource {
void (*sample)(struct GBALuminanceSource*);

View File

@ -278,9 +278,11 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
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 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
#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 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
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 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
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 @@ int32_t GBAMemoryRunDMAs(struct GBA* gba, int32_t cycles) {
}
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);

View File

@ -116,6 +116,9 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
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;

View File

@ -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 @@ void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
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;
@ -327,6 +329,11 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
return value;
}
static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
UNUSED(renderer);
UNUSED(address);
}
static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
softwareRenderer->oamDirty = 1;

View File

@ -87,6 +87,10 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
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;
@ -99,6 +103,10 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
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) {
GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: audio eventDiff is negative");
error = true;

View File

@ -247,6 +247,7 @@ void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) {
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
opts->audioBuffers = audioBuffers;
}
_lookupUIntValue(config, "sampleRate", &opts->sampleRate);
int fakeBool;
if (_lookupIntValue(config, "useBios", &fakeBool)) {
@ -305,6 +306,7 @@ void GBAConfigLoadDefaults(struct GBAConfig* config, const struct GBAOptions* op
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);

View File

@ -29,6 +29,7 @@ struct GBAOptions {
int rewindBufferInterval;
float fpsTarget;
size_t audioBuffers;
unsigned sampleRate;
int fullscreen;
int width;

View File

@ -419,6 +419,16 @@ bool GBAThreadStart(struct GBAThread* threadContext) {
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);

View File

@ -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 @@ static struct GBAVideoRenderer dummyRenderer = {
.reset = GBAVideoDummyRendererReset,
.deinit = GBAVideoDummyRendererDeinit,
.writeVideoRegister = GBAVideoDummyRendererWriteVideoRegister,
.writeVRAM = GBAVideoDummyRendererWriteVRAM,
.writePalette = GBAVideoDummyRendererWritePalette,
.writeOAM = GBAVideoDummyRendererWriteOAM,
.drawScanline = GBAVideoDummyRendererDrawScanline,
@ -65,7 +67,7 @@ void GBAVideoReset(struct GBAVideo* video) {
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;
@ -222,6 +224,12 @@ static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer*
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) {
UNUSED(renderer);
UNUSED(address);

View File

@ -161,6 +161,7 @@ struct GBAVideoRenderer {
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);

View File

@ -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"
@ -18,6 +19,8 @@
#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;
static retro_audio_sample_batch_t audioCallback;
@ -31,6 +34,8 @@ static void GBARetroLog(struct GBAThread* thread, enum GBALogLevel level, const
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 @@ unsigned retro_api_version(void) {
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 @@ void retro_init(void) {
{ 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 @@ void retro_init(void) {
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 @@ void retro_init(void) {
if (rumbleCallback) {
gba.rumble = &rumble;
}
gba.luminanceSource = &lux;
rom = 0;
const char* sysDir = 0;
@ -201,6 +223,26 @@ void retro_run(void) {
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 @@ static void _setRumble(struct GBARumble* rumble, int enable) {
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;
}

136
src/platform/opengl/gles2.c Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -186,7 +186,7 @@ static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet)
}
}
GBASyncWaitFrameEnd(&context->sync);
if (*frames == duration) {
if (duration > 0 && *frames == duration) {
_GBAPerfShutdown(0);
}
if (_dispatchExiting) {

View File

@ -10,7 +10,7 @@
#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 ThreadJoin(Thread thread) {
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

View File

@ -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);
}

View File

@ -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

View File

@ -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>&lt;a href=&quot;http://mgba.io/&quot;&gt;Website&lt;/a&gt; • &lt;a href=&quot;https://forums.mgba.io/&quot;&gt;Forums / Support&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/tree/{gitBranch}&quot;&gt;Source&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/blob/{gitBranch}/LICENSE&quot;&gt;License&lt;/a&gt;</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: &lt;tt&gt;{gitBranch}&lt;/tt&gt;&lt;br/&gt;Revision: &lt;tt&gt;{gitCommit}&lt;/tt&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -38,10 +38,10 @@ AudioProcessor* AudioProcessor::create() {
#endif
default:
#ifdef BUILD_QT_MULTIMEDIA
return new AudioProcessorQt();
#else
#ifdef BUILD_SDL
return new AudioProcessorSDL();
#else
return new AudioProcessorQt();
#endif
}
}

View File

@ -31,6 +31,7 @@ public:
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 @@ public slots:
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; }

View File

@ -20,6 +20,7 @@ AudioProcessorQt::AudioProcessorQt(QObject* parent)
: AudioProcessor(parent)
, m_audioOutput(nullptr)
, m_device(nullptr)
, m_sampleRate(44100)
{
}
@ -45,7 +46,7 @@ void AudioProcessorQt::start() {
if (!m_audioOutput) {
QAudioFormat format;
format.setSampleRate(44100);
format.setSampleRate(m_sampleRate);
format.setChannelCount(2);
format.setSampleSize(16);
format.setCodec("audio/pcm");
@ -84,6 +85,15 @@ void AudioProcessorQt::inputParametersChanged() {
}
}
void AudioProcessorQt::requestSampleRate(unsigned rate) {
m_sampleRate = rate;
if (m_device) {
QAudioFormat format(m_audioOutput->format());
format.setSampleRate(rate);
m_device->setFormat(format);
}
}
unsigned AudioProcessorQt::sampleRate() const {
if (!m_audioOutput) {
return 0;

View File

@ -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 @@ public slots:
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;
};
}

View File

@ -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::setBufferSamples(int samples) {
void AudioProcessorSDL::inputParametersChanged() {
}
unsigned AudioProcessorSDL::sampleRate() const {
return m_audio.obtainedSpec.freq;
void AudioProcessorSDL::requestSampleRate(unsigned rate) {
m_audio.sampleRate = rate;
if (m_audio.thread) {
GBASDLDeinitAudio(&m_audio);
GBASDLInitAudio(&m_audio, input());
}
}
unsigned AudioProcessorSDL::sampleRate() const {
if (m_audio.thread) {
return m_audio.obtainedSpec.freq;
} else {
return 0;
}
}

View File

@ -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 @@ public slots:
virtual void setBufferSamples(int samples);
virtual void inputParametersChanged();
virtual unsigned sampleRate() const override;
virtual void requestSampleRate(unsigned) override;
private:
GBASDLAudio m_audio;

View File

@ -28,17 +28,31 @@ endif()
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
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 @@ if(QT_TYPE STREQUAL STATIC_LIBRARY)
endif()
set(SOURCE_FILES
AboutScreen.cpp
AudioProcessor.cpp
CheatsModel.cpp
CheatsView.cpp
@ -83,6 +98,7 @@ set(SOURCE_FILES
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
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
@ -154,6 +170,9 @@ if(UNIX AND NOT APPLE)
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})
endif()

View File

@ -99,19 +99,29 @@ void CheatsView::removeSet() {
}
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();
}

View File

@ -107,7 +107,7 @@ ConfigController::ConfigController(QObject* parent)
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 @@ ConfigController::ConfigController(QObject* parent)
m_opts.rewindBufferCapacity = 0;
m_opts.useBios = true;
m_opts.suspendScreensaver = true;
GBAConfigLoadDefaults(&m_config, &m_opts);
GBAConfigLoad(&m_config);
GBAConfigLoadDefaults(&m_config, &m_opts);
GBAConfigMap(&m_config, &m_opts);
}
@ -126,7 +126,11 @@ ConfigController::~ConfigController() {
}
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) {

View File

@ -22,7 +22,7 @@ Display::Driver Display::s_driver = Display::Driver::QT;
Display* Display::create(QWidget* parent) {
#ifdef BUILD_GL
QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer));
QGLFormat format(QGLFormat(QGL::Rgba | QGL::SingleBuffer));
format.setSwapInterval(1);
#endif
@ -72,6 +72,9 @@ void Display::filter(bool filter) {
void Display::showMessage(const QString& message) {
m_messagePainter.showMessage(message);
if (!isDrawing()) {
forceDraw();
}
}
void Display::mouseMoveEvent(QMouseEvent*) {

View File

@ -33,6 +33,8 @@ public:
bool isAspectRatioLocked() const { return m_lockAspectRatio; }
bool isFiltered() const { return m_filter; }
virtual bool isDrawing() const = 0;
signals:
void showCursor();
void hideCursor();

View File

@ -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::startDrawing(GBAThread* thread) {
void DisplayGL::stopDrawing() {
if (m_drawThread) {
m_isDrawing = false;
if (GBAThreadIsActive(m_context)) {
GBAThreadInterrupt(m_context);
}
@ -69,6 +72,7 @@ void DisplayGL::stopDrawing() {
void DisplayGL::pauseDrawing() {
if (m_drawThread) {
m_isDrawing = false;
if (GBAThreadIsActive(m_context)) {
GBAThreadInterrupt(m_context);
}
@ -81,6 +85,7 @@ void DisplayGL::pauseDrawing() {
void DisplayGL::unpauseDrawing() {
if (m_drawThread) {
m_isDrawing = true;
if (GBAThreadIsActive(m_context)) {
GBAThreadInterrupt(m_context);
}
@ -113,7 +118,8 @@ void DisplayGL::filter(bool filter) {
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 @@ PainterGL::PainterGL(QGLWidget* parent)
, 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 @@ PainterGL::PainterGL(QGLWidget* parent)
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) {
@ -153,15 +176,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) {
m_size = size;
if (m_active) {
@ -191,7 +205,11 @@ void PainterGL::start() {
}
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();
@ -200,6 +218,9 @@ void PainterGL::draw() {
} else {
GBASyncWaitFrameEnd(&m_context->sync);
}
if (!m_queue.isEmpty()) {
QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
}
}
void PainterGL::forceDraw() {
@ -212,6 +233,7 @@ void PainterGL::forceDraw() {
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::stop() {
void PainterGL::pause() {
m_active = false;
// Make sure both buffers are filled
forceDraw();
forceDraw();
}
void PainterGL::unpause() {
@ -241,3 +260,41 @@ void PainterGL::performDraw() {
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();
}

View File

@ -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 @@ protected:
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 @@ public slots:
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;
};

View File

@ -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) {

View File

@ -21,11 +21,13 @@ Q_OBJECT
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;
};

View File

@ -15,6 +15,7 @@
#include <QIcon>
extern "C" {
#include "gba/supervisor/thread.h"
#include "platform/commandline.h"
#include "util/socket.h"
}
@ -33,10 +34,13 @@ GBAApp::GBAApp(int& argc, char* argv[])
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 @@ GBAApp::GBAApp(int& argc, char* argv[])
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 @@ Window* GBAApp::newWindow() {
});
m_windows[windowId] = w;
w->setAttribute(Qt::WA_DeleteOnClose);
#ifndef Q_OS_MAC
w->show();
#endif
w->loadConfig();
w->controller()->setMultiplayerController(&m_multiplayer);
#ifdef Q_OS_MAC
w->show();
#endif
w->controller()->setMultiplayerController(&m_multiplayer);
return w;
}

View File

@ -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 @@ void GBAKeyEditor::resizeEvent(QResizeEvent* event) {
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);

View File

@ -18,7 +18,7 @@
using namespace QGBA;
GDBWindow::GDBWindow(GDBController* controller, QWidget* parent)
: QWidget(parent)
: QDialog(parent)
, m_gdbController(controller)
{
setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint);

View File

@ -6,7 +6,7 @@
#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:

View File

@ -29,11 +29,10 @@ extern "C" {
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 @@ GameController::GameController(QObject* parent)
, 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 @@ GameController::GameController(QObject* parent)
m_threadContext.startCallback = [](GBAThread* context) {
GameController* controller = static_cast<GameController*>(context->userData);
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 @@ GameController::GameController(QObject* parent)
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 (GBASyncDrawingFrame(&controller->m_threadContext.sync)) {
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 {
QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, nullptr));
}
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);
} else {
controller->frameAvailable(nullptr);
QMetaObject::invokeMethod(controller, "gamePaused", Q_ARG(GBAThread*, context));
}
};
@ -146,7 +161,7 @@ GameController::GameController(QObject* parent)
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 @@ GameController::GameController(QObject* parent)
}
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 @@ GameController::~GameController() {
GBACheatDeviceDestroy(&m_cheatDevice);
delete m_renderer;
delete[] m_drawContext;
delete[] m_frontBuffer;
delete m_backupLoadState;
}
@ -328,6 +344,7 @@ void GameController::openGame(bool biosOnly) {
}
m_inputController->recalibrateAxes();
memset(m_drawContext, 0xF8, 1024 * VIDEO_HORIZONTAL_PIXELS);
if (!GBAThreadStart(&m_threadContext)) {
m_gameOpen = false;
@ -453,8 +470,7 @@ void GameController::setPaused(bool paused) {
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 @@ void GameController::startRewinding() {
return;
}
m_wasPaused = isPaused();
bool signalsBlocked = blockSignals(true);
setPaused(true);
blockSignals(signalsBlocked);
if (!GBAThreadIsPaused(&m_threadContext)) {
GBAThreadPause(&m_threadContext);
}
m_rewindTimer.start();
}
@ -574,11 +590,65 @@ void GameController::clearKeys() {
}
void GameController::setAudioBufferSamples(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) {
threadInterrupt();
@ -587,7 +657,9 @@ void GameController::setFPSTarget(float fps) {
if (m_turbo && m_turboSpeed > 0) {
m_threadContext.fpsTarget *= m_turboSpeed;
}
if (m_audioProcessor) {
redoSamples(m_audioProcessor->getBufferSamples());
}
threadContinue();
}
@ -598,9 +670,14 @@ void GameController::setSkipBIOS(bool set) {
}
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 @@ void GameController::enableTurbo() {
m_threadContext.sync.audioWait = true;
m_threadContext.sync.videoFrameWait = false;
}
if (m_audioProcessor) {
redoSamples(m_audioProcessor->getBufferSamples());
}
threadContinue();
}
@ -773,11 +852,21 @@ void GameController::screenshot() {
#endif
void GameController::reloadAudioDriver() {
int samples = 0;
unsigned sampleRate = 0;
if (m_audioProcessor) {
QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
int samples = m_audioProcessor->getBufferSamples();
samples = m_audioProcessor->getBufferSamples();
sampleRate = m_audioProcessor->sampleRate();
delete m_audioProcessor;
}
m_audioProcessor = AudioProcessor::create();
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 @@ void GameController::setLuminanceValue(uint8_t 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);
}

View File

@ -72,6 +72,8 @@ public:
void setOptions(const GBAOptions*);
int stateSlot() const { return m_stateSlot; }
#ifdef USE_GDB_STUB
ARMDebugger* debugger();
void setDebugger(ARMDebugger*);
@ -117,6 +119,9 @@ public slots:
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 @@ private:
void enableTurbo();
uint32_t* m_drawContext;
uint32_t* m_frontBuffer;
GBAThread m_threadContext;
GBAVideoSoftwareRenderer* m_renderer;
GBACheatDevice m_cheatDevice;
@ -193,6 +199,9 @@ private:
QTimer m_rewindTimer;
bool m_wasPaused;
bool m_audioChannels[6];
bool m_videoLayers[5];
int m_stateSlot;
GBASerializedState* m_backupLoadState;
QByteArray m_backupSaveState;
@ -207,8 +216,6 @@ private:
uint8_t m_luxValue;
int m_luxLevel;
static const int LUX_LEVELS[10];
GBARTCGenericSource m_rtc;
};

View File

@ -208,9 +208,11 @@ void InputController::setPreferredGamepad(uint32_t type, const QString& device)
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
}
void InputController::stealFocus(QWidget* focus) {
m_focusParent = focus;

View File

@ -87,12 +87,10 @@ signals:
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);

View File

@ -23,9 +23,10 @@ using namespace QGBA;
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;
@ -45,6 +46,13 @@ LoadSaveState::LoadSaveState(GameController* controller, QWidget* parent)
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);
escape->connect(escape, SIGNAL(triggered()), this, SLOT(close()));
escape->setShortcut(QKeySequence("Esc"));
@ -201,6 +209,5 @@ void LoadSaveState::showEvent(QShowEvent* event) {
void LoadSaveState::paintEvent(QPaintEvent*) {
QPainter painter(this);
QRect full(QPoint(), size());
painter.drawPixmap(full, m_currentImage);
painter.fillRect(full, QColor(0, 0, 0, 128));
}

View File

@ -15,7 +15,7 @@ extern "C" {
using namespace QGBA;
OverrideView::OverrideView(GameController* controller, ConfigController* config, QWidget* parent)
: QWidget(parent)
: QDialog(parent)
, m_controller(controller)
, m_config(config)
{

View File

@ -6,7 +6,7 @@
#ifndef QGBA_OVERRIDE_VIEW
#define QGBA_OVERRIDE_VIEW
#include <QWidget>
#include <QDialog>
#include "ui_OverrideView.h"
@ -21,7 +21,7 @@ namespace QGBA {
class ConfigController;
class GameController;
class OverrideView : public QWidget {
class OverrideView : public QDialog {
Q_OBJECT
public:

View File

@ -13,7 +13,7 @@ using namespace QGBA;
SavestateButton::SavestateButton(QWidget* parent)
: QAbstractButton(parent)
{
// Nothing to do
setAttribute(Qt::WA_TranslucentBackground);
}
void SavestateButton::paintEvent(QPaintEvent*) {

View File

@ -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())

View File

@ -7,7 +7,7 @@
#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:

View File

@ -13,7 +13,7 @@
using namespace QGBA;
SettingsView::SettingsView(ConfigController* controller, QWidget* parent)
: QWidget(parent)
: QDialog(parent)
, m_controller(controller)
{
m_ui.setupUi(this);
@ -22,6 +22,7 @@ SettingsView::SettingsView(ConfigController* controller, QWidget* parent)
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 @@ void SettingsView::updateConfig() {
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);

View File

@ -6,7 +6,7 @@
#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:

View File

@ -62,26 +62,41 @@
<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">
<string>512</string>
</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>
<property name="text">
<string>2048</string>
</property>
</item>
<item>
<property name="text">
<string>3072</string>
</property>
</item>
<item>
<property name="text">
<string>4096</string>
@ -99,13 +114,20 @@
</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 @@
</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 @@
</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 @@
</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,32 +269,66 @@
</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>
<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>
<item>
<widget class="Line" name="line_5">
<property name="orientation">

View File

@ -103,6 +103,8 @@ public:
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;

View File

@ -13,6 +13,7 @@
#include <QPainter>
#include <QStackedLayout>
#include "AboutScreen.h"
#include "CheatsView.h"
#include "ConfigController.h"
#include "Display.h"
@ -40,8 +41,8 @@ extern "C" {
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 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
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 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
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::argumentsPassed(GBAArguments* args) {
void Window::resizeFrame(int width, int height) {
QSize newSize(width, height);
m_screenWidget->setSizeHint(newSize);
newSize -= m_screenWidget->size();
newSize += size();
resize(newSize);
@ -193,6 +204,7 @@ void Window::loadConfig() {
m_controller->loadBIOS(opts->bios);
}
// TODO: Move these to ConfigController
if (opts->fpsTarget) {
emit fpsTargetChanged(opts->fpsTarget);
}
@ -201,6 +213,10 @@ void Window::loadConfig() {
emit audioBufferSamplesChanged(opts->audioBuffers);
}
if (opts->sampleRate) {
emit sampleRateChanged(opts->sampleRate);
}
if (opts->width && opts->height) {
resizeFrame(opts->width, opts->height);
}
@ -344,6 +360,11 @@ void Window::openMemoryWindow() {
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 @@ void Window::gdbOpen() {
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,14 +439,34 @@ void Window::keyReleaseEvent(QKeyEvent* event) {
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());
@ -435,6 +474,10 @@ void Window::closeEvent(QCloseEvent* event) {
QMainWindow::closeEvent(event);
}
void Window::focusInEvent(QFocusEvent*) {
m_display->forceDraw();
}
void Window::focusOutEvent(QFocusEvent*) {
m_controller->setTurbo(false, false);
m_controller->stopRewinding();
@ -475,7 +518,6 @@ void Window::enterFullScreen() {
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 @@ void Window::gameStarted(GBAThread* context) {
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 @@ void Window::openStateWindow(LoadSave ls) {
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);
});
@ -752,6 +797,14 @@ void Window::setupMenu(QMenuBar* menubar) {
});
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 @@ void Window::setupMenu(QMenuBar* menubar) {
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 @@ void Window::setupMenu(QMenuBar* menubar) {
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 @@ void Window::setupMenu(QMenuBar* menubar) {
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 @@ void Window::setupMenu(QMenuBar* menubar) {
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 @@ void Window::setupMenu(QMenuBar* menubar) {
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 @@ void Window::setupMenu(QMenuBar* menubar) {
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::setupMenu(QMenuBar* menubar) {
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 @@ void WindowBackground::paintEvent(QPaintEvent*) {
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);

View File

@ -54,6 +54,7 @@ signals:
void startDrawing(GBAThread*);
void shutdown();
void audioBufferSamplesChanged(int samples);
void sampleRateChanged(unsigned samples);
void fpsTargetChanged(float target);
public slots:
@ -82,6 +83,8 @@ public slots:
void openPaletteWindow();
void openMemoryWindow();
void openAboutScreen();
#ifdef BUILD_SDL
void openGamepadWindow();
#endif
@ -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 @@ private:
GameController* m_controller;
Display* m_display;
QList<QAction*> m_gameActions;
QMap<int, QAction*> m_frameSizes;
LogController m_log;
LogView* m_logView;
LoadSaveState* m_stateWindow;

View File

@ -10,9 +10,11 @@
#include <QtPlugin>
#ifdef _WIN32
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
#ifdef BUILD_QT_MULTIMEDIA
Q_IMPORT_PLUGIN(QWindowsAudioPlugin);
#endif
#endif
#endif
int main(int argc, char* argv[]) {
QGBA::GBAApp application(argc, argv);

View File

@ -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()

View File

@ -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();
}

View File

@ -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
}

View File

@ -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

View File

@ -5,18 +5,11 @@
* 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);
v->clear(v);
@ -35,34 +28,7 @@ void GBASDLGLCreate(struct SDLSoftwareRenderer* renderer) {
}
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 @@ bool GBASDLGLInit(struct SDLSoftwareRenderer* renderer) {
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);

View File

@ -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);
}

View File

@ -86,6 +86,8 @@ int main(int argc, char** argv) {
#ifdef BUILD_GL
GBASDLGLCreate(&renderer);
#elif defined(BUILD_GLES2)
GBASDLGLES2Create(&renderer);
#else
GBASDLSWCreate(&renderer);
#endif
@ -110,6 +112,10 @@ int main(int argc, char** argv) {
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;
}

View File

@ -20,13 +20,16 @@
#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
#include <pixman.h>
#endif
@ -57,6 +60,8 @@ struct SDLSoftwareRenderer {
#ifdef BUILD_GL
struct GBAGLContext gl;
#elif BUILD_GLES2
struct GBAGLES2Context gl;
#endif
#ifdef USE_PIXMAN
@ -69,13 +74,6 @@ struct SDLSoftwareRenderer {
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
@ -90,4 +88,8 @@ void GBASDLSWCreate(struct SDLSoftwareRenderer* renderer);
#ifdef BUILD_GL
void GBASDLGLCreate(struct SDLSoftwareRenderer* renderer);
#endif
#ifdef BUILD_GLES2
void GBASDLGLES2Create(struct SDLSoftwareRenderer* renderer);
#endif
#endif

View File

@ -22,7 +22,7 @@ bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContex
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;

View File

@ -15,6 +15,7 @@
struct GBASDLAudio {
// Input
size_t samples;
unsigned sampleRate;
// State
SDL_AudioSpec desiredSpec;

View File

@ -22,7 +22,7 @@
#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);

5
tools/debian/changelog Normal file
View File

@ -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

2
tools/debian/clean Normal file
View File

@ -0,0 +1,2 @@
debian/libmgba.install
debian/libretro-mgba.install

1
tools/debian/compat Normal file
View File

@ -0,0 +1 @@
9

78
tools/debian/control Normal file
View File

@ -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.

472
tools/debian/copyright Normal file
View File

@ -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'.

2
tools/debian/docs Normal file
View File

@ -0,0 +1,2 @@
README.md
CHANGES

View File

@ -0,0 +1 @@
obj/libmgba.so* usr/lib/@DEB_HOST_MULTIARCH@/

View File

@ -0,0 +1 @@
obj/mgba_libretro.so usr/lib/@DEB_HOST_MULTIARCH@/libretro

View File

@ -0,0 +1,3 @@
usr/bin/mgba-qt
res/mgba-qt.desktop usr/share/applications
usr/share/icons

View File

@ -0,0 +1 @@
doc/mgba-qt.6

View File

@ -0,0 +1 @@
usr/bin/mgba

View File

@ -0,0 +1 @@
doc/mgba.6

27
tools/debian/rules Executable file
View File

@ -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

View File

@ -0,0 +1 @@
3.0 (quilt)

3
tools/debian/watch Normal file
View File

@ -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

View File

@ -56,8 +56,8 @@ while [ $# -gt 0 ]; do
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