Compare commits

...

56 Commits

Author SHA1 Message Date
Fabrice de Gans
31fee679b3 Use relative paths for installation
The CMake documentation [1] advises against using `CMAKE_INSTALL_FULL_*`
in `install()` commands.

[1] https://cmake.org/cmake/help/v3.30/module/GNUInstallDirs.html#result-variables
2024-09-12 14:18:18 +00:00
Rafael Kitover
fe0791762a build: update Windows dependencies submodule
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-12 14:17:16 +00:00
Fabrice de Gans
61f427dec1 Write shortcuts in the proper section (#1335)
Shortcuts were mistakenly written in `Keyboard/Keyboard` rather than in
the `Keyboard` section.
In addition, this properly fixes toggling checkable menu item options
via a shortcut.

Fixes #1334
2024-09-11 22:48:06 -07:00
Rafael Kitover
d619ee2bb1 build: fix installing GoogleTest
Add the `EXCLUDE_FROM_ALL` keyword to the `FetchContent_Declare` call
for GoogleTest, so that gtest/gmock are not installed with the project.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 14:43:34 +00:00
Rafael Kitover
7128e6dd08 build: update wxWidgets for macOS builder to 3.2.6
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 12:40:27 +00:00
Rafael Kitover
0decffea8d build: update macOS builder dists
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 11:42:26 +00:00
Rafael Kitover
9b60e17746 build: default to ENABLE_FFMPEG=OFF for MinGW32
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 11:20:02 +00:00
Rafael Kitover
26207038c9 Update WinSparkle to 0.8.1 and add ARM64
Update the included WinSparkle dll to 0.8.1 and add support for using
the ARM64 WinSparkle dll as well.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 11:07:19 +00:00
Rafael Kitover
90867bc802 doc: update developer manual release steps
Update the release steps in the developer manual to mention the
requirement for a GnuPG key, emphasize the editing the changelog step
and add instructions for putting `signtool.exe` in the `PATH` for the 32
bit Windows binary.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 10:10:13 +00:00
Rafael Kitover
7f49cd33ad build: rm FAudio from installdeps MINGW32 target
Do not try to install FAudio for the MINGW32 32 bit Windows MSYS2 target
in `installdeps`. We do not use it for the 32 bit build and there is no
package for it for MINGW32.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 09:23:27 +00:00
Rafael Kitover
d8342d3d67 doc: add ENABLE_FAUDIO to CMake opts in README.md
Describe `ENABLE_FAUDIO` in the table of CMake options in `README.md`.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 08:59:26 +00:00
Rafael Kitover
12e840a8fa Remove non-user-facing changes from CHANGELOG.md
Remove non-user-facing changes from `CHANGELOG.md` for 2.1.10.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 08:52:12 +00:00
Zach Bacon
6ca609ac7e release v2.1.10
Signed-off-by: Zach Bacon <zachbacon@vba-m.com>
2024-09-08 22:55:37 -04:00
Fabrice de Gans
2b8f9f71ff Copy string options rather than referencing them
This can lead to dandling references, though this is very unlikely. This
commit also removes a useless check.
2024-09-09 02:29:11 +00:00
Fabrice de Gans
e76cef79d2 Remove cast for UTF-16 -> UTF-8 conversion
Add convenience functions to call `config::utf16_to_utf8()` without a
cast.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 02:29:11 +00:00
Fabrice de Gans
7f06428df8 Disable dialog position save/restore on wxGTK (#1331)
This is currently broken on Wayland, resulting in dialogs showing up in
random locations.
2024-09-08 16:56:30 -07:00
Rafael Kitover
e4a9340409 Update README.md and Developer Manual
Update README.md and remove outdated information.

Update the Developer Manual and describe the current release process.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-08 05:42:34 +00:00
Rafael Kitover
5d7023a5d6 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-07 18:00:22 +00:00
Rafael Kitover
3bd7c918cc build: fix cmake -DTAG_RELEASE functionality
Update the cmake -DTAG_RELEASE script to work with recent cmake changes.

Change it to not automatically make the release commit and tag, instead
specifying in the instructions that the person making the release must
edit the CHANGELOG.md and then run the commands, given in the
instructions, to make the release commit and tag.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-07 06:30:14 +00:00
Rafael Kitover
67e4944cdb translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-01 10:00:23 +00:00
Rafael Kitover
df4ff16e2c translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-01 09:00:24 +00:00
Rafael Kitover
d4805065b3 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-08-31 14:00:23 +00:00
Rafael Kitover
e39b1f2c53 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-08-28 01:00:24 +00:00
Rafael Kitover
73b65a6553 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-08-28 00:00:23 +00:00
Rafael Kitover
9e556e6a56 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-08-21 14:00:34 +00:00
Rafael Kitover
11e73f2c61 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-08-21 13:00:24 +00:00
Rafael Kitover
e97b0448b7 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-08-20 17:00:22 +00:00
Fabrice de Gans
aa6ed14b2a [FAudio] Always convert UTF-16 names (#1328)
On Windows, wchar_t is 16 bits and represents UTF-16 code units.
However, on other platforms, wchar_t is typically implemented as 32 bits
to represent UTF-32 code units. In order to work around this problem, we
always convert the string we get from FAudio, which is represented as
UTF-16 code units, into UTF-8 and let wxString handle the conversion to
its native type internally.
2024-08-17 21:02:01 -07:00
Rafael Kitover
2ce20c4f59 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-08-16 06:00:25 +00:00
Fabrice de Gans
e4ef4aa625 Propagate key events (#1323)
Previously, `wxEVT_KEY_DOWN` and `wxEVT_KEY_UP` were transformed into
`VBAM_EVT_USER_INPUT` and marked as processed early. This broke a number
of controls that rely on the traditional widgets events.

To fix this, we now filter the `wxEVT_KEY_*` and `wxEVT_CHAR` events in
the only widget we use that cares about them, `UserInputCtrl` instead of
filtering them at the application level.
2024-07-28 15:19:19 -07:00
Rafael Kitover
cf5cb40cb9 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-26 09:00:24 +00:00
Rafael Kitover
c450d14311 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-26 08:00:24 +00:00
Rafael Kitover
41572be3f2 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-24 11:00:23 +00:00
Rafael Kitover
4f8da1c574 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-24 09:00:24 +00:00
Rafael Kitover
32091669d4 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-23 12:00:29 +00:00
Rafael Kitover
abd72a5b2e translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-23 02:00:23 +00:00
Rafael Kitover
7e6349b19f translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-22 13:00:22 +00:00
Rafael Kitover
0782be749e translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-22 12:00:24 +00:00
Rafael Kitover
a7b545ab1a translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-15 03:00:22 +00:00
Rafael Kitover
1a564f900c translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-13 10:00:25 +00:00
Rafael Kitover
3d4e03f85f translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-08 12:00:23 +00:00
Rafael Kitover
fc17209ac7 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-06 05:00:23 +00:00
Rafael Kitover
38877ef209 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-05 21:00:24 +00:00
Rafael Kitover
8691a15be8 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-05 20:00:32 +00:00
Rafael Kitover
961fd0304c translations: rebuild source .pot
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-05 20:00:15 +00:00
Rafael Kitover
e2cf6ecba6 Add option to mute sound during speedup
Add core option to mute sound during speedup, implemented in the gba
core. Link the option to the SDL port as well.

Add the new Mute Sound option as a checkbox to the xrc for the
Speedup/Turbo dialog and improve the dialog appearance.

Fix the max values for the options speedup_throttle and
speedup_frame_skip.

Add base class for numeric OptionProxy types implementing the +=, -=
operators, increment/decrement operators and operator T(). Add tests for
these operators on numeric types.

Move Speedup/Turbo dialog code from guiinit.cpp to a dialog class and
rewrite it using validators.

Remove the "GL VIEWPORT" debug prints from panel.cpp.

Fix #865

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-07-05 12:14:58 -07:00
Avindra Goolcharan
7a0826a60c Migrate vba-m.com links in .github folder 2024-06-21 18:02:38 -04:00
Rafael Kitover
d516683a77 build: fix for wx using GTK2
Use the __WXGTK3__ macro to check for GTK3 when setting the gdk backend
to X11 when Wayland EGL is not available, GTK2 does not have this
function and always uses X11 anyway.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-06-14 11:10:00 +00:00
Rafael Kitover
834c7de86c build: update macOS builder dists
Update sdl2, faudio, wxWidgets and ffmpeg to the latest versions for the
macOS builder.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-06-06 05:32:51 +00:00
Fabrice de Gans
4f1a5dd726 [Test] Add tests for widgets code
This creates a vbam-wx-widgets target for the custom widgets code and
adds tests for them.
2024-05-29 19:30:35 -07:00
Rafael Kitover
5766b9b9c7 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-05-27 10:00:25 +00:00
Rafael Kitover
5d8426d317 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-05-27 09:00:26 +00:00
Rafael Kitover
63ec3528f1 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-05-26 07:00:24 +00:00
Fabrice de Gans
f646c3848c [Test] Change assert to custom CHECK macros
This changes the way we handle asserts to use a set of custom macros.
This greatly speeds up crashing, especially on Windows, and provides
assertions in release mode too.
2024-05-21 18:47:50 +00:00
Fabrice de Gans
09433875bc [CI] Remove workaround for MSVC CI
Upstream GitHub actions now uses a single toolchain version.
Fixes #1297
2024-05-19 16:14:22 -07:00
Fabrice de Gans
05c09ff506 [Build] Add devkitpro-based libretro targets to CI 2024-05-10 13:36:05 -07:00
126 changed files with 12101 additions and 10634 deletions

View File

@@ -11,7 +11,7 @@ body:
- type: markdown
attributes:
value: |
Please try the nightly build from: https://nightly.vba-m.com and factory resetting the emulator from the Help menu.
Please try the nightly build from: https://nightly.visualboyadvance-m.org and factory resetting the emulator from the Help menu.
On Linux build master from source or use the edge snap.
And last but not least, search for existing reports via the filters bar on the issues page.
- type: markdown

View File

@@ -1,7 +1,7 @@
blank_issues_enabled: false
contact_links:
- name: VBA-M Forum
url: https://board.vba-m.com/
url: https://board.visualboyadvance-m.org/
about: For general questions and support please join our forum or our
- name: VBA-M IRC Channel
url: https://web.libera.chat/#vba-m

View File

@@ -14,7 +14,7 @@ body:
- type: markdown
attributes:
value: |
Please try a nightly build from: https://nightly.vba-m.com to see if your idea has already been implemented.
Please try a nightly build from: https://nightly.visualboyadvance-m.org to see if your idea has already been implemented.
On Linux build master from source or use the edge snap.
And last but not least, search for existing requests via the filters bar on the issues page.
- type: textarea

33
.github/workflows/devkitpro-build.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Libretro Devkitpro
on: [push, pull_request]
jobs:
build:
strategy:
matrix:
target_name: [ngc, wii, wiiu, switch]
build_type: [release, debug]
include:
- libretro_build: 'DEBUG=0'
build_type: release
- libretro_build: 'DEBUG=1'
build_type: debug
- devkit_container: 'devkitpro/devkitppc:latest'
target_name: ngc
- devkit_container: 'devkitpro/devkitppc:latest'
target_name: wii
- devkit_container: 'devkitpro/devkitppc:latest'
target_name: wiiu
- devkit_container: 'devkitpro/devkita64:latest'
target_name: switch
runs-on: ubuntu-latest
container: ${{ matrix.devkit_container }}
steps:
- name: Checkout the code
uses: actions/checkout@v4
with:
submodules: recursive
# Libretro build
- name: Build libretro core
run: make -C src/libretro ${{ matrix.libretro_build }} platform=${{ matrix.target_name }}

View File

@@ -1,4 +1,4 @@
name: macOS Latest Build
name: macOS Latest
on: [push, pull_request]
jobs:
@@ -60,4 +60,4 @@ jobs:
- if: matrix.build_options == 'default'
name: Run tests
run: >-
nix-shell --command 'cd build && ctest -j'
nix-shell --command 'cd build && ctest -j --output-on-failure'

View File

@@ -1,4 +1,4 @@
name: MSYS2 Build
name: MSYS2
on: [push, pull_request]
jobs:

View File

@@ -1,4 +1,4 @@
name: Ubuntu Latest Build
name: Ubuntu Latest
on: [push, pull_request]
jobs:
@@ -45,6 +45,9 @@ jobs:
run: >-
bash installdeps; if [ "${{ matrix.build_compiler }}" = clang ]; then sudo apt -y install clang; fi
- name: Install xvfb
run: sudo apt -y install xvfb
# CMake build
- if: matrix.build_options != 'libretro'
name: Configure CMake
@@ -65,4 +68,4 @@ jobs:
# Run tests
- if: matrix.build_options == 'default'
name: Run tests
run: cd build && ctest -j
run: cd build && xvfb-run ctest -j --output-on-failure

View File

@@ -1,4 +1,4 @@
name: Visual Studio Build
name: Visual Studio
on: [push, pull_request]
jobs:
@@ -37,54 +37,6 @@ jobs:
msvc_arch: amd64_arm64
runs-on: windows-latest
steps:
# TODO: Remove these 2 steps after May 13, 2024.
# See https://github.com/actions/runner-images/issues/9701 for details.
- name: Delete components
run: |
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
$InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
$componentsToRemove= @(
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ARM"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ARM.Spectre"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ARM64"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ARM64.Spectre"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64.Spectre"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL.Spectre"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL.ARM"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL.ARM.Spectre"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL.ARM64"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL.ARM64.Spectre"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.MFC"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.MFC.Spectre"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.MFC.ARM"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.MFC.ARM.Spectre"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.MFC.ARM64"
"Microsoft.VisualStudio.Component.VC.14.38.17.8.MFC.ARM64.Spectre"
)
[string]$workloadArgs = $componentsToRemove | ForEach-Object {" --remove " + $_}
$Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache')
# should be run twice
$process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
$process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
- name: Install components
run: |
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
$InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
$componentsToAdd= @(
"Microsoft.VisualStudio.Component.VC.14.39.17.9.ARM64"
"Microsoft.VisualStudio.Component.VC.14.39.17.9.ARM64.Spectre"
"Microsoft.VisualStudio.Component.VC.14.39.17.9.x86.x64"
"Microsoft.VisualStudio.Component.VC.14.39.17.9.x86.x64.Spectre"
)
[string]$workloadArgs = $componentsToAdd | ForEach-Object {" --add " + $_}
$Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache')
# should be run twice
$process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
$process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
- name: Checkout the code
uses: actions/checkout@v4
with:
@@ -105,4 +57,4 @@ jobs:
# Run tests
- if: matrix.build_options == 'default' && matrix.msvc_arch != 'amd64_arm64'
name: Run tests
run: cd build && ctest -j
run: cd build && ctest -j --output-on-failure

View File

@@ -4,6 +4,58 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [2.1.10] - 2024-09-08
==========================
* 7f06428d - Disable dialog position save/restore on wxGTK (#1331) [Steelskin]
* e4ef4aa6 - Propagate key events (#1323) [Steelskin]
* e2cf6ecb - Add option to mute sound during speedup [rkitover]
* d516683a - build: fix for wx using GTK2 [rkitover]
* 834c7de8 - build: update macOS builder dists [rkitover]
* fc82e062 - build: do not build SDL bin on Windows or macOS [rkitover]
* d543784a - [UserInput] Filter key events globally [steelskin]
* 902c6c8e - [UserInput] Only process shortcut commands once [steelskin]
* b7765092 - [bindings] Set default shortcut for recent file 3 [steelskin]
* cc65ef28 - doc: add system requirements to README.md [danialhorton]
* 32627f6b - [Dialogs] Save and restore dialog positions [steelskin]
* 41952d06 - build: update macOS linker tool to 1.5 [rkitover]
* 0c39a5ba - build: override FindGettext to not update po files [rkitover]
* 1b77d659 - build: update macOS build to ffmpeg 7.0 [rkitover]
* 8d08223d - build: fix compatibility with older ffmpeg [rkitover]
* af6028a9 - build: fix build for nix on macOS [rkitover]
* b52edf52 - build: fix building on macOS with Homebrew [rkitover]
* 6766b9ca - build: fix ffmpeg 7.x compat [rkitover]
* 795f25bb - build: fix nix deps for OpenGL [rkitover]
* 647be137 - gba: set cpsr=spsr when switching to FIQ mode [40356555+Aikku93]
* 8abe3e79 - build: remove -lgcc from static link flags [rkitover]
* f4835674 - [Audio] Rework audio devices enumeration [steelskin]
* 775a571f - build: fix detecting Visual Studio default vcpkg [rkitover]
* 64abd3e8 - [Audio] Remove manual memory allocations [steelskin]
* 047ad277 - [Dialogs] Prevent viewers from causing a crash [steelskin]
* 045c98d8 - build: only use -Werror=lto-type-mismatch on gcc [rkitover]
* 3518dc6a - build: fix LTO on Linux [rkitover]
* cc9a03ce - Add toggle: SDL GameController mode for joysticks [rkitover]
* 8576733c - [Build] Remove lingering references to OpenAl [steelskin]
* 05561922 - build: fix MSYS2 check [rkitover]
* d9432ebb - build: fix build on MINGW{64,32}/UCRT64 on MSYS2 [rkitover]
* f57cad67 - build: fix static linking on MSYS2 CLANG64 [rkitover]
* 23e15734 - build: set wxWidgets_DIR with vcpkg [rkitover]
* 98b51910 - [Build] Remove ENABLE_NLS, fix TRANSLATIONS_ONLY [steelskin]
* a565cea8 - [Build] Remove the OpenGL check [steelskin]
* f96e42fe - build: cmake refactor and improvements [Steelskin]
* 07e49025 - Fix most remaining release warnings (#1243) [Steelskin]
* 18b97b43 - Fix various build warnings (#1242) [Steelskin]
* 13a16eb7 - Fix various warnings in filters and headers (#1241) [Steelskin]
* f46da1c5 - build: remove our version of FindSDL2.cmake [rkitover]
* 404e9a1a - build: add clang to ./installdeps for MSYS2 [rkitover]
* 613bd403 - Make menu more reasonably organized (#1230) [wwrustc]
* 215e3c5a - build: use find_program() to find powershell [rkitover]
* e5aa685f - build: don't use wx utils as UNIX cmds on Windows [rkitover]
* 9e4c8e17 - build: fix gentoo dependency namespaces [68k]
* 5f853b99 - Update metainfo.xml to new standards [jhonny.oliveira]
* e7d135db - Update links to new domain visualboyadvance-m.org [rkitover]
* 60fc096f - build: add libglu-devel for solus in installdeps [rkitover]
## [2.1.9] - 2024-02-03
=======================
* 84b0a3e3 - Remove SDL sound driver [rkitover]

View File

@@ -5,6 +5,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
if(WIN32)
include(RemoveStrawberryPerlFromPATH)
find_program(POWERSHELL
NAMES powershell.exe pwsh.exe
HINTS "/Windows/System32/WindowsPowerShell/v1.0"
REQUIRED)
else()
find_program(POWERSHELL pwsh)
endif()
if(UPDATE_APPCAST)
@@ -23,15 +30,6 @@ set(VCPKG_DEPS_OPTIONAL
faudio ENABLE_FAUDIO
)
if(WIN32)
find_program(POWERSHELL
NAMES powershell.exe pwsh.exe
HINTS "/Windows/System32/WindowsPowerShell/v1.0"
REQUIRED)
else()
find_program(POWERSHELL pwsh)
endif()
include(Set-Toolchain-vcpkg)
# Use ccache if available and not already enabled on the command line.
@@ -89,6 +87,7 @@ include(Dependencies)
if(BUILD_TESTING)
FetchContent_Declare(googletest
URL https://github.com/google/googletest/archive/2d16ed055d09c3689d44b272adc097393de948a0.zip
EXCLUDE_FROM_ALL
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

View File

@@ -9,17 +9,15 @@
- [Commit Message](#commit-message)
- [Collaboration on a Branch](#collaboration-on-a-branch)
- [Commits from Maintainers](#commits-from-maintainers)
- [Strings, Character Sets and Translations](#strings-character-sets-and-translations)
- [Pulling Updated Translations](#pulling-updated-translations)
- [Translations Message Catalog](#translations-message-catalog)
- [Interaction with non-wxWidgets Code](#interaction-with-non-wxwidgets-code)
- [Windows Native Development Environment Setup](#windows-native-development-environment-setup)
- [Miscellaneous](#miscellaneous)
- [Debug Messages](#debug-messages)
- [Release Process](#release-process)
- [Environment](#environment)
- [Certificates](#certificates)
- [Release Commit and Tag](#release-commit-and-tag)
- [64-bit Windows Binary](#64-bit-windows-binary)
- [32-bit Windows Binary](#32-bit-windows-binary)
- [64-bit Mac Binary](#64-bit-mac-binary)
- [ARM64 Windows Binary](#arm64-windows-binary)
- [macOS Binary](#macos-binary)
- [Final steps](#final-steps)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -53,11 +51,12 @@ Follow the following steps to process newly submitted issues:
- An issue is resolved by closing it in github. A commit that fixes the issue
should have the following line near the end of the body of the commit message:
```
- Fix #999.
Fix #999.
```
This will automatically close the issue and assign the closing commit in the
github metadata when it is merged to master. The issue can be reopened if
needed.
- A commit that is work towards resolving an issue, should have the issue number
preceded by a pound sign either at the end of a commit message title, if it is
of primary relevance to the issue, or the body otherwise.
@@ -70,7 +69,7 @@ Follow these guidelines always:
https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
the description of your work should be in the **commit message NOT the pull
, the description of your work should be in the **commit message NOT the pull
request description**.
The title line must be no more than 50 characters and the description must be
@@ -95,16 +94,21 @@ other projects, fewer is better.
The commit title line should be prefixed with an area, unless it involves the
wxWidgets GUI app, in which case it should **NOT** have a prefix.
If the commit is a user-facing functionality change or enhancement, the title
line of the commit must be a non-technical description of this change. For
example "Mute on speedup" because this will go into the changelog.
The text after the area prefix should not be capitalized.
Please use one of these area prefixes otherwise:
Please use one of these area prefixes for non-main-GUI-app commits:
- doc: documentation, README.md etc.
- build: cmake, installdeps, preprocessor compatibility defines, etc.
- gb-core: the GameBoy emulator core
- gba-core: the GameBoy Advance emulator core
- build: cmake, installdeps, preprocessor compatibility defines, compatibility
headers, etc.
- gb: the GameBoy emulator core
- gba: the GameBoy Advance emulator core
- libretro: the libretro core glue and build
- sdl-app: anything for the SDL app
- sdl: anything for the SDL port
- translations: anything related to translations
. Add other areas here if needed.
@@ -138,137 +142,75 @@ things in mind:
better to edit the history than to add more commits. Never add commits fixing
previous commits, only improving or adding to them.
- To update when someone else (very rudely you might say) did a `push -f`, `pull
--rebase` will **USUALLY** work. Verify the log, and if necessary do this
instead:
- To update when someone else updated the branch with a `push -f`
```bash
git status # should be clean, with your work having been already pushed
git fetch --all --prune
git reset --hard origin/<branch-name>
```
.
While actively working on a branch, keep it rebased on top of master.
- While actively working on a branch, keep it rebased on top of master.
#### Commits from Maintainers
Maintainers have the power to commit directly to master. This power must be
used responsibly, something I fail to do myself often, and will try to improve
upon.
Maintainers and project members have the power to commit directly to master.
This power must be used responsibly.
Make your most earnest attempt to follow these general guidelines to keep our
Make your best attempt to follow these general guidelines to keep our
history clean:
- Things that are a straight fix or improvement that does not require discussion
- Things that are a minor fix or improvement that does not require discussion
can be committed directly, keeping the following guidelines in mind.
- Bigger new features, code refactors and changes in architecture should go
through the PR process.
- Push code changes to a branch first, so they can run through the CI.
Differences in what different compilers allow is a problem that comes up
**VERY** frequently. As well as incompatibilities between different
configurations for both the C++ code and any supporting code.
- Push code changes to a branch first, so they can run through the CI. When you
open the commit in GitHub there is a little icon in the upper left corner that
shows the CI status for this commit. Differences in what different compilers
allow is a problem that comes up **VERY** frequently. As well as
incompatibilities between different configurations for both the C++ code and
any supporting code.
### Miscellaneous
### Strings, Character Sets and Translations
#### Debug Messages
#### Pulling Updated Translations
We have an override for `wxLogDebug()` to make it work even in non-debug builds
of wx and on windows, even in mintty.
Once in a while it is necessary to pull new and updated translations from
transifex.
For this you need the transifex client, available for Windows as well from
chocolatey as `transifex-client`.
To pull translations run:
```bash
tx pull -af
```
then check `git status` and if any message catalogs were updated, commit the
result as:
```bash
git commit -a --signoff -S -m'Transifex pull.'
git push
```
#### Translations Message Catalog
Strings that need to be translated by our wonderful translators on transifex
(thank you guys very much) need to be enclosed in `_("...")`, for example:
It works like `printf()`, e.g.:
```cpp
wxLogError(_("error: something very wrong"));
int foo = 42;
wxLogDebug(wxT("the value of foo = %d"), foo);
```
The next time you run cmake after adding a string to be translated, the `.pot`
message catalog source will be regenerated, and you will see a loud message
telling you to push to transifex.
Strings in the XRC XML GUI definition files are automatically added to the
message catalog as well.
If you are working on a branch or a PR, don't push to transifex until it has
been merged to master.
Once it is, push it with:
```bash
tx push -s
```
#### Interaction with non-wxWidgets Code
Use our `UTF8(...)` function to force any `wxString` to UTF-8 for use by other
libraries, screen output or OS APIs. For example:
From the core etc. the usual:
```cpp
fprintf(STDERR, "Error: %s\n", UTF8(err_msg));
fprintf(stderr, "...", ...);
```
, will work fine.
There is one exception to this, when using `wxString::Printf()` and such, you
can't pass another `wxString` to the `%s` format directly, use something like
this:
```cpp
wxString err;
err.Printf("Cannot read file: %s", fname.wc_str());
```
this uses the `wchar_t` UTF-16 representation on Windows and does nothing
elsewhere.
For calling Windows APIs with strings, use the wide char `W` variants and the
`wc_str()` method as well.
### Windows Native Development Environment Setup
This guide has been moved to:
https://github.com/rkitover/windows-dev-guide
You need a debug build for this to work or to even have a console on Windows.
Pass `-DCMAKE_BUILD_TYPE=Debug` to cmake.
### Release Process
#### Environment
#### GnuPG Key
The variable `VBAM_NO_PAUSE`, if set, will cause cmake to not pause before gpg
signing operations, you want to set this if you've disabled your gpg passphrase
to not require interaction during release builds.
You will need to create a GnuPG key for signing your commits and release tags,
and upload it to a keyserver.
gpg set up with your key is helpful for the release process on all environments
where a binary is built, but you can also make the detached signature files
yourself at the end of the process.
Make sure to install GnuPG on all environments where you will be making commits
and tags.
For codesigning windows binaries, put your certificate into
`~/.codesign/windows_comodo.pkcs12`.
#### Certificates
On Mac the 'Developer ID Application' certificate stored in your login keychain
is used, `keychain unlock` will prompt you for your login keychain password, to
avoid that set the `LOGIN_KEYCHAIN_PASSWORD` environment variable to your
password.
Make sure you have set up a Windows code signing certificate with the right
password and a Mac 'Developer ID Application' certificate.
#### Release Commit and Tag
@@ -280,8 +222,10 @@ tag:
mkdir build && cd build
cmake .. -DTAG_RELEASE=TRUE
```
, follow the instructions to edit the `CHANGELOG.md` and then push the release:
then push the release:
To reiterate, **make sure you edit the `CHANGELOG.md`** to remove any
non-user-facing changes before you make the release commit.
```bash
git push
@@ -296,84 +240,107 @@ cmake .. -DTAG_RELEASE=UNDO
#### 64-bit Windows Binary
For this you will preferably need the powershell environment setup described
earlier, however you can use a regular Visual Studio 64 bit native developer
command prompt as well.
For this you will preferably need the PowerShell environment setup described
[here](https://github.com/rkitover/windows-dev-guide), or by starting the `x64
Native Tools Command Prompt` from your Start Menu.
```powershell
mkdir build
cd build
cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_BUILD_TYPE=Release -DUPSTREAM_RELEASE=TRUE -G Ninja
mkdir build-msvc64
cd build-msvc64
cmake .. -DCMAKE_BUILD_TYPE=Release -DUPSTREAM_RELEASE=TRUE -G Ninja
ninja
```
Collect the following files for the release:
- `visualboyadvance-m-Win-64bit.zip`
- `visualboyadvance-m-Win-64bit.zip.asc`
- `visualboyadvance-m-Win-x86_64.zip`
- `translations.zip`
- `translations.zip.asc`
Repeat the process for the debug build, with `-DCMAKE_BUILD_TYPE=Debug` and
collect this file:
- `visualboyadvance-m-Win-x86_64-debug.zip`
.
#### 32-bit Windows Binary
For this the optimal environment is a linux distribution with the mingw
toolchain, I use fedora.
The 32-bit build is a legacy build for Windows XP compatibility. You will need
the MinGW toolchain to build it. The easiest method is to use the MINGW32 MSYS2
environment.
You can set up a shell on a fedora distribution with docker as described here:
Make sure the Visual Studio `signtool.exe` is in your path, you can start MSYS2
with an inherited `PATH` from a Visual Studio enabled environment or add it to
your shell configuration.
https://gist.github.com/rkitover/6379764c619c10e829e4b2fa0ae243fd
If using fedora, the cross script will install all necessary dependencies, if
not install the base toolchain (mingw gcc, binutils, winpthreads) using the
preferred method for your distribution, you can also use mxe for this.
https://mxe.cc/
First install dependencies with:
```bash
sh tools/win/linux-cross-builder -32
./installdeps
```
You can also use msys2 on Windows, this is not recommended:
. Then build the 32-bit binary as follows:
```bash
sh tools/win/msys2-builder -32
mkdir build-mingw32
cd build-mingw32
cmake .. -DCMAKE_BUILD_TYPE=Release -DUPSTREAM_RELEASE=TRUE -G Ninja
ninja
```
. Collect this file for the release:
To set up msys2, see this guide:
- `visualboyadvance-m-Win-x86_32.zip`
https://gist.github.com/rkitover/d008324309044fc0cc742bdb16064454
. Then repeat the process for the debug build with `-DCMAKE_BUILD_TYPE=Debug`,
and collect this file:
Collect the following files from `~/vbam-build-mingw32/project` if using linux,
or `~/vbam-build-msys2-x86_64/project` if using msys2:
- `visualboyadvance-m-Win-x86_32-debug.zip`
.
- `visualboyadvance-m-Win-32bit.zip`
- `visualboyadvance-m-Win-32bit.zip.asc`
#### ARM64 Windows Binary
#### 64-bit Mac Binary
You will need the MSVC ARM64 cross toolchain to build this binary, if you used
the install script from [here](https://github.com/rkitover/windows-dev-guide)
you will have it installed, otherwise run Visual Studio Installer and install
the component.
To enter the ARM64 cross environment, edit the PowerShell profile described
[here](https://github.com/rkitover/windows-dev-guide) or use the `vcvarsall.bat`
script with the `amd64_arm64` argument as described
[here](https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170).
From there the process is the same as for the 64-bit build, collect the
following files for the release:
- `visualboyadvance-m-Win-arm64.zip`
- 'visualboyadvance-m-Win-arm64-debug.zip'
.
#### macOS Binary
Install the latest Xcode for your OS.
You will need bash and (optionally) gpg from homebrew (which you will also need
to install):
```bash
brew install bash gnupg
```
You will need bash from Homebrew/nix/MacPorts/whatever to run the build script.
You will need a codesigning certificate from Apple, which you will be able to
generate once you join their developer program. This is the certificate of the
type 'Developer ID Application' stored in your login keychain. `keychain
unlock` will prompt you for your login keychain password, to avoid that set the
`LOGIN_KEYCHAIN_PASSWORD` environment variable to your password.
generate once you join their developer program from XCode. This is the
certificate of the type 'Developer ID Application' stored in your login
keychain.
If you are not using a GUI session, you will need to use a method to unlock your
login keychain before building. Adding the certificate and key to the System
keychain is also a method that some people use.
Then run:
```bash
/usr/local/bin/bash tools/osx/builder -64
tools/osx/builder
```
, this will take a while because it builds all of the dependencies.
Collect the following files from `~/vbam-build-mac-64bit/project`:
- `visualboyadvance-m-Mac-64bit.zip`
- `visualboyadvance-m-Mac-64bit.zip.asc`
- `visualboyadvance-m-Mac-x86_64.zip`
- `visualboyadvance-m-Mac-x86_64-debug.zip`
.
#### Final steps
@@ -381,27 +348,26 @@ Go to the github releases tab, and make a release for the tag you pushed
earlier.
Put any notes to users and distro maintainers into the description as well as
the entries from `CHANGELOG.md` generated earlier from git by the release
commit script.
the generated entries from `CHANGELOG.md` you edited earlier.
Upload all files collected during the earlier builds, the complete list is:
- `translations.zip`
- `translations.zip.asc`
- `visualboyadvance-m-Mac-64bit.zip`
- `visualboyadvance-m-Mac-64bit.zip.asc`
- `visualboyadvance-m-Win-32bit.zip`
- `visualboyadvance-m-Win-32bit.zip.asc`
- `visualboyadvance-m-Win-64bit.zip`
- `visualboyadvance-m-Win-64bit.zip.asc`
- `visualboyadvance-m-Win-x86_64.zip`
- `visualboyadvance-m-Win-x86_64-debug.zip`
- `visualboyadvance-m-Win-x86_32.zip`
- `visualboyadvance-m-Win-x86_32-debug.zip`
- `visualboyadvance-m-Win-arm64.zip`
- 'visualboyadvance-m-Win-arm64-debug.zip'
- `visualboyadvance-m-Mac-x86_64.zip`
- `visualboyadvance-m-Mac-x86_64-debug.zip`
Update the winsparkle appcast.xml by running this cmake command:
Update the winsparkle `appcast.xml` by running this cmake command:
```bash
cmake .. -DUPDATE_APPCAST=TRUE
```
follow the instructions to push the change to the web data repo.
, follow the instructions to push the change to the web data repo.
Announce the release on reddit r/emulation and the forum.

172
README.md
View File

@@ -19,8 +19,6 @@
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
[![Join the chat at https://gitter.im/visualboyadvance-m/Lobby](https://badges.gitter.im/visualboyadvance-m/Lobby.svg)](https://gitter.im/visualboyadvance-m/Lobby)
Our bridged Discord server is [Here](https://discord.gg/EpfxEuGMKH).
We are also on *`#vba-m`* on [Libera IRC](https://libera.chat/) which has a [Web
@@ -59,35 +57,39 @@ Factory Reset`.
## System Requirements
Windows 7, 8.1 or 10/11, Linux distro's or macOS.
Windows XP, Vista, 7, 8.1 or 10/11, Linux distros or macOS.
2Ghz x86 (or x86-64) Intel Core 2 or AMD Athlon processor with SSE, Snapdragon 835
or newer CPU compatible with Arm for Windows.
2Ghz x86(or x86-64) Intel Core 2 or AMD Athlon processor with SSE, Snapdragon 835
or newer cpu compatible with Arm for Windows.
- Just a guideline, lower clock speeds and Celeron processors may be able to run at full
speed on lower settings, and Linux based ARM Operating systems have wider cpu support.
speed on lower settings, and Linux based ARM Operating systems have wider CPU support.
DirectX June 2010 Redist [Full](https://www.microsoft.com/en-au/download/details.aspx?id=8109) / [Websetup](https://www.microsoft.com/en-au/download/details.aspx?id=35) for Xaudio (Remember to uncheck Bing on the websetup)
DirectX June 2010 Redist
[Full](https://www.microsoft.com/en-au/download/details.aspx?id=8109) /
[Websetup](https://www.microsoft.com/en-au/download/details.aspx?id=35) for
Xaudio (Remember to uncheck Bing on the websetup.)
## Building
The basic formula to build vba-m is:
```shell
```bash
cd ~ && mkdir src && cd src
git clone https://github.com/visualboyadvance-m/visualboyadvance-m.git
cd visualboyadvance-m
./installdeps
# ./installdeps will give you build instructions, which will be similar to:
./installdeps # On Linux or macOS
mkdir build && cd build
cmake .. -G Ninja
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja
```
`./installdeps` is supported on MSys2, Linux (Debian/Ubuntu, Fedora, Arch,
Solus, OpenSUSE, Gentoo and RHEL/CentOS) and Mac OS X (homebrew, macports or
fink.)
`./installdeps` is supported on MSYS2, Linux (Debian/Ubuntu, Fedora, Arch,
Solus, OpenSUSE, Gentoo and RHEL/CentOS) and Mac OS X (homebrew, MacPorts or
Fink.)
## Building a Libretro core
@@ -98,7 +100,7 @@ cd src/libretro
make -j`nproc`
```
Copy vbam_libretro.so to your RetroArch cores directory.
Copy `vbam_libretro.so` to your RetroArch cores directory.
## Visual Studio Support
@@ -118,14 +120,16 @@ environment loaded.
Using your own user-wide installation of vcpkg is supported, just make sure the
environment variable `VCPKG_ROOT` is set.
To build in the visual studio command prompt, use something like this:
To build in the Visual Studio `x64 Native Tools Command Prompt`, use something
like this:
```
mkdir build
cd build
cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_BUILD_TYPE=Debug -G Ninja
cmake .. -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja
```
.
## Visual Studio Code Support
@@ -163,7 +167,7 @@ And the following development libraries:
- [gettext](https://www.gnu.org/software/gettext/) and gettext-tools
- [SDL2](https://www.libsdl.org/) (required)
- [SFML](https://www.sfml-dev.org/) (optional, for link)
- [OpenAL](https://www.openal.org/) or [openal-soft](https://kcat.strangesoft.net/openal.html) (required, a sound interface)
- [openal-soft](https://kcat.strangesoft.net/openal.html) (required, a sound interface)
- [wxWidgets](https://wxwidgets.org/) (required for GUI, 2.8 and non-stl builds are no longer supported)
On Linux and similar, you also need the version of GTK your wxWidgets is linked
@@ -183,61 +187,47 @@ This is supported on Fedora, Arch, Solus and MSYS2.
may be `win32` which is an alias for `mingw-w64-i686` to target 32 bit Windows,
or `mingw-w64-x86_64` for 64 bit Windows targets.
The target is implicit on MSys2 depending on which MINGW shell you started (the
The target is implicit on MSYS2 depending on which MINGW shell you started (the
value of `$MSYSTEM`.)
On Debian/Ubuntu this uses the MXE apt repository and works quite well.
On Fedora it can build using the Fedora MinGW packages, albeit with wx 2.8, no
OpenGL support, and no Link support for lack of SFML.
On Arch it currently doesn't work at all because the AUR stuff is completely
broken, I will at some point redo the arch stuff to use MXE as well.
## CMake Options
The CMake code tries to guess reasonable defaults for options, but you can
override them, for example:
```shell
cmake .. -DENABLE_LINK=NO -G Ninja
cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_LINK=NO -G Ninja
```
Of particular interest is making **Release** or **Debug** builds, the default
mode is **Release**, to make a **Debug** build use something like:
```shell
cmake .. -DCMAKE_BUILD_TYPE=Debug -G Ninja
```
Here is the complete list:
. Here is the complete list:
| **CMake Option** | **What it Does** | **Defaults** |
|-----------------------|----------------------------------------------------------------------|-----------------------|
| ENABLE_SDL | Build the SDL port | OFF |
| ENABLE_WX | Build the wxWidgets port | ON |
| ENABLE_DEBUGGER | Enable the debugger | ON |
| ENABLE_ASM_CORE | Enable x86 ASM CPU cores (**BUGGY AND DANGEROUS**) | OFF |
| ENABLE_ASM | Enable the following two ASM options | ON for 32 bit builds |
| ENABLE_ASM_SCALERS | Enable x86 ASM graphic filters | ON for 32 bit builds |
| ENABLE_MMX | Enable MMX | ON for 32 bit builds |
| ENABLE_LINK | Enable GBA linking functionality (requires SFML) | AUTO |
| ENABLE_LIRC | Enable LIRC support | OFF |
| ENABLE_FFMPEG | Enable ffmpeg A/V recording | AUTO |
| ENABLE_ONLINEUPDATES | Enable online update checks | ON |
| ENABLE_LTO | Compile with Link Time Optimization (gcc and clang only) | ON for release build |
| ENABLE_GBA_LOGGING | Enable extended GBA logging | ON |
| ENABLE_DIRECT3D | Direct3D rendering for wxWidgets (Windows, **NOT IMPLEMENTED!!!**) | ON |
| ENABLE_XAUDIO2 | Enable xaudio2 sound output for wxWidgets (Windows only) | ON |
| ENABLE_ASAN | Enable libasan sanitizers (by default address, only in debug mode) | OFF |
| UPSTREAM_RELEASE | Do some release tasks, like codesigning, making zip and gpg sigs. | OFF |
| BUILD_TESTING | Build the tests and enable ctest support. | ON |
| VBAM_STATIC | Try link all libs statically (the following are set to ON if ON) | OFF |
| SDL2_STATIC | Try to link static SDL2 libraries | OFF |
| SFML_STATIC_LIBRARIES | Try to link static SFML libraries | OFF |
| FFMPEG_STATIC | Try to link static ffmpeg libraries | OFF |
| OPENAL_STATIC | Try to link static OpenAL libraries | OFF |
| TRANSLATIONS_ONLY | Build only the translations.zip and nothing else | OFF |
| `ENABLE_SDL` | Build the SDL port | OFF |
| `ENABLE_WX` | Build the wxWidgets port | ON |
| `ENABLE_DEBUGGER` | Enable the debugger | ON |
| `ENABLE_ASM_CORE` | Enable x86 ASM CPU cores (**BUGGY AND DANGEROUS**) | OFF |
| `ENABLE_ASM` | Enable the following two ASM options | ON for 32 bit builds |
| `ENABLE_ASM_SCALERS` | Enable x86 ASM graphic filters | ON for 32 bit builds |
| `ENABLE_MMX` | Enable MMX | ON for 32 bit builds |
| `ENABLE_LINK` | Enable GBA linking functionality (requires SFML) | AUTO |
| `ENABLE_LIRC` | Enable LIRC support | OFF |
| `ENABLE_FFMPEG` | Enable ffmpeg A/V recording | AUTO |
| `ENABLE_ONLINEUPDATES` | Enable online update checks | ON |
| `ENABLE_LTO` | Compile with Link Time Optimization (gcc and clang only) | ON for release build |
| `ENABLE_GBA_LOGGING` | Enable extended GBA logging | ON |
| `ENABLE_XAUDIO2` | Enable xaudio2 sound output for wxWidgets (Windows only) | ON |
| `ENABLE_FAUDIO` | Enable faudio sound output for wxWidgets, | ON, not 32 bit Win |
| `ENABLE_ASAN` | Enable libasan sanitizers (by default address, only in debug mode) | OFF |
| `UPSTREAM_RELEASE` | Do some release tasks, like codesigning, making zip and gpg sigs. | OFF |
| `BUILD_TESTING` | Build the tests and enable ctest support. | ON |
| `VBAM_STATIC` | Try link all libs statically (the following are set to ON if ON) | OFF |
| `SDL2_STATIC` | Try to link static SDL2 libraries | OFF |
| `SFML_STATIC_LIBRARIES` | Try to link static SFML libraries | OFF |
| `FFMPEG_STATIC` | Try to link static ffmpeg libraries | OFF |
| `OPENAL_STATIC` | Try to link static OpenAL libraries | OFF |
| `TRANSLATIONS_ONLY` | Build only the translations.zip and nothing else | OFF |
Note for distro packagers, we use the CMake module
[GNUInstallDirs](https://cmake.org/cmake/help/v2.8.12/cmake.html#module:GNUInstallDirs)
@@ -247,52 +237,6 @@ On Unix to use a different version of wxWidgets, set
`wxWidgets_CONFIG_EXECUTABLE` to the path to the `wx-config` script you want to
use.
## MSys2 Notes
To run the resulting binary, you can simply type:
```shell
./visualboyadvance-m
```
in the shell where you built it.
If you built with `-DCMAKE_BUILD_TYPE=Debug`, you will get a console app and
will see debug messages, even in mintty.
If you want to start the binary from e.g. a shortcut or Explorer, you will need
to put `c:\msys64\mingw32\bin` for 32 bit builds and `c:\msys64\mingw64\bin`
for 64 bit builds in your PATH (to edit system PATH, go to Control Panel ->
System -> Advanced system settings -> Environment Variables.)
If you want to package the binary, you will need to include the MinGW DLLs it
depends on, they can install to the same directory as the binary.
Our own builds are static.
## Debug Messages
We have an override for `wxLogDebug()` to make it work even in non-debug builds
of wx and on windows, even in mintty.
It works like `printf()`, e.g.:
```cpp
int foo = 42;
wxLogDebug(wxT("the value of foo = %d"), foo);
```
From the core etc. the usual:
```cpp
fprintf(stderr, "...", ...);
```
will work fine.
You need a debug build for this to work or to even have a console on Windows.
Pass `-DCMAKE_BUILD_TYPE=Debug` to cmake.
## Reporting Crash Bugs
If the emulator crashes and you wish to report the bug, a backtrace made with
@@ -311,27 +255,25 @@ do something such as:
```shell
ulimit -c unlimited
```
in your shell to enable coredump files.
, in your shell to enable core files.
[This
post](https://ask.fedoraproject.org/en/question/98776/where-is-core-dump-located/?answer=98779#post-id-98779)
explains how to retrieve core dump on Fedora Linux (and possibly other
distributions.)
explains how to retrieve core dump on some distributions, when they are managed
by systemd.
Once you have the core dump file, open it with `gdb`, for example:
Once you have the core file, open it with `gdb`, for example:
```shell
gdb -c core ./visualboyadvance-m
```
In the `gdb` shell, to print the backtrace, type:
. In the `gdb` shell, to start the process and print the backtrace, type:
```
run
bt
```
This may be a bit of a hassle, but it helps us out immensely.
. This may be a bit of a hassle, but it helps us out immensely.
## Contributing

View File

@@ -56,9 +56,11 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "[xX]86|i[3-9]86|[aA][mM][dD]64")
endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "[aA][aA][rR][cC][hH]|[aA][rR][mM]")
if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4) # 32 bit
set(ARM32 ON)
set(ARCH_NAME arm32)
set(WINARCH arm)
elseif(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
set(ARM64 ON)
set(ARCH_NAME arm64)
set(WINARCH arm64)
endif()

View File

@@ -10,10 +10,6 @@ if(NOT EXISTS "${CMAKE_SOURCE_DIR}/.git")
message(FATAL_ERROR "releases can only be done from a git clone")
endif()
if(NOT GIT_FOUND)
message(FATAL_ERROR "git is required to make a release")
endif()
find_program(GPG_EXECUTABLE gpg)
if(NOT GPG_EXECUTABLE)
@@ -35,7 +31,7 @@ function(make_release_commit_and_tag)
# First make sure we are on master.
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short --abbrev-ref=strict HEAD
COMMAND git rev-parse --short --abbrev-ref=strict HEAD
OUTPUT_VARIABLE git_branch
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
@@ -46,7 +42,7 @@ function(make_release_commit_and_tag)
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} status --porcelain=2
COMMAND git status --porcelain=2
OUTPUT_VARIABLE git_status
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
@@ -59,7 +55,7 @@ function(make_release_commit_and_tag)
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} tag --sort=-v:refname
COMMAND git tag --sort=-v:refname
OUTPUT_VARIABLE git_tags
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
@@ -80,7 +76,7 @@ function(make_release_commit_and_tag)
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} log ${git_last_tag}.. "--pretty=format:* %h - %s [%aL]"
COMMAND git log ${git_last_tag}.. "--pretty=format:* %h - %s [%aL]"
OUTPUT_VARIABLE release_log
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
@@ -90,17 +86,17 @@ function(make_release_commit_and_tag)
if(TAG_RELEASE STREQUAL UNDO)
execute_process(
COMMAND ${GIT_EXECUTABLE} tag -d ${git_last_tag}
COMMAND git tag -d ${git_last_tag}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
execute_process(
COMMAND ${GIT_EXECUTABLE} reset HEAD~1
COMMAND git reset HEAD~1
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
execute_process(
COMMAND ${GIT_EXECUTABLE} checkout HEAD CHANGELOG.md
COMMAND git checkout HEAD CHANGELOG.md
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
@@ -212,45 +208,37 @@ Ignore the following cmake error.
)
execute_process(
COMMAND ${GIT_EXECUTABLE} add CHANGELOG.md
COMMAND git add CHANGELOG.md
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
# Make the release commit.
message(FATAL_ERROR "
execute_process(
COMMAND ${GIT_EXECUTABLE} commit -m "release ${new_tag}" --signoff -S
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
Release prepared.
# Make release tag.
Edit CHANGELOG.md to remove any non-user-facing commits, and optionally add any
release notes.
execute_process(
COMMAND ${GIT_EXECUTABLE} tag -s -m${new_tag} ${new_tag}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
Run the following commands to commit the change:
message(FATAL_ERROR [=[
Release commit and tag generated.
**** IF YOU ARE SURE YOU WANT TO RELEASE ****
Run the following commands to push the release commit and tag:
git push
git push --tags
**** TO UNDO THE RELEASE ****
git commit -m'release ${new_tag}' --signoff -S
git tag -s -m'${new_tag}' ${new_tag}
To rollback these changes, run this command:
cmake .. -DTAG_RELEASE=UNDO
Ignore the "configuration incomplete" message following, this mode does not
==== IF YOU ARE SURE YOU WANT TO RELEASE, THIS **CANNOT** BE REVERSED ====
Run the following to push the release commit and tag:
git push
git push --tags
Ignore the 'configuration incomplete' message following, this mode does not
build anything.
]=])
")
endfunction()
make_release_commit_and_tag()

View File

@@ -91,7 +91,7 @@ set(FFMPEG_DEFAULT OFF)
set(FFMPEG_COMPONENTS AVCODEC AVFORMAT SWSCALE AVUTIL SWRESAMPLE)
set(FFMPEG_COMPONENT_VERSIONS AVCODEC>=58.18.100 AVFORMAT>=58.12.100 SWSCALE>=5.1.100 AVUTIL>=56.14.100 SWRESAMPLE>=3.1.100)
if(NOT TRANSLATIONS_ONLY AND NOT DEFINED ENABLE_FFMPEG OR ENABLE_FFMPEG)
if(NOT TRANSLATIONS_ONLY AND (NOT DEFINED ENABLE_FFMPEG OR ENABLE_FFMPEG) AND (NOT (X86 AND MINGW)))
set(FFMPEG_DEFAULT ON)
find_package(FFmpeg COMPONENTS ${FFMPEG_COMPONENTS})

View File

@@ -51,6 +51,28 @@ if(NOT DEFINED VCPKG_TARGET_TRIPLET)
message(STATUS "Inferred VCPKG_TARGET_TRIPLET=${VCPKG_TARGET_TRIPLET}")
endif()
function(vcpkg_seconds)
if(CMAKE_HOST_SYSTEM MATCHES Windows OR ((NOT DEFINED CMAKE_HOST_SYSTEM) AND WIN32))
execute_process(
COMMAND cmd /c echo %TIME:~0,8%
OUTPUT_VARIABLE time
)
else()
execute_process(
COMMAND date +%H:%M:%S
OUTPUT_VARIABLE time
)
endif()
string(SUBSTRING "${time}" 0 2 hours)
string(SUBSTRING "${time}" 3 2 minutes)
string(SUBSTRING "${time}" 6 2 secs)
math(EXPR seconds "(${hours} * 60 * 60) + (${minutes} * 60) + ${secs}")
set(seconds ${seconds} PARENT_SCOPE)
endfunction()
function(vcpkg_check_git_status git_status)
# The VS vcpkg component cannot be written to without elevation.
if(NOT git_status EQUAL 0 AND NOT VCPKG_ROOT MATCHES "Visual Studio")

View File

@@ -1118,9 +1118,12 @@ windows_installdeps() {
*i686*)
pkgs="$pkgs nasm"
;;
*)
pkgs="$pkgs FAudio"
;;
esac
pkgs="$pkgs SDL2 sfml FAudio wxWidgets3.2 zlib binutils cmake crt-git headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal"
pkgs="$pkgs SDL2 sfml wxWidgets3.2 zlib binutils cmake crt-git headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal"
case "$target" in
*x86_64)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -35,6 +35,7 @@ target_sources(vbam-core-base
version.cpp
PUBLIC
check.h
array.h
file_util.h
image_util.h
@@ -59,3 +60,5 @@ target_link_libraries(vbam-core-base
PRIVATE vbam-fex stb-image
PUBLIC ${ZLIB_LIBRARY}
)
add_subdirectory(test)

58
src/core/base/check.h Normal file
View File

@@ -0,0 +1,58 @@
#ifndef VBAM_CORE_BASE_CHECK_H_
#define VBAM_CORE_BASE_CHECK_H_
// This header defines a number of macros for checking conditions and crashing
// the program if they are not met.
// * VBAM_CHECK(condition) - crashes the program if the condition is not met.
// * VBAM_NOTREACHED() - crashes the program if this line of code is reached.
// In release builds, this macro also tells the compiler that this code path
// is unreachable, which can help the compiler generate better code.
// * VBAM_STRINGIFY(x) - converts the argument to a string literal.
// While a number of other macros are defined in this file, they are not
// intended for general use and should be avoided.
#if defined(__GNUC__) || defined(__clang__)
// GCC/Clang.
#define VBAM_IMMEDIATE_CRASH_DETAIL() __builtin_trap()
#define VBAM_INTRINSIC_UNREACHABLE_DETAIL() __builtin_unreachable()
#elif defined(_MSC_VER) // defined(__GNUC__) || defined(__clang__)
// MSVC.
#define VBAM_IMMEDIATE_CRASH_DETAIL() __debugbreak()
#define VBAM_INTRINSIC_UNREACHABLE_DETAIL() __assume(0)
#else // defined(__GNUC__) || defined(__clang__)
#error "Unsupported compiler"
#endif // defined(__GNUC__) || defined(__clang__)
#define VBAM_STRINGIFY_DETAIL(x) #x
#define VBAM_STRINGIFY(x) VBAM_STRINGIFY_DETAIL(x)
#define VBAM_REQUIRE_SEMICOLON_DETAIL() \
static_assert(true, "Require a semicolon after macros invocation.")
#define VBAM_CHECK(condition) \
if (!(condition)) { \
fputs("CHECK failed at " __FILE__ ":" VBAM_STRINGIFY(__LINE__) ": " #condition "\n", \
stderr); \
VBAM_IMMEDIATE_CRASH_DETAIL(); \
} \
VBAM_REQUIRE_SEMICOLON_DETAIL()
#if defined(DEBUG)
#define VBAM_NOTREACHED() \
fputs("NOTREACHED code reached at " __FILE__ ":" VBAM_STRINGIFY(__LINE__) "\n", stderr); \
VBAM_IMMEDIATE_CRASH_DETAIL()
#else // defined(DEBUG)
#define VBAM_NOTREACHED() VBAM_INTRINSIC_UNREACHABLE_DETAIL()
#endif // defined(DEBUG)
#endif // VBAM_CORE_BASE_CHECK_H_

View File

@@ -59,6 +59,7 @@ extern struct CoreOptions {
bool speedHack = false;
bool speedup = false;
bool speedup_throttle_frame_skip = false;
bool speedup_mute = true;
int cheatsEnabled = 1;
int cpuDisableSfx = 0;
int cpuSaveType = 0;
@@ -125,4 +126,4 @@ extern int systemSpeed;
#define SYSTEM_SAVE_UPDATED 30
#define SYSTEM_SAVE_NOT_UPDATED 0
#endif // VBAM_CORE_BASE_SYSTEM_H_
#endif // VBAM_CORE_BASE_SYSTEM_H_

View File

@@ -0,0 +1,12 @@
# This defines the `vbam-core-base-test` library.
if(NOT BUILD_TESTING)
return()
endif()
add_library(vbam-core-base-test
INTERFACE
notreached.h)
target_link_libraries(vbam-core-base-test
INTERFACE GTest::gtest)

View File

@@ -0,0 +1,22 @@
#ifndef VBAM_CORE_BASE_TEST_NOTREACHED_H_
#define VBAM_CORE_BASE_TEST_NOTREACHED_H_
#include <gtest/gtest.h>
#if defined(DEBUG)
#define VBAM_EXPECT_NOTREACHED(statement) EXPECT_DEATH(statement, "NOTREACHED")
#else // defined(DEBUG)
// There is no way to test this in release builds as the compiler might optimize
// this code path away. Eat the statement to avoid unused variable warnings.
#define VBAM_EXPECT_NOTREACHED(statement) \
if constexpr (false) { \
statement; \
} \
static_assert(true, "")
#endif // defined(DEBUG)
#endif // VBAM_CORE_BASE_TEST_NOTREACHED_H_

View File

@@ -1,13 +1,13 @@
#include "core/gb/gb.h"
#include <array>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include "core/base/check.h"
#include "core/base/file_util.h"
#include "core/base/message.h"
#include "core/base/sizes.h"
@@ -291,8 +291,7 @@ bool gbInitializeRom(size_t romSize) {
switch (g_gbCartData.validity()) {
case gbCartData::Validity::kValid:
case gbCartData::Validity::kUninitialized:
// Unreachable.
assert(false);
VBAM_NOTREACHED();
break;
case gbCartData::Validity::kSizeTooSmall:
systemMessage(MSG_UNSUPPORTED_ROM_SIZE,

View File

@@ -2,8 +2,8 @@
#include <algorithm>
#include <array>
#include <cassert>
#include "core/base/check.h"
#include "core/base/sizes.h"
namespace {
@@ -62,7 +62,7 @@ char byte_to_char(uint8_t byte) {
} else if (byte < 16) {
return 'A' + (byte - 10);
} else {
assert(false);
VBAM_NOTREACHED();
return '\0';
}
}
@@ -98,7 +98,7 @@ bool is_valid_manufacturer_code(const std::string& manufacturer_code) {
constexpr size_t kHeaderGlobalChecksumAdress = 0x14e;
uint16_t get_rom_checksum(const uint8_t* romData, size_t romDataSize) {
assert(romData);
VBAM_CHECK(romData);
uint16_t checksum = 0;
for (size_t i = 0; i < romDataSize; i++) {
@@ -160,7 +160,7 @@ constexpr size_t kHeaderChecksumEndAdress = 0x14c;
} // namespace
gbCartData::gbCartData(const uint8_t* romData, size_t romDataSize) {
assert(romData);
VBAM_CHECK(romData);
if (romDataSize < sizeof(gbRomHeader)) {
validity_ = Validity::kSizeTooSmall;

View File

@@ -3845,6 +3845,8 @@ void CPULoop(int ticks)
bool turbo_button_pressed = (joy >> 10) & 1;
#ifndef __LIBRETRO__
static uint32_t last_throttle;
static bool current_volume_saved = false;
static float current_volume;
if (turbo_button_pressed) {
if (coreOptions.speedup_frame_skip)
@@ -3859,10 +3861,23 @@ void CPULoop(int ticks)
if (coreOptions.speedup_throttle_frame_skip)
framesToSkip += static_cast<int>(std::ceil(double(coreOptions.speedup_throttle) / 100.0) - 1);
}
if (coreOptions.speedup_mute && !current_volume_saved) {
current_volume = soundGetVolume();
current_volume_saved = true;
soundSetVolume(0);
}
}
else if (speedup_throttle_set) {
soundSetThrottle(DowncastU16(last_throttle));
speedup_throttle_set = false;
else {
if (current_volume_saved) {
soundSetVolume(current_volume);
current_volume_saved = false;
}
if (speedup_throttle_set) {
soundSetThrottle(DowncastU16(last_throttle));
speedup_throttle_set = false;
}
}
#else
if (turbo_button_pressed)

View File

@@ -311,7 +311,7 @@ else ifeq ($(platform), wiiu)
CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++$(EXE_EXT)
AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
ENDIANNESS_DEFINES += -DMSB_FIRST -DWORDS_BIGENDIAN=1
PLATFORM_DEFINES += -DGEKKO -DWIIU -DHW_RVL -mwup -mcpu=750 -meabi -mhard-float -D__ppc__
PLATFORM_DEFINES += -DGEKKO -DWIIU -DHW_WUP -mcpu=750 -meabi -mhard-float -D__ppc__
PLATFORM_DEFINES += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int
STATIC_LINKING=1
TILED_RENDERING=1
@@ -322,17 +322,18 @@ else ifeq ($(platform), libnx)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
DEFINES := -DSWITCH=1 -U__linux__ -U__linux -DRARCH_INTERNAL -DHAVE_THREADS=1
CFLAGS := $(DEFINES) -g -O3 \
-fPIE -I$(LIBNX)/include/ -ffunction-sections -fdata-sections -ftls-model=local-exec -Wl,--allow-multiple-definition -specs=$(LIBNX)/switch.specs
-fPIE -I$(LIBNX)/include/ -ffunction-sections -fdata-sections -ftls-model=local-exec -Wl,--allow-multiple-definition -specs=$(LIBNX)/switch.specs
CFLAGS += $(INCDIRS)
CFLAGS += $(INCLUDE) -D__SWITCH__ -DHAVE_LIBNX
CXXFLAGS := $(ASFLAGS) $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CFLAGS += -std=gnu11
CXXFLAGS := $(ASFLAGS) $(CFLAGS) -fno-rtti -fno-exceptions
STATIC_LINKING=1
# Nintendo Switch (libtransistor)
# Nintendo Switch (devkitpro)
else ifeq ($(platform), switch)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
include $(LIBTRANSISTOR_HOME)/libtransistor.mk
CC = $(DEVKITPRO)/devkitA64/bin/aarch64-none-elf-gcc$(EXE_EXT)
CXX = $(DEVKITPRO)/devkitA64/bin/aarch64-none-elf-g++$(EXE_EXT)
AR = $(DEVKITPRO)/devkitA64/bin/aarch64-none-elf-ar$(EXE_EXT)
STATIC_LINKING=1
TILED_RENDERING=1

View File

@@ -1,4 +1,3 @@
#include <cassert>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
@@ -12,6 +11,7 @@
#include "components/filters_agb/filters_agb.h"
#include "components/filters_interframe/interframe.h"
#include "core/base/check.h"
#include "core/base/system.h"
#include "core/base/file_util.h"
#include "core/base/sizes.h"
@@ -177,8 +177,7 @@ static void* gb_rtcdata_prt(void)
case gbCartData::MapperType::kGameGenie:
case gbCartData::MapperType::kGameShark:
case gbCartData::MapperType::kUnknown:
// Unreachable.
assert(false);
VBAM_NOTREACHED();
return nullptr;
}
return nullptr;
@@ -205,8 +204,7 @@ static size_t gb_rtcdata_size(void)
case gbCartData::MapperType::kGameGenie:
case gbCartData::MapperType::kGameShark:
case gbCartData::MapperType::kUnknown:
// Unreachable.
assert(false);
VBAM_NOTREACHED();
break;
}
return 0;
@@ -272,8 +270,7 @@ static void gbInitRTC(void)
case gbCartData::MapperType::kGameGenie:
case gbCartData::MapperType::kGameShark:
case gbCartData::MapperType::kUnknown:
// Unreachable.
assert(false);
VBAM_NOTREACHED();
break;
}
}
@@ -1456,8 +1453,7 @@ void retro_run(void)
case gbCartData::MapperType::kGameGenie:
case gbCartData::MapperType::kGameShark:
case gbCartData::MapperType::kUnknown:
// Unreachable.
assert(false);
VBAM_NOTREACHED();
break;
}
/* Initialize RTC using local time if needed */

View File

@@ -71,15 +71,15 @@ endif()
# Installation scripts.
install(
PROGRAMS ${PROJECT_BINARY_DIR}/vbam${CMAKE_EXECUTABLE_SUFFIX}
DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
FILES ${CMAKE_CURRENT_LIST_DIR}/vbam.cfg-example
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}
RENAME vbam.cfg
)
if(UNIX)
# man pages.
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/vbam.6 DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man6)
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/vbam.6 DESTINATION ${CMAKE_INSTALL_MANDIR}/man6)
endif()

View File

@@ -98,7 +98,8 @@ enum named_opts
OPT_SOUND_FILTERING,
OPT_SPEEDUP_THROTTLE,
OPT_SPEEDUP_FRAME_SKIP,
OPT_NO_SPEEDUP_THROTTLE_FRAME_SKIP
OPT_NO_SPEEDUP_THROTTLE_FRAME_SKIP,
OPT_NO_SPEEDUP_MUTE
};
#define SOUND_MAX_VOLUME 2.0
@@ -232,6 +233,7 @@ struct option argOptions[] = {
{ "speedup-throttle", required_argument, 0, OPT_SPEEDUP_THROTTLE },
{ "speedup-frame-skip", required_argument, 0, OPT_SPEEDUP_FRAME_SKIP },
{ "no-speedup-throttle-frame-skip", no_argument, 0, OPT_NO_SPEEDUP_THROTTLE_FRAME_SKIP },
{ "no-speedup-mute", no_argument, 0, OPT_NO_SPEEDUP_MUTE },
{ "use-bios", no_argument, &coreOptions.useBios, 1 },
{ "verbose", required_argument, 0, 'v' },
{ "win-gb-printer-enabled", no_argument, &coreOptions.winGbPrinterEnabled, 1 },
@@ -347,6 +349,7 @@ void LoadConfig()
coreOptions.speedup_throttle = ReadPref("speedupThrottle", 100);
coreOptions.speedup_frame_skip = ReadPref("speedupFrameSkip", 9);
coreOptions.speedup_throttle_frame_skip = ReadPref("speedupThrottleFrameSkip", 0);
coreOptions.speedup_mute = ReadPref("speedupMute", 1);
coreOptions.useBios = ReadPrefHex("useBiosGBA");
coreOptions.winGbPrinterEnabled = ReadPref("gbPrinter", 0);
@@ -990,6 +993,9 @@ int ReadOpts(int argc, char ** argv)
case OPT_NO_SPEEDUP_THROTTLE_FRAME_SKIP:
coreOptions.speedup_throttle_frame_skip = false;
break;
case OPT_NO_SPEEDUP_MUTE:
coreOptions.speedup_mute = false;
break;
}
}
return op;

View File

@@ -31,6 +31,8 @@ set(VBAM_WX_COMMON
dialogs/joypad-config.h
dialogs/sound-config.cpp
dialogs/sound-config.h
dialogs/speedup-config.cpp
dialogs/speedup-config.h
drawing.h
extra-translations.cpp
gfxviewers.cpp
@@ -46,33 +48,6 @@ set(VBAM_WX_COMMON
viewsupt.h
wayland.cpp
wayland.h
# from external source with minor modifications
widgets/checkedlistctrl.cpp
widgets/checkedlistctrl.h
widgets/client-data.h
widgets/dpi-support.h
widgets/event-handler-provider.h
widgets/group-check-box.cpp
widgets/group-check-box.h
widgets/keep-on-top-styler.cpp
widgets/keep-on-top-styler.h
widgets/option-validator.cpp
widgets/option-validator.h
widgets/render-plugin.cpp
widgets/render-plugin.h
widgets/user-input-ctrl.cpp
widgets/user-input-ctrl.h
widgets/user-input-event.cpp
widgets/user-input-event.h
widgets/sdl-poller.cpp
widgets/sdl-poller.h
widgets/shortcut-menu-bar.cpp
widgets/shortcut-menu-bar.h
widgets/utils.cpp
widgets/utils.h
widgets/webupdatedef.h
widgets/wxmisc.h
widgets/wxmisc.cpp
wxhead.h
wxlogdebug.h
wxvbam.cpp
@@ -253,6 +228,9 @@ function(configure_wx_target target)
endif()
endfunction()
# Core emulator.
_add_link_libraries(vbam-core)
# Nonstd.
_add_link_libraries(nonstd-lib)
_add_include_directories(${NONSTD_INCLUDE_DIR})
@@ -311,6 +289,7 @@ endfunction()
# Sub-projects.
add_subdirectory(test)
add_subdirectory(config)
add_subdirectory(widgets)
set(VBAM_ICON visualboyadvance-m.icns)
set(VBAM_ICON_PATH ${CMAKE_CURRENT_SOURCE_DIR}/icons/${VBAM_ICON})
@@ -326,13 +305,13 @@ target_include_directories(visualboyadvance-m PRIVATE ${SDL2_INCLUDE_DIRS})
target_link_libraries(
visualboyadvance-m
vbam-core
vbam-components-draw-text
vbam-components-filters
vbam-components-filters-agb
vbam-components-filters-interframe
vbam-components-user-config
vbam-wx-config
vbam-wx-widgets
)
# adjust link command when making a static binary for gcc
@@ -410,11 +389,6 @@ endif()
if(APPLE)
target_sources(visualboyadvance-m PRIVATE
macsupport.mm
widgets/dpi-support-mac.mm
)
else()
target_sources(visualboyadvance-m PRIVATE
widgets/dpi-support.cpp
)
endif()
@@ -681,9 +655,9 @@ if(APPLE)
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks")
endif()
if(WIN32 AND (X86_64 OR X86_32) AND ENABLE_ONLINEUPDATES)
if(WIN32 AND (X86_64 OR ARM64 OR X86_32) AND ENABLE_ONLINEUPDATES)
if(NOT DEFINED WINSPARKLE_BIN_RELEASE_DIR)
set(WINSPARKLE_BIN_RELEASE_DIR ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.6.0)
set(WINSPARKLE_BIN_RELEASE_DIR ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.8.1)
endif()
target_include_directories(
@@ -692,9 +666,11 @@ if(WIN32 AND (X86_64 OR X86_32) AND ENABLE_ONLINEUPDATES)
)
if(X86_64)
set(WINSPARKLE_DLL ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.6.0/x64/Release/WinSparkle.dll)
else()
set(WINSPARKLE_DLL ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.6.0/Release/WinSparkle.dll)
set(WINSPARKLE_DLL ${WINSPARKLE_BIN_RELEASE_DIR}/x64/Release/WinSparkle.dll)
elseif(ARM64)
set(WINSPARKLE_DLL ${WINSPARKLE_BIN_RELEASE_DIR}/ARM64/Release/WinSparkle.dll)
elseif(X86_32)
set(WINSPARKLE_DLL ${WINSPARKLE_BIN_RELEASE_DIR}/Release/WinSparkle.dll)
endif()
configure_file(autoupdater/wxmsw/winsparkle-path.h.in ${CMAKE_BINARY_DIR}/winsparkle-path.h)
@@ -1034,13 +1010,13 @@ install(
# Installation scripts.
install(
PROGRAMS ${PROJECT_BINARY_DIR}/visualboyadvance-m${CMAKE_EXECUTABLE_SUFFIX}
DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
FILES ${CMAKE_SOURCE_DIR}/src/vba-over.ini
DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/vbam
DESTINATION ${CMAKE_INSTALL_DATADIR}/vbam
)
if (UNIX)
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/visualboyadvance-m.6 DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man6)
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/visualboyadvance-m.6 DESTINATION ${CMAKE_INSTALL_MANDIR}/man6)
endif()

View File

@@ -1,12 +1,12 @@
#include "wx/audio/audio.h"
#include "core/base/check.h"
#include "wx/audio/internal/openal.h"
#if defined(__WXMSW__)
#include "wx/audio/internal/dsound.h"
#endif
#if defined(VBAM_ENABLE_FAUDIO)
#include "wx/audio/internal/faudio.h"
#endif
@@ -39,8 +39,7 @@ std::vector<AudioDevice> EnumerateAudioDevices(const config::AudioApi& audio_api
case config::AudioApi::kLast:
default:
// This should never happen.
assert(false);
VBAM_NOTREACHED();
return {};
}
}
@@ -67,8 +66,7 @@ std::unique_ptr<SoundDriver> CreateSoundDriver(const config::AudioApi& api) {
case config::AudioApi::kLast:
default:
// This should never happen.
assert(false);
VBAM_NOTREACHED();
return nullptr;
}
}

View File

@@ -5,8 +5,6 @@
#include "wx/audio/internal/faudio.h"
#include <cassert>
#include <condition_variable>
#include <mutex>
#include <vector>
@@ -18,9 +16,11 @@
#include <wx/log.h>
#include <wx/translation.h>
#include "core/base/check.h"
#include "core/base/system.h"
#include "core/gba/gbaGlobals.h"
#include "wx/config/option-proxy.h"
#include "wx/config/strutils.h"
namespace audio {
namespace internal {
@@ -28,7 +28,7 @@ namespace internal {
namespace {
int FAGetDev(FAudio* fa) {
const wxString& audio_device = OPTION(kSoundAudioDevice);
const wxString audio_device = OPTION(kSoundAudioDevice).Get();
if (audio_device.empty()) {
// Just use the default device.
return 0;
@@ -48,7 +48,7 @@ int FAGetDev(FAudio* fa) {
if (hr != 0) {
continue;
}
const wxString device_id(reinterpret_cast<wchar_t*>(dd.DeviceID));
const wxString device_id = config::utf16_to_utf8(dd.DeviceID);
if (audio_device == device_id) {
return i;
}
@@ -120,8 +120,6 @@ public:
FAudio_Output();
~FAudio_Output();
void device_change();
private:
void close();
@@ -176,7 +174,7 @@ void FAudio_Output::close() {
if (sVoice) {
if (playing) {
assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0);
VBAM_CHECK(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0);
}
FAudioVoice_DestroyVoice(sVoice);
@@ -194,10 +192,6 @@ void FAudio_Output::close() {
}
}
void FAudio_Output::device_change() {
device_changed = true;
}
bool FAudio_Output::init(long sampleRate) {
if (failed || initialized)
return false;
@@ -259,7 +253,7 @@ bool FAudio_Output::init(long sampleRate) {
if (OPTION(kSoundUpmix)) {
// set up stereo upmixing
FAudioDeviceDetails dd{};
assert(FAudio_GetDeviceDetails(faud, 0, &dd) == 0);
VBAM_CHECK(FAudio_GetDeviceDetails(faud, 0, &dd) == 0);
std::vector<float> matrix(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels);
bool matrixAvailable = true;
@@ -353,12 +347,12 @@ bool FAudio_Output::init(long sampleRate) {
if (matrixAvailable) {
hr = FAudioVoice_SetOutputMatrix(sVoice, nullptr, 2, dd.OutputFormat.Format.nChannels,
matrix.data(), FAUDIO_DEFAULT_CHANNELS);
assert(hr == 0);
VBAM_CHECK(hr == 0);
}
}
hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
VBAM_CHECK(hr == 0);
playing = true;
currentBuffer = 0;
device_changed = false;
@@ -380,7 +374,7 @@ void FAudio_Output::write(uint16_t* finalWave, int) {
}
FAudioSourceVoice_GetState(sVoice, &vState, flags);
assert(vState.BuffersQueued <= buffer_count_);
VBAM_CHECK(vState.BuffersQueued <= buffer_count_);
if (vState.BuffersQueued < buffer_count_) {
if (vState.BuffersQueued == 0) {
@@ -414,7 +408,7 @@ void FAudio_Output::write(uint16_t* finalWave, int) {
currentBuffer++;
currentBuffer %= (buffer_count_ + 1); // + 1 because we need one temporary buffer
[[maybe_unused]] uint32_t hr = FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, nullptr);
assert(hr == 0);
VBAM_CHECK(hr == 0);
}
void FAudio_Output::pause() {
@@ -423,7 +417,7 @@ void FAudio_Output::pause() {
if (playing) {
[[maybe_unused]] uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
VBAM_CHECK(hr == 0);
playing = false;
}
}
@@ -434,7 +428,7 @@ void FAudio_Output::resume() {
if (!playing) {
[[maybe_unused]] int32_t hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
VBAM_CHECK(hr == 0);
playing = true;
}
}
@@ -445,7 +439,7 @@ void FAudio_Output::reset() {
if (playing) {
[[maybe_unused]] uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
assert(hr == 0);
VBAM_CHECK(hr == 0);
}
FAudioSourceVoice_FlushSourceBuffers(sVoice);
@@ -462,7 +456,7 @@ void FAudio_Output::setThrottle(unsigned short throttle_) {
[[maybe_unused]] uint32_t hr =
FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_COMMIT_NOW);
assert(hr == 0);
VBAM_CHECK(hr == 0);
}
} // namespace
@@ -489,8 +483,11 @@ std::vector<AudioDevice> GetFAudioDevices() {
}
std::vector<AudioDevice> devices;
#if defined(__WXMSW__)
// Add a separate default device on Windows.
devices.reserve(dev_count + 1);
devices.push_back({_("Default device"), wxEmptyString});
#endif
for (uint32_t i = 0; i < dev_count; i++) {
FAudioDeviceDetails dd;
@@ -499,9 +496,9 @@ std::vector<AudioDevice> GetFAudioDevices() {
continue;
}
const wxString display_name(reinterpret_cast<wchar_t*>(dd.DisplayName));
const wxString device_id(reinterpret_cast<wchar_t*>(dd.DeviceID));
// Convert to UTF-8.
const wxString display_name = config::utf16_to_utf8(dd.DisplayName);
const wxString device_id = config::utf16_to_utf8(dd.DeviceID);
devices.push_back({display_name, device_id});
}

View File

@@ -30,13 +30,12 @@ typedef ALCboolean(ALC_APIENTRY* LPALCISEXTENSIONPRESENT)(ALCdevice* device,
typedef const ALCchar*(ALC_APIENTRY* LPALCGETSTRING)(ALCdevice* device, ALCenum param);
#endif
#include <cassert>
#include <wx/arrstr.h>
#include <wx/log.h>
#include <wx/translation.h>
#include <wx/utils.h>
#include "core/base/check.h"
#include "core/gba/gbaGlobals.h"
#include "core/gba/gbaSound.h"
#include "wx/config/option-proxy.h"
@@ -47,7 +46,7 @@ namespace internal {
namespace {
// Debug
#define ASSERT_SUCCESS assert(AL_NO_ERROR == alGetError())
#define ASSERT_SUCCESS VBAM_CHECK(AL_NO_ERROR == alGetError())
#ifndef LOGALL
// replace logging functions with comments
@@ -171,7 +170,7 @@ void OpenAL::debugState() {
bool OpenAL::init(long sampleRate) {
winlog("OpenAL::init\n");
assert(initialized == false);
VBAM_CHECK(initialized == false);
const wxString& audio_device = OPTION(kSoundAudioDevice);
if (!audio_device.empty()) {
@@ -191,9 +190,9 @@ bool OpenAL::init(long sampleRate) {
}
context = alcCreateContext(device, nullptr);
assert(context != nullptr);
VBAM_CHECK(context != nullptr);
ALCboolean retVal = alcMakeContextCurrent(context);
assert(ALC_TRUE == retVal);
VBAM_CHECK(ALC_TRUE == retVal);
alGenBuffers(OPTION(kSoundBuffers), buffer);
ASSERT_SUCCESS;
alGenSources(1, &source);
@@ -338,7 +337,7 @@ void OpenAL::write(uint16_t* finalWave, int length) {
return;
}
assert(nBuffersProcessed > 0);
VBAM_CHECK(nBuffersProcessed > 0);
// unqueue buffer
tempBuffer = 0;

View File

@@ -11,6 +11,7 @@
#include <wx/msgdlg.h>
#include "components/filters_interframe/interframe.h"
#include "core/base/check.h"
#include "core/base/version.h"
#include "core/gb/gb.h"
#include "core/gb/gbCheats.h"
@@ -32,7 +33,7 @@
void MainFrame::GetMenuOptionBool(const wxString& menuName, bool* field)
{
assert(field);
VBAM_CHECK(field);
*field = !*field;
int id = wxXmlResource::GetXRCID(menuName);
@@ -48,7 +49,7 @@ void MainFrame::GetMenuOptionBool(const wxString& menuName, bool* field)
void MainFrame::GetMenuOptionConfig(const wxString& menu_name,
const config::OptionID& option_id) {
config::Option* option = config::Option::ByID(option_id);
assert(option);
VBAM_CHECK(option);
int id = wxXmlResource::GetXRCID(menu_name);
for (size_t i = 0; i < checkable_mi.size(); i++) {
@@ -64,7 +65,7 @@ void MainFrame::GetMenuOptionConfig(const wxString& menu_name,
option->SetInt(is_checked);
break;
default:
assert(false);
VBAM_CHECK(false);
return;
}
break;
@@ -73,7 +74,7 @@ void MainFrame::GetMenuOptionConfig(const wxString& menu_name,
void MainFrame::GetMenuOptionInt(const wxString& menuName, int* field, int mask)
{
assert(field);
VBAM_CHECK(field);
int value = mask;
bool is_checked = ((*field) & (mask)) != (value);
int id = wxXmlResource::GetXRCID(menuName);
@@ -125,7 +126,7 @@ static void toggleBitVar(bool *menuValue, int *globalVar, int mask)
EVT_HANDLER(wxID_OPEN, "Open ROM...")
{
static int open_ft = 0;
const wxString& gba_rom_dir = OPTION(kGBAROMDir);
const wxString gba_rom_dir = OPTION(kGBAROMDir);
// FIXME: ignore if non-existent or not a dir
wxString pats = _(
@@ -157,7 +158,7 @@ EVT_HANDLER(wxID_OPEN, "Open ROM...")
EVT_HANDLER(OpenGB, "Open GB...")
{
static int open_ft = 0;
const wxString& gb_rom_dir = OPTION(kGBROMDir);
const wxString gb_rom_dir = OPTION(kGBROMDir);
// FIXME: ignore if non-existent or not a dir
wxString pats = _(
@@ -184,7 +185,7 @@ EVT_HANDLER(OpenGB, "Open GB...")
EVT_HANDLER(OpenGBC, "Open GBC...")
{
static int open_ft = 0;
const wxString& gbc_rom_dir = OPTION(kGBGBCROMDir);
const wxString gbc_rom_dir = OPTION(kGBGBCROMDir);
// FIXME: ignore if non-existent or not a dir
wxString pats = _(

View File

@@ -1,5 +1,5 @@
# This defines the vbam-wx-config target and the
# `VBAM_LOCALIZABLE_WX_CONFIG_FILES` variable, containing the list of
# `VBAM_LOCALIZABLE_WX_CONFIG_FILES` variable, containing the list of
# localizable files in the vbam-wx-config target.
# I don't like duplicating/triplicating code, so I only declare
@@ -61,15 +61,9 @@ target_sources(vbam-wx-config
user-input.h
)
target_link_libraries(vbam-wx-config
PUBLIC
nonstd-lib
vbam-core
)
configure_wx_target(vbam-wx-config)
if (BUILD_TESTING)
if(BUILD_TESTING)
add_executable(vbam-wx-config-tests
bindings-test.cpp
command-test.cpp
@@ -79,10 +73,12 @@ if (BUILD_TESTING)
user-input-test.cpp
)
target_link_libraries(vbam-wx-config-tests
vbam-core
# Test deps.
vbam-core-fake
vbam-wx-config
vbam-wx-fake-opts
# Target deps.
vbam-wx-config
GTest::gtest_main
)

View File

@@ -176,7 +176,7 @@ void Bindings::AssignInputsToCommand(const std::unordered_set<UserInput>& inputs
}
void Bindings::UnassignInput(const UserInput& input) {
assert(input);
VBAM_CHECK(input);
auto iter = input_to_control_.find(input);
if (iter == input_to_control_.end()) {
@@ -194,7 +194,7 @@ void Bindings::UnassignInput(const UserInput& input) {
// Otherwise, just remove it from the 2 maps.
auto command_iter = control_to_inputs_.find(iter->second);
assert(command_iter != control_to_inputs_.end());
VBAM_CHECK(command_iter != control_to_inputs_.end());
command_iter->second.erase(input);
if (command_iter->second.empty()) {
@@ -238,7 +238,7 @@ void Bindings::UnassignDefaultBinding(const UserInput& input) {
}
auto command_iter = control_to_inputs_.find(input_iter->second);
assert(command_iter != control_to_inputs_.end());
VBAM_CHECK(command_iter != control_to_inputs_.end());
command_iter->second.erase(input);
if (command_iter->second.empty()) {

View File

@@ -4,6 +4,8 @@
#include <wx/wxcrt.h>
#include "core/base/check.h"
// Initializer for struct cmditem
cmditem new_cmditem(const wxString cmd,
const wxString name,
@@ -22,7 +24,7 @@ namespace config {
}
// Command not found. This should never happen.
assert(false);
VBAM_NOTREACHED();
return wxEmptyString;
}
@@ -34,7 +36,7 @@ namespace config {
}
// Command not found. This should never happen.
assert(false);
VBAM_NOTREACHED();
return wxEmptyString;
}

View File

@@ -2,7 +2,6 @@
#define VBAM_WX_CONFIG_COMMAND_H_
#include <array>
#include <cassert>
#include <functional>
#include <optional.hpp>
@@ -10,6 +9,8 @@
#include <wx/string.h>
#include "core/base/check.h"
namespace config {
// clang-format off
@@ -56,7 +57,7 @@ static constexpr size_t kNbJoypads = 4;
// Represents an emulated joypad. The internal index is zero-based.
class GameJoy {
public:
constexpr explicit GameJoy(size_t index) : index_(index) { assert(index < kNbJoypads); }
constexpr explicit GameJoy(size_t index) : index_(index) { VBAM_CHECK(index < kNbJoypads); }
// The underlying zero-based index for this emulated joypad.
constexpr size_t index() const { return index_; }
@@ -179,12 +180,12 @@ public:
bool is_shortcut() const { return tag() == Tag::kShortcut; }
const GameCommand& game() const {
assert(is_game());
VBAM_CHECK(is_game());
return nonstd::get<GameCommand>(control_);
}
const ShortcutCommand& shortcut() const {
assert(is_shortcut());
VBAM_CHECK(is_shortcut());
return nonstd::get<ShortcutCommand>(control_);
}
@@ -201,8 +202,7 @@ public:
return shortcut() < other.shortcut();
}
// Unreachable.
assert(false);
VBAM_NOTREACHED();
return false;
} else {
return tag_ < other.tag_;
@@ -260,8 +260,7 @@ struct std::hash<config::Command> {
return std::hash<config::ShortcutCommand>{}(control.shortcut());
}
// Unreachable.
assert(false);
VBAM_NOTREACHED();
return 0;
}
};

View File

@@ -135,7 +135,8 @@ TEST_F(GamepadTest, NonDefaultInput) {
const config::KeyboardInput f1(WXK_F1);
// Assign F1 to "Up".
bindings()->AssignInputToCommand(f1, config::GameCommand(config::GameJoy(0), config::GameKey::Up));
bindings()->AssignInputToCommand(f1,
config::GameCommand(config::GameJoy(0), config::GameKey::Up));
// Press F1, the up key should be pressed.
EXPECT_TRUE(gamepad()->OnInputPressed(f1));

View File

@@ -1,5 +1,7 @@
#include "wx/config/emulated-gamepad.h"
#include "core/base/check.h"
namespace config {
namespace {
@@ -62,7 +64,7 @@ EmulatedGamepad::EmulatedGamepad(const BindingsProvider bindings_provider)
: joypads_({0, 0, 0, 0}), bindings_provider_(bindings_provider) {}
bool EmulatedGamepad::OnInputPressed(const config::UserInput& user_input) {
assert(user_input);
VBAM_CHECK(user_input);
const auto command = bindings_provider_()->CommandForInput(user_input);
if (!command || !command->is_game()) {
@@ -85,7 +87,7 @@ bool EmulatedGamepad::OnInputPressed(const config::UserInput& user_input) {
}
bool EmulatedGamepad::OnInputReleased(const config::UserInput& user_input) {
assert(user_input);
VBAM_CHECK(user_input);
const auto command = bindings_provider_()->CommandForInput(user_input);
if (!command || !command->is_game()) {

View File

@@ -11,6 +11,7 @@
#include <wx/log.h>
#include <wx/translation.h>
#include "core/base/check.h"
#include "core/base/system.h"
#include "core/gb/gbGlobals.h"
#include "core/gba/gbaSound.h"
@@ -327,9 +328,10 @@ std::array<Option, kNbOptions>& Option::All() {
Option(OptionID::kPrefSkipSaveGameCheats, &coreOptions.skipSaveGameCheats, 0, 1),
Option(OptionID::kPrefSkipSaveGameBattery, &coreOptions.skipSaveGameBattery, 0, 1),
Option(OptionID::kPrefThrottle, &coreOptions.throttle, 0, 450),
Option(OptionID::kPrefSpeedupThrottle, &coreOptions.speedup_throttle, 0, 3000),
Option(OptionID::kPrefSpeedupFrameSkip, &coreOptions.speedup_frame_skip, 0, 300),
Option(OptionID::kPrefSpeedupThrottle, &coreOptions.speedup_throttle, 0, 450),
Option(OptionID::kPrefSpeedupFrameSkip, &coreOptions.speedup_frame_skip, 0, 40),
Option(OptionID::kPrefSpeedupThrottleFrameSkip, &coreOptions.speedup_throttle_frame_skip),
Option(OptionID::kPrefSpeedupMute, &coreOptions.speedup_mute),
Option(OptionID::kPrefUseBiosGB, &g_owned_opts.use_bios_file_gb),
Option(OptionID::kPrefUseBiosGBA, &g_owned_opts.use_bios_file_gba),
Option(OptionID::kPrefUseBiosGBC, &g_owned_opts.use_bios_file_gbc),
@@ -530,6 +532,8 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
"throttle)")},
OptionData{"preferences/speedupThrottleFrameSkip", "",
_("Use frame skip for speedup throttle")},
OptionData{"preferences/speedupMute", "",
_("Mute sound during speedup")},
OptionData{"preferences/useBiosGB", "BootRomGB", _("Use the specified BIOS file for Game Boy")},
OptionData{"preferences/useBiosGBA", "BootRomEn", _("Use the specified BIOS file")},
OptionData{"preferences/useBiosGBC", "BootRomGBC",
@@ -580,14 +584,14 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
};
nonstd::optional<OptionID> StringToOptionId(const wxString& input) {
static std::map<wxString, OptionID> kStringToOptionId;
if (kStringToOptionId.empty()) {
static const std::map<wxString, OptionID> kStringToOptionId([] {
std::map<wxString, OptionID> string_to_option_id;
for (size_t i = 0; i < kNbOptions; i++) {
kStringToOptionId.emplace(kAllOptionsData[i].config_name,
static_cast<OptionID>(i));
string_to_option_id.emplace(kAllOptionsData[i].config_name, static_cast<OptionID>(i));
}
assert(kStringToOptionId.size() == kNbOptions);
}
VBAM_CHECK(string_to_option_id.size() == kNbOptions);
return string_to_option_id;
}());
const auto iter = kStringToOptionId.find(input);
if (iter == kStringToOptionId.end()) {
@@ -598,42 +602,43 @@ nonstd::optional<OptionID> StringToOptionId(const wxString& input) {
wxString FilterToString(const Filter& value) {
const size_t size_value = static_cast<size_t>(value);
assert(size_value < kNbFilters);
VBAM_CHECK(size_value < kNbFilters);
return kFilterStrings[size_value];
}
wxString InterframeToString(const Interframe& value) {
const size_t size_value = static_cast<size_t>(value);
assert(size_value < kNbInterframes);
VBAM_CHECK(size_value < kNbInterframes);
return kInterframeStrings[size_value];
}
wxString RenderMethodToString(const RenderMethod& value) {
const size_t size_value = static_cast<size_t>(value);
assert(size_value < kNbRenderMethods);
VBAM_CHECK(size_value < kNbRenderMethods);
return kRenderMethodStrings[size_value];
}
wxString AudioApiToString(const AudioApi& value) {
const size_t size_value = static_cast<size_t>(value);
assert(size_value < kNbAudioApis);
VBAM_CHECK(size_value < kNbAudioApis);
return kAudioApiStrings[size_value];
}
wxString AudioRateToString(const AudioRate& value) {
const size_t size_value = static_cast<size_t>(value);
assert(size_value < kNbSoundRate);
VBAM_CHECK(size_value < kNbSoundRate);
return kAudioRateStrings[size_value];
}
Filter StringToFilter(const wxString& config_name, const wxString& input) {
static std::map<wxString, Filter> kStringToFilter;
if (kStringToFilter.empty()) {
static const std::map<wxString, Filter> kStringToFilter([] {
std::map<wxString, Filter> string_to_filter;
for (size_t i = 0; i < kNbFilters; i++) {
kStringToFilter.emplace(kFilterStrings[i], static_cast<Filter>(i));
string_to_filter.emplace(kFilterStrings[i], static_cast<Filter>(i));
}
assert(kStringToFilter.size() == kNbFilters);
}
VBAM_CHECK(string_to_filter.size() == kNbFilters);
return string_to_filter;
}());
const auto iter = kStringToFilter.find(input);
if (iter == kStringToFilter.end()) {
@@ -646,14 +651,14 @@ Filter StringToFilter(const wxString& config_name, const wxString& input) {
}
Interframe StringToInterframe(const wxString& config_name, const wxString& input) {
static std::map<wxString, Interframe> kStringToInterframe;
if (kStringToInterframe.empty()) {
static const std::map<wxString, Interframe> kStringToInterframe([] {
std::map<wxString, Interframe> string_to_interframe;
for (size_t i = 0; i < kNbInterframes; i++) {
kStringToInterframe.emplace(kInterframeStrings[i],
static_cast<Interframe>(i));
string_to_interframe.emplace(kInterframeStrings[i], static_cast<Interframe>(i));
}
assert(kStringToInterframe.size() == kNbInterframes);
}
VBAM_CHECK(string_to_interframe.size() == kNbInterframes);
return string_to_interframe;
}());
const auto iter = kStringToInterframe.find(input);
if (iter == kStringToInterframe.end()) {
@@ -667,14 +672,14 @@ Interframe StringToInterframe(const wxString& config_name, const wxString& input
RenderMethod StringToRenderMethod(const wxString& config_name,
const wxString& input) {
static std::map<wxString, RenderMethod> kStringToRenderMethod;
if (kStringToRenderMethod.empty()) {
static const std::map<wxString, RenderMethod> kStringToRenderMethod([] {
std::map<wxString, RenderMethod> string_to_render_method;
for (size_t i = 0; i < kNbRenderMethods; i++) {
kStringToRenderMethod.emplace(kRenderMethodStrings[i],
static_cast<RenderMethod>(i));
string_to_render_method.emplace(kRenderMethodStrings[i], static_cast<RenderMethod>(i));
}
assert(kStringToRenderMethod.size() == kNbRenderMethods);
}
VBAM_CHECK(string_to_render_method.size() == kNbRenderMethods);
return string_to_render_method;
}());
const auto iter = kStringToRenderMethod.find(input);
if (iter == kStringToRenderMethod.end()) {
@@ -687,14 +692,14 @@ RenderMethod StringToRenderMethod(const wxString& config_name,
}
AudioApi StringToAudioApi(const wxString& config_name, const wxString& input) {
static std::map<wxString, AudioApi> kStringToAudioApi;
if (kStringToAudioApi.empty()) {
static const std::map<wxString, AudioApi> kStringToAudioApi([] {
std::map<wxString, AudioApi> string_to_audio_api;
for (size_t i = 0; i < kNbAudioApis; i++) {
kStringToAudioApi.emplace(kAudioApiStrings[i],
static_cast<AudioApi>(i));
string_to_audio_api.emplace(kAudioApiStrings[i], static_cast<AudioApi>(i));
}
assert(kStringToAudioApi.size() == kNbAudioApis);
}
VBAM_CHECK(string_to_audio_api.size() == kNbAudioApis);
return string_to_audio_api;
}());
const auto iter = kStringToAudioApi.find(input);
if (iter == kStringToAudioApi.end()) {
@@ -707,13 +712,14 @@ AudioApi StringToAudioApi(const wxString& config_name, const wxString& input) {
}
AudioRate StringToSoundQuality(const wxString& config_name, const wxString& input) {
static std::map<wxString, AudioRate> kStringToSoundQuality;
if (kStringToSoundQuality.empty()) {
static const std::map<wxString, AudioRate> kStringToSoundQuality([] {
std::map<wxString, AudioRate> string_to_sound_quality;
for (size_t i = 0; i < kNbSoundRate; i++) {
kStringToSoundQuality.emplace(kAudioRateStrings[i], static_cast<AudioRate>(i));
string_to_sound_quality.emplace(kAudioRateStrings[i], static_cast<AudioRate>(i));
}
assert(kStringToSoundQuality.size() == kNbSoundRate);
}
VBAM_CHECK(string_to_sound_quality.size() == kNbSoundRate);
return string_to_sound_quality;
}());
const auto iter = kStringToSoundQuality.find(input);
if (iter == kStringToSoundQuality.end()) {
@@ -727,28 +733,23 @@ AudioRate StringToSoundQuality(const wxString& config_name, const wxString& inpu
wxString AllEnumValuesForType(Option::Type type) {
switch (type) {
case Option::Type::kFilter: {
static const wxString kAllFilterValues =
AllEnumValuesForArray(kFilterStrings);
static const wxString kAllFilterValues(AllEnumValuesForArray(kFilterStrings));
return kAllFilterValues;
}
case Option::Type::kInterframe: {
static const wxString kAllInterframeValues =
AllEnumValuesForArray(kInterframeStrings);
static const wxString kAllInterframeValues(AllEnumValuesForArray(kInterframeStrings));
return kAllInterframeValues;
}
case Option::Type::kRenderMethod: {
static const wxString kAllRenderValues =
AllEnumValuesForArray(kRenderMethodStrings);
static const wxString kAllRenderValues(AllEnumValuesForArray(kRenderMethodStrings));
return kAllRenderValues;
}
case Option::Type::kAudioApi: {
static const wxString kAllAudioApiValues =
AllEnumValuesForArray(kAudioApiStrings);
static const wxString kAllAudioApiValues(AllEnumValuesForArray(kAudioApiStrings));
return kAllAudioApiValues;
}
case Option::Type::kAudioRate: {
static const wxString kAllSoundQualityValues =
AllEnumValuesForArray(kAudioRateStrings);
static const wxString kAllSoundQualityValues(AllEnumValuesForArray(kAudioRateStrings));
return kAllSoundQualityValues;
}
@@ -761,10 +762,10 @@ wxString AllEnumValuesForType(Option::Type type) {
case Option::Type::kUnsigned:
case Option::Type::kString:
case Option::Type::kGbPalette:
assert(false);
VBAM_NOTREACHED();
return wxEmptyString;
}
assert(false);
VBAM_NOTREACHED();
return wxEmptyString;
}
@@ -790,10 +791,10 @@ size_t MaxForType(Option::Type type) {
case Option::Type::kUnsigned:
case Option::Type::kString:
case Option::Type::kGbPalette:
assert(false);
VBAM_NOTREACHED();
return 0;
}
assert(false);
VBAM_NOTREACHED();
return 0;
}

View File

@@ -99,6 +99,7 @@ enum class OptionID {
kPrefSpeedupThrottle,
kPrefSpeedupFrameSkip,
kPrefSpeedupThrottleFrameSkip,
kPrefSpeedupMute,
kPrefUseBiosGB,
kPrefUseBiosGBA,
kPrefUseBiosGBC,

View File

@@ -1,7 +1,10 @@
#include "wx/config/option-observer.h"
#include "core/base/check.h"
#include "wx/config/option.h"
#include "core/base/check.h"
namespace config {
// An Option::Observer that calls a callback when an option has changed.
@@ -10,7 +13,7 @@ public:
CallbackOptionObserver(const OptionID& option_id,
std::function<void(Option*)> callback)
: Option::Observer(option_id), callback_(std::move(callback)) {
assert(callback_);
VBAM_CHECK(callback_);
}
~CallbackOptionObserver() override = default;

View File

@@ -103,6 +103,7 @@ static constexpr std::array<Option::Type, kNbOptions> kOptionsTypes = {
/*kPrefSpeedupThrottle*/ Option::Type::kUnsigned,
/*kPrefSpeedupFrameSkip*/ Option::Type::kUnsigned,
/*kPrefSpeedupThrottleFrameSkip*/ Option::Type::kBool,
/*kPrefSpeedupMute*/ Option::Type::kBool,
/*kPrefUseBiosGB*/ Option::Type::kBool,
/*kPrefUseBiosGBA*/ Option::Type::kBool,
/*kPrefUseBiosGBC*/ Option::Type::kBool,
@@ -174,11 +175,43 @@ private:
Option* option_;
};
template <typename T>
class OptionProxyNumeric {
public:
virtual T Get() const = 0;
virtual bool Set(T value) = 0;
virtual T Min() const = 0;
virtual T Max() const = 0;
bool operator++() { return *this += 1; }
bool operator--() { return *this -= 1; }
bool operator++(int) { return *this += 1; }
bool operator--(int) { return *this -= 1; }
bool operator+=(T value) {
const T new_value = Get() + value;
if (new_value > Max()) {
return Set(Max());
} else {
return Set(new_value);
}
}
bool operator-=(T value) {
const T new_value = Get() - value;
if (new_value < Min()) {
return Set(Min());
} else {
return Set(new_value);
}
}
operator T() const { return Get(); }
};
template <OptionID ID>
class OptionProxy<
ID,
typename std::enable_if<kOptionsTypes[static_cast<size_t>(ID)] ==
Option::Type::kDouble>::type> {
Option::Type::kDouble>::type> : public OptionProxyNumeric<double> {
public:
OptionProxy() : option_(Option::ByID(ID)) {}
~OptionProxy() = default;
@@ -187,9 +220,7 @@ public:
bool Set(double value) { return option_->SetDouble(value); }
double Min() const { return option_->GetDoubleMin(); }
double Max() const { return option_->GetDoubleMax(); }
bool operator=(double value) { return Set(value); }
operator double() const { return Get(); }
private:
Option* option_;
@@ -199,7 +230,7 @@ template <OptionID ID>
class OptionProxy<
ID,
typename std::enable_if<kOptionsTypes[static_cast<size_t>(ID)] ==
Option::Type::kInt>::type> {
Option::Type::kInt>::type> : public OptionProxyNumeric<int32_t> {
public:
OptionProxy() : option_(Option::ByID(ID)) {}
~OptionProxy() = default;
@@ -208,25 +239,7 @@ public:
bool Set(int32_t value) { return option_->SetInt(value); }
int32_t Min() const { return option_->GetIntMin(); }
int32_t Max() const { return option_->GetIntMax(); }
bool operator=(int32_t value) { return Set(value); }
bool operator+=(int32_t value) {
const int new_value = Get() + value;
if (new_value > Max()) {
return Set(Max());
} else {
return Set(new_value);
}
}
bool operator-=(int32_t value) {
const int new_value = Get() - value;
if (new_value < Min()) {
return Set(Min());
} else {
return Set(new_value);
}
}
operator int32_t() const { return Get(); }
private:
Option* option_;
@@ -236,7 +249,7 @@ template <OptionID ID>
class OptionProxy<
ID,
typename std::enable_if<kOptionsTypes[static_cast<size_t>(ID)] ==
Option::Type::kUnsigned>::type> {
Option::Type::kUnsigned>::type> : public OptionProxyNumeric<uint32_t> {
public:
OptionProxy() : option_(Option::ByID(ID)) {}
~OptionProxy() = default;
@@ -245,9 +258,7 @@ public:
bool Set(uint32_t value) { return option_->SetUnsigned(value); }
uint32_t Min() const { return option_->GetUnsignedMin(); }
uint32_t Max() const { return option_->GetUnsignedMax(); }
bool operator=(int32_t value) { return Set(value); }
operator int32_t() const { return Get(); }
bool operator=(uint32_t value) { return Set(value); }
private:
Option* option_;

View File

@@ -21,18 +21,16 @@ TEST(OptionTest, Bool) {
EXPECT_TRUE(option->SetBool(false));
EXPECT_FALSE(option->GetBool());
#if !defined(NDEBUG)
EXPECT_DEATH(option->SetDouble(2.0), ".*");
EXPECT_DEATH(option->SetInt(2), ".*");
EXPECT_DEATH(option->SetUnsigned(2), ".*");
EXPECT_DEATH(option->SetString("foo"), ".*");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), ".*");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), ".*");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), ".*");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), ".*");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), ".*");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), ".*");
#endif // !defined(NDEBUG)
EXPECT_DEATH(option->SetDouble(2.0), "is_double\\(\\)");
EXPECT_DEATH(option->SetInt(2), "is_int\\(\\)");
EXPECT_DEATH(option->SetUnsigned(2), "is_unsigned\\(\\)");
EXPECT_DEATH(option->SetString("foo"), "is_string\\(\\)");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), "is_filter\\(\\)");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), "is_interframe\\(\\)");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), "is_render_method\\(\\)");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), "is_audio_api\\(\\)");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), "is_audio_rate\\(\\)");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), "is_gb_palette\\(\\)");
}
TEST(OptionTest, Double) {
@@ -56,18 +54,205 @@ TEST(OptionTest, Double) {
EXPECT_FALSE(option->SetDouble(7.0));
EXPECT_DOUBLE_EQ(option->GetDouble(), 2.5);
#if !defined(NDEBUG)
EXPECT_DEATH(option->SetBool(true), ".*");
EXPECT_DEATH(option->SetInt(2), ".*");
EXPECT_DEATH(option->SetUnsigned(2), ".*");
EXPECT_DEATH(option->SetString("foo"), ".*");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), ".*");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), ".*");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), ".*");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), ".*");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), ".*");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), ".*");
#endif // !defined(NDEBUG)
EXPECT_DEATH(option->SetBool(true), "is_bool\\(\\)");
EXPECT_DEATH(option->SetInt(2), "is_int\\(\\)");
EXPECT_DEATH(option->SetUnsigned(2), "is_unsigned\\(\\)");
EXPECT_DEATH(option->SetString("foo"), "is_string\\(\\)");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), "is_filter\\(\\)");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), "is_interframe\\(\\)");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), "is_render_method\\(\\)");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), "is_audio_api\\(\\)");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), "is_audio_rate\\(\\)");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), "is_gb_palette\\(\\)");
}
TEST(OptionTest, Int) {
config::Option* option = config::Option::ByID(config::OptionID::kSoundBuffers);
ASSERT_TRUE(option);
EXPECT_TRUE(option->command().empty());
EXPECT_EQ(option->config_name(), "Sound/Buffers");
EXPECT_EQ(option->id(), config::OptionID::kSoundBuffers);
EXPECT_EQ(option->type(), config::Option::Type::kInt);
EXPECT_TRUE(option->is_int());
EXPECT_TRUE(option->SetInt(8));
EXPECT_EQ(option->GetInt(), 8);
// Need to disable logging to test for errors.
const wxLogNull disable_logging;
// Test out of bounds values.
EXPECT_FALSE(option->SetInt(-1));
EXPECT_FALSE(option->SetInt(42));
EXPECT_EQ(option->GetInt(), 8);
EXPECT_DEATH(option->SetBool(true), "is_bool\\(\\)");
EXPECT_DEATH(option->SetDouble(2.0), "is_double\\(\\)");
EXPECT_DEATH(option->SetUnsigned(2), "is_unsigned\\(\\)");
EXPECT_DEATH(option->SetString("foo"), "is_string\\(\\)");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), "is_filter\\(\\)");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), "is_interframe\\(\\)");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), "is_render_method\\(\\)");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), "is_audio_api\\(\\)");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), "is_audio_rate\\(\\)");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), "is_gb_palette\\(\\)");
}
TEST(OptionTest, Unsigned) {
config::Option* option = config::Option::ByID(config::OptionID::kGeomWindowHeight);
ASSERT_TRUE(option);
EXPECT_EQ(option->config_name(), "geometry/windowHeight");
EXPECT_EQ(option->id(), config::OptionID::kGeomWindowHeight);
EXPECT_EQ(option->type(), config::Option::Type::kUnsigned);
EXPECT_TRUE(option->is_unsigned());
EXPECT_TRUE(option->SetUnsigned(100));
EXPECT_EQ(option->GetUnsigned(), 100);
// Need to disable logging to test for errors.
const wxLogNull disable_logging;
// Test out of bounds values.
EXPECT_FALSE(option->SetUnsigned(100000));
EXPECT_EQ(option->GetUnsigned(), 100);
EXPECT_DEATH(option->SetBool(true), "is_bool\\(\\)");
EXPECT_DEATH(option->SetDouble(2.0), "is_double\\(\\)");
EXPECT_DEATH(option->SetInt(2), "is_int\\(\\)");
EXPECT_DEATH(option->SetString("foo"), "is_string\\(\\)");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), "is_filter\\(\\)");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), "is_interframe\\(\\)");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), "is_render_method\\(\\)");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), "is_audio_api\\(\\)");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), "is_audio_rate\\(\\)");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), "is_gb_palette\\(\\)");
}
TEST(OptionTest, String) {
config::Option* option = config::Option::ByID(config::OptionID::kGenStateDir);
ASSERT_TRUE(option);
EXPECT_TRUE(option->command().empty());
EXPECT_EQ(option->config_name(), "General/StateDir");
EXPECT_EQ(option->id(), config::OptionID::kGenStateDir);
EXPECT_EQ(option->type(), config::Option::Type::kString);
EXPECT_TRUE(option->is_string());
EXPECT_TRUE(option->SetString("/path/to/sthg"));
EXPECT_EQ(option->GetString(), "/path/to/sthg");
EXPECT_DEATH(option->SetBool(true), "is_bool\\(\\)");
EXPECT_DEATH(option->SetDouble(2.0), "is_double\\(\\)");
EXPECT_DEATH(option->SetInt(2), "is_int\\(\\)");
EXPECT_DEATH(option->SetUnsigned(2), "is_unsigned\\(\\)");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), "is_filter\\(\\)");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), "is_interframe\\(\\)");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), "is_render_method\\(\\)");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), "is_audio_api\\(\\)");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), "is_audio_rate\\(\\)");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), "is_gb_palette\\(\\)");
}
TEST(OptionTest, Filter) {
config::Option* option = config::Option::ByID(config::OptionID::kDispFilter);
ASSERT_TRUE(option);
EXPECT_TRUE(option->command().empty());
EXPECT_EQ(option->config_name(), "Display/Filter");
EXPECT_EQ(option->id(), config::OptionID::kDispFilter);
EXPECT_EQ(option->type(), config::Option::Type::kFilter);
EXPECT_TRUE(option->is_filter());
EXPECT_TRUE(option->SetFilter(config::Filter::kNone));
EXPECT_EQ(option->GetFilter(), config::Filter::kNone);
EXPECT_DEATH(option->SetBool(true), "is_bool\\(\\)");
EXPECT_DEATH(option->SetDouble(2.0), "is_double\\(\\)");
EXPECT_DEATH(option->SetInt(2), "is_int\\(\\)");
EXPECT_DEATH(option->SetUnsigned(2), "is_unsigned\\(\\)");
EXPECT_DEATH(option->SetString("foo"), "is_string\\(\\)");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), "is_interframe\\(\\)");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), "is_render_method\\(\\)");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), "is_audio_api\\(\\)");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), "is_audio_rate\\(\\)");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), "is_gb_palette\\(\\)");
}
TEST(OptionTest, Interframe) {
config::Option* option = config::Option::ByID(config::OptionID::kDispIFB);
ASSERT_TRUE(option);
EXPECT_TRUE(option->command().empty());
EXPECT_EQ(option->config_name(), "Display/IFB");
EXPECT_EQ(option->id(), config::OptionID::kDispIFB);
EXPECT_EQ(option->type(), config::Option::Type::kInterframe);
EXPECT_TRUE(option->is_interframe());
EXPECT_TRUE(option->SetInterframe(config::Interframe::kNone));
EXPECT_EQ(option->GetInterframe(), config::Interframe::kNone);
EXPECT_DEATH(option->SetBool(true), "is_bool\\(\\)");
EXPECT_DEATH(option->SetDouble(2.0), "is_double\\(\\)");
EXPECT_DEATH(option->SetInt(2), "is_int\\(\\)");
EXPECT_DEATH(option->SetUnsigned(2), "is_unsigned\\(\\)");
EXPECT_DEATH(option->SetString("foo"), "is_string\\(\\)");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), "is_filter\\(\\)");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), "is_render_method\\(\\)");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), "is_audio_api\\(\\)");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), "is_audio_rate\\(\\)");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), "is_gb_palette\\(\\)");
}
TEST(OptionTest, AudioApi) {
config::Option* option = config::Option::ByID(config::OptionID::kSoundAudioAPI);
ASSERT_TRUE(option);
EXPECT_TRUE(option->command().empty());
EXPECT_EQ(option->config_name(), "Sound/AudioAPI");
EXPECT_EQ(option->id(), config::OptionID::kSoundAudioAPI);
EXPECT_EQ(option->type(), config::Option::Type::kAudioApi);
EXPECT_TRUE(option->is_audio_api());
EXPECT_TRUE(option->SetAudioApi(config::AudioApi::kOpenAL));
EXPECT_EQ(option->GetAudioApi(), config::AudioApi::kOpenAL);
EXPECT_DEATH(option->SetBool(true), "is_bool\\(\\)");
EXPECT_DEATH(option->SetDouble(2.0), "is_double\\(\\)");
EXPECT_DEATH(option->SetInt(2), "is_int\\(\\)");
EXPECT_DEATH(option->SetUnsigned(2), "is_unsigned\\(\\)");
EXPECT_DEATH(option->SetString("foo"), "is_string\\(\\)");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), "is_filter\\(\\)");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), "is_interframe\\(\\)");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), "is_render_method\\(\\)");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), "is_audio_rate\\(\\)");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), "is_gb_palette\\(\\)");
}
TEST(OptionTest, AudioRate) {
config::Option* option = config::Option::ByID(config::OptionID::kSoundAudioRate);
ASSERT_TRUE(option);
EXPECT_TRUE(option->command().empty());
EXPECT_EQ(option->config_name(), "Sound/Quality");
EXPECT_EQ(option->id(), config::OptionID::kSoundAudioRate);
EXPECT_EQ(option->type(), config::Option::Type::kAudioRate);
EXPECT_TRUE(option->is_audio_rate());
EXPECT_TRUE(option->SetAudioRate(config::AudioRate::k11kHz));
EXPECT_EQ(option->GetAudioRate(), config::AudioRate::k11kHz);
EXPECT_DEATH(option->SetBool(true), "is_bool\\(\\)");
EXPECT_DEATH(option->SetDouble(2.0), "is_double\\(\\)");
EXPECT_DEATH(option->SetInt(2), "is_int\\(\\)");
EXPECT_DEATH(option->SetUnsigned(2), "is_unsigned\\(\\)");
EXPECT_DEATH(option->SetString("foo"), "is_string\\(\\)");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), "is_filter\\(\\)");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), "is_interframe\\(\\)");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), "is_render_method\\(\\)");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), "is_audio_api\\(\\)");
EXPECT_DEATH(option->SetGbPalette({0, 1, 2, 3, 4, 5, 6, 7}), "is_gb_palette\\(\\)");
}
TEST(OptionTest, Enum) {
@@ -110,6 +295,16 @@ TEST(Optiontest, GbPalette) {
EXPECT_FALSE(option->SetGbPaletteString(""));
EXPECT_FALSE(option->SetGbPaletteString("0000,0001,0002,0003,0004,0005,0006,000Q"));
EXPECT_DEATH(option->SetBool(true), "is_bool\\(\\)");
EXPECT_DEATH(option->SetDouble(2.0), "is_double\\(\\)");
EXPECT_DEATH(option->SetInt(2), "is_int\\(\\)");
EXPECT_DEATH(option->SetUnsigned(2), "is_unsigned\\(\\)");
EXPECT_DEATH(option->SetString("foo"), "is_string\\(\\)");
EXPECT_DEATH(option->SetFilter(config::Filter::kNone), "is_filter\\(\\)");
EXPECT_DEATH(option->SetInterframe(config::Interframe::kNone), "is_interframe\\(\\)");
EXPECT_DEATH(option->SetRenderMethod(config::RenderMethod::kSimple), "is_render_method\\(\\)");
EXPECT_DEATH(option->SetAudioApi(config::AudioApi::kOpenAL), "is_audio_api\\(\\)");
EXPECT_DEATH(option->SetAudioRate(config::AudioRate::k11kHz), "is_audio_rate\\(\\)");
}
TEST(OptionObserverTest, Basic) {
@@ -158,3 +353,109 @@ TEST(OptionProxyTest, MatchingTypes) {
EXPECT_EQ(option->type(), proxy_type);
}
}
TEST(OptionProxyTest, NumericOperators) {
wxLogNull disable_logging;
int32_t int_opt = OPTION(kDispMaxThreads);
int_opt++;
OPTION(kDispMaxThreads)++;
EXPECT_EQ(int_opt, OPTION(kDispMaxThreads));
int_opt--;
OPTION(kDispMaxThreads)--;
EXPECT_EQ(int_opt, OPTION(kDispMaxThreads));
++int_opt;
OPTION(kDispMaxThreads)++;
EXPECT_EQ(int_opt, OPTION(kDispMaxThreads));
--int_opt;
OPTION(kDispMaxThreads)--;
EXPECT_EQ(int_opt, OPTION(kDispMaxThreads));
int_opt += 2;
OPTION(kDispMaxThreads) += 2;
EXPECT_EQ(int_opt, OPTION(kDispMaxThreads));
int_opt -= 2;
OPTION(kDispMaxThreads) -= 2;
EXPECT_EQ(int_opt, OPTION(kDispMaxThreads));
OPTION(kDispMaxThreads) = OPTION(kDispMaxThreads).Max();
OPTION(kDispMaxThreads)++;
EXPECT_EQ(OPTION(kDispMaxThreads), OPTION(kDispMaxThreads).Max());
OPTION(kDispMaxThreads) = OPTION(kDispMaxThreads).Min();
OPTION(kDispMaxThreads)--;
EXPECT_EQ(OPTION(kDispMaxThreads), OPTION(kDispMaxThreads).Min());
uint32_t unsigned_opt = OPTION(kJoyDefault);
unsigned_opt++;
OPTION(kJoyDefault)++;
EXPECT_EQ(unsigned_opt, OPTION(kJoyDefault));
unsigned_opt--;
OPTION(kJoyDefault)--;
EXPECT_EQ(unsigned_opt, OPTION(kJoyDefault));
++unsigned_opt;
OPTION(kJoyDefault)++;
EXPECT_EQ(unsigned_opt, OPTION(kJoyDefault));
--unsigned_opt;
OPTION(kJoyDefault)--;
EXPECT_EQ(unsigned_opt, OPTION(kJoyDefault));
unsigned_opt += 2;
OPTION(kJoyDefault) += 2;
EXPECT_EQ(unsigned_opt, OPTION(kJoyDefault));
unsigned_opt -= 2;
OPTION(kJoyDefault) -= 2;
EXPECT_EQ(unsigned_opt, OPTION(kJoyDefault));
OPTION(kJoyDefault) = OPTION(kJoyDefault).Max();
OPTION(kJoyDefault)++;
EXPECT_EQ(OPTION(kJoyDefault), OPTION(kJoyDefault).Max());
OPTION(kJoyDefault) = OPTION(kJoyDefault).Min();
OPTION(kJoyDefault)--;
EXPECT_EQ(OPTION(kJoyDefault), OPTION(kJoyDefault).Min());
double double_opt = OPTION(kDispScale);
double_opt++;
OPTION(kDispScale)++;
EXPECT_EQ(double_opt, OPTION(kDispScale));
double_opt--;
OPTION(kDispScale)--;
EXPECT_EQ(double_opt, OPTION(kDispScale));
++double_opt;
OPTION(kDispScale)++;
EXPECT_EQ(double_opt, OPTION(kDispScale));
--double_opt;
OPTION(kDispScale)--;
EXPECT_EQ(double_opt, OPTION(kDispScale));
double_opt += 2;
OPTION(kDispScale) += 2;
EXPECT_EQ(double_opt, OPTION(kDispScale));
double_opt -= 2;
OPTION(kDispScale) -= 2;
EXPECT_EQ(double_opt, OPTION(kDispScale));
OPTION(kDispScale) = OPTION(kDispScale).Max();
OPTION(kDispScale)++;
EXPECT_EQ(OPTION(kDispScale), OPTION(kDispScale).Max());
OPTION(kDispScale) = OPTION(kDispScale).Min();
OPTION(kDispScale)--;
EXPECT_EQ(OPTION(kDispScale), OPTION(kDispScale).Min());
}

View File

@@ -11,6 +11,7 @@
#include "wx/config/internal/option-internal.h"
#undef VBAM_OPTION_INTERNAL_INCLUDE
#include "core/base/check.h"
#include "wx/config/option-proxy.h"
namespace config {
@@ -26,14 +27,14 @@ Option* Option::ByName(const wxString& config_name) {
// static
Option* Option::ByID(OptionID id) {
assert(id != OptionID::Last);
VBAM_CHECK(id != OptionID::Last);
return &All()[static_cast<size_t>(id)];
}
Option::~Option() = default;
Option::Observer::Observer(OptionID option_id) : option_(Option::ByID(option_id)) {
assert(option_);
VBAM_CHECK(option_);
option_->AddObserver(this);
}
Option::Observer::~Observer() {
@@ -49,8 +50,8 @@ Option::Option(OptionID id)
value_(),
min_(),
max_() {
assert(id != OptionID::Last);
assert(is_none());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_none());
}
Option::Option(OptionID id, bool* option)
@@ -62,8 +63,8 @@ Option::Option(OptionID id, bool* option)
value_(option),
min_(),
max_() {
assert(id != OptionID::Last);
assert(is_bool());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_bool());
}
Option::Option(OptionID id, double* option, double min, double max)
@@ -75,8 +76,8 @@ Option::Option(OptionID id, double* option, double min, double max)
value_(option),
min_(min),
max_(max) {
assert(id != OptionID::Last);
assert(is_double());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_double());
// Validate the initial value.
SetDouble(*option);
@@ -91,8 +92,8 @@ Option::Option(OptionID id, int32_t* option, int32_t min, int32_t max)
value_(option),
min_(min),
max_(max) {
assert(id != OptionID::Last);
assert(is_int());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_int());
// Validate the initial value.
SetInt(*option);
@@ -107,8 +108,8 @@ Option::Option(OptionID id, uint32_t* option, uint32_t min, uint32_t max)
value_(option),
min_(min),
max_(max) {
assert(id != OptionID::Last);
assert(is_unsigned());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_unsigned());
// Validate the initial value.
SetUnsigned(*option);
@@ -123,8 +124,8 @@ Option::Option(OptionID id, wxString* option)
value_(option),
min_(),
max_() {
assert(id != OptionID::Last);
assert(is_string());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_string());
}
Option::Option(OptionID id, Filter* option)
@@ -136,8 +137,8 @@ Option::Option(OptionID id, Filter* option)
value_(option),
min_(),
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
assert(id != OptionID::Last);
assert(is_filter());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_filter());
}
Option::Option(OptionID id, Interframe* option)
@@ -149,8 +150,8 @@ Option::Option(OptionID id, Interframe* option)
value_(option),
min_(),
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
assert(id != OptionID::Last);
assert(is_interframe());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_interframe());
}
Option::Option(OptionID id, RenderMethod* option)
@@ -162,8 +163,8 @@ Option::Option(OptionID id, RenderMethod* option)
value_(option),
min_(),
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
assert(id != OptionID::Last);
assert(is_render_method());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_render_method());
}
Option::Option(OptionID id, AudioApi* option)
@@ -175,8 +176,8 @@ Option::Option(OptionID id, AudioApi* option)
value_(option),
min_(),
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
assert(id != OptionID::Last);
assert(is_audio_api());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_audio_api());
}
Option::Option(OptionID id, AudioRate* option)
@@ -188,8 +189,8 @@ Option::Option(OptionID id, AudioRate* option)
value_(option),
min_(),
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
assert(id != OptionID::Last);
assert(is_audio_rate());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_audio_rate());
}
Option::Option(OptionID id, uint16_t* option)
@@ -201,57 +202,57 @@ Option::Option(OptionID id, uint16_t* option)
value_(option),
min_(),
max_() {
assert(id != OptionID::Last);
assert(is_gb_palette());
VBAM_CHECK(id != OptionID::Last);
VBAM_CHECK(is_gb_palette());
}
bool Option::GetBool() const {
assert(is_bool());
VBAM_CHECK(is_bool());
return *(nonstd::get<bool*>(value_));
}
double Option::GetDouble() const {
assert(is_double());
VBAM_CHECK(is_double());
return *(nonstd::get<double*>(value_));
}
int32_t Option::GetInt() const {
assert(is_int());
VBAM_CHECK(is_int());
return *(nonstd::get<int32_t*>(value_));
}
uint32_t Option::GetUnsigned() const {
assert(is_unsigned());
VBAM_CHECK(is_unsigned());
return *(nonstd::get<uint32_t*>(value_));
}
const wxString& Option::GetString() const {
assert(is_string());
VBAM_CHECK(is_string());
return *(nonstd::get<wxString*>(value_));
}
Filter Option::GetFilter() const {
assert(is_filter());
VBAM_CHECK(is_filter());
return *(nonstd::get<Filter*>(value_));
}
Interframe Option::GetInterframe() const {
assert(is_interframe());
VBAM_CHECK(is_interframe());
return *(nonstd::get<Interframe*>(value_));
}
RenderMethod Option::GetRenderMethod() const {
assert(is_render_method());
VBAM_CHECK(is_render_method());
return *(nonstd::get<RenderMethod*>(value_));
}
AudioApi Option::GetAudioApi() const {
assert(is_audio_api());
VBAM_CHECK(is_audio_api());
return *(nonstd::get<AudioApi*>(value_));
}
AudioRate Option::GetAudioRate() const {
assert(is_audio_rate());
VBAM_CHECK(is_audio_rate());
return *(nonstd::get<AudioRate*>(value_));
}
@@ -277,15 +278,15 @@ wxString Option::GetEnumString() const {
case Option::Type::kUnsigned:
case Option::Type::kString:
case Option::Type::kGbPalette:
assert(false);
VBAM_CHECK(false);
return wxEmptyString;
}
assert(false);
VBAM_NOTREACHED();
return wxEmptyString;
}
std::array<uint16_t, 8> Option::GetGbPalette() const {
assert(is_gb_palette());
VBAM_CHECK(is_gb_palette());
const uint16_t* raw_palette = (nonstd::get<uint16_t*>(value_));
std::array<uint16_t, 8> palette;
@@ -294,7 +295,7 @@ std::array<uint16_t, 8> Option::GetGbPalette() const {
}
wxString Option::GetGbPaletteString() const {
assert(is_gb_palette());
VBAM_CHECK(is_gb_palette());
wxString palette_string;
uint16_t const* value = nonstd::get<uint16_t*>(value_);
@@ -304,7 +305,7 @@ wxString Option::GetGbPaletteString() const {
}
bool Option::SetBool(bool value) {
assert(is_bool());
VBAM_CHECK(is_bool());
bool old_value = GetBool();
*nonstd::get<bool*>(value_) = value;
if (old_value != value) {
@@ -314,7 +315,7 @@ bool Option::SetBool(bool value) {
}
bool Option::SetDouble(double value) {
assert(is_double());
VBAM_CHECK(is_double());
double old_value = GetDouble();
if (value < nonstd::get<double>(min_) || value > nonstd::get<double>(max_)) {
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), value,
@@ -329,7 +330,7 @@ bool Option::SetDouble(double value) {
}
bool Option::SetInt(int32_t value) {
assert(is_int());
VBAM_CHECK(is_int());
int old_value = GetInt();
if (value < nonstd::get<int32_t>(min_) || value > nonstd::get<int32_t>(max_)) {
wxLogWarning(_("Invalid value %d for option %s; valid values are %d - %d"), value,
@@ -344,7 +345,7 @@ bool Option::SetInt(int32_t value) {
}
bool Option::SetUnsigned(uint32_t value) {
assert(is_unsigned());
VBAM_CHECK(is_unsigned());
uint32_t old_value = GetUnsigned();
if (value < nonstd::get<uint32_t>(min_) || value > nonstd::get<uint32_t>(max_)) {
wxLogWarning(_("Invalid value %d for option %s; valid values are %d - %d"), value,
@@ -359,7 +360,7 @@ bool Option::SetUnsigned(uint32_t value) {
}
bool Option::SetString(const wxString& value) {
assert(is_string());
VBAM_CHECK(is_string());
const wxString old_value = GetString();
*nonstd::get<wxString*>(value_) = value;
if (old_value != value) {
@@ -369,8 +370,8 @@ bool Option::SetString(const wxString& value) {
}
bool Option::SetFilter(const Filter& value) {
assert(is_filter());
assert(value < Filter::kLast);
VBAM_CHECK(is_filter());
VBAM_CHECK(value < Filter::kLast);
const Filter old_value = GetFilter();
*nonstd::get<Filter*>(value_) = value;
if (old_value != value) {
@@ -380,8 +381,8 @@ bool Option::SetFilter(const Filter& value) {
}
bool Option::SetInterframe(const Interframe& value) {
assert(is_interframe());
assert(value < Interframe::kLast);
VBAM_CHECK(is_interframe());
VBAM_CHECK(value < Interframe::kLast);
const Interframe old_value = GetInterframe();
*nonstd::get<Interframe*>(value_) = value;
if (old_value != value) {
@@ -391,8 +392,8 @@ bool Option::SetInterframe(const Interframe& value) {
}
bool Option::SetRenderMethod(const RenderMethod& value) {
assert(is_render_method());
assert(value < RenderMethod::kLast);
VBAM_CHECK(is_render_method());
VBAM_CHECK(value < RenderMethod::kLast);
const RenderMethod old_value = GetRenderMethod();
*nonstd::get<RenderMethod*>(value_) = value;
if (old_value != value) {
@@ -402,8 +403,8 @@ bool Option::SetRenderMethod(const RenderMethod& value) {
}
bool Option::SetAudioApi(const AudioApi& value) {
assert(is_audio_api());
assert(value < AudioApi::kLast);
VBAM_CHECK(is_audio_api());
VBAM_CHECK(value < AudioApi::kLast);
const AudioApi old_value = GetAudioApi();
*nonstd::get<AudioApi*>(value_) = value;
if (old_value != value) {
@@ -413,8 +414,8 @@ bool Option::SetAudioApi(const AudioApi& value) {
}
bool Option::SetAudioRate(const AudioRate& value) {
assert(is_audio_rate());
assert(value < AudioRate::kLast);
VBAM_CHECK(is_audio_rate());
VBAM_CHECK(value < AudioRate::kLast);
const AudioRate old_value = GetAudioRate();
*nonstd::get<AudioRate*>(value_) = value;
if (old_value != value) {
@@ -445,15 +446,15 @@ bool Option::SetEnumString(const wxString& value) {
case Option::Type::kUnsigned:
case Option::Type::kString:
case Option::Type::kGbPalette:
assert(false);
VBAM_CHECK(false);
return false;
}
assert(false);
VBAM_NOTREACHED();
return false;
}
bool Option::SetGbPalette(const std::array<uint16_t, 8>& value) {
assert(is_gb_palette());
VBAM_CHECK(is_gb_palette());
uint16_t* dest = nonstd::get<uint16_t*>(value_);
@@ -471,7 +472,7 @@ bool Option::SetGbPalette(const std::array<uint16_t, 8>& value) {
}
bool Option::SetGbPaletteString(const wxString& value) {
assert(is_gb_palette());
VBAM_CHECK(is_gb_palette());
// 8 values of 4 chars and 7 commas.
static constexpr size_t kPaletteStringSize = 8 * 4 + 7;
@@ -497,50 +498,50 @@ bool Option::SetGbPaletteString(const wxString& value) {
}
double Option::GetDoubleMin() const {
assert(is_double());
VBAM_CHECK(is_double());
return nonstd::get<double>(min_);
}
double Option::GetDoubleMax() const {
assert(is_double());
VBAM_CHECK(is_double());
return nonstd::get<double>(max_);
}
int32_t Option::GetIntMin() const {
assert(is_int());
VBAM_CHECK(is_int());
return nonstd::get<int32_t>(min_);
}
int32_t Option::GetIntMax() const {
assert(is_int());
VBAM_CHECK(is_int());
return nonstd::get<int32_t>(max_);
}
uint32_t Option::GetUnsignedMin() const {
assert(is_unsigned());
VBAM_CHECK(is_unsigned());
return nonstd::get<uint32_t>(min_);
}
uint32_t Option::GetUnsignedMax() const {
assert(is_unsigned());
VBAM_CHECK(is_unsigned());
return nonstd::get<uint32_t>(max_);
}
size_t Option::GetEnumMax() const {
assert(is_filter() || is_interframe() || is_render_method() || is_audio_api() ||
VBAM_CHECK(is_filter() || is_interframe() || is_render_method() || is_audio_api() ||
is_audio_rate());
return nonstd::get<size_t>(max_);
}
void Option::NextFilter() {
assert(is_filter());
VBAM_CHECK(is_filter());
const int old_value = static_cast<int>(GetFilter());
const int new_value = (old_value + 1) % kNbFilters;
SetFilter(static_cast<Filter>(new_value));
}
void Option::NextInterframe() {
assert(is_interframe());
VBAM_CHECK(is_interframe());
const int old_value = static_cast<int>(GetInterframe());
const int new_value = (old_value + 1) % kNbInterframes;
SetInterframe(static_cast<Interframe>(new_value));
@@ -588,19 +589,19 @@ wxString Option::ToHelperString() const {
}
void Option::AddObserver(Observer* observer) {
assert(observer);
VBAM_CHECK(observer);
[[maybe_unused]] const auto pair = observers_.emplace(observer);
assert(pair.second);
VBAM_CHECK(pair.second);
}
void Option::RemoveObserver(Observer* observer) {
assert(observer);
VBAM_CHECK(observer);
[[maybe_unused]] const size_t removed = observers_.erase(observer);
assert(removed == 1u);
VBAM_CHECK(removed == 1u);
}
void Option::CallObservers() {
assert(!calling_observers_);
VBAM_CHECK(!calling_observers_);
calling_observers_ = true;
for (const auto observer : observers_) {
observer->OnValueChanged();

View File

@@ -94,3 +94,91 @@ TEST(StrSplitWithSepTest, MultipleSepTokens) {
EXPECT_EQ(vec[4], "baz");
EXPECT_EQ(vec[5], "|-|");
}
TEST(UTF16ToUTF8Test, Basic) {
const std::vector<uint16_t> utf16 = {'f', 'o', 'o', 0};
auto vec = config::utf16_to_utf8_vector(utf16.data());
ASSERT_EQ(vec.size(), 3);
EXPECT_EQ(vec[0], 'f');
EXPECT_EQ(vec[1], 'o');
EXPECT_EQ(vec[2], 'o');
}
TEST(UTF16ToUTF8Test, MultiByte) {
// U+20AC EURO SIGN.
const std::vector<uint16_t> utf16 = {0x20AC, 0};
auto vec = config::utf16_to_utf8_vector(utf16.data());
ASSERT_EQ(vec.size(), 3);
EXPECT_EQ(vec[0], 0xE2);
EXPECT_EQ(vec[1], 0x82);
EXPECT_EQ(vec[2], 0xAC);
}
TEST(UTF16ToUTF8Test, DualMultiByte) {
// This is a variant of the above to test the buffer reset is done properly.
const std::vector<uint16_t> utf16 = {0x20AC, 0x20AC, 0};
auto vec = config::utf16_to_utf8_vector(utf16.data());
ASSERT_EQ(vec.size(), 6);
EXPECT_EQ(vec[0], 0xE2);
EXPECT_EQ(vec[1], 0x82);
EXPECT_EQ(vec[2], 0xAC);
EXPECT_EQ(vec[3], 0xE2);
EXPECT_EQ(vec[4], 0x82);
EXPECT_EQ(vec[5], 0xAC);
}
TEST(UTF16ToUTF8Test, SurrogatePair) {
// U+1F914 THINKING FACE.
const std::vector<uint16_t> utf16 = {0xD83E, 0xDD14, 0};
auto vec = config::utf16_to_utf8_vector(utf16.data());
ASSERT_EQ(vec.size(), 4);
EXPECT_EQ(vec[0], 0xF0);
EXPECT_EQ(vec[1], 0x9F);
EXPECT_EQ(vec[2], 0xA4);
EXPECT_EQ(vec[3], 0x94);
}
TEST(UTF16ToUTF8Test, InvalidSurrogatePair) {
// U+D800 HIGH SURROGATE.
const std::vector<uint16_t> utf16 = {0xD800, 0};
EXPECT_DEATH(config::utf16_to_utf8_vector(utf16.data()), ".*");
}
TEST(UTF16ToUTF8Test, InvalidSurrogatePair2) {
// U+D800 HIGH SURROGATE followed by U+0020 SPACE.
const std::vector<uint16_t> utf16 = {0xD800, 0x0020, 0};
EXPECT_DEATH(config::utf16_to_utf8_vector(utf16.data()), ".*");
}
TEST(UTF16ToUTF8Test, InvalidSurrogatePair3) {
// U+D800 HIGH SURROGATE followed by U+D800 HIGH SURROGATE.
const std::vector<uint16_t> utf16 = {0xD800, 0xD800, 0};
EXPECT_DEATH(config::utf16_to_utf8_vector(utf16.data()), ".*");
}
TEST(UTF16ToUTF8Test, FullString) {
// "foo€🤔"
const std::vector<uint16_t> utf16 = {'f', 'o', 'o', 0x20AC, 0xD83E, 0xDD14, 0};
auto vec = config::utf16_to_utf8_vector(utf16.data());
ASSERT_EQ(vec.size(), 10);
EXPECT_EQ(vec[0], 'f');
EXPECT_EQ(vec[1], 'o');
EXPECT_EQ(vec[2], 'o');
EXPECT_EQ(vec[3], 0xE2);
EXPECT_EQ(vec[4], 0x82);
EXPECT_EQ(vec[5], 0xAC);
EXPECT_EQ(vec[6], 0xF0);
EXPECT_EQ(vec[7], 0x9F);
EXPECT_EQ(vec[8], 0xA4);
EXPECT_EQ(vec[9], 0x94);
}

View File

@@ -1,7 +1,11 @@
#include "wx/config/strutils.h"
#include <cstdint>
#include <wx/tokenzr.h>
#include "core/base/check.h"
namespace config {
// From: https://stackoverflow.com/a/7408245/262458
@@ -37,4 +41,50 @@ wxArrayString str_split_with_sep(const wxString& text, const wxString& sep)
return str_split(text, sep, true);
}
std::vector<uint8_t> utf16_to_utf8_vector(const uint16_t* utf16) {
std::vector<uint8_t> out;
for (size_t i = 0; utf16[i]; i++) {
const uint16_t c = utf16[i];
if (c < 0x80) {
out.push_back(c);
} else if (c < 0x800) {
out.push_back(0xC0 | (c >> 6));
out.push_back(0x80 | (c & 0x3F));
} else if (c < 0xD800 || c >= 0xE000) {
// Regular 3-byte UTF-8 character.
out.push_back(0xE0 | (c >> 12));
out.push_back(0x80 | ((c >> 6) & 0x3F));
out.push_back(0x80 | (c & 0x3F));
} else {
// Surrogate pair, construct the original code point.
const uint32_t high = c;
// The next code unit must be a low surrogate.
i++;
const uint32_t low = utf16[i];
VBAM_CHECK(low);
VBAM_CHECK(low >= 0xDC00 && low < 0xE000);
const uint32_t codepoint = 0x10000 + ((high & 0x3FF) << 10) + (low & 0x3FF);
// Convert to UTF-8.
out.push_back(0xF0 | (codepoint >> 18));
out.push_back(0x80 | ((codepoint >> 12) & 0x3F));
out.push_back(0x80 | ((codepoint >> 6) & 0x3F));
out.push_back(0x80 | (codepoint & 0x3F));
}
}
return out;
}
wxString utf16_to_utf8(const uint16_t* utf16) {
std::vector<uint8_t> output_vector = utf16_to_utf8_vector(utf16);
return wxString::FromUTF8(reinterpret_cast<const char*>(output_vector.data()),
output_vector.size());
}
wxString utf16_to_utf8(const int16_t* utf16) {
return utf16_to_utf8(reinterpret_cast<const uint16_t*>(utf16));
}
} // namespace config

View File

@@ -1,6 +1,9 @@
#ifndef VBAM_WX_CONFIG_STRUTILS_H_
#define VBAM_WX_CONFIG_STRUTILS_H_
#include <cstdint>
#include <vector>
#include <wx/string.h>
#include <wx/arrstr.h>
@@ -14,6 +17,14 @@ wxArrayString str_split(const wxString& text, const wxString& sep, bool empty_to
// 'A', ',' and 'B' will be in the output.
wxArrayString str_split_with_sep(const wxString& text, const wxString& sep);
// Converts a null-terminated array of UTF-16 code units to a vector of UTF-8 code units.
// This will assert if the input is not a valid UTF-16 string.
std::vector<uint8_t> utf16_to_utf8_vector(const uint16_t* utf16);
// Convenience functions to convert a null-terminated array of UTF-16 code units to a wxString.
wxString utf16_to_utf8(const uint16_t* utf16);
wxString utf16_to_utf8(const int16_t* utf16);
} // namespace config
#endif // VBAM_WX_CONFIG_STRUTILS_H_

View File

@@ -257,8 +257,7 @@ wxString JoyInput::ToConfigString() const {
return wxString::Format("%s-Hat%dE", joy_string, control_index_);
}
// Unreachable.
assert(false);
VBAM_NOTREACHED();
return wxEmptyString;
}
@@ -281,8 +280,7 @@ wxString JoyInput::ToLocalizedString() const {
return wxString::Format(_("%s: Hat %d East"), joy_string, control_index_);
}
// Unreachable.
assert(false);
VBAM_NOTREACHED();
return wxEmptyString;
}
@@ -368,8 +366,7 @@ wxString UserInput::ToConfigString() const {
return joy_input().ToConfigString();
}
// Unreachable.
assert(false);
VBAM_NOTREACHED();
return wxEmptyString;
}
@@ -383,8 +380,7 @@ wxString UserInput::ToLocalizedString() const {
return joy_input().ToLocalizedString();
}
// Unreachable.
assert(false);
VBAM_NOTREACHED();
return wxEmptyString;
}

View File

@@ -1,7 +1,6 @@
#ifndef VBAM_WX_CONFIG_USER_INPUT_H_
#define VBAM_WX_CONFIG_USER_INPUT_H_
#include <cassert>
#include <cstdint>
#include <unordered_set>
@@ -9,6 +8,8 @@
#include <wx/string.h>
#include "core/base/check.h"
namespace config {
// Abstract representation of a keyboard input. This class is used to represent
@@ -160,12 +161,12 @@ public:
Device device() const { return device_; }
const KeyboardInput& keyboard_input() const {
assert(is_keyboard());
VBAM_CHECK(is_keyboard());
return nonstd::get<KeyboardInput>(input_);
};
const JoyInput& joy_input() const {
assert(is_joystick());
VBAM_CHECK(is_joystick());
return nonstd::get<JoyInput>(input_);
};
@@ -247,8 +248,7 @@ struct std::hash<config::UserInput> {
std::hash<config::KeyboardInput>{}(user_input.keyboard_input());
}
// Unreachable.
assert(false);
VBAM_NOTREACHED();
return 0;
}
};

View File

@@ -6,6 +6,7 @@
#include <wx/menu.h>
#include <wx/msgdlg.h>
#include "core/base/check.h"
#include "wx/config/bindings.h"
#include "wx/config/cmdtab.h"
#include "wx/config/command.h"
@@ -112,9 +113,9 @@ AccelConfig* AccelConfig::NewInstance(wxWindow* parent,
wxMenuBar* menu,
wxMenu* recents,
const config::BindingsProvider bindings_provider) {
assert(parent);
assert(menu);
assert(recents);
VBAM_CHECK(parent);
VBAM_CHECK(menu);
VBAM_CHECK(recents);
return new AccelConfig(parent, menu, recents, bindings_provider);
}
@@ -123,7 +124,7 @@ AccelConfig::AccelConfig(wxWindow* parent,
wxMenu* recents,
const config::BindingsProvider bindings_provider)
: BaseDialog(parent, "AccelConfig"), bindings_provider_(bindings_provider) {
assert(menu);
VBAM_CHECK(menu);
// Loads the various dialog elements.
tree_ = GetValidatedChild<wxTreeCtrl>("Commands");
@@ -157,7 +158,7 @@ AccelConfig::AccelConfig(wxWindow* parent,
for (const auto& iter : command_to_item_id_) {
const CommandTreeItemData* item_data =
static_cast<const CommandTreeItemData*>(tree_->GetItemData(iter.second));
assert(item_data);
VBAM_CHECK(item_data);
currently_assigned_label_->GetTextExtent(item_data->assigned_string(), &w, &h);
size.SetWidth(std::max(w, size.GetWidth()));
@@ -176,6 +177,14 @@ AccelConfig::AccelConfig(wxWindow* parent,
Bind(wxEVT_BUTTON, &AccelConfig::OnResetAll, this, XRCID("ResetAll"));
Bind(wxEVT_TEXT, &AccelConfig::OnKeyInput, this, key_input_->GetId());
#if !WX_RESIZE_DIALOGS
// On wxGTK, the dialog resizing is broken, so we need to set a fixed size.
SetWindowStyle(GetWindowStyle() & ~wxRESIZE_BORDER);
const wxSize dialog_size(800, 800);
SetMinSize(dialog_size);
SetMaxSize(dialog_size);
#endif // !WX_RESIZE_DIALOGS
// And fit everything nicely.
Fit();
}
@@ -277,10 +286,10 @@ void AccelConfig::OnAssignBinding(wxCommandEvent&) {
break;
case config::Command::Tag::kShortcut:
const auto iter = command_to_item_id_.find(old_command->shortcut());
assert(iter != command_to_item_id_.end());
VBAM_CHECK(iter != command_to_item_id_.end());
const CommandTreeItemData* old_command_item_data =
static_cast<const CommandTreeItemData*>(tree_->GetItemData(iter->second));
assert(old_command_item_data);
VBAM_CHECK(old_command_item_data);
old_command_name = old_command_item_data->message_string();
break;
}
@@ -318,7 +327,7 @@ void AccelConfig::OnKeyInput(wxCommandEvent&) {
break;
case config::Command::Tag::kShortcut:
const auto iter = command_to_item_id_.find(command->shortcut());
assert(iter != command_to_item_id_.end());
VBAM_CHECK(iter != command_to_item_id_.end());
currently_assigned_label_->SetLabel(
static_cast<CommandTreeItemData*>(tree_->GetItemData(iter->second))
->assigned_string());

View File

@@ -1,11 +1,10 @@
#include "wx/dialogs/base-dialog.h"
#include <cassert>
#include <wx/persist.h>
#include <wx/persist/toplevel.h>
#include <wx/xrc/xmlres.h>
#include "core/base/check.h"
#include "wx/config/option-proxy.h"
#include "wx/widgets/utils.h"
@@ -39,15 +38,19 @@ private:
return;
}
#if WX_RESIZE_DIALOGS
const wxRect dialog_rect = this->Get()->GetRect();
this->SaveValue("x", dialog_rect.x);
this->SaveValue("y", dialog_rect.y);
this->SaveValue("width", dialog_rect.width);
this->SaveValue("height", dialog_rect.height);
#endif // WX_RESIZE_DIALOGS
}
bool Restore() override {
dialog_shown_ = true;
#if WX_RESIZE_DIALOGS
wxRect dialog_rect(0, 0, 0, 0);
if (!this->RestoreValue("x", &dialog_rect.x)) {
return false;
@@ -63,6 +66,7 @@ private:
};
this->Get()->SetSize(dialog_rect);
#endif // WX_RESIZE_DIALOGS
return true;
}
@@ -73,7 +77,7 @@ private:
// static
wxDialog* BaseDialog::LoadDialog(wxWindow* parent, const wxString& xrc_file) {
assert(parent);
VBAM_CHECK(parent);
return new BaseDialog(parent, xrc_file);
}
@@ -85,8 +89,7 @@ BaseDialog::BaseDialog(wxWindow* parent, const wxString& xrc_file)
this->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
#endif
[[maybe_unused]] const bool success = wxXmlResource::Get()->LoadDialog(this, parent, xrc_file);
assert(success);
VBAM_CHECK(wxXmlResource::Get()->LoadDialog(this, parent, xrc_file));
// Bind the event handler.
this->Bind(wxEVT_SHOW, &BaseDialog::OnBaseDialogShow, this);
@@ -96,7 +99,7 @@ BaseDialog::BaseDialog(wxWindow* parent, const wxString& xrc_file)
wxWindow* BaseDialog::GetValidatedChild(const wxString& name) const {
wxWindow* window = this->FindWindow(name);
assert(window);
VBAM_CHECK(window);
return window;
}

View File

@@ -6,6 +6,15 @@
#include "wx/widgets/keep-on-top-styler.h"
#include "core/base/check.h"
#if defined(__WXGTK__)
// Moving and resizing dialogs does not work properly on wxGTK.
#define WX_RESIZE_DIALOGS 0
#else
#define WX_RESIZE_DIALOGS 1
#endif
namespace dialogs {
class BaseDialog : public wxDialog {
@@ -23,7 +32,7 @@ protected:
template <class T>
T* GetValidatedChild(const wxString& name) const {
T* child = wxDynamicCast(this->GetValidatedChild(name), T);
assert(child);
VBAM_CHECK(child);
return child;
}

View File

@@ -2,6 +2,7 @@
#include <wx/filepicker.h>
#include "core/base/check.h"
#include "wx/dialogs/base-dialog.h"
#include "wx/widgets/option-validator.h"
@@ -14,7 +15,7 @@ class DirectoryStringValidator final : public widgets::OptionValidator {
public:
DirectoryStringValidator(config::OptionID option_id)
: widgets::OptionValidator(option_id) {
assert(option()->is_string());
VBAM_CHECK(option()->is_string());
}
~DirectoryStringValidator() final = default;
@@ -29,7 +30,7 @@ private:
bool WriteToWindow() final {
wxDirPickerCtrl* dir_picker =
wxDynamicCast(GetWindow(), wxDirPickerCtrl);
assert(dir_picker);
VBAM_CHECK(dir_picker);
dir_picker->SetPath(option()->GetString());
return true;
}
@@ -37,7 +38,7 @@ private:
bool WriteToOption() final {
const wxDirPickerCtrl* dir_picker =
wxDynamicCast(GetWindow(), wxDirPickerCtrl);
assert(dir_picker);
VBAM_CHECK(dir_picker);
return option()->SetString(dir_picker->GetPath());
}
};
@@ -52,7 +53,7 @@ void SetUpDirPicker(wxDirPickerCtrl* dir_picker,
// static
DirectoriesConfig* DirectoriesConfig::NewInstance(wxWindow* parent) {
assert(parent);
VBAM_CHECK(parent);
return new DirectoriesConfig(parent);
}

View File

@@ -146,7 +146,7 @@ public:
explicit RenderValidator(config::RenderMethod render_method)
: OptionValidator(config::OptionID::kDispRenderMethod),
render_method_(render_method) {
assert(render_method != config::RenderMethod::kLast);
VBAM_CHECK(render_method != config::RenderMethod::kLast);
}
~RenderValidator() override = default;
@@ -189,7 +189,7 @@ private:
bool WriteToWindow() override {
wxChoice* plugin_selector = wxDynamicCast(GetWindow(), wxChoice);
assert(plugin_selector);
VBAM_CHECK(plugin_selector);
const wxString selected_plugin = option()->GetString();
for (size_t i = 0; i < plugin_selector->GetCount(); i++) {
const wxString& plugin_data =
@@ -206,7 +206,7 @@ private:
bool WriteToOption() override {
wxChoice* plugin_selector = wxDynamicCast(GetWindow(), wxChoice);
assert(plugin_selector);
VBAM_CHECK(plugin_selector);
const wxString& selected_window_plugin =
dynamic_cast<wxStringClientData*>(
plugin_selector->GetClientObject(
@@ -220,7 +220,7 @@ private:
// static
DisplayConfig* DisplayConfig::NewInstance(wxWindow* parent) {
assert(parent);
VBAM_CHECK(parent);
return new DisplayConfig(parent);
}
@@ -330,7 +330,7 @@ void DisplayConfig::PopulatePluginOptions() {
plugin_selector_->Clear();
plugin_selector_->Append(_("None"), new wxStringClientData());
const wxString& selected_plugin = OPTION(kDispFilterPlugin);
const wxString selected_plugin = OPTION(kDispFilterPlugin);
bool is_plugin_selected = false;
for (const wxString& plugin : plugins) {

View File

@@ -13,6 +13,7 @@
#include <wx/xrc/xmlres.h>
#include "core/base/check.h"
#include "wx/config/option-observer.h"
#include "wx/config/option-proxy.h"
#include "wx/dialogs/base-dialog.h"
@@ -90,8 +91,8 @@ public:
PaletteValidator(GBPalettePanelData* palette_data)
: widgets::OptionValidator(palette_data->option_id_),
palette_data_(palette_data) {
assert(option()->is_gb_palette());
assert(palette_data);
VBAM_CHECK(option()->is_gb_palette());
VBAM_CHECK(palette_data);
}
~PaletteValidator() final = default;
@@ -122,8 +123,8 @@ class BIOSPickerValidator final : public widgets::OptionValidator {
public:
BIOSPickerValidator(config::OptionID option_id, wxStaticText* label)
: widgets::OptionValidator(option_id), label_(label) {
assert(label_);
assert(option()->is_string());
VBAM_CHECK(label_);
VBAM_CHECK(option()->is_string());
}
~BIOSPickerValidator() final = default;
@@ -143,7 +144,7 @@ private:
} else {
wxFilePickerCtrl* file_picker =
wxDynamicCast(GetWindow(), wxFilePickerCtrl);
assert(file_picker);
VBAM_CHECK(file_picker);
file_picker->SetPath(selection);
label_->SetLabel(selection);
}
@@ -154,7 +155,7 @@ private:
bool WriteToOption() final {
const wxFilePickerCtrl* file_picker =
wxDynamicCast(GetWindow(), wxFilePickerCtrl);
assert(file_picker);
VBAM_CHECK(file_picker);
return option()->SetString(file_picker->GetPath());
}
@@ -175,7 +176,7 @@ private:
bool TransferFromWindow() final {
const wxChoice* borders_selector = wxDynamicCast(GetWindow(), wxChoice);
assert(borders_selector);
VBAM_CHECK(borders_selector);
switch (borders_selector->GetSelection()) {
case 0:
OPTION(kPrefBorderOn) = false;
@@ -199,7 +200,7 @@ private:
bool TransferToWindow() final {
wxChoice* borders_selector = wxDynamicCast(GetWindow(), wxChoice);
assert(borders_selector);
VBAM_CHECK(borders_selector);
if (!OPTION(kPrefBorderOn) && !OPTION(kPrefBorderAutomatic)) {
borders_selector->SetSelection(0);
@@ -225,8 +226,8 @@ GBPalettePanelData::GBPalettePanelData(wxPanel* panel, size_t palette_id)
default_selector_(widgets::GetValidatedChild<wxChoice>(panel, "DefaultPalette")),
option_id_(static_cast<config::OptionID>(static_cast<size_t>(config::OptionID::kGBPalette0) +
palette_id)) {
assert(panel);
assert(palette_id < kNbPalettes);
VBAM_CHECK(panel);
VBAM_CHECK(palette_id < kNbPalettes);
default_selector_->Bind(
wxEVT_CHOICE, &GBPalettePanelData::OnDefaultPaletteSelected, this);
@@ -274,7 +275,7 @@ void GBPalettePanelData::UpdateColourPickers() {
void GBPalettePanelData::OnColourChanged(size_t colour_index,
wxColourPickerEvent& event) {
assert(colour_index < palette_.size());
VBAM_CHECK(colour_index < palette_.size());
// Update the colour value.
const wxColour colour = event.GetColour();
@@ -313,7 +314,7 @@ void GBPalettePanelData::OnPaletteReset(wxCommandEvent& event) {
// static
GameBoyConfig* GameBoyConfig::NewInstance(wxWindow* parent) {
assert(parent);
VBAM_CHECK(parent);
return new GameBoyConfig(parent);
}

View File

@@ -3,6 +3,7 @@
#include <wx/control.h>
#include <wx/xrc/xmlres.h>
#include "core/base/check.h"
#include "core/base/sizes.h"
#include "core/gb/gb.h"
#include "wx/dialogs/base-dialog.h"
@@ -92,11 +93,11 @@ wxString GetCartCGBFlag() {
return wxString::Format(_("%02X (Supported)"), g_gbCartData.cgb_flag());
case gbCartData::CGBSupport::kRequired:
return wxString::Format(_("%02X (Required)"), g_gbCartData.cgb_flag());
default:
// Unreachable.
assert(false);
return "";
}
VBAM_NOTREACHED();
return "";
}
// Returns a localized string indicating the ROM size of the loaded GB/GBC cartridge.
@@ -156,18 +157,17 @@ wxString GetCartDestinationCode() {
return wxString::Format(_("%02X (World)"), g_gbCartData.destination_code_flag());
case gbCartData::DestinationCode::kUnknown:
return wxString::Format(_("%02X (Unknown)"), g_gbCartData.destination_code_flag());
default:
// Unreachable.
assert(false);
return "";
}
VBAM_NOTREACHED();
return "";
}
} // namespace
// static
GbRomInfo* GbRomInfo::NewInstance(wxWindow* parent) {
assert(parent);
VBAM_CHECK(parent);
return new GbRomInfo(parent);
}

View File

@@ -2,6 +2,7 @@
#include <wx/checkbox.h>
#include "core/base/check.h"
#include "wx/config/command.h"
#include "wx/config/option-proxy.h"
#include "wx/dialogs/base-dialog.h"
@@ -40,7 +41,7 @@ protected:
UserInputCtrlValidator::UserInputCtrlValidator(const config::GameCommand game_control,
config::BindingsProvider const bindings_provider)
: wxValidator(), game_control_(game_control), bindings_provider(bindings_provider) {
assert(bindings_provider);
VBAM_CHECK(bindings_provider);
}
wxObject* UserInputCtrlValidator::Clone() const {
@@ -49,7 +50,7 @@ wxObject* UserInputCtrlValidator::Clone() const {
bool UserInputCtrlValidator::TransferToWindow() {
widgets::UserInputCtrl* control = wxDynamicCast(GetWindow(), widgets::UserInputCtrl);
assert(control);
VBAM_CHECK(control);
control->SetInputs(bindings_provider()->InputsForCommand(config::Command(game_control_)));
return true;
@@ -57,7 +58,7 @@ bool UserInputCtrlValidator::TransferToWindow() {
bool UserInputCtrlValidator::TransferFromWindow() {
widgets::UserInputCtrl* control = wxDynamicCast(GetWindow(), widgets::UserInputCtrl);
assert(control);
VBAM_CHECK(control);
bindings_provider()->ClearCommandAssignments(config::Command(game_control_));
for (const auto& input : control->inputs()) {
@@ -72,8 +73,8 @@ bool UserInputCtrlValidator::TransferFromWindow() {
// static
JoypadConfig* JoypadConfig::NewInstance(wxWindow* parent,
const config::BindingsProvider bindings_provider) {
assert(parent);
assert(bindings_provider);
VBAM_CHECK(parent);
VBAM_CHECK(bindings_provider);
return new JoypadConfig(parent, bindings_provider);
}
@@ -110,7 +111,7 @@ JoypadConfig::JoypadConfig(wxWindow* parent, const config::BindingsProvider bind
if (current_parent == prev_parent) {
// The first control will be skipped here, but that's fine since
// we don't care where it fits in the tab order.
assert(prev);
VBAM_CHECK(prev);
game_key_control->MoveAfterInTabOrder(prev);
}
prev = game_key_control;

View File

@@ -12,6 +12,7 @@
#include <wx/xrc/xmlres.h>
#include <functional>
#include "core/base/check.h"
#include "wx/audio/audio.h"
#include "wx/config/option-id.h"
#include "wx/config/option-proxy.h"
@@ -37,14 +38,14 @@ private:
bool WriteToWindow() override {
wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice);
assert(choice);
VBAM_CHECK(choice);
choice->SetSelection(static_cast<int>(option()->GetAudioRate()));
return true;
}
bool WriteToOption() override {
const wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice);
assert(choice);
VBAM_CHECK(choice);
const int selection = choice->GetSelection();
if (selection == wxNOT_FOUND) {
return false;
@@ -63,7 +64,7 @@ class AudioApiValidator : public widgets::OptionValidator {
public:
explicit AudioApiValidator(config::AudioApi audio_api)
: OptionValidator(config::OptionID::kSoundAudioAPI), audio_api_(audio_api) {
assert(audio_api < config::AudioApi::kLast);
VBAM_CHECK(audio_api < config::AudioApi::kLast);
}
~AudioApiValidator() override = default;
@@ -103,7 +104,7 @@ private:
bool WriteToWindow() override {
wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice);
assert(choice);
VBAM_CHECK(choice);
const wxString& device_id = option()->GetString();
for (size_t i = 0; i < choice->GetCount(); i++) {
@@ -121,7 +122,7 @@ private:
bool WriteToOption() override {
const wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice);
assert(choice);
VBAM_CHECK(choice);
const int selection = choice->GetSelection();
if (selection == wxNOT_FOUND) {
return option()->SetString(wxEmptyString);
@@ -136,7 +137,7 @@ private:
// static
SoundConfig* SoundConfig::NewInstance(wxWindow* parent) {
assert(parent);
VBAM_CHECK(parent);
return new SoundConfig(parent);
}

View File

@@ -0,0 +1,148 @@
#include "wx/dialogs/speedup-config.h"
#include <wx/checkbox.h>
#include <wx/spinctrl.h>
#include <wx/xrc/xmlres.h>
#include "core/base/check.h"
#include "wx/config/option-proxy.h"
#include "wx/dialogs/base-dialog.h"
#include "wx/widgets/option-validator.h"
// This dialog has a spin control representing the speedup value as it feels
// like to the user. Values below 450 can be an actual throttle or a number of
// frames to skip depending on whether the Frame Skip checkbox is checked. The
// number of frames is the spin control value divided by 100, for example 300
// with the Frame Skip checkbox checked means 3 frames to skip. Values above 450
// are always the number of frames to skip, for example 900 means 9 frames to skip.
//
// The config option SpeedupThrottle is the throttle value, if frame skip is
// used it is 0. The config option SpeedupFrameSkip is the number of frames to
// skip, if throttle is used it is 0. The config option SpeedupThrottleFrameSkip
// represents the Frame Skip checkbox for values under 450, that is if they are
// interpreted as a frame skip or a throttle.
namespace dialogs {
bool SpeedupConfigValidator::TransferToWindow() {
uint32_t opt_frame_skip = OPTION(kPrefSpeedupFrameSkip);
uint32_t opt_throttle = OPTION(kPrefSpeedupThrottle);
bool opt_throttle_frame_skip = OPTION(kPrefSpeedupThrottleFrameSkip);
auto dialog = static_cast<SpeedupConfig*>(GetWindow());
dialog->prev_frame_skip_cb_ = opt_throttle_frame_skip;
if (opt_frame_skip != 0) {
dialog->speedup_throttle_spin_->SetValue(opt_frame_skip * 100);
dialog->prev_throttle_spin_ = opt_frame_skip * 100;
dialog->frame_skip_cb_->SetValue(true);
dialog->frame_skip_cb_->Disable();
} else {
dialog->speedup_throttle_spin_->SetValue(opt_throttle);
dialog->prev_throttle_spin_ = opt_throttle;
dialog->frame_skip_cb_->SetValue(opt_throttle_frame_skip);
if (opt_throttle != 0)
dialog->frame_skip_cb_->Enable();
else
dialog->frame_skip_cb_->Disable();
}
return true;
}
bool SpeedupConfigValidator::TransferFromWindow() {
auto dialog = static_cast<SpeedupConfig*>(GetWindow());
uint32_t val = dialog->speedup_throttle_spin_->GetValue();
if (val == 0) {
OPTION(kPrefSpeedupThrottle) = 0;
OPTION(kPrefSpeedupFrameSkip) = 0;
OPTION(kPrefSpeedupThrottleFrameSkip) = false;
} else if (val <= OPTION(kPrefSpeedupThrottle).Max()) {
OPTION(kPrefSpeedupThrottle) = val;
OPTION(kPrefSpeedupFrameSkip) = 0;
OPTION(kPrefSpeedupThrottleFrameSkip) = dialog->frame_skip_cb_->GetValue();
} else { // val > throttle_max
OPTION(kPrefSpeedupThrottle) = 100;
OPTION(kPrefSpeedupFrameSkip) = val / 100;
OPTION(kPrefSpeedupThrottleFrameSkip) = false;
}
return true;
}
// static
SpeedupConfig* SpeedupConfig::NewInstance(wxWindow* parent) {
VBAM_CHECK(parent);
return new SpeedupConfig(parent);
}
SpeedupConfig::SpeedupConfig(wxWindow* parent)
: BaseDialog(parent, "SpeedupConfig") {
speedup_throttle_spin_ = GetValidatedChild<wxSpinCtrl>("SpeedupThrottleSpin");
frame_skip_cb_ = GetValidatedChild<wxCheckBox>("SpeedupThrottleFrameSkip");
speedup_throttle_spin_->SetRange(OPTION(kPrefSpeedupThrottle).Min(), OPTION(kPrefSpeedupFrameSkip).Max() * 100);
speedup_throttle_spin_->SetWindowStyle(wxSP_ARROW_KEYS);
#ifdef wxTE_PROCESS_ENTER
speedup_throttle_spin_->SetWindowStyleFlag(wxTE_PROCESS_ENTER);
#endif
GetValidatedChild<wxCheckBox>("SpeedupMute")->SetValidator(widgets::OptionBoolValidator(config::OptionID::kPrefSpeedupMute));
using namespace std::placeholders;
Bind(wxEVT_SPIN_UP, std::bind(&SpeedupConfig::SetSpeedupThrottle, this, _1), XRCID("SpeedupThrottleSpin"));
Bind(wxEVT_SPIN_DOWN, std::bind(&SpeedupConfig::SetSpeedupThrottle, this, _1), XRCID("SpeedupThrottleSpin"));
Bind(wxEVT_SPIN, std::bind(&SpeedupConfig::SetSpeedupThrottle, this, _1), XRCID("SpeedupThrottleSpin"));
Bind(wxEVT_TEXT, std::bind(&SpeedupConfig::SetSpeedupThrottle, this, _1), XRCID("SpeedupThrottleSpin"));
Bind(wxEVT_CHECKBOX, std::bind(&SpeedupConfig::ToggleSpeedupFrameSkip, this), XRCID("SpeedupThrottleFrameSkip"));
SetValidator(SpeedupConfigValidator());
Fit();
}
void SpeedupConfig::SetSpeedupThrottle(wxCommandEvent& evt)
{
VBAM_CHECK(evt.GetEventObject() == speedup_throttle_spin_);
uint32_t val = speedup_throttle_spin_->GetValue();
if (val == 0) {
frame_skip_cb_->SetValue(false);
frame_skip_cb_->Disable();
} else if (val <= OPTION(kPrefSpeedupThrottle).Max()) {
frame_skip_cb_->SetValue(prev_frame_skip_cb_);
frame_skip_cb_->Enable();
} else { // val > throttle_max
if (val > prev_throttle_spin_)
val += 100;
val = std::floor((double)val / 100) * 100;
uint32_t max = speedup_throttle_spin_->GetMax();
if (val > max)
val = max;
frame_skip_cb_->SetValue(true);
frame_skip_cb_->Disable();
}
speedup_throttle_spin_->SetValue(val);
prev_throttle_spin_ = val;
}
void SpeedupConfig::ToggleSpeedupFrameSkip()
{
prev_frame_skip_cb_ = frame_skip_cb_->GetValue();
}
} // namespace dialogs

View File

@@ -0,0 +1,54 @@
#ifndef VBAM_WX_DIALOGS_SPEEDUP_CONFIG_H_
#define VBAM_WX_DIALOGS_SPEEDUP_CONFIG_H_
// Manages the Speedup/Turbo configuration dialog.
//
// See the explanation for the implementation in the .cpp file.
#include <wx/checkbox.h>
#include "wx/dialogs/base-dialog.h"
#include <wx/spinctrl.h>
#include <wx/validate.h>
namespace dialogs {
class SpeedupConfigValidator : public wxValidator {
public:
SpeedupConfigValidator() = default;
~SpeedupConfigValidator() override = default;
bool TransferToWindow() override;
bool TransferFromWindow() override;
bool Validate([[maybe_unused]]wxWindow* parent) override { return true; }
private:
wxObject* Clone() const override { return new SpeedupConfigValidator(); }
};
class SpeedupConfig : public BaseDialog {
public:
static SpeedupConfig* NewInstance(wxWindow* parent);
~SpeedupConfig() override = default;
void ToggleSpeedupFrameSkip();
void ToggleSpeedupMute();
void SetSpeedupThrottle(wxCommandEvent& evt);
private:
// The constructor is private so initialization has to be done via the
// static method. This is because this class is destroyed when its
// owner, `parent` is destroyed. This prevents accidental deletion.
SpeedupConfig(wxWindow* parent);
wxSpinCtrl* speedup_throttle_spin_;
wxCheckBox* frame_skip_cb_;
bool prev_frame_skip_cb_;
unsigned prev_throttle_spin_;
friend class SpeedupConfigValidator;
};
} // namespace dialogs
#endif // VBAM_WX_DIALOGS_SPEEDUP_CONFIG_H_

View File

@@ -48,6 +48,7 @@
#include "wx/dialogs/gb-rom-info.h"
#include "wx/dialogs/joypad-config.h"
#include "wx/dialogs/sound-config.h"
#include "wx/dialogs/speedup-config.h"
#include "wx/opts.h"
#include "wx/widgets/checkedlistctrl.h"
#include "wx/widgets/option-validator.h"
@@ -1483,92 +1484,6 @@ public:
}
} throttle_ctrl;
static class SpeedupThrottleCtrl_t : public wxEvtHandler {
public:
wxSpinCtrl* speedup_throttle_spin;
wxCheckBox* frame_skip_cb;
void SetSpeedupThrottle(wxCommandEvent& evt)
{
unsigned val = speedup_throttle_spin->GetValue();
evt.Skip(false);
if (val == 0) {
coreOptions.speedup_throttle = 0;
coreOptions.speedup_frame_skip = 0;
coreOptions.speedup_throttle_frame_skip = false;
frame_skip_cb->SetValue(false);
frame_skip_cb->Disable();
if (evt.GetEventType() == wxEVT_TEXT)
return; // Do not update value if user cleared text box.
}
else if (val <= 450) {
coreOptions.speedup_throttle = val;
coreOptions.speedup_frame_skip = 0;
frame_skip_cb->SetValue(prev_frame_skip_cb);
frame_skip_cb->Enable();
}
else { // val > 450
coreOptions.speedup_throttle = 100;
coreOptions.speedup_throttle_frame_skip = false;
unsigned rounded = std::round((double)val / 100) * 100;
coreOptions.speedup_frame_skip = rounded / 100 - 1;
// Round up or down to the nearest 100%.
// For example, when the up/down buttons are pressed on the spin
// control.
if ((int)(val - rounded) > 0)
coreOptions.speedup_frame_skip++;
else if ((int)(val - rounded) < 0)
coreOptions.speedup_frame_skip--;
frame_skip_cb->SetValue(true);
frame_skip_cb->Disable();
val = (coreOptions.speedup_frame_skip + 1) * 100;
}
speedup_throttle_spin->SetValue(val);
}
void SetSpeedupFrameSkip(wxCommandEvent& evt)
{
(void)evt; // Unused param.
bool checked = frame_skip_cb->GetValue();
coreOptions.speedup_throttle_frame_skip = prev_frame_skip_cb = checked;
}
void Init(wxShowEvent& ev)
{
if (coreOptions.speedup_frame_skip != 0) {
speedup_throttle_spin->SetValue((coreOptions.speedup_frame_skip + 1) * 100);
frame_skip_cb->SetValue(true);
frame_skip_cb->Disable();
}
else {
speedup_throttle_spin->SetValue(coreOptions.speedup_throttle);
frame_skip_cb->SetValue(coreOptions.speedup_throttle_frame_skip);
if (coreOptions.speedup_throttle != 0)
frame_skip_cb->Enable();
else
frame_skip_cb->Disable();
}
ev.Skip();
}
private:
bool prev_frame_skip_cb = coreOptions.speedup_throttle_frame_skip;
} speedup_throttle_ctrl;
/////////////////////////////
//Check if a pointer from the XRC file is valid. If it's not, throw an error telling the user.
template <typename T>
@@ -2376,38 +2291,6 @@ bool MainFrame::BindControls()
d->Fit();
}
// SpeedUp Key Config
d = LoadXRCDialog("SpeedupConfig");
{
speedup_throttle_ctrl.frame_skip_cb = SafeXRCCTRL<wxCheckBox>(d, "SpeedupThrottleFrameSkip");
speedup_throttle_ctrl.speedup_throttle_spin = SafeXRCCTRL<wxSpinCtrl>(d, "SpeedupThrottleSpin");
speedup_throttle_ctrl.speedup_throttle_spin->Connect(wxEVT_SPIN_UP,
wxCommandEventHandler(SpeedupThrottleCtrl_t::SetSpeedupThrottle),
NULL, &speedup_throttle_ctrl);
speedup_throttle_ctrl.speedup_throttle_spin->Connect(wxEVT_SPIN_DOWN,
wxCommandEventHandler(SpeedupThrottleCtrl_t::SetSpeedupThrottle),
NULL, &speedup_throttle_ctrl);
speedup_throttle_ctrl.speedup_throttle_spin->Connect(wxEVT_SPIN,
wxCommandEventHandler(SpeedupThrottleCtrl_t::SetSpeedupThrottle),
NULL, &speedup_throttle_ctrl);
speedup_throttle_ctrl.speedup_throttle_spin->Connect(wxEVT_TEXT,
wxCommandEventHandler(SpeedupThrottleCtrl_t::SetSpeedupThrottle),
NULL, &speedup_throttle_ctrl);
speedup_throttle_ctrl.frame_skip_cb->Connect(wxEVT_CHECKBOX,
wxCommandEventHandler(SpeedupThrottleCtrl_t::SetSpeedupFrameSkip),
NULL, &speedup_throttle_ctrl);
d->Connect(wxEVT_SHOW, wxShowEventHandler(SpeedupThrottleCtrl_t::Init),
NULL, &speedup_throttle_ctrl);
d->Fit();
}
wxMenuItem* suspend_scr_saver_mi = XRCITEM("SuspendScreenSaver");
if (suspend_scr_saver_mi)
{
@@ -2470,6 +2353,7 @@ bool MainFrame::BindControls()
dialogs::SoundConfig::NewInstance(this);
dialogs::DirectoriesConfig::NewInstance(this);
dialogs::JoypadConfig::NewInstance(this, std::bind(&wxvbamApp::bindings, &wxGetApp()));
dialogs::SpeedupConfig::NewInstance(this);
#ifndef NO_LINK
d = LoadXRCDialog("LinkConfig");

View File

@@ -118,7 +118,7 @@ opts_t::opts_t()
void load_opts(bool first_time_launch) {
// just for sanity...
static bool did_init = false;
assert(!did_init);
VBAM_CHECK(!did_init);
did_init = true;
// enumvals should not be translated, since they would cause config file
@@ -416,11 +416,9 @@ void update_shortcut_opts() {
// For keyboard shortcuts, it's easier to delete everything and start over.
cfg->DeleteGroup("/Keyboard");
cfg->SetPath("/Keyboard");
for (const auto& iter : wxGetApp().bindings()->GetKeyboardConfiguration()) {
cfg->Write(iter.first, iter.second);
}
cfg->SetPath("/");
// For joypads, we just compare the strings.
bool game_bindings_changed = false;
@@ -447,8 +445,7 @@ void opt_set(const wxString& name, const wxString& val) {
if (opt && !opt->is_none()) {
switch (opt->type()) {
case config::Option::Type::kNone:
// This should never happen.
assert(false);
VBAM_NOTREACHED();
return;
case config::Option::Type::kBool:
if (val != '0' && val != '1') {

View File

@@ -25,6 +25,7 @@
#include "components/filters/filters.h"
#include "components/filters_agb/filters_agb.h"
#include "components/filters_interframe/interframe.h"
#include "core/base/check.h"
#include "core/base/file_util.h"
#include "core/base/patch.h"
#include "core/base/system.h"
@@ -89,10 +90,10 @@ double GetFilterScale() {
return 6.0;
case config::Filter::kPlugin:
case config::Filter::kLast:
assert(false);
VBAM_NOTREACHED();
return 1.0;
}
assert(false);
VBAM_NOTREACHED();
return 1.0;
}
@@ -111,10 +112,10 @@ long GetSampleRate() {
return 11025;
break;
case config::AudioRate::kLast:
assert(false);
VBAM_NOTREACHED();
return 44100;
}
assert(false);
VBAM_NOTREACHED();
return 44100;
}
@@ -188,19 +189,19 @@ void GameArea::LoadGame(const wxString& name)
wxString rp = fnfn.GetPath();
// can't really decide which dir to use, so try GBA first, then GB
const wxString& gba_rom_dir = OPTION(kGBAROMDir);
const wxString gba_rom_dir = OPTION(kGBAROMDir);
if (!wxGetApp().GetAbsolutePath(gba_rom_dir).empty()) {
fnfn.SetPath(wxGetApp().GetAbsolutePath(gba_rom_dir) + '/' + rp);
badfile = !fnfn.IsFileReadable();
}
const wxString& gb_rom_dir = OPTION(kGBROMDir);
const wxString gb_rom_dir = OPTION(kGBROMDir);
if (badfile && !wxGetApp().GetAbsolutePath(gb_rom_dir).empty()) {
fnfn.SetPath(wxGetApp().GetAbsolutePath(gb_rom_dir) + '/' + rp);
badfile = !fnfn.IsFileReadable();
}
const wxString& gbc_rom_dir = OPTION(kGBGBCROMDir);
const wxString gbc_rom_dir = OPTION(kGBGBCROMDir);
if (badfile && !wxGetApp().GetAbsolutePath(gbc_rom_dir).empty()) {
fnfn.SetPath(wxGetApp().GetAbsolutePath(gbc_rom_dir) + '/' + rp);
badfile = !fnfn.IsFileReadable();
@@ -1169,7 +1170,7 @@ void GameArea::OnIdle(wxIdleEvent& event)
break;
#endif
case config::RenderMethod::kLast:
assert(false);
VBAM_NOTREACHED();
return;
}
@@ -1615,7 +1616,7 @@ private:
break;
case config::Interframe::kLast:
assert(false);
VBAM_NOTREACHED();
break;
}
}
@@ -1758,7 +1759,7 @@ private:
case config::Filter::kNone:
case config::Filter::kLast:
assert(false);
VBAM_NOTREACHED();
break;
}
}
@@ -2367,13 +2368,6 @@ void GLDrawingPanel::AdjustViewport()
int x, y;
widgets::GetRealPixelClientSize(this, &x, &y);
glViewport(0, 0, x, y);
#ifdef DEBUG
// you can use this to check that the gl surface is indeed high res
GLint m_viewport[4];
glGetIntegerv(GL_VIEWPORT, m_viewport);
wxLogDebug(wxT("GL VIEWPORT: %d, %d, %d, %d"), m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3]);
#endif
}
void GLDrawingPanel::RefreshGL()

View File

@@ -6,10 +6,8 @@ if(NOT BUILD_TESTING)
endif()
add_library(vbam-wx-fake-opts OBJECT)
target_sources(vbam-wx-fake-opts
PRIVATE
fake_opts.cpp
fake-opts.cpp
)
configure_wx_target(vbam-wx-fake-opts)

View File

@@ -0,0 +1,72 @@
# This defines the vbam-wx-widgets target.
# This does not export any localizable file as there is no localized string
# in this target.
add_library(vbam-wx-widgets OBJECT)
target_sources(vbam-wx-widgets
PRIVATE
# from external source with minor modifications
checkedlistctrl.cpp
$<IF:$<BOOL:${APPLE}>,dpi-support-mac.mm,dpi-support.cpp>
group-check-box.cpp
keep-on-top-styler.cpp
option-validator.cpp
render-plugin.cpp
user-input-ctrl.cpp
user-input-event.cpp
sdl-poller.cpp
shortcut-menu-bar.cpp
utils.cpp
wxmisc.cpp
PUBLIC
# from external source with minor modifications
checkedlistctrl.h
client-data.h
dpi-support.h
event-handler-provider.h
group-check-box.h
keep-on-top-styler.h
option-validator.h
render-plugin.h
user-input-ctrl.h
user-input-event.h
sdl-poller.h
shortcut-menu-bar.h
utils.h
wxmisc.h
)
target_link_libraries(vbam-wx-widgets PUBLIC vbam-wx-config)
configure_wx_target(vbam-wx-widgets)
if(BUILD_TESTING)
add_executable(vbam-wx-widgets-tests
client-data-test.cpp
group-check-box-test.cpp
keep-on-top-styler-test.cpp
option-validator-test.cpp
user-input-ctrl-test.cpp
)
target_link_libraries(vbam-wx-widgets-tests
# Test deps.
vbam-core-base-test
vbam-core-fake
vbam-wx-fake-opts
vbam-wx-widgets-test-fixture
# Target deps.
vbam-wx-config
vbam-wx-widgets
GTest::gtest_main
)
configure_wx_target(vbam-wx-widgets-tests)
if (NOT CMAKE_CROSSCOMPILING)
gtest_discover_tests(vbam-wx-widgets-tests)
endif()
endif()
add_subdirectory(test)

View File

@@ -13,10 +13,21 @@
#define VBAM_WX_WIDGETS_CHECKEDLISTCTRL_H_
// wxWidgets headers
#include "wx/widgets/webupdatedef.h" // for the WXDLLIMPEXP_WEBUPDATE macro
#include <wx/imaglist.h>
#include <wx/listctrl.h>
// this will never be part of a DLL
#define WXDLLIMPEXP_WEBUPDATE
// why would I include it and not enable it?
#define wxUSE_CHECKEDLISTCTRL 1
// enable customizations:
// - include wx/settings.h (bugfix; always enabled)
// - make a dynamic class so it can be subclass in xrc
// - only make col0 checkable
// - use "native" checkbox (also requires CLC_USE_SYSICONS)
#define CLC_VBAM_USAGE 1
#define CLC_USE_SYSICONS 1
#if wxUSE_CHECKEDLISTCTRL
// image indexes (used internally by wxCheckedListCtrl)

View File

@@ -0,0 +1,30 @@
#include "wx/widgets/client-data.h"
#include <memory>
#include <gtest/gtest.h>
#include <wx/window.h>
#include "wx/config/user-input.h"
namespace widgets {
// Test the ClientData class with a simple type.
TEST(ClientDataTest, Int) {
std::unique_ptr<wxWindow> window(std::make_unique<wxWindow>());
window->SetClientObject(new ClientData<int>(42));
EXPECT_EQ(ClientData<int>::From(window.get()), 42);
}
// Test the ClientData class with a complex type.
TEST(ClientDataTest, Object) {
std::unique_ptr<wxWindow> window(std::make_unique<wxWindow>());
window->SetClientObject(new ClientData<config::UserInput>(config::KeyboardInput('4')));
EXPECT_EQ(ClientData<config::UserInput>::From(window.get()).device(),
config::UserInput::Device::Keyboard);
}
} // namespace widgets

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