mirror of
https://github.com/visualboyadvance-m/visualboyadvance-m
synced 2025-10-05 23:52:49 +02:00
Compare commits
49 Commits
linux-lto
...
user-input
Author | SHA1 | Date | |
---|---|---|---|
|
91b0bbaebd | ||
|
cc65ef2849 | ||
|
32627f6b85 | ||
|
41952d0625 | ||
|
0c39a5ba7a | ||
|
1b77d6594c | ||
|
8d08223dbc | ||
|
af6028a9dd | ||
|
ff3b5ee042 | ||
|
b52edf52ff | ||
|
6766b9ca54 | ||
|
8eae2e5b90 | ||
|
50d17363ea | ||
|
795f25bb85 | ||
|
647be137f6 | ||
|
8abe3e79da | ||
|
710ffeb1b2 | ||
|
a855ff54f4 | ||
|
dbb5e534e4 | ||
|
753956963c | ||
|
b00e23f5b5 | ||
|
8ef9a66b74 | ||
|
0e503a525e | ||
|
b4b020401c | ||
|
f4835674ed | ||
|
4104a3d179 | ||
|
1e1ec2e330 | ||
|
ff21f8da21 | ||
|
775a571f75 | ||
|
64abd3e8dc | ||
|
56320ec6d1 | ||
|
311b232ee5 | ||
|
0e13cc9346 | ||
|
b455de01c6 | ||
|
c3053d3819 | ||
|
c8106573d8 | ||
|
047ad27777 | ||
|
ecd16a21dc | ||
|
1594fda1b6 | ||
|
de9b3a211b | ||
|
045c98d8fa | ||
|
4ace296b3a | ||
|
011adce23e | ||
|
cc99ec0c14 | ||
|
1d652edf83 | ||
|
db08ca93af | ||
|
3518dc6a05 | ||
|
cc9a03ce48 | ||
|
8576733c0d |
42
.github/workflows/macos-build.yml
vendored
42
.github/workflows/macos-build.yml
vendored
@@ -1,27 +1,57 @@
|
||||
name: macOS Latest Build
|
||||
on: [push, pull_request]
|
||||
#on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
cmake_build: ['-DCMAKE_BUILD_TYPE=Release', '-DCMAKE_BUILD_TYPE=Debug']
|
||||
cmake_options: ['', '-DENABLE_LINK=OFF', '-DENABLE_SDL=ON']
|
||||
build_type: [release, debug]
|
||||
build_options: [default, link_off, translations_only, libretro]
|
||||
include:
|
||||
- cmake_build: '-DCMAKE_BUILD_TYPE=Release'
|
||||
build_type: release
|
||||
- cmake_build: '-DCMAKE_BUILD_TYPE=Debug'
|
||||
build_type: debug
|
||||
- cmake_options: '-DENABLE_LINK=OFF'
|
||||
build_options: link_off
|
||||
- cmake_options: '-DTRANSLATIONS_ONLY=ON'
|
||||
build_options: translations_only
|
||||
- libretro_build: 'DEBUG=0'
|
||||
build_type: release
|
||||
build_options: libretro
|
||||
- libretro_build: 'DEBUG=1'
|
||||
build_type: debug
|
||||
build_options: libretro
|
||||
exclude:
|
||||
# Exclude debug/translations_only build
|
||||
- build_type: debug
|
||||
build_options: translations_only
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM: 1
|
||||
|
||||
steps:
|
||||
- name: Checkout the code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nix
|
||||
uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- name: Configure
|
||||
|
||||
# Cmake build
|
||||
- if: matrix.build_options != 'libretro'
|
||||
name: Configure CMake
|
||||
run: >-
|
||||
nix-shell --command 'cmake -B build -G Ninja ${{ matrix.cmake_build }} -DENABLE_LTO=OFF ${{ matrix.cmake_options }}'
|
||||
- name: Build
|
||||
- if: matrix.build_options != 'libretro'
|
||||
name: Build
|
||||
run: >-
|
||||
nix-shell --command 'ninja -C build'
|
||||
|
||||
# Libretro build
|
||||
- if: matrix.build_options == 'libretro'
|
||||
name: Build libretro core
|
||||
run: >-
|
||||
nix-shell --command 'make -C src/libretro ${{ matrix.libretro_build }}'
|
||||
|
45
.github/workflows/msys2-build.yml
vendored
45
.github/workflows/msys2-build.yml
vendored
@@ -5,8 +5,27 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
cmake_build: ['-DCMAKE_BUILD_TYPE=Release', '-DCMAKE_BUILD_TYPE=Debug']
|
||||
cmake_options: ['', '-DENABLE_LINK=OFF', '-DENABLE_SDL=ON']
|
||||
build_type: [release, debug]
|
||||
build_options: [default, link_off, translations_only, libretro]
|
||||
include:
|
||||
- cmake_build: '-DCMAKE_BUILD_TYPE=Release'
|
||||
build_type: release
|
||||
- cmake_build: '-DCMAKE_BUILD_TYPE=Debug'
|
||||
build_type: debug
|
||||
- cmake_options: '-DENABLE_LINK=OFF'
|
||||
build_options: link_off
|
||||
- cmake_options: '-DTRANSLATIONS_ONLY=ON'
|
||||
build_options: translations_only
|
||||
- libretro_build: 'DEBUG=0'
|
||||
build_type: release
|
||||
build_options: libretro
|
||||
- libretro_build: 'DEBUG=1'
|
||||
build_type: debug
|
||||
build_options: libretro
|
||||
exclude:
|
||||
# Exclude debug/translations_only build
|
||||
- build_type: debug
|
||||
build_options: translations_only
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
MSYSTEM: CLANG64
|
||||
@@ -16,7 +35,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout the code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup msys2
|
||||
@@ -27,11 +46,19 @@ jobs:
|
||||
- name: Install deps
|
||||
run: >-
|
||||
bash installdeps
|
||||
- name: Configure
|
||||
run: >-
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug ${{ matrix.cmake_build }} ${{ matrix.cmake_options }}
|
||||
- name: Build
|
||||
run: ninja -C build
|
||||
|
||||
- name: Install
|
||||
# CMake build
|
||||
- if: matrix.build_options != 'libretro'
|
||||
name: Configure CMake
|
||||
run: cmake -B build -G Ninja ${{ matrix.cmake_build }} -DENABLE_LTO=OFF ${{ matrix.cmake_options }}
|
||||
- if: matrix.build_options != 'libretro'
|
||||
name: Build
|
||||
run: ninja -C build
|
||||
- if: matrix.build_options != 'libretro'
|
||||
name: Install
|
||||
run: ninja -C build install
|
||||
|
||||
# Libretro build
|
||||
- if: matrix.build_options == 'libretro'
|
||||
name: Build libretro core
|
||||
run: make -C src/libretro ${{ matrix.libretro_build }} CC=clang CXX=clang++
|
||||
|
53
.github/workflows/ubuntu-build.yml
vendored
53
.github/workflows/ubuntu-build.yml
vendored
@@ -5,26 +5,59 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
cmake_compiler: ['-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++', '-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++']
|
||||
cmake_build: ['-DCMAKE_BUILD_TYPE=Release', '-DCMAKE_BUILD_TYPE=Debug']
|
||||
cmake_options: ['', '-DENABLE_LINK=OFF', '-DENABLE_SDL=ON']
|
||||
build_compiler: [gcc, clang]
|
||||
build_type: [release, debug]
|
||||
build_options: [default, link_off, translations_only, libretro]
|
||||
include:
|
||||
- cmake_compiler: '-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++'
|
||||
build_compiler: gcc
|
||||
- cmake_compiler: '-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++'
|
||||
build_compiler: clang
|
||||
- cmake_build: '-DCMAKE_BUILD_TYPE=Release'
|
||||
build_type: release
|
||||
- cmake_build: '-DCMAKE_BUILD_TYPE=Debug'
|
||||
build_type: debug
|
||||
- cmake_options: '-DENABLE_LINK=OFF'
|
||||
build_options: link_off
|
||||
- cmake_options: '-DTRANSLATIONS_ONLY=ON'
|
||||
build_options: translations_only
|
||||
- libretro_build: 'DEBUG=0'
|
||||
build_type: release
|
||||
build_options: libretro
|
||||
- libretro_build: 'DEBUG=1'
|
||||
build_type: debug
|
||||
build_options: libretro
|
||||
exclude:
|
||||
# Exclude debug/translations_only build
|
||||
- build_type: debug
|
||||
build_options: translations_only
|
||||
- build_type: release
|
||||
build_options: translations_only
|
||||
build_compiler: clang
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Dependencies
|
||||
run: >-
|
||||
bash installdeps; if [ "${{ matrix.compiler }}" = clang ]; then sudo apt -y install clang; fi
|
||||
bash installdeps; if [ "${{ matrix.build_compiler }}" = clang ]; then sudo apt -y install clang; fi
|
||||
|
||||
- name: Configure
|
||||
# CMake build
|
||||
- if: matrix.build_options != 'libretro'
|
||||
name: Configure CMake
|
||||
run: >-
|
||||
cmake -B build -G Ninja ${{ matrix.cmake_compiler }} ${{ matrix.cmake_build }} ${{ matrix.cmake_options }}
|
||||
|
||||
- name: Build
|
||||
- if: matrix.build_options != 'libretro'
|
||||
name: Build
|
||||
run: ninja -C build
|
||||
|
||||
- name: Install
|
||||
- if: matrix.build_options != 'libretro'
|
||||
name: Install
|
||||
run: sudo ninja -C build install
|
||||
|
||||
# Libretro build
|
||||
- name: Build libretro core
|
||||
if: matrix.build_options == 'libretro'
|
||||
run: make -C src/libretro ${{ matrix.libretro_build }}
|
||||
|
47
.github/workflows/visual-studio-build.yml
vendored
47
.github/workflows/visual-studio-build.yml
vendored
@@ -5,32 +5,51 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
msvc_arch: ['x64', 'x64_x86']
|
||||
msvc_arch: ['x64', 'amd64_x86', 'amd64_arm64']
|
||||
# TODO: Re-add "Visual Studio 17 2022" once it's working.
|
||||
cmake_generator: ['Ninja']
|
||||
# cmake_generator: ['Ninja', '"Visual Studio 17 2022"']
|
||||
cmake_build: ['-DCMAKE_BUILD_TYPE=Release', '-DCMAKE_BUILD_TYPE=Debug']
|
||||
cmake_options: ['', '-DENABLE_LINK=OFF', '-DENABLE_SDL=ON']
|
||||
build_type: [release, debug]
|
||||
build_options: [default, link_off, translations_only]
|
||||
include:
|
||||
- cmake_build: '-DCMAKE_BUILD_TYPE=Release'
|
||||
build_type: release
|
||||
- cmake_build: '-DCMAKE_BUILD_TYPE=Debug'
|
||||
build_type: debug
|
||||
- cmake_options: '-DENABLE_LINK=OFF'
|
||||
build_options: link_off
|
||||
- cmake_options: '-DTRANSLATIONS_ONLY=ON'
|
||||
build_options: translations_only
|
||||
- cmake_vcpkg_triplet: 'x64-windows-static'
|
||||
msvc_arch: x64
|
||||
- cmake_vcpkg_triplet: 'x86-windows-static'
|
||||
msvc_arch: amd64_x86
|
||||
- cmake_vcpkg_triplet: 'arm64-windows-static'
|
||||
msvc_arch: amd64_arm64
|
||||
exclude:
|
||||
# Exclude debug/translations_only build
|
||||
- build_type: debug
|
||||
build_options: translations_only
|
||||
- build_type: release
|
||||
build_options: translations_only
|
||||
msvc_arch: amd64_x86
|
||||
- build_type: release
|
||||
build_options: translations_only
|
||||
msvc_arch: amd64_arm64
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout the code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Prepare Visual Studio environment
|
||||
uses: ilammy/msvc-dev-cmd@v1.12.0
|
||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: ${{ matrix.msvc_arch }}
|
||||
|
||||
- name: Configure (x64)
|
||||
if: matrix.msvc_arch == 'x64'
|
||||
- name: Configure
|
||||
run: >-
|
||||
cmake -B build -G ${{ matrix.cmake_generator }} -DVCPKG_TARGET_TRIPLET=x64-windows-static ${{ matrix.cmake_build }} ${{ matrix.cmake_options }}
|
||||
|
||||
- name: Configure (x86)
|
||||
if: matrix.msvc_arch == 'x64_x86'
|
||||
run: >-
|
||||
cmake -B build -G ${{ matrix.cmake_generator }} -DVCPKG_TARGET_TRIPLET=x86-windows-static ${{ matrix.cmake_build }} ${{ matrix.cmake_options }}
|
||||
cmake -B build -G ${{ matrix.cmake_generator }} -DVCPKG_TARGET_TRIPLET=${{ matrix.cmake_vcpkg_triplet }} ${{ matrix.cmake_build }} ${{ matrix.cmake_options }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build
|
||||
|
174
CMakeLists.txt
174
CMakeLists.txt
@@ -77,8 +77,6 @@ set(CMAKE_C_STANDARD_REQUIRED True)
|
||||
|
||||
project(VBA-M C CXX)
|
||||
|
||||
find_package(PkgConfig)
|
||||
|
||||
if(NOT CMAKE_PREFIX_PATH AND (NOT ("$ENV{CMAKE_PREFIX_PATH}" STREQUAL "")))
|
||||
set(CMAKE_PREFIX_PATH "$ENV{CMAKE_PREFIX_PATH}")
|
||||
endif()
|
||||
@@ -108,26 +106,7 @@ include(GNUInstallDirs)
|
||||
include(Options)
|
||||
include(Architecture)
|
||||
include(Toolchain)
|
||||
|
||||
#Output all binaries at top level
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
|
||||
|
||||
if(NOT HTTPS)
|
||||
add_compile_definitions(NO_HTTPS)
|
||||
endif()
|
||||
|
||||
if(ENABLE_GBA_LOGGING)
|
||||
add_compile_definitions(GBA_LOGGING )
|
||||
endif()
|
||||
|
||||
if(ENABLE_MMX)
|
||||
add_compile_definitions(MMX)
|
||||
endif()
|
||||
|
||||
# The SDL port can't be built without debugging support
|
||||
if(NOT ENABLE_DEBUGGER AND ENABLE_SDL)
|
||||
message(SEND_ERROR "The SDL port can't be built without debugging support")
|
||||
endif()
|
||||
include(Dependencies)
|
||||
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
include(GitTagVersion)
|
||||
@@ -185,159 +164,8 @@ if(NOT VBAM_VERSION)
|
||||
changelog_version(VBAM_VERSION VBAM_REVISION VBAM_VERSION_RELEASE)
|
||||
endif()
|
||||
|
||||
# We do not support amd64 asm yet
|
||||
if(X86_64 AND (ENABLE_ASM_CORE OR ENABLE_ASM_SCALERS OR ENABLE_MMX))
|
||||
message(FATAL_ERROR "The options ASM_CORE, ASM_SCALERS and MMX are not supported on X86_64 yet.")
|
||||
endif()
|
||||
|
||||
# Look for some dependencies using CMake scripts
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
set(OpenGL_GL_PREFERENCE GLVND)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
||||
set(OpenGL_GL_PREFERENCE LEGACY)
|
||||
endif()
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
|
||||
# Add libsamplerate to SDL2 with vcpkg
|
||||
unset(SDL2_LIBRARY_TEMP)
|
||||
if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
|
||||
if(WIN32)
|
||||
unset(arch_suffix)
|
||||
unset(path_prefix)
|
||||
if(VCPKG_TARGET_TRIPLET MATCHES -static)
|
||||
set(arch_suffix -static)
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo)$")
|
||||
set(path_prefix debug)
|
||||
endif()
|
||||
set(installed_prefix ${_VCPKG_INSTALLED_DIR}/${WINARCH}-windows${arch_suffix}/${path_prefix})
|
||||
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${installed_prefix}/lib/samplerate.lib)
|
||||
else()
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} -lsamplerate)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(VBAM_STATIC)
|
||||
set(VBAM_SDL2_LIBS SDL2::SDL2-static ${SDL2_LIBRARY_TEMP})
|
||||
else()
|
||||
set(VBAM_SDL2_LIBS SDL2::SDL2 ${SDL2_LIBRARY_TEMP})
|
||||
endif()
|
||||
|
||||
set(VBAM_GENERATED_DIR ${CMAKE_BINARY_DIR}/generated)
|
||||
|
||||
# Set up "src" and generated directory as a global include directory.
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${VBAM_GENERATED_DIR}
|
||||
)
|
||||
|
||||
if(ENABLE_FFMPEG)
|
||||
if(NOT FFMPEG_LIBRARIES)
|
||||
message(FATAL_ERROR "ENABLE_FFMPEG was specified, but required versions of ffmpeg libraries cannot be found!")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND FFMPEG_LDFLAGS "SHELL:-framework CoreText" "SHELL:-framework ApplicationServices")
|
||||
|
||||
if(UPSTREAM_RELEASE)
|
||||
list(APPEND FFMPEG_LDFLAGS -lbz2 -ltiff "SHELL:-framework DiskArbitration" -lfreetype -lfontconfig -llzma -lxml2 -lharfbuzz)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(WIN32_MEDIA_FOUNDATION_LIBS dxva2 evr mf mfplat mfplay mfreadwrite mfuuid amstrmid)
|
||||
list(APPEND FFMPEG_LIBRARIES secur32 bcrypt ${WIN32_MEDIA_FOUNDATION_LIBS})
|
||||
|
||||
if(MSYS AND VBAM_STATIC)
|
||||
foreach(lib tiff jbig lzma)
|
||||
cygpath(lib "$ENV{MSYSTEM_PREFIX}/lib/lib${lib}.a")
|
||||
|
||||
list(APPEND FFMPEG_LIBRARIES "${lib}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
add_compile_definitions(NO_FFMPEG)
|
||||
endif()
|
||||
|
||||
if(NOT ENABLE_ONLINEUPDATES)
|
||||
add_compile_definitions(NO_ONLINEUPDATES)
|
||||
endif()
|
||||
|
||||
# C defines
|
||||
add_compile_definitions(HAVE_NETINET_IN_H HAVE_ARPA_INET_H HAVE_ZLIB_H FINAL_VERSION SDL USE_OPENGL SYSCONF_INSTALL_DIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}")
|
||||
add_compile_definitions(PKGDATADIR="${CMAKE_INSTALL_FULL_DATADIR}/vbam")
|
||||
add_compile_definitions(__STDC_FORMAT_MACROS)
|
||||
|
||||
if(ENABLE_LINK)
|
||||
# IPC linking code needs sem_timedwait which can be either in librt or pthreads
|
||||
if(NOT WIN32)
|
||||
find_library(RT_LIB rt)
|
||||
if(RT_LIB)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${RT_LIB})
|
||||
set(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${RT_LIB})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckFunctionExists)
|
||||
check_function_exists(sem_timedwait SEM_TIMEDWAIT)
|
||||
if(SEM_TIMEDWAIT)
|
||||
add_compile_definitions(HAVE_SEM_TIMEDWAIT)
|
||||
endif()
|
||||
else()
|
||||
add_compile_definitions(NO_LINK)
|
||||
endif()
|
||||
|
||||
# The debugger is enabled by default
|
||||
if(ENABLE_DEBUGGER)
|
||||
add_compile_definitions(VBAM_ENABLE_DEBUGGER)
|
||||
endif()
|
||||
|
||||
# The ASM core is disabled by default because we don't know on which platform we are
|
||||
if(NOT ENABLE_ASM_CORE)
|
||||
add_compile_definitions(C_CORE)
|
||||
endif()
|
||||
|
||||
# Enable internationalization
|
||||
set(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale)
|
||||
add_compile_definitions(LOCALEDIR="${LOCALEDIR}")
|
||||
|
||||
# for now, only GBALink.cpp uses gettext() directly
|
||||
if(APPLE)
|
||||
# use Homebrew gettext if available
|
||||
if(EXISTS "/usr/local/opt/gettext")
|
||||
set(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH};/usr/local/opt/gettext/include")
|
||||
set(CMAKE_LIBRARY_PATH "${CMAKE_LIBRARY_PATH};/usr/local/opt/gettext/lib")
|
||||
set(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};/usr/local/opt/gettext/bin")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_LINK OR ENABLE_WX)
|
||||
find_path(LIBINTL_INC libintl.h)
|
||||
find_library(LIBINTL_LIB NAMES libintl intl)
|
||||
find_library(LIBICONV_LIB NAMES libiconv iconv)
|
||||
find_library(LIBCHARSET_LIB NAMES libcharset charset)
|
||||
if(LIBINTL_LIB)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBINTL_LIB})
|
||||
list(APPEND NLS_LIBS ${LIBINTL_LIB})
|
||||
endif()
|
||||
if(LIBICONV_LIB)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBICONV_LIB})
|
||||
list(APPEND NLS_LIBS ${LIBICONV_LIB})
|
||||
endif()
|
||||
if(LIBCHARSET_LIB)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBCHARSET_LIB})
|
||||
list(APPEND NLS_LIBS ${LIBCHARSET_LIB})
|
||||
endif()
|
||||
include(CheckFunctionExists)
|
||||
check_function_exists(gettext GETTEXT_FN)
|
||||
if(NOT (LIBINTL_INC OR GETTEXT_FN))
|
||||
message(FATAL_ERROR "NLS requires libintl/gettext")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TRANSLATIONS_ONLY)
|
||||
add_subdirectory(third_party/include/nonstd)
|
||||
|
13
README.md
13
README.md
@@ -3,6 +3,7 @@
|
||||
|
||||
|
||||
- [Visual Boy Advance - M](#visual-boy-advance---m)
|
||||
- [System Requirements](#system-requirements)
|
||||
- [Building](#building)
|
||||
- [Building a Libretro core](#building-a-libretro-core)
|
||||
- [Visual Studio Support](#visual-studio-support)
|
||||
@@ -56,6 +57,17 @@ the `translations.zip` to the same directory as the executable.
|
||||
If you are having issues, try resetting the config file first, go to `Help ->
|
||||
Factory Reset`.
|
||||
|
||||
## System Requirements
|
||||
|
||||
Windows 7, 8.1 or 10/11, Linux distro's 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.
|
||||
- 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.
|
||||
|
||||
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:
|
||||
@@ -217,7 +229,6 @@ Here is the complete list:
|
||||
| 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_OPENAL | Enable OpenAL for the wxWidgets port | AUTO |
|
||||
| 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 |
|
||||
|
@@ -1,3 +1,7 @@
|
||||
if(TRANSLATIONS_ONLY)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SYSTEM_PROCESSOR)
|
||||
if(NOT CMAKE_TOOLCHAIN_FILE AND CMAKE_HOST_SYSTEM_PROCESSOR)
|
||||
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR})
|
||||
@@ -63,3 +67,8 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "[aA][aA][rR][cC][hH]|[aA][rR][mM]")
|
||||
set(CMAKE_CROSSCOMPILING TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# We do not support amd64 asm yet
|
||||
if(X86_64 AND (ENABLE_ASM_CORE OR ENABLE_ASM_SCALERS OR ENABLE_MMX))
|
||||
message(FATAL_ERROR "The options ASM_CORE, ASM_SCALERS and MMX are not supported on X86_64 yet.")
|
||||
endif()
|
||||
|
122
cmake/Dependencies.cmake
Normal file
122
cmake/Dependencies.cmake
Normal file
@@ -0,0 +1,122 @@
|
||||
if(TRANSLATIONS_ONLY)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Look for some dependencies using CMake scripts
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
set(OpenGL_GL_PREFERENCE GLVND)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
||||
set(OpenGL_GL_PREFERENCE LEGACY)
|
||||
endif()
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
|
||||
# Add libsamplerate to SDL2 with vcpkg
|
||||
unset(SDL2_LIBRARY_TEMP)
|
||||
if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
|
||||
if(WIN32)
|
||||
unset(arch_suffix)
|
||||
unset(path_prefix)
|
||||
if(VCPKG_TARGET_TRIPLET MATCHES -static)
|
||||
set(arch_suffix -static)
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo)$")
|
||||
set(path_prefix debug)
|
||||
endif()
|
||||
set(installed_prefix ${_VCPKG_INSTALLED_DIR}/${WINARCH}-windows${arch_suffix}/${path_prefix})
|
||||
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${installed_prefix}/lib/samplerate.lib)
|
||||
else()
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} -lsamplerate)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(VBAM_STATIC)
|
||||
set(VBAM_SDL2_LIBS SDL2::SDL2-static ${SDL2_LIBRARY_TEMP})
|
||||
else()
|
||||
set(VBAM_SDL2_LIBS SDL2::SDL2 ${SDL2_LIBRARY_TEMP})
|
||||
endif()
|
||||
|
||||
if(ENABLE_FFMPEG)
|
||||
if(NOT FFMPEG_LIBRARIES)
|
||||
message(FATAL_ERROR "ENABLE_FFMPEG was specified, but required versions of ffmpeg libraries cannot be found!")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND FFMPEG_LDFLAGS "SHELL:-framework CoreText" "SHELL:-framework ApplicationServices")
|
||||
|
||||
if(UPSTREAM_RELEASE)
|
||||
list(APPEND FFMPEG_LDFLAGS -lbz2 -ltiff "SHELL:-framework DiskArbitration" -lfreetype -lfontconfig -llzma -lxml2 -lharfbuzz)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(WIN32_MEDIA_FOUNDATION_LIBS dxva2 evr mf mfplat mfplay mfreadwrite mfuuid amstrmid)
|
||||
list(APPEND FFMPEG_LIBRARIES secur32 bcrypt ${WIN32_MEDIA_FOUNDATION_LIBS})
|
||||
|
||||
if(MSYS AND VBAM_STATIC)
|
||||
foreach(lib tiff jbig lzma)
|
||||
cygpath(lib "$ENV{MSYSTEM_PREFIX}/lib/lib${lib}.a")
|
||||
|
||||
list(APPEND FFMPEG_LIBRARIES "${lib}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
add_compile_definitions(NO_FFMPEG)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LINK)
|
||||
# IPC linking code needs sem_timedwait which can be either in librt or pthreads
|
||||
if(NOT WIN32)
|
||||
find_library(RT_LIB rt)
|
||||
if(RT_LIB)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${RT_LIB})
|
||||
set(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${RT_LIB})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckFunctionExists)
|
||||
check_function_exists(sem_timedwait SEM_TIMEDWAIT)
|
||||
if(SEM_TIMEDWAIT)
|
||||
add_compile_definitions(HAVE_SEM_TIMEDWAIT)
|
||||
endif()
|
||||
else()
|
||||
add_compile_definitions(NO_LINK)
|
||||
endif()
|
||||
|
||||
# for now, only GBALink.cpp uses gettext() directly
|
||||
if(APPLE)
|
||||
# use Homebrew gettext if available
|
||||
if(EXISTS "/usr/local/opt/gettext")
|
||||
set(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH};/usr/local/opt/gettext/include")
|
||||
set(CMAKE_LIBRARY_PATH "${CMAKE_LIBRARY_PATH};/usr/local/opt/gettext/lib")
|
||||
set(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};/usr/local/opt/gettext/bin")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_LINK OR ENABLE_WX)
|
||||
find_path(LIBINTL_INC libintl.h)
|
||||
find_library(LIBINTL_LIB NAMES libintl intl)
|
||||
find_library(LIBICONV_LIB NAMES libiconv iconv)
|
||||
find_library(LIBCHARSET_LIB NAMES libcharset charset)
|
||||
if(LIBINTL_LIB)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBINTL_LIB})
|
||||
list(APPEND NLS_LIBS ${LIBINTL_LIB})
|
||||
endif()
|
||||
if(LIBICONV_LIB)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBICONV_LIB})
|
||||
list(APPEND NLS_LIBS ${LIBICONV_LIB})
|
||||
endif()
|
||||
if(LIBCHARSET_LIB)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBCHARSET_LIB})
|
||||
list(APPEND NLS_LIBS ${LIBCHARSET_LIB})
|
||||
endif()
|
||||
include(CheckFunctionExists)
|
||||
check_function_exists(gettext GETTEXT_FN)
|
||||
if(NOT (LIBINTL_INC OR GETTEXT_FN))
|
||||
message(FATAL_ERROR "NLS requires libintl/gettext")
|
||||
endif()
|
||||
endif()
|
||||
|
@@ -1,231 +1,232 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#.rst:
|
||||
# FindGettext
|
||||
# -----------
|
||||
#
|
||||
# Find GNU gettext tools
|
||||
#
|
||||
# This module looks for the GNU gettext tools. This module defines the
|
||||
# following values:
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# GETTEXT_MSGMERGE_EXECUTABLE: the full path to the msgmerge tool.
|
||||
# GETTEXT_MSGFMT_EXECUTABLE: the full path to the msgfmt tool.
|
||||
# GETTEXT_FOUND: True if gettext has been found.
|
||||
# GETTEXT_VERSION_STRING: the version of gettext found (since CMake 2.8.8)
|
||||
#
|
||||
#
|
||||
#
|
||||
# Additionally it provides the following macros:
|
||||
#
|
||||
# GETTEXT_CREATE_TRANSLATIONS ( outputFile [ALL] file1 ... fileN )
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# This will create a target "translations" which will convert the
|
||||
# given input po files into the binary output mo file. If the
|
||||
# ALL option is used, the translations will also be created when
|
||||
# building the default target.
|
||||
#
|
||||
# GETTEXT_PROCESS_POT_FILE( <potfile> [ALL] [INSTALL_DESTINATION <destdir>]
|
||||
# LANGUAGES <lang1> <lang2> ... )
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# Process the given pot file to mo files.
|
||||
# If INSTALL_DESTINATION is given then automatically install rules will
|
||||
# be created, the language subdirectory will be taken into account
|
||||
# (by default use share/locale/).
|
||||
# If ALL is specified, the pot file is processed when building the all traget.
|
||||
# It creates a custom target "potfile".
|
||||
#
|
||||
# GETTEXT_PROCESS_PO_FILES( <lang> [ALL] [INSTALL_DESTINATION <dir>]
|
||||
# PO_FILES <po1> <po2> ... )
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# Process the given po files to mo files for the given language.
|
||||
# If INSTALL_DESTINATION is given then automatically install rules will
|
||||
# be created, the language subdirectory will be taken into account
|
||||
# (by default use share/locale/).
|
||||
# If ALL is specified, the po files are processed when building the all traget.
|
||||
# It creates a custom target "pofiles".
|
||||
#
|
||||
# .. note::
|
||||
# If you wish to use the Gettext library (libintl), use :module:`FindIntl`.
|
||||
#[=======================================================================[.rst:
|
||||
FindGettext
|
||||
-----------
|
||||
|
||||
Find GNU gettext tools
|
||||
|
||||
This module looks for the GNU gettext tools. This module defines the
|
||||
following values:
|
||||
|
||||
::
|
||||
|
||||
GETTEXT_MSGMERGE_EXECUTABLE: the full path to the msgmerge tool.
|
||||
GETTEXT_MSGFMT_EXECUTABLE: the full path to the msgfmt tool.
|
||||
GETTEXT_FOUND: True if gettext has been found.
|
||||
GETTEXT_VERSION_STRING: the version of gettext found (since CMake 2.8.8)
|
||||
|
||||
|
||||
|
||||
Additionally it provides the following macros:
|
||||
|
||||
GETTEXT_CREATE_TRANSLATIONS ( outputFile [ALL] file1 ... fileN )
|
||||
|
||||
::
|
||||
|
||||
This will create a target "translations" which will convert the
|
||||
given input po files into the binary output mo file. If the
|
||||
ALL option is used, the translations will also be created when
|
||||
building the default target.
|
||||
|
||||
GETTEXT_PROCESS_POT_FILE( <potfile> [ALL] [INSTALL_DESTINATION <destdir>]
|
||||
LANGUAGES <lang1> <lang2> ... )
|
||||
|
||||
::
|
||||
|
||||
Process the given pot file to mo files.
|
||||
If INSTALL_DESTINATION is given then automatically install rules will
|
||||
be created, the language subdirectory will be taken into account
|
||||
(by default use share/locale/).
|
||||
If ALL is specified, the pot file is processed when building the all target.
|
||||
It creates a custom target "potfile".
|
||||
|
||||
GETTEXT_PROCESS_PO_FILES( <lang> [ALL] [INSTALL_DESTINATION <dir>]
|
||||
PO_FILES <po1> <po2> ... )
|
||||
|
||||
::
|
||||
|
||||
Process the given po files to mo files for the given language.
|
||||
If INSTALL_DESTINATION is given then automatically install rules will
|
||||
be created, the language subdirectory will be taken into account
|
||||
(by default use share/locale/).
|
||||
If ALL is specified, the po files are processed when building the all target.
|
||||
It creates a custom target "pofiles".
|
||||
|
||||
.. versionadded:: 3.2
|
||||
If you wish to use the Gettext library (libintl), use :module:`FindIntl`.
|
||||
#]=======================================================================]
|
||||
|
||||
find_program(GETTEXT_MSGMERGE_EXECUTABLE msgmerge)
|
||||
|
||||
find_program(GETTEXT_MSGFMT_EXECUTABLE msgfmt)
|
||||
|
||||
if(GETTEXT_MSGMERGE_EXECUTABLE)
|
||||
execute_process(COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} --version
|
||||
execute_process(COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} --version
|
||||
OUTPUT_VARIABLE gettext_version
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
get_filename_component(msgmerge_name ${GETTEXT_MSGMERGE_EXECUTABLE} NAME)
|
||||
get_filename_component(msgmerge_namewe ${GETTEXT_MSGMERGE_EXECUTABLE} NAME_WE)
|
||||
if (gettext_version MATCHES "^(${msgmerge_name}|${msgmerge_namewe}) \\([^\\)]*\\) ([0-9\\.]+[^ \n]*)")
|
||||
set(GETTEXT_VERSION_STRING "${CMAKE_MATCH_2}")
|
||||
endif()
|
||||
unset(gettext_version)
|
||||
unset(msgmerge_name)
|
||||
unset(msgmerge_namewe)
|
||||
get_filename_component(msgmerge_name ${GETTEXT_MSGMERGE_EXECUTABLE} NAME)
|
||||
get_filename_component(msgmerge_namewe ${GETTEXT_MSGMERGE_EXECUTABLE} NAME_WE)
|
||||
if (gettext_version MATCHES "^(${msgmerge_name}|${msgmerge_namewe}) \\([^\\)]*\\) ([0-9\\.]+[^ \n]*)")
|
||||
set(GETTEXT_VERSION_STRING "${CMAKE_MATCH_2}")
|
||||
endif()
|
||||
unset(gettext_version)
|
||||
unset(msgmerge_name)
|
||||
unset(msgmerge_namewe)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Gettext
|
||||
REQUIRED_VARS GETTEXT_MSGMERGE_EXECUTABLE GETTEXT_MSGFMT_EXECUTABLE
|
||||
VERSION_VAR GETTEXT_VERSION_STRING)
|
||||
|
||||
function(_GETTEXT_GET_UNIQUE_TARGET_NAME _name _unique_name)
|
||||
set(propertyName "_GETTEXT_UNIQUE_COUNTER_${_name}")
|
||||
get_property(currentCounter GLOBAL PROPERTY "${propertyName}")
|
||||
if(NOT currentCounter)
|
||||
set(currentCounter 1)
|
||||
endif()
|
||||
set(${_unique_name} "${_name}_${currentCounter}" PARENT_SCOPE)
|
||||
math(EXPR currentCounter "${currentCounter} + 1")
|
||||
set_property(GLOBAL PROPERTY ${propertyName} ${currentCounter} )
|
||||
set(propertyName "_GETTEXT_UNIQUE_COUNTER_${_name}")
|
||||
get_property(currentCounter GLOBAL PROPERTY "${propertyName}")
|
||||
if(NOT currentCounter)
|
||||
set(currentCounter 1)
|
||||
endif()
|
||||
set(${_unique_name} "${_name}_${currentCounter}" PARENT_SCOPE)
|
||||
math(EXPR currentCounter "${currentCounter} + 1")
|
||||
set_property(GLOBAL PROPERTY ${propertyName} ${currentCounter} )
|
||||
endfunction()
|
||||
|
||||
macro(GETTEXT_CREATE_TRANSLATIONS _potFile _firstPoFileArg)
|
||||
# make it a real variable, so we can modify it here
|
||||
set(_firstPoFile "${_firstPoFileArg}")
|
||||
# make it a real variable, so we can modify it here
|
||||
set(_firstPoFile "${_firstPoFileArg}")
|
||||
|
||||
set(_gmoFiles)
|
||||
get_filename_component(_potName ${_potFile} NAME)
|
||||
string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" _potBasename ${_potName})
|
||||
get_filename_component(_absPotFile ${_potFile} ABSOLUTE)
|
||||
set(_gmoFiles)
|
||||
get_filename_component(_potName ${_potFile} NAME)
|
||||
string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" _potBasename ${_potName})
|
||||
get_filename_component(_absPotFile ${_potFile} ABSOLUTE)
|
||||
|
||||
set(_addToAll)
|
||||
if(${_firstPoFile} STREQUAL "ALL")
|
||||
set(_addToAll "ALL")
|
||||
set(_firstPoFile)
|
||||
endif()
|
||||
set(_addToAll)
|
||||
if(${_firstPoFile} STREQUAL "ALL")
|
||||
set(_addToAll "ALL")
|
||||
set(_firstPoFile)
|
||||
endif()
|
||||
|
||||
foreach (_currentPoFile ${_firstPoFile} ${ARGN})
|
||||
get_filename_component(_absFile ${_currentPoFile} ABSOLUTE)
|
||||
get_filename_component(_abs_PATH ${_absFile} PATH)
|
||||
string(REGEX REPLACE "^.*/([^/]+)(\\.[^.]+)$" "\\1" _lang ${_absFile})
|
||||
set(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo)
|
||||
foreach (_currentPoFile ${_firstPoFile} ${ARGN})
|
||||
get_filename_component(_absFile ${_currentPoFile} ABSOLUTE)
|
||||
get_filename_component(_abs_PATH ${_absFile} PATH)
|
||||
get_filename_component(_lang ${_absFile} NAME_WE)
|
||||
set(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${_gmoFile}
|
||||
#COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} --quiet --update --backup=none -s ${_absFile} ${_absPotFile}
|
||||
COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_absFile}
|
||||
DEPENDS ${_absPotFile} ${_absFile}
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${_gmoFile}
|
||||
# COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} --quiet --update --backup=none -s ${_absFile} ${_absPotFile}
|
||||
COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_absFile}
|
||||
DEPENDS ${_absPotFile} ${_absFile}
|
||||
)
|
||||
|
||||
install(FILES ${_gmoFile} DESTINATION share/locale/${_lang}/LC_MESSAGES RENAME ${_potBasename}.mo)
|
||||
set(_gmoFiles ${_gmoFiles} ${_gmoFile})
|
||||
install(FILES ${_gmoFile} DESTINATION share/locale/${_lang}/LC_MESSAGES RENAME ${_potBasename}.mo)
|
||||
set(_gmoFiles ${_gmoFiles} ${_gmoFile})
|
||||
|
||||
endforeach ()
|
||||
endforeach ()
|
||||
|
||||
if(NOT TARGET translations)
|
||||
add_custom_target(translations)
|
||||
endif()
|
||||
if(NOT TARGET translations)
|
||||
add_custom_target(translations)
|
||||
endif()
|
||||
|
||||
_GETTEXT_GET_UNIQUE_TARGET_NAME(translations uniqueTargetName)
|
||||
|
||||
add_custom_target(${uniqueTargetName} ${_addToAll} DEPENDS ${_gmoFiles})
|
||||
add_custom_target(${uniqueTargetName} ${_addToAll} DEPENDS ${_gmoFiles})
|
||||
|
||||
add_dependencies(translations ${uniqueTargetName})
|
||||
|
||||
add_dependencies(translations ${uniqueTargetName})
|
||||
endmacro()
|
||||
|
||||
|
||||
function(GETTEXT_PROCESS_POT_FILE _potFile)
|
||||
set(_gmoFiles)
|
||||
set(_options ALL)
|
||||
set(_oneValueArgs INSTALL_DESTINATION)
|
||||
set(_multiValueArgs LANGUAGES)
|
||||
set(_gmoFiles)
|
||||
set(_options ALL)
|
||||
set(_oneValueArgs INSTALL_DESTINATION)
|
||||
set(_multiValueArgs LANGUAGES)
|
||||
|
||||
CMAKE_PARSE_ARGUMENTS(_parsedArguments "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
|
||||
CMAKE_PARSE_ARGUMENTS(_parsedArguments "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
|
||||
|
||||
get_filename_component(_potName ${_potFile} NAME)
|
||||
string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" _potBasename ${_potName})
|
||||
get_filename_component(_absPotFile ${_potFile} ABSOLUTE)
|
||||
get_filename_component(_potName ${_potFile} NAME)
|
||||
string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" _potBasename ${_potName})
|
||||
get_filename_component(_absPotFile ${_potFile} ABSOLUTE)
|
||||
|
||||
foreach (_lang ${_parsedArguments_LANGUAGES})
|
||||
set(_poFile "${CMAKE_CURRENT_BINARY_DIR}/${_lang}.po")
|
||||
set(_gmoFile "${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo")
|
||||
foreach (_lang ${_parsedArguments_LANGUAGES})
|
||||
set(_poFile "${CMAKE_CURRENT_BINARY_DIR}/${_lang}.po")
|
||||
set(_gmoFile "${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${_poFile}"
|
||||
COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} --quiet --update --backup=none -s ${_poFile} ${_absPotFile}
|
||||
DEPENDS ${_absPotFile}
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT "${_poFile}"
|
||||
COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} --quiet --update --backup=none -s ${_poFile} ${_absPotFile}
|
||||
DEPENDS ${_absPotFile}
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${_gmoFile}"
|
||||
COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_poFile}
|
||||
DEPENDS ${_absPotFile} ${_poFile}
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT "${_gmoFile}"
|
||||
COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_poFile}
|
||||
DEPENDS ${_absPotFile} ${_poFile}
|
||||
)
|
||||
|
||||
if(_parsedArguments_INSTALL_DESTINATION)
|
||||
install(FILES ${_gmoFile} DESTINATION ${_parsedArguments_INSTALL_DESTINATION}/${_lang}/LC_MESSAGES RENAME ${_potBasename}.mo)
|
||||
endif()
|
||||
list(APPEND _gmoFiles ${_gmoFile})
|
||||
endforeach ()
|
||||
if(_parsedArguments_INSTALL_DESTINATION)
|
||||
install(FILES ${_gmoFile} DESTINATION ${_parsedArguments_INSTALL_DESTINATION}/${_lang}/LC_MESSAGES RENAME ${_potBasename}.mo)
|
||||
endif()
|
||||
list(APPEND _gmoFiles ${_gmoFile})
|
||||
endforeach ()
|
||||
|
||||
if(NOT TARGET potfiles)
|
||||
add_custom_target(potfiles)
|
||||
add_custom_target(potfiles)
|
||||
endif()
|
||||
|
||||
_GETTEXT_GET_UNIQUE_TARGET_NAME( potfiles uniqueTargetName)
|
||||
|
||||
if(_parsedArguments_ALL)
|
||||
add_custom_target(${uniqueTargetName} ALL DEPENDS ${_gmoFiles})
|
||||
else()
|
||||
add_custom_target(${uniqueTargetName} DEPENDS ${_gmoFiles})
|
||||
endif()
|
||||
if(_parsedArguments_ALL)
|
||||
add_custom_target(${uniqueTargetName} ALL DEPENDS ${_gmoFiles})
|
||||
else()
|
||||
add_custom_target(${uniqueTargetName} DEPENDS ${_gmoFiles})
|
||||
endif()
|
||||
|
||||
add_dependencies(potfiles ${uniqueTargetName})
|
||||
add_dependencies(potfiles ${uniqueTargetName})
|
||||
|
||||
endfunction()
|
||||
|
||||
|
||||
function(GETTEXT_PROCESS_PO_FILES _lang)
|
||||
set(_options ALL)
|
||||
set(_oneValueArgs INSTALL_DESTINATION)
|
||||
set(_multiValueArgs PO_FILES)
|
||||
set(_gmoFiles)
|
||||
set(_options ALL)
|
||||
set(_oneValueArgs INSTALL_DESTINATION)
|
||||
set(_multiValueArgs PO_FILES)
|
||||
set(_gmoFiles)
|
||||
|
||||
CMAKE_PARSE_ARGUMENTS(_parsedArguments "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
|
||||
CMAKE_PARSE_ARGUMENTS(_parsedArguments "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
|
||||
|
||||
foreach(_current_PO_FILE ${_parsedArguments_PO_FILES})
|
||||
get_filename_component(_name ${_current_PO_FILE} NAME)
|
||||
string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" _basename ${_name})
|
||||
set(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.gmo)
|
||||
add_custom_command(OUTPUT ${_gmoFile}
|
||||
COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_current_PO_FILE}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
DEPENDS ${_current_PO_FILE}
|
||||
)
|
||||
foreach(_current_PO_FILE ${_parsedArguments_PO_FILES})
|
||||
get_filename_component(_name ${_current_PO_FILE} NAME)
|
||||
string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" _basename ${_name})
|
||||
set(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.gmo)
|
||||
add_custom_command(OUTPUT ${_gmoFile}
|
||||
COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_current_PO_FILE}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
DEPENDS ${_current_PO_FILE}
|
||||
)
|
||||
|
||||
if(_parsedArguments_INSTALL_DESTINATION)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.gmo DESTINATION ${_parsedArguments_INSTALL_DESTINATION}/${_lang}/LC_MESSAGES/ RENAME ${_basename}.mo)
|
||||
endif()
|
||||
list(APPEND _gmoFiles ${_gmoFile})
|
||||
endforeach()
|
||||
if(_parsedArguments_INSTALL_DESTINATION)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.gmo DESTINATION ${_parsedArguments_INSTALL_DESTINATION}/${_lang}/LC_MESSAGES/ RENAME ${_basename}.mo)
|
||||
endif()
|
||||
list(APPEND _gmoFiles ${_gmoFile})
|
||||
endforeach()
|
||||
|
||||
|
||||
if(NOT TARGET pofiles)
|
||||
add_custom_target(pofiles)
|
||||
add_custom_target(pofiles)
|
||||
endif()
|
||||
|
||||
_GETTEXT_GET_UNIQUE_TARGET_NAME( pofiles uniqueTargetName)
|
||||
|
||||
if(_parsedArguments_ALL)
|
||||
add_custom_target(${uniqueTargetName} ALL DEPENDS ${_gmoFiles})
|
||||
else()
|
||||
add_custom_target(${uniqueTargetName} DEPENDS ${_gmoFiles})
|
||||
endif()
|
||||
if(_parsedArguments_ALL)
|
||||
add_custom_target(${uniqueTargetName} ALL DEPENDS ${_gmoFiles})
|
||||
else()
|
||||
add_custom_target(${uniqueTargetName} DEPENDS ${_gmoFiles})
|
||||
endif()
|
||||
|
||||
add_dependencies(pofiles ${uniqueTargetName})
|
||||
add_dependencies(pofiles ${uniqueTargetName})
|
||||
|
||||
endfunction()
|
||||
|
@@ -11,8 +11,8 @@
|
||||
# In addition, the following commands are called with the package manager's
|
||||
# paths:
|
||||
#
|
||||
# INCLUDE_DIRECTORIES()
|
||||
# LINK_DIRECTORIES()
|
||||
# include_directories()
|
||||
# link_directories()
|
||||
#
|
||||
# The paths of package managers not currently in $ENV{PATH} are added to
|
||||
# CMAKE_IGNORE_PATH .
|
||||
@@ -41,68 +41,75 @@
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
IF(NOT APPLE)
|
||||
RETURN()
|
||||
ENDIF()
|
||||
if(NOT APPLE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
IF(EXISTS /usr/local/bin/brew AND $ENV{PATH} MATCHES "(^|:)/usr/local/bin/?(:|$)")
|
||||
MESSAGE("-- Configuring for Mac Homebrew")
|
||||
if(NOT "$ENV{IN_NIX_SHELL}" STREQUAL "")
|
||||
message(STATUS "Configuring for Nix")
|
||||
|
||||
SET(MAC_HOMEBREW ON)
|
||||
set(NIX ON)
|
||||
|
||||
SET(CMAKE_IGNORE_PATH /opt/local /opt/local/bin /opt/local/include /opt/local/Library/Frameworks /opt/local/lib ${CMAKE_IGNORE_PATH})
|
||||
SET(CMAKE_IGNORE_PATH /sw /sw/bin /sw/include /sw/Library/Frameworks /sw/lib ${CMAKE_IGNORE_PATH})
|
||||
set(CMAKE_IGNORE_PATH /opt/local /opt/local/bin /opt/local/include /opt/local/Library/Frameworks /opt/local/lib ${CMAKE_IGNORE_PATH})
|
||||
set(CMAKE_IGNORE_PATH /sw /sw/bin /sw/include /sw/Library/Frameworks /sw/lib ${CMAKE_IGNORE_PATH})
|
||||
elseif(EXISTS /usr/local/bin/brew AND $ENV{PATH} MATCHES "(^|:)/usr/local/bin/?(:|$)")
|
||||
message(STATUS "Configuring for Mac Homebrew")
|
||||
|
||||
SET(CMAKE_FRAMEWORK_PATH /usr/local/Frameworks ${CMAKE_FRAMEWORK_PATH})
|
||||
set(MAC_HOMEBREW ON)
|
||||
|
||||
SET(CMAKE_INCLUDE_PATH /usr/local/include ${CMAKE_INCLUDE_PATH})
|
||||
INCLUDE_DIRECTORIES("/usr/local/include")
|
||||
set(CMAKE_IGNORE_PATH /opt/local /opt/local/bin /opt/local/include /opt/local/Library/Frameworks /opt/local/lib ${CMAKE_IGNORE_PATH})
|
||||
set(CMAKE_IGNORE_PATH /sw /sw/bin /sw/include /sw/Library/Frameworks /sw/lib ${CMAKE_IGNORE_PATH})
|
||||
|
||||
SET(CMAKE_LIBRARY_PATH /usr/local/lib ${CMAKE_LIBRARY_PATH})
|
||||
LINK_DIRECTORIES("/usr/local/lib")
|
||||
set(CMAKE_FRAMEWORK_PATH /usr/local/Frameworks ${CMAKE_FRAMEWORK_PATH})
|
||||
|
||||
SET(CMAKE_PROGRAM_PATH /usr/local/bin ${CMAKE_PROGRAM_PATH})
|
||||
set(CMAKE_INCLUDE_PATH /usr/local/include ${CMAKE_INCLUDE_PATH})
|
||||
include_directories("/usr/local/include")
|
||||
|
||||
set(CMAKE_LIBRARY_PATH /usr/local/lib ${CMAKE_LIBRARY_PATH})
|
||||
link_directories("/usr/local/lib")
|
||||
|
||||
set(CMAKE_PROGRAM_PATH /usr/local/bin ${CMAKE_PROGRAM_PATH})
|
||||
|
||||
set(ZLIB_ROOT /usr/local/opt/zlib)
|
||||
ELSEIF(EXISTS /opt/local/bin/port AND $ENV{PATH} MATCHES "(^|:)/opt/local/bin/?(:|$)")
|
||||
MESSAGE("-- Configuring for MacPorts")
|
||||
elseif(EXISTS /opt/local/bin/port AND $ENV{PATH} MATCHES "(^|:)/opt/local/bin/?(:|$)")
|
||||
message(STATUS "Configuring for MacPorts")
|
||||
|
||||
SET(MACPORTS ON)
|
||||
set(MACPORTS ON)
|
||||
|
||||
SET(CMAKE_IGNORE_PATH /sw /sw/bin /sw/include /sw/Library/Frameworks /sw/lib ${CMAKE_IGNORE_PATH})
|
||||
set(CMAKE_IGNORE_PATH /sw /sw/bin /sw/include /sw/Library/Frameworks /sw/lib ${CMAKE_IGNORE_PATH})
|
||||
|
||||
SET(CMAKE_FRAMEWORK_PATH /opt/local/Library/Frameworks ${CMAKE_FRAMEWORK_PATH})
|
||||
set(CMAKE_FRAMEWORK_PATH /opt/local/Library/Frameworks ${CMAKE_FRAMEWORK_PATH})
|
||||
|
||||
SET(CMAKE_INCLUDE_PATH /opt/local/include ${CMAKE_INCLUDE_PATH})
|
||||
INCLUDE_DIRECTORIES("/opt/local/include")
|
||||
set(CMAKE_INCLUDE_PATH /opt/local/include ${CMAKE_INCLUDE_PATH})
|
||||
include_directories("/opt/local/include")
|
||||
|
||||
SET(CMAKE_LIBRARY_PATH /opt/local/lib ${CMAKE_LIBRARY_PATH})
|
||||
LINK_DIRECTORIES("/opt/local/lib")
|
||||
set(CMAKE_LIBRARY_PATH /opt/local/lib ${CMAKE_LIBRARY_PATH})
|
||||
link_directories("/opt/local/lib")
|
||||
|
||||
SET(CMAKE_PROGRAM_PATH /opt/local/bin ${CMAKE_PROGRAM_PATH})
|
||||
ELSEIF(EXISTS /sw/bin/fink AND $ENV{PATH} MATCHES "(^|:)/sw/bin/?(:|$)")
|
||||
MESSAGE("-- Configuring for Fink")
|
||||
set(CMAKE_PROGRAM_PATH /opt/local/bin ${CMAKE_PROGRAM_PATH})
|
||||
elseif(EXISTS /sw/bin/fink AND $ENV{PATH} MATCHES "(^|:)/sw/bin/?(:|$)")
|
||||
message(STATUS "Configuring for Fink")
|
||||
|
||||
SET(FINK ON)
|
||||
set(FINK ON)
|
||||
|
||||
SET(CMAKE_IGNORE_PATH /opt/local /opt/local/bin /opt/local/include /opt/local/Library/Frameworks /opt/local/lib ${CMAKE_IGNORE_PATH})
|
||||
set(CMAKE_IGNORE_PATH /opt/local /opt/local/bin /opt/local/include /opt/local/Library/Frameworks /opt/local/lib ${CMAKE_IGNORE_PATH})
|
||||
|
||||
SET(CMAKE_FRAMEWORK_PATH /sw/Library/Frameworks ${CMAKE_FRAMEWORK_PATH})
|
||||
set(CMAKE_FRAMEWORK_PATH /sw/Library/Frameworks ${CMAKE_FRAMEWORK_PATH})
|
||||
|
||||
SET(CMAKE_INCLUDE_PATH /sw/include ${CMAKE_INCLUDE_PATH})
|
||||
INCLUDE_DIRECTORIES("/sw/include")
|
||||
set(CMAKE_INCLUDE_PATH /sw/include ${CMAKE_INCLUDE_PATH})
|
||||
include_directories("/sw/include")
|
||||
|
||||
SET(CMAKE_LIBRARY_PATH /sw/lib ${CMAKE_LIBRARY_PATH})
|
||||
LINK_DIRECTORIES("/sw/lib")
|
||||
set(CMAKE_LIBRARY_PATH /sw/lib ${CMAKE_LIBRARY_PATH})
|
||||
link_directories("/sw/lib")
|
||||
|
||||
SET(CMAKE_PROGRAM_PATH /sw/bin ${CMAKE_PROGRAM_PATH})
|
||||
ELSE()
|
||||
set(CMAKE_PROGRAM_PATH /sw/bin ${CMAKE_PROGRAM_PATH})
|
||||
else()
|
||||
# no package manager found or active, do nothing
|
||||
RETURN()
|
||||
ENDIF()
|
||||
return()
|
||||
endif()
|
||||
|
||||
# only ignore /usr/local if brew is installed and not in the PATH
|
||||
# in other cases, it is the user's personal installations
|
||||
IF(NOT MAC_HOMEBREW AND EXISTS /usr/local/bin/brew)
|
||||
SET(CMAKE_IGNORE_PATH /usr/local /usr/local/bin /usr/local/include /usr/local/Library/Frameworks /usr/local/lib ${CMAKE_IGNORE_PATH})
|
||||
ENDIF()
|
||||
if(NOT MAC_HOMEBREW AND EXISTS /usr/local/bin/brew)
|
||||
set(CMAKE_IGNORE_PATH /usr/local /usr/local/bin /usr/local/include /usr/local/Library/Frameworks /usr/local/lib /usr/local/opt/gettext/bin /usr/local/opt/gettext/lib ${CMAKE_IGNORE_PATH})
|
||||
endif()
|
||||
|
@@ -54,18 +54,30 @@ cmake_dependent_option(ENABLE_MMX "Enable MMX" ${MMX_DEFAULT} "ENABLE_ASM_SCALER
|
||||
|
||||
option(ENABLE_LIRC "Enable LIRC support" OFF)
|
||||
|
||||
# Link / SFML
|
||||
find_package(SFML 2.4 COMPONENTS network system)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
if(SFML_STATIC_LIBRARIES AND SFML_NETWORK_LIBRARY_STATIC_DEBUG AND SFML_SYSTEM_LIBRARY_STATIC_DEBUG)
|
||||
set(SFML_LIBRARIES ${SFML_NETWORK_LIBRARY_STATIC_DEBUG} ${SFML_SYSTEM_LIBRARY_STATIC_DEBUG})
|
||||
elseif(SFML_NETWORK_LIBRARY_DYNAMIC_DEBUG AND SFML_SYSTEM_LIBRARY_DYNAMIC_DEBUG)
|
||||
set(SFML_LIBRARIES ${SFML_NETWORK_LIBRARY_DYNAMIC_DEBUG} ${SFML_SYSTEM_LIBRARY_DYNAMIC_DEBUG})
|
||||
endif()
|
||||
# Add support for Homebrew, MacPorts and Fink on macOS
|
||||
option(DISABLE_MACOS_PACKAGE_MANAGERS "Set to TRUE to disable support for macOS Homebrew, MacPorts and Fink." FALSE)
|
||||
if(APPLE AND NOT DISABLE_MACOS_PACKAGE_MANAGERS)
|
||||
include(MacPackageManagers)
|
||||
endif()
|
||||
set(ENABLE_LINK_DEFAULT OFF)
|
||||
if(SFML_FOUND)
|
||||
set(ENABLE_LINK_DEFAULT ON)
|
||||
|
||||
find_package(PkgConfig)
|
||||
|
||||
# Link / SFML
|
||||
if(TRANSLATIONS_ONLY)
|
||||
set(ENABLE_LINK_DEFAULT OFF)
|
||||
else()
|
||||
find_package(SFML 2.4 COMPONENTS network system)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
if(SFML_STATIC_LIBRARIES AND SFML_NETWORK_LIBRARY_STATIC_DEBUG AND SFML_SYSTEM_LIBRARY_STATIC_DEBUG)
|
||||
set(SFML_LIBRARIES ${SFML_NETWORK_LIBRARY_STATIC_DEBUG} ${SFML_SYSTEM_LIBRARY_STATIC_DEBUG})
|
||||
elseif(SFML_NETWORK_LIBRARY_DYNAMIC_DEBUG AND SFML_SYSTEM_LIBRARY_DYNAMIC_DEBUG)
|
||||
set(SFML_LIBRARIES ${SFML_NETWORK_LIBRARY_DYNAMIC_DEBUG} ${SFML_SYSTEM_LIBRARY_DYNAMIC_DEBUG})
|
||||
endif()
|
||||
endif()
|
||||
set(ENABLE_LINK_DEFAULT OFF)
|
||||
if(SFML_FOUND)
|
||||
set(ENABLE_LINK_DEFAULT ON)
|
||||
endif()
|
||||
endif()
|
||||
option(ENABLE_LINK "Enable GBA linking functionality" ${ENABLE_LINK_DEFAULT})
|
||||
|
||||
@@ -74,7 +86,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 DEFINED ENABLE_FFMPEG OR ENABLE_FFMPEG)
|
||||
if(NOT TRANSLATIONS_ONLY AND NOT DEFINED ENABLE_FFMPEG OR ENABLE_FFMPEG)
|
||||
set(FFMPEG_DEFAULT ON)
|
||||
|
||||
find_package(FFmpeg COMPONENTS ${FFMPEG_COMPONENTS})
|
||||
@@ -115,12 +127,6 @@ option(ENABLE_LTO "Compile with Link Time Optimization" ${LTO_DEFAULT})
|
||||
|
||||
option(ENABLE_GBA_LOGGING "Enable extended GBA logging" ON)
|
||||
|
||||
# Add support for Homebrew, MacPorts and Fink on macOS
|
||||
option(DISABLE_MACOS_PACKAGE_MANAGERS "Set to TRUE to disable support for macOS Homebrew, MacPorts and Fink." FALSE)
|
||||
if(APPLE AND NOT DISABLE_MACOS_PACKAGE_MANAGERS)
|
||||
include(MacPackageManagers)
|
||||
endif()
|
||||
|
||||
option(UPSTREAM_RELEASE "do some optimizations and release automation tasks" OFF)
|
||||
|
||||
if(WIN32)
|
||||
@@ -136,6 +142,23 @@ if(WIN32)
|
||||
option(ENABLE_XAUDIO2 "Enable xaudio2 sound output for the wxWidgets port" ${XAUDIO2_DEFAULT})
|
||||
endif()
|
||||
|
||||
option(ENABLE_FAUDIO "Enable FAudio sound output for the wxWidgets port" OFF)
|
||||
set(ENABLE_FAUDIO_DEFAULT OFF)
|
||||
|
||||
find_package(FAudio QUIET)
|
||||
|
||||
if(FAudio_FOUND)
|
||||
set(ENABLE_FAUDIO_DEFAULT ON)
|
||||
endif()
|
||||
|
||||
option(ENABLE_FAUDIO "Enable FAudio sound output for the wxWidgets port" ${ENABLE_FAUDIO_DEFAULT})
|
||||
|
||||
option(ZIP_SUFFIX [=[suffix for release zip files, e.g. "-somebranch".zip]=] OFF)
|
||||
|
||||
# The SDL port can't be built without debugging support
|
||||
if(NOT ENABLE_DEBUGGER AND ENABLE_SDL)
|
||||
message(FATAL_ERROR "The SDL port can't be built without debugging support")
|
||||
endif()
|
||||
|
||||
if(TRANSLATIONS_ONLY AND (ENABLE_SDL OR ENABLE_WX))
|
||||
message(FATAL_ERROR "The SDL and wxWidgets ports can't be built when TRANSLATIONS_ONLY is enabled")
|
||||
endif()
|
||||
|
@@ -1,3 +1,7 @@
|
||||
if(TRANSLATIONS_ONLY)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED VCPKG_TARGET_TRIPLET)
|
||||
if(NOT WIN32)
|
||||
return()
|
||||
@@ -49,7 +53,7 @@ endif()
|
||||
|
||||
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 "^C:/Program Files/Microsoft Visual Studio/")
|
||||
if(NOT git_status EQUAL 0 AND NOT VCPKG_ROOT MATCHES "Visual Studio")
|
||||
message(FATAL_ERROR "Error updating vcpkg from git, please make sure git for windows is installed correctly, it can be installed from Visual Studio components")
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -116,7 +120,7 @@ function(vcpkg_is_installed vcpkg_exe pkg_name pkg_ver pkg_triplet powershell ou
|
||||
string(REPLACE "-" "." pkg_ver ${pkg_ver})
|
||||
|
||||
if(NOT DEFINED VCPKG_INSTALLED_COUNT)
|
||||
if(VCPKG_ROOT MATCHES "^C:/Program Files/Microsoft Visual Studio/")
|
||||
if(VCPKG_ROOT MATCHES "Visual Studio")
|
||||
execute_process(
|
||||
COMMAND ${powershell}
|
||||
-executionpolicy bypass -noprofile
|
||||
|
@@ -2,12 +2,14 @@ if(X86_32 OR X86_64)
|
||||
add_compile_options(-mfpmath=sse -msse2)
|
||||
endif()
|
||||
|
||||
if(X86_64)
|
||||
# Require and optimize for Core2 level support, tune for generic.
|
||||
add_compile_options(-march=core2 -mtune=generic)
|
||||
elseif(X86_32)
|
||||
# Optimize for pentium-mmx and tune for generic for older builds.
|
||||
add_compile_options(-march=pentium-mmx -mtune=generic)
|
||||
if(UPSTREAM_RELEASE)
|
||||
if(X86_64)
|
||||
# Require and optimize for Core2 level support, tune for generic.
|
||||
add_compile_options(-march=core2 -mtune=generic)
|
||||
elseif(X86_32)
|
||||
# Optimize for pentium-mmx and tune for generic for older builds.
|
||||
add_compile_options(-march=pentium-mmx -mtune=generic)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Common flags.
|
||||
@@ -16,16 +18,17 @@ add_compile_options(
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-deprecated-copy>
|
||||
-Wformat
|
||||
-Wformat-security
|
||||
-feliminate-unused-debug-types
|
||||
-fdiagnostics-color=always
|
||||
)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
add_compile_options(-Wno-unused-command-line-argument)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
add_compile_options(-feliminate-unused-debug-types)
|
||||
endif()
|
||||
|
||||
# check if ssp flags are supported.
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
check_cxx_compiler_flag(-fstack-protector-strong STACK_PROTECTOR_SUPPORTED)
|
||||
|
||||
if(STACK_PROTECTOR_SUPPORTED)
|
||||
@@ -42,17 +45,30 @@ if(NOT ENABLE_ASM) # inline asm is not allowed with -fPIC
|
||||
add_compile_options(-fPIC)
|
||||
endif()
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_compile_options(-ggdb3 -Og -fno-omit-frame-pointer -Wall -Wextra)
|
||||
else()
|
||||
add_compile_options(-Ofast -fomit-frame-pointer)
|
||||
endif()
|
||||
|
||||
# for some reason this is necessary
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||
include_directories(/usr/local/include)
|
||||
endif()
|
||||
|
||||
if(VBAM_STATIC)
|
||||
add_link_options("-static-libgcc" "-static-libstdc++" "-Wl,-Bstatic" "-lstdc++" "-lpthread")
|
||||
if(APPLE)
|
||||
add_link_options(-static-libstdc++)
|
||||
else()
|
||||
add_link_options(-static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# To support LTO, this must always fail.
|
||||
add_compile_options(-Werror=odr -Werror=strict-aliasing)
|
||||
add_link_options( -Werror=odr -Werror=strict-aliasing)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
add_compile_options(-Werror=lto-type-mismatch)
|
||||
add_link_options( -Werror=lto-type-mismatch)
|
||||
endif()
|
||||
|
@@ -1,3 +1,7 @@
|
||||
if(TRANSLATIONS_ONLY)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Compiler stuff
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
@@ -8,7 +12,7 @@ if(ENABLE_LTO)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT LTO_SUPPORTED)
|
||||
|
||||
# MINGW64 on x64 does not support LTO
|
||||
# MINGW64 does not support LTO
|
||||
if(WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(LTO_SUPPORTED FALSE)
|
||||
endif()
|
||||
@@ -21,6 +25,49 @@ if(ENABLE_LTO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Output all binaries at top level
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
|
||||
|
||||
if(NOT HTTPS)
|
||||
add_compile_definitions(NO_HTTPS)
|
||||
endif()
|
||||
|
||||
if(ENABLE_GBA_LOGGING)
|
||||
add_compile_definitions(GBA_LOGGING )
|
||||
endif()
|
||||
|
||||
if(ENABLE_MMX)
|
||||
add_compile_definitions(MMX)
|
||||
endif()
|
||||
|
||||
|
||||
if(NOT ENABLE_ONLINEUPDATES)
|
||||
add_compile_definitions(NO_ONLINEUPDATES)
|
||||
endif()
|
||||
|
||||
# The debugger is enabled by default
|
||||
if(ENABLE_DEBUGGER)
|
||||
add_compile_definitions(VBAM_ENABLE_DEBUGGER)
|
||||
endif()
|
||||
|
||||
# The ASM core is disabled by default because we don't know on which platform we are
|
||||
if(NOT ENABLE_ASM_CORE)
|
||||
add_compile_definitions(C_CORE)
|
||||
endif()
|
||||
|
||||
# Set up "src" and generated directory as a global include directory.
|
||||
set(VBAM_GENERATED_DIR ${CMAKE_BINARY_DIR}/generated)
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${VBAM_GENERATED_DIR}
|
||||
)
|
||||
|
||||
# C defines
|
||||
add_compile_definitions(HAVE_NETINET_IN_H HAVE_ARPA_INET_H HAVE_ZLIB_H FINAL_VERSION SDL USE_OPENGL SYSCONF_INSTALL_DIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}")
|
||||
add_compile_definitions(PKGDATADIR="${CMAKE_INSTALL_FULL_DATADIR}/vbam")
|
||||
add_compile_definitions(__STDC_FORMAT_MACROS)
|
||||
add_compile_definitions(LOCALEDIR="${LOCALEDIR}")
|
||||
|
||||
# Common compiler settings.
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_compile_definitions(DEBUG)
|
||||
|
@@ -2,7 +2,7 @@ with import <nixpkgs> {};
|
||||
stdenv.mkDerivation {
|
||||
name = "visualboyadvance-m";
|
||||
buildInputs = if stdenv.isDarwin then
|
||||
[ ninja cmake gcc nasm gettext pkg-config zip sfml zlib openal ffmpeg wxGTK32 SDL2 pcre pcre2 darwin.apple_sdk.frameworks.IOKit darwin.apple_sdk.frameworks.Carbon darwin.apple_sdk.frameworks.Cocoa darwin.apple_sdk.frameworks.QuartzCore darwin.apple_sdk.frameworks.AudioToolbox darwin.apple_sdk.frameworks.OpenGL darwin.apple_sdk.frameworks.OpenAL llvmPackages_latest.clang llvmPackages_latest.bintools ]
|
||||
[ ninja cmake gcc nasm faudio gettext libintl pkg-config zip sfml zlib openal ffmpeg wxGTK32 SDL2 pcre pcre2 darwin.apple_sdk.frameworks.System darwin.apple_sdk.frameworks.IOKit darwin.apple_sdk.frameworks.Carbon darwin.apple_sdk.frameworks.Cocoa darwin.apple_sdk.frameworks.QuartzCore darwin.apple_sdk.frameworks.AudioToolbox darwin.apple_sdk.frameworks.OpenGL darwin.apple_sdk.frameworks.OpenAL llvmPackages_latest.clang llvmPackages_latest.bintools ]
|
||||
else
|
||||
[ ninja cmake gcc nasm gettext pkg-config zip sfml zlib openal ffmpeg wxGTK32 mesa glfw SDL2 gtk3-x11 pcre pcre2 util-linuxMinimal libselinux libsepol libthai libdatrie xorg.libXdmcp xorg.libXtst libxkbcommon epoxy dbus at-spi2-core ];
|
||||
[ ninja cmake gcc nasm faudio gettext libintl pkg-config zip sfml zlib openal ffmpeg wxGTK32 libGL libGLU glfw SDL2 gtk3-x11 pcre pcre2 util-linuxMinimal libselinux libsepol libthai libdatrie xorg.libXdmcp xorg.libXtst libxkbcommon epoxy dbus at-spi2-core ];
|
||||
}
|
||||
|
@@ -2,8 +2,6 @@
|
||||
Developer Information File
|
||||
==========================
|
||||
|
||||
|
||||
|
||||
Known preprocessor switches:
|
||||
- SDL: Defined for the SDL version
|
||||
- GBA_LOGGING: Enables logging for the GBA core
|
||||
@@ -13,47 +11,18 @@ Known preprocessor switches:
|
||||
- RGB555: Use 16bit colors with 5bit green instead of 6bit green in hq3x/4x filters (C++ version)
|
||||
- NO_OGL: Exclude OpenGL code
|
||||
- NO_D3D: Exclude Direct3D code
|
||||
- NO_OAL: Exclude OpenAL code
|
||||
- NO_XAUDIO2: Exclude XAudio2 code (the XAudio2 interface is DirectSound's successor)
|
||||
- VBAM_ENABLE_XAUDIO2: Enable XAudio2 code (the XAudio2 interface is DirectSound's successor)
|
||||
- VBAM_ENABLE_FAUDIO: Enable FAudio code (the FAudio interface is an open source multiplatform re-implementation of XAudio2)
|
||||
- NO_LINK: Exclude linking code (joybus, multilink, ...)
|
||||
- WIN64: This macro is only defined for 64 bit builds
|
||||
|
||||
|
||||
|
||||
Download locations:
|
||||
NASM: http://nasm.us/
|
||||
DirectX SDK: http://msdn.microsoft.com/en-us/xna/aa937788.aspx
|
||||
OpenAL SDK: http://connect.creativelabs.com/openal/default.aspx
|
||||
OpenGL files: http://www.opengl.org/registry/
|
||||
zlib: http://zlib.net/
|
||||
libpng: http://libpng.org/pub/png/libpng.html
|
||||
|
||||
You can find pre-built versions of zlib & libpng at:
|
||||
http://spacy51.sp.funpic.de/VBA-M/libs/
|
||||
Just extract them somewhere and point Visual C++ 2008 to the include & lib folders.
|
||||
They are built with the static C runtime (this is what the release builds use).
|
||||
|
||||
|
||||
|
||||
###########################
|
||||
# --- Build Systems --- #
|
||||
###########################
|
||||
|
||||
===Win32/MFC===
|
||||
This is the full-featured Windows build using the MFC GUI.
|
||||
The project files are located in /project/vc2008_mfc (VBA2008.sln) and /project/vs2010_mfc (VBA2010.sln).
|
||||
Anyone distributing builds should be using MSVC 2010 SP1, the unpatched release has a bug where it applies SSE2 updates to mov and other instructions resulting in illegal instruction errors on cpu's only supporting SSE.
|
||||
You also have to install Microsoft's DirectX SDK for Direct3D, DirectInput & XAudio2.
|
||||
If you want to enable OpenAL sound output, install the OpenAL SDK. If you do not want it, add NO_OAL to the VBA-M project's preprocessor definitions.
|
||||
SubWCRev.exe is used to append the svn versioning to the output executable, this as of TortoiseSVN 1.7, is only available by installing TortoiseSVN.
|
||||
All other dependencies for MSVC builds may be found in the ../dependencies directory (above /trunk).
|
||||
Normally, Windows users will want to checkout the root of the repository instead of just the trunk directory. Afterwards, simply opening the .sln of choice, setting preprocessor definitions, and hitting build is all that's required.
|
||||
|
||||
===*nix/GTK===
|
||||
===src/sdl===
|
||||
This is the standard build configuration on non-Windows.
|
||||
Running cmake will inform you of any packages you need to install.
|
||||
|
||||
===*/wxw===
|
||||
The wxWidgets interface is an in-development frontend meant to be more cross-platform friendly than MFC and GTK.
|
||||
===src/wx===
|
||||
The wxWidgets interface is an in-development frontend meant to be more cross-platform friendly than MFC and SDL.
|
||||
Running cmake will inform you of any packages you need to install.
|
||||
NOTE: In addition to what cmake currently checks for, you will also need the wxrc tool and libgdiplus.
|
55
installdeps
55
installdeps
@@ -1,7 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
CMAKE=cmake
|
||||
ENABLE_OPENAL=1
|
||||
ENABLE_FFMPEG=1
|
||||
|
||||
main() {
|
||||
@@ -25,10 +24,6 @@ check_command_line_args() {
|
||||
usage
|
||||
quit 0
|
||||
;;
|
||||
--no-openal)
|
||||
ENABLE_OPENAL=
|
||||
shift
|
||||
;;
|
||||
--no-ffmpeg)
|
||||
ENABLE_FFMPEG=
|
||||
shift
|
||||
@@ -355,9 +350,8 @@ debian_installdeps() {
|
||||
;;
|
||||
esac
|
||||
|
||||
pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl2-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev libsfml-dev $sfml_libs $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build"
|
||||
pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl2-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev libsfml-dev $sfml_libs $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build libopenal-dev"
|
||||
|
||||
[ -n "$ENABLE_OPENAL" ] && pkgs="$pkgs libopenal-dev"
|
||||
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs libavcodec-dev libavformat-dev libswscale-dev libavutil-dev $libswresample_dev"
|
||||
|
||||
check sudo apt-get -qy install $pkgs
|
||||
@@ -407,8 +401,7 @@ debian_installdeps() {
|
||||
fi
|
||||
fi
|
||||
|
||||
deps="gcc zlib ffmpeg gettext sdl2 sfml openal wxwidgets"
|
||||
[ -n "$ENABLE_OPENAL" ] && deps="$deps openal"
|
||||
deps="gcc zlib ffmpeg gettext sdl2 sfml openal wxwidgets openal"
|
||||
[ -n "$ENABLE_FFMPEG" ] && deps="$deps ffmpeg"
|
||||
|
||||
set --
|
||||
@@ -510,9 +503,6 @@ fedora_installdeps() {
|
||||
*ffmpeg*)
|
||||
[ -z "$ENABLE_FFMPEG" ] && continue
|
||||
;;
|
||||
*openal*)
|
||||
[ -z "$ENABLE_OPENAL" ] && continue
|
||||
;;
|
||||
esac
|
||||
|
||||
pkg_arch=
|
||||
@@ -601,15 +591,9 @@ fedora_installdeps() {
|
||||
;;
|
||||
esac
|
||||
# install static deps
|
||||
for pkg in zlib gettext SDL2 wxWidgets3; do
|
||||
for pkg in zlib gettext SDL2 wxWidgets3 openal-soft; do
|
||||
set -- "$@" "${target}-${pkg}-static"
|
||||
done
|
||||
# install deps that are not available as static
|
||||
if [ -n "$ENABLE_OPENAL" ]; then
|
||||
for pkg in openal-soft; do
|
||||
set -- "$@" "${target}-${pkg}"
|
||||
done
|
||||
fi
|
||||
|
||||
# get the necessary win32 headers
|
||||
git submodule update --init --remote --recursive
|
||||
@@ -707,9 +691,6 @@ rhel_installdeps() {
|
||||
*ffmpeg*)
|
||||
[ -z "$ENABLE_FFMPEG" ] && continue
|
||||
;;
|
||||
*openal*)
|
||||
[ -z "$ENABLE_OPENAL" ] && continue
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "$amd64" ]; then
|
||||
@@ -790,15 +771,9 @@ rhel_installdeps() {
|
||||
;;
|
||||
esac
|
||||
# install static deps
|
||||
for pkg in zlib gettext SDL2 wxWidgets; do
|
||||
for pkg in zlib gettext SDL2 wxWidgets openal-soft; do
|
||||
set -- "$@" "${target}-${pkg}-static"
|
||||
done
|
||||
# install deps that are not available as static
|
||||
if [ -n "$ENABLE_OPENAL" ]; then
|
||||
for pkg in openal-soft; do
|
||||
set -- "$@" "${target}-${pkg}"
|
||||
done
|
||||
fi
|
||||
|
||||
# get the necessary win32 headers
|
||||
git submodule update --init --remote --recursive
|
||||
@@ -824,9 +799,8 @@ suse_installdeps() {
|
||||
|
||||
tools="make cmake ccache nasm gettext-tools pkg-config ccache zip sfml2-devel ninja"
|
||||
|
||||
libs="gcc gcc-c++ libSDL2-devel wxWidgets-3_0-devel" # ffmpeg-devel
|
||||
libs="gcc gcc-c++ libSDL2-devel wxWidgets-3_0-devel openal-soft-devel" # ffmpeg-devel
|
||||
|
||||
[ -n "$ENABLE_OPENAL" ] && libs="$libs openal-soft-devel"
|
||||
# ffmpeg requires packman repos
|
||||
|
||||
if [ "$target" = m32 ]; then
|
||||
@@ -894,9 +868,8 @@ archlinux_installdeps() {
|
||||
|
||||
$pacman -Q gtk3-classic >/dev/null 2>&1 && gtk=gtk3-classic
|
||||
|
||||
libs="zlib mesa gettext sdl2 wxgtk3 $gtk sfml"
|
||||
libs="zlib mesa gettext sdl2 wxgtk3 $gtk sfml openal"
|
||||
|
||||
[ -n "$ENABLE_OPENAL" ] && libs="$libs openal"
|
||||
[ -n "$ENABLE_FFMPEG" ] && libs="$libs ffmpeg"
|
||||
|
||||
if [ -z "$target" -o "$target" = m32 ]; then
|
||||
@@ -991,9 +964,7 @@ EOF
|
||||
fi
|
||||
done
|
||||
|
||||
deps="zlib gettext pkg-config sdl2 wxmsw"
|
||||
|
||||
[ -n "$ENABLE_OPENAL" ] && deps="$deps openal"
|
||||
deps="zlib gettext pkg-config sdl2 wxmsw openal"
|
||||
|
||||
# and the actual deps
|
||||
for p in $deps; do
|
||||
@@ -1024,9 +995,7 @@ solus_installdeps() {
|
||||
check sudo eopkg -y install -c system.devel
|
||||
check sudo eopkg -y install git ccache ninja
|
||||
|
||||
set -- sdl2-devel wxwidgets-devel libgtk-2-devel libgtk-3-devel libglu-devel
|
||||
|
||||
[ -n "$ENABLE_OPENAL" ] && set -- "$@" openal-soft-devel
|
||||
set -- sdl2-devel wxwidgets-devel libgtk-2-devel libgtk-3-devel libglu-devel openal-soft-devel
|
||||
|
||||
if [ -n "$amd64" -a "$target" = m32 ]; then
|
||||
info_msg 'Calculating dependencies, this will take a while..'
|
||||
@@ -1102,14 +1071,13 @@ gentoo_installdeps() {
|
||||
sys-devel/binutils \
|
||||
media-libs/libsdl2 \
|
||||
media-libs/libsfml \
|
||||
media-libs/openal \
|
||||
x11-libs/wxGTK:$wx_slot \
|
||||
sys-libs/zlib \
|
||||
dev-util/pkgconf \
|
||||
dev-lang/nasm \
|
||||
dev-build/ninja"
|
||||
|
||||
[ -n "$ENABLE_OPENAL" ] && ebuilds="$ebuilds media-libs/openal"
|
||||
|
||||
[ -n "$ENABLE_FFMPEG" ] && ebuilds="$ebuilds media-video/ffmpeg"
|
||||
|
||||
check sudo emerge -vna $ebuilds
|
||||
@@ -1152,7 +1120,7 @@ windows_installdeps() {
|
||||
;;
|
||||
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"
|
||||
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"
|
||||
|
||||
case "$target" in
|
||||
*x86_64)
|
||||
@@ -1160,7 +1128,6 @@ windows_installdeps() {
|
||||
;;
|
||||
esac
|
||||
|
||||
[ -n "$ENABLE_OPENAL" ] && pkgs="$pkgs openal"
|
||||
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs ffmpeg"
|
||||
|
||||
set --
|
||||
@@ -1225,7 +1192,7 @@ brew_installdeps() {
|
||||
|
||||
check brew -v update
|
||||
|
||||
brews="nasm cmake ccache gettext pkg-config sdl2 wxwidgets ccache ninja"
|
||||
brews="nasm cmake ccache gettext pkg-config sdl2 wxwidgets faudio ccache ninja zlib"
|
||||
|
||||
[ -n "$ENABLE_FFMPEG" ] && brews="$brews ffmpeg"
|
||||
|
||||
|
756
po/wxvbam/bg.po
756
po/wxvbam/bg.po
File diff suppressed because it is too large
Load Diff
756
po/wxvbam/br.po
756
po/wxvbam/br.po
File diff suppressed because it is too large
Load Diff
756
po/wxvbam/cs.po
756
po/wxvbam/cs.po
File diff suppressed because it is too large
Load Diff
756
po/wxvbam/de.po
756
po/wxvbam/de.po
File diff suppressed because it is too large
Load Diff
756
po/wxvbam/el.po
756
po/wxvbam/el.po
File diff suppressed because it is too large
Load Diff
756
po/wxvbam/es.po
756
po/wxvbam/es.po
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
756
po/wxvbam/gl.po
756
po/wxvbam/gl.po
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
756
po/wxvbam/id.po
756
po/wxvbam/id.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
756
po/wxvbam/ja.po
756
po/wxvbam/ja.po
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
756
po/wxvbam/nb.po
756
po/wxvbam/nb.po
File diff suppressed because it is too large
Load Diff
756
po/wxvbam/nl.po
756
po/wxvbam/nl.po
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
756
po/wxvbam/sv.po
756
po/wxvbam/sv.po
File diff suppressed because it is too large
Load Diff
756
po/wxvbam/tr.po
756
po/wxvbam/tr.po
File diff suppressed because it is too large
Load Diff
758
po/wxvbam/uk.po
758
po/wxvbam/uk.po
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
@@ -51,7 +51,6 @@ parts:
|
||||
- libopenal-dev
|
||||
- libwxgtk3.0-gtk3-dev
|
||||
cmake-parameters:
|
||||
- -DENABLE_OPENAL=ON
|
||||
- -DENABLE_SDL=OFF
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
add_subdirectory(audio_sdl)
|
||||
add_subdirectory(av_recording)
|
||||
add_subdirectory(draw_text)
|
||||
add_subdirectory(filters)
|
||||
|
@@ -1,14 +0,0 @@
|
||||
add_library(vbam-components-audio-sdl OBJECT)
|
||||
|
||||
target_sources(vbam-components-audio-sdl
|
||||
PRIVATE audio_sdl.cpp
|
||||
PUBLIC audio_sdl.h
|
||||
)
|
||||
|
||||
target_include_directories(vbam-components-audio-sdl
|
||||
PUBLIC ${SDL2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(vbam-components-audio-sdl
|
||||
PUBLIC vbam-core-base ${VBAM_SDL2_LIBS}
|
||||
)
|
@@ -137,18 +137,12 @@ recording::MediaRet recording::MediaRecorder::setup_audio_stream()
|
||||
}
|
||||
}
|
||||
if (!isSupported && acodec->supported_samplerates) return MRET_ERR_NOCODEC;
|
||||
aenc->channels = av_get_channel_layout_nb_channels(aenc->channel_layout);
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 60
|
||||
av_channel_layout_from_mask(&(aenc->ch_layout), AV_CH_LAYOUT_STEREO);
|
||||
#else
|
||||
aenc->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
if (acodec->channel_layouts)
|
||||
{
|
||||
aenc->channel_layout = acodec->channel_layouts[0];
|
||||
for (int i = 0; acodec->channel_layouts[i]; ++i)
|
||||
{
|
||||
if (acodec->channel_layouts[i] == AV_CH_LAYOUT_STEREO)
|
||||
aenc->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
}
|
||||
}
|
||||
aenc->channels = av_get_channel_layout_nb_channels(aenc->channel_layout);
|
||||
aenc->channels = 2;
|
||||
#endif
|
||||
aenc->time_base = { 1, aenc->sample_rate };
|
||||
ast->time_base = { 1, STREAM_FRAME_RATE };
|
||||
// open and use codec on stream
|
||||
@@ -169,7 +163,11 @@ recording::MediaRet recording::MediaRecorder::setup_audio_stream()
|
||||
audioframeTmp = av_frame_alloc();
|
||||
if (!audioframeTmp) return MRET_ERR_BUFSIZE;
|
||||
audioframeTmp->format = IN_SOUND_FORMAT;
|
||||
audioframeTmp->channel_layout = aenc->channel_layout;
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 60
|
||||
audioframeTmp->ch_layout = aenc->ch_layout;
|
||||
#else
|
||||
audioframeTmp->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
#endif
|
||||
audioframeTmp->sample_rate = aenc->sample_rate;
|
||||
audioframeTmp->nb_samples = nb_samples;
|
||||
if (nb_samples)
|
||||
@@ -181,7 +179,11 @@ recording::MediaRet recording::MediaRecorder::setup_audio_stream()
|
||||
audioframe = av_frame_alloc();
|
||||
if (!audioframe) return MRET_ERR_BUFSIZE;
|
||||
audioframe->format = aenc->sample_fmt;
|
||||
audioframe->channel_layout = aenc->channel_layout;
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 60
|
||||
audioframe->ch_layout = aenc->ch_layout;
|
||||
#else
|
||||
audioframe->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
#endif
|
||||
audioframe->sample_rate = aenc->sample_rate;
|
||||
audioframe->nb_samples = nb_samples;
|
||||
if (nb_samples)
|
||||
@@ -195,10 +197,15 @@ recording::MediaRet recording::MediaRecorder::setup_audio_stream()
|
||||
{
|
||||
return MRET_ERR_BUFSIZE;
|
||||
}
|
||||
av_opt_set_int (swr, "in_channel_count", aenc->channels, 0);
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 60
|
||||
av_opt_set_chlayout (swr, "in_chlayout", &(aenc->ch_layout),0);
|
||||
av_opt_set_chlayout (swr, "out_chlayout", &(aenc->ch_layout),0);
|
||||
#else
|
||||
av_opt_set_int (swr, "in_channel_count", 2, 0);
|
||||
av_opt_set_int (swr, "out_channel_count", 2, 0);
|
||||
#endif
|
||||
av_opt_set_int (swr, "in_sample_rate", aenc->sample_rate, 0);
|
||||
av_opt_set_sample_fmt(swr, "in_sample_fmt", IN_SOUND_FORMAT, 0);
|
||||
av_opt_set_int (swr, "out_channel_count", aenc->channels, 0);
|
||||
av_opt_set_int (swr, "out_sample_rate", aenc->sample_rate, 0);
|
||||
av_opt_set_sample_fmt(swr, "out_sample_fmt", aenc->sample_fmt, 0);
|
||||
if (swr_init(swr) < 0)
|
||||
@@ -207,8 +214,8 @@ recording::MediaRet recording::MediaRecorder::setup_audio_stream()
|
||||
return MRET_ERR_BUFSIZE;
|
||||
}
|
||||
// auxiliary buffer for setting up frames for encode
|
||||
audioBufferSize = nb_samples * aenc->channels * sizeof(uint16_t);
|
||||
audioBuffer = (uint16_t *) calloc(nb_samples * aenc->channels, sizeof(uint16_t));
|
||||
audioBufferSize = nb_samples * 2 * sizeof(uint16_t);
|
||||
audioBuffer = (uint16_t *) calloc(nb_samples * 2, sizeof(uint16_t));
|
||||
if (!audioBuffer) return MRET_ERR_BUFSIZE;
|
||||
samplesInAudioBuffer = 0;
|
||||
posInAudioBuffer = 0;
|
||||
@@ -565,7 +572,7 @@ recording::MediaRet recording::MediaRecorder::AddFrame(const uint16_t *aud, int
|
||||
{
|
||||
if (!isRecording) return MRET_OK;
|
||||
AVCodecContext *c = aenc;
|
||||
int samples_size = av_samples_get_buffer_size(NULL, c->channels, audioframeTmp->nb_samples, IN_SOUND_FORMAT, 1);
|
||||
int samples_size = av_samples_get_buffer_size(NULL, 2, audioframeTmp->nb_samples, IN_SOUND_FORMAT, 1);
|
||||
|
||||
int realLength = length / sizeof *aud;
|
||||
bool isMissing = false;
|
||||
@@ -606,7 +613,7 @@ recording::MediaRet recording::MediaRecorder::AddFrame(const uint16_t *aud, int
|
||||
pkt->data = NULL;
|
||||
pkt->size = 0;
|
||||
|
||||
if (avcodec_fill_audio_frame(audioframeTmp, c->channels, IN_SOUND_FORMAT, (const uint8_t *)audioBuffer, samples_size, 1) < 0)
|
||||
if (avcodec_fill_audio_frame(audioframeTmp, 2, IN_SOUND_FORMAT, (const uint8_t *)audioBuffer, samples_size, 1) < 0)
|
||||
{
|
||||
return MRET_ERR_RECORDING;
|
||||
}
|
||||
|
@@ -2,8 +2,9 @@
|
||||
#define VBAM_CORE_BASE_SYSTEM_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
class SoundDriver;
|
||||
#include "core/base/sound_driver.h"
|
||||
|
||||
enum IMAGE_TYPE {
|
||||
IMAGE_UNKNOWN = -1,
|
||||
@@ -89,7 +90,7 @@ extern bool systemReadJoypads();
|
||||
extern uint32_t systemReadJoypad(int);
|
||||
extern uint32_t systemGetClock();
|
||||
extern void systemSetTitle(const char*);
|
||||
extern SoundDriver* systemSoundInit();
|
||||
extern std::unique_ptr<SoundDriver> systemSoundInit();
|
||||
extern void systemOnWriteDataToSoundBuffer(const uint16_t* finalWave, int length);
|
||||
extern void systemOnSoundShutdown();
|
||||
extern void systemScreenMessage(const char*);
|
||||
|
@@ -1936,6 +1936,7 @@ void CPUSwitchMode(int mode, bool saveState, bool breakLoop)
|
||||
CPUSwap(®[12].I, ®[R12_FIQ].I);
|
||||
reg[13].I = reg[R13_FIQ].I;
|
||||
reg[14].I = reg[R14_FIQ].I;
|
||||
reg[16].I = SPSR;
|
||||
if (saveState)
|
||||
reg[17].I = CPSR;
|
||||
else
|
||||
|
@@ -34,7 +34,7 @@
|
||||
#define NR51 0x81
|
||||
#define NR52 0x84
|
||||
|
||||
SoundDriver* soundDriver = 0;
|
||||
std::unique_ptr<SoundDriver> soundDriver;
|
||||
|
||||
extern bool stopState; // TODO: silence sound when true
|
||||
|
||||
@@ -469,10 +469,7 @@ static void remake_stereo_buffer()
|
||||
|
||||
void soundShutdown()
|
||||
{
|
||||
if (soundDriver) {
|
||||
delete soundDriver;
|
||||
soundDriver = 0;
|
||||
}
|
||||
soundDriver.reset();
|
||||
|
||||
systemOnSoundShutdown();
|
||||
|
||||
|
@@ -66,7 +66,7 @@ endif
|
||||
ifneq (,$(findstring unix,$(platform)))
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
fpic := -fPIC
|
||||
SHARED := -shared -Wl,-version-script=$(LIBRETRO_DIR)/link.T -Wl,-no-undefined
|
||||
SHARED := -shared -Wl,-no-undefined
|
||||
TILED_RENDERING=1
|
||||
|
||||
# Classic Platforms ####################
|
||||
@@ -78,7 +78,7 @@ ifneq (,$(findstring unix,$(platform)))
|
||||
else ifeq ($(platform), classic_armv7_a7)
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
fpic := -fPIC
|
||||
SHARED := -shared -Wl,--version-script=$(LIBRETRO_DIR)/link.T -Wl,--no-undefined -fPIC
|
||||
SHARED := -shared -Wl,--no-undefined -fPIC
|
||||
CFLAGS += -Ofast \
|
||||
-flto=4 -fwhole-program -fuse-linker-plugin \
|
||||
-fdata-sections -ffunction-sections -Wl,--gc-sections \
|
||||
@@ -216,7 +216,7 @@ else ifeq ($(platform), theos_ios)
|
||||
else ifeq ($(platform), qnx)
|
||||
TARGET := $(TARGET_NAME)_libretro_$(platform).so
|
||||
fpic := -fPIC
|
||||
SHARED := -lcpp -lm -shared -Wl,-version-script=$(LIBRETRO_DIR)/link.T -Wl,-no-undefined
|
||||
SHARED := -lcpp -lm -shared -Wl,-no-undefined
|
||||
CC = qcc -Vgcc_ntoarmv7le
|
||||
CXX = QCC -Vgcc_ntoarmv7le_cpp
|
||||
AR = QCC -Vgcc_ntoarmv7le
|
||||
@@ -527,7 +527,7 @@ else
|
||||
TARGET := $(TARGET_NAME)_libretro.dll
|
||||
CC ?= gcc
|
||||
CXX ?= g++
|
||||
SHARED := -shared -static-libgcc -static-libstdc++ -Wl,-no-undefined -Wl,-version-script=$(LIBRETRO_DIR)/link.T
|
||||
SHARED := -shared -static-libgcc -static-libstdc++ -Wl,-no-undefined
|
||||
TILED_RENDERING=1
|
||||
endif
|
||||
|
||||
|
@@ -23,14 +23,16 @@
|
||||
class SoundRetro : public SoundDriver {
|
||||
public:
|
||||
SoundRetro();
|
||||
virtual ~SoundRetro();
|
||||
~SoundRetro() override;
|
||||
|
||||
virtual bool init(long sampleRate);
|
||||
virtual void pause();
|
||||
virtual void reset();
|
||||
virtual void resume();
|
||||
virtual void write(uint16_t* finalWave, int length);
|
||||
virtual void setThrottle(unsigned short throttle);
|
||||
private:
|
||||
// SoundDriver implementation.
|
||||
bool init(long sampleRate) override;
|
||||
void pause() override;
|
||||
void reset() override;
|
||||
void resume() override;
|
||||
void write(uint16_t *finalWave, int length) override;
|
||||
void setThrottle(unsigned short throttle_) override;
|
||||
};
|
||||
|
||||
#endif // __VBA_SOUND_RETRO_H__
|
||||
|
@@ -1949,10 +1949,10 @@ uint32_t systemGetClock(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
SoundDriver* systemSoundInit(void)
|
||||
std::unique_ptr<SoundDriver> systemSoundInit(void)
|
||||
{
|
||||
soundShutdown();
|
||||
return new SoundRetro();
|
||||
return std::make_unique<SoundRetro>();
|
||||
}
|
||||
|
||||
void log(const char* defaultMsg, ...)
|
||||
|
@@ -1,4 +0,0 @@
|
||||
{
|
||||
global: retro_*;
|
||||
local: *;
|
||||
};
|
@@ -7,6 +7,8 @@ add_executable(vbam WIN32)
|
||||
|
||||
target_sources(vbam
|
||||
PRIVATE
|
||||
audio_sdl.cpp
|
||||
audio_sdl.h
|
||||
ConfigManager.cpp
|
||||
ConfigManager.h
|
||||
dictionary.c
|
||||
@@ -38,15 +40,19 @@ if(ENABLE_LIRC)
|
||||
set(LIRC_CLIENT_LIBRARY lirc_client)
|
||||
endif()
|
||||
|
||||
target_include_directories(vbam
|
||||
PRIVATE ${SDL2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(vbam
|
||||
vbam-core
|
||||
vbam-components-audio-sdl
|
||||
vbam-components-draw-text
|
||||
vbam-components-filters
|
||||
vbam-components-filters-agb
|
||||
vbam-components-filters-interframe
|
||||
vbam-components-user-config
|
||||
${OPENGL_LIBRARIES}
|
||||
${VBAM_SDL2_LIBS}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
@@ -68,6 +68,8 @@
|
||||
#pragma comment(lib, "OpenGL32")
|
||||
#include <Windows.h>
|
||||
|
||||
#define strdup _strdup
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
@@ -91,7 +93,6 @@
|
||||
#include <sys/poll.h>
|
||||
#endif
|
||||
|
||||
#include "components/audio_sdl/audio_sdl.h"
|
||||
#include "components/draw_text/draw_text.h"
|
||||
#include "components/filters_agb/filters_agb.h"
|
||||
#include "components/user_config/user_config.h"
|
||||
@@ -110,6 +111,7 @@
|
||||
#include "core/gba/gbaRtc.h"
|
||||
#include "core/gba/gbaSound.h"
|
||||
#include "sdl/ConfigManager.h"
|
||||
#include "sdl/audio_sdl.h"
|
||||
#include "sdl/filters.h"
|
||||
#include "sdl/inputSDL.h"
|
||||
|
||||
@@ -2315,11 +2317,10 @@ uint8_t systemGetSensorDarkness()
|
||||
return 0xE8;
|
||||
}
|
||||
|
||||
SoundDriver* systemSoundInit()
|
||||
{
|
||||
std::unique_ptr<SoundDriver> systemSoundInit() {
|
||||
soundShutdown();
|
||||
|
||||
return new SoundSDL();
|
||||
return std::make_unique<SoundSDL>();
|
||||
}
|
||||
|
||||
void systemOnSoundShutdown()
|
||||
|
@@ -15,7 +15,7 @@
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "components/audio_sdl/audio_sdl.h"
|
||||
#include "sdl/audio_sdl.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
@@ -15,34 +15,34 @@
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef VBAM_COMPONENTS_AUDIO_SDL_AUDIO_SDL_H_
|
||||
#define VBAM_COMPONENTS_AUDIO_SDL_AUDIO_SDL_H_
|
||||
#ifndef VBAM_SDL_AUDIO_SDL_H_
|
||||
#define VBAM_SDL_AUDIO_SDL_H_
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "core/base/ringbuffer.h"
|
||||
#include "core/base/sound_driver.h"
|
||||
|
||||
class SoundSDL : public SoundDriver {
|
||||
class SoundSDL final : public SoundDriver {
|
||||
public:
|
||||
SoundSDL();
|
||||
virtual ~SoundSDL();
|
||||
|
||||
virtual bool init(long sampleRate);
|
||||
virtual void pause();
|
||||
virtual void reset();
|
||||
virtual void resume();
|
||||
virtual void write(uint16_t *finalWave, int length);
|
||||
virtual void setThrottle(unsigned short throttle_);
|
||||
|
||||
protected:
|
||||
static void soundCallback(void* data, uint8_t* stream, int length);
|
||||
virtual void read(uint16_t* stream, int length);
|
||||
virtual bool should_wait();
|
||||
virtual std::size_t buffer_size();
|
||||
virtual void deinit();
|
||||
~SoundSDL() override;
|
||||
|
||||
private:
|
||||
static void soundCallback(void* data, uint8_t* stream, int length);
|
||||
void read(uint16_t* stream, int length);
|
||||
bool should_wait();
|
||||
std::size_t buffer_size();
|
||||
void deinit();
|
||||
|
||||
// SoundDriver implementation.
|
||||
bool init(long sampleRate) override;
|
||||
void pause() override;
|
||||
void reset() override;
|
||||
void resume() override;
|
||||
void write(uint16_t *finalWave, int length) override;
|
||||
void setThrottle(unsigned short throttle_) override;
|
||||
|
||||
RingBuffer<uint16_t> samples_buf;
|
||||
|
||||
SDL_AudioDeviceID sound_device = 0;
|
||||
@@ -60,4 +60,4 @@ private:
|
||||
static const double buftime;
|
||||
};
|
||||
|
||||
#endif // VBAM_COMPONENTS_AUDIO_SDL_AUDIO_SDL_H_
|
||||
#endif // VBAM_SDL_AUDIO_SDL_H_
|
@@ -151,7 +151,7 @@ typedef unsigned int flex_uint32_t;
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
|
||||
#define YY_TYPEDEF_YY_BUFFER_STATE
|
||||
typedef struct yy_buffer_state* YY_BUFFER_STATE;
|
||||
typedef struct yy_buffer_state_sdl* YY_BUFFER_STATE;
|
||||
#endif
|
||||
|
||||
extern int yyleng;
|
||||
@@ -184,7 +184,7 @@ typedef size_t yy_size_t;
|
||||
|
||||
#ifndef YY_STRUCT_YY_BUFFER_STATE
|
||||
#define YY_STRUCT_YY_BUFFER_STATE
|
||||
struct yy_buffer_state {
|
||||
struct yy_buffer_state_sdl {
|
||||
FILE* yy_input_file;
|
||||
|
||||
char* yy_ch_buf; /* input buffer */
|
||||
@@ -1218,7 +1218,7 @@ YY_BUFFER_STATE yy_create_buffer(FILE* file, int size)
|
||||
{
|
||||
YY_BUFFER_STATE b;
|
||||
|
||||
b = (YY_BUFFER_STATE)yyalloc(sizeof(struct yy_buffer_state));
|
||||
b = (YY_BUFFER_STATE)yyalloc(sizeof(struct yy_buffer_state_sdl));
|
||||
if (!b)
|
||||
YY_FATAL_ERROR("out of dynamic memory in yy_create_buffer()");
|
||||
|
||||
@@ -1381,11 +1381,11 @@ static void yyensure_buffer_stack(void)
|
||||
* immediate realloc on the next call.
|
||||
*/
|
||||
num_to_alloc = 1;
|
||||
(yy_buffer_stack) = (struct yy_buffer_state**)yyalloc(num_to_alloc * sizeof(struct yy_buffer_state*));
|
||||
(yy_buffer_stack) = (struct yy_buffer_state_sdl**)yyalloc(num_to_alloc * sizeof(struct yy_buffer_state_sdl*));
|
||||
if (!(yy_buffer_stack))
|
||||
YY_FATAL_ERROR("out of dynamic memory in yyensure_buffer_stack()");
|
||||
|
||||
memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
|
||||
memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state_sdl*));
|
||||
|
||||
(yy_buffer_stack_max) = num_to_alloc;
|
||||
(yy_buffer_stack_top) = 0;
|
||||
@@ -1398,13 +1398,13 @@ static void yyensure_buffer_stack(void)
|
||||
int grow_size = 8 /* arbitrary grow size */;
|
||||
|
||||
num_to_alloc = (yy_buffer_stack_max) + grow_size;
|
||||
(yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc((yy_buffer_stack),
|
||||
num_to_alloc * sizeof(struct yy_buffer_state*));
|
||||
(yy_buffer_stack) = (struct yy_buffer_state_sdl**)yyrealloc((yy_buffer_stack),
|
||||
num_to_alloc * sizeof(struct yy_buffer_state_sdl*));
|
||||
if (!(yy_buffer_stack))
|
||||
YY_FATAL_ERROR("out of dynamic memory in yyensure_buffer_stack()");
|
||||
|
||||
/* zero only the new slots.*/
|
||||
memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
|
||||
memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state_sdl*));
|
||||
(yy_buffer_stack_max) = num_to_alloc;
|
||||
}
|
||||
}
|
||||
@@ -1423,7 +1423,7 @@ YY_BUFFER_STATE yy_scan_buffer(char* base, yy_size_t size)
|
||||
/* They forgot to leave room for the EOB's. */
|
||||
return 0;
|
||||
|
||||
b = (YY_BUFFER_STATE)yyalloc(sizeof(struct yy_buffer_state));
|
||||
b = (YY_BUFFER_STATE)yyalloc(sizeof(struct yy_buffer_state_sdl));
|
||||
if (!b)
|
||||
YY_FATAL_ERROR("out of dynamic memory in yy_scan_buffer()");
|
||||
|
||||
|
@@ -6,6 +6,10 @@ endif()
|
||||
include(VbamFunctions)
|
||||
|
||||
set(VBAM_WX_COMMON
|
||||
audio/audio.cpp
|
||||
audio/audio.h
|
||||
audio/internal/openal.cpp
|
||||
audio/internal/openal.h
|
||||
background-input.cpp
|
||||
background-input.h
|
||||
cmdevents.cpp
|
||||
@@ -27,6 +31,8 @@ set(VBAM_WX_COMMON
|
||||
config/user-input.h
|
||||
dialogs/accel-config.cpp
|
||||
dialogs/accel-config.h
|
||||
dialogs/base-dialog.cpp
|
||||
dialogs/base-dialog.h
|
||||
dialogs/directories-config.cpp
|
||||
dialogs/directories-config.h
|
||||
dialogs/display-config.cpp
|
||||
@@ -39,14 +45,13 @@ set(VBAM_WX_COMMON
|
||||
dialogs/gb-rom-info.h
|
||||
dialogs/joypad-config.cpp
|
||||
dialogs/joypad-config.h
|
||||
dialogs/validated-child.h
|
||||
dialogs/sound-config.cpp
|
||||
dialogs/sound-config.h
|
||||
drawing.h
|
||||
extra-translations.cpp
|
||||
gfxviewers.cpp
|
||||
guiinit.cpp
|
||||
ioregs.h
|
||||
openal.cpp
|
||||
openal.h
|
||||
opts.cpp
|
||||
opts.h
|
||||
panel.cpp
|
||||
@@ -73,25 +78,27 @@ set(VBAM_WX_COMMON
|
||||
widgets/render-plugin.h
|
||||
widgets/user-input-ctrl.cpp
|
||||
widgets/user-input-ctrl.h
|
||||
widgets/sdljoy.cpp
|
||||
widgets/sdljoy.h
|
||||
widgets/user-input-event.cpp
|
||||
widgets/user-input-event.h
|
||||
widgets/sdl-poller.cpp
|
||||
widgets/sdl-poller.h
|
||||
widgets/utils.cpp
|
||||
widgets/utils.h
|
||||
widgets/webupdatedef.h
|
||||
widgets/wxmisc.h
|
||||
widgets/wxmisc.cpp
|
||||
wxhead.h
|
||||
wxlogdebug.h
|
||||
wxutil.cpp
|
||||
wxutil.h
|
||||
wxvbam.cpp
|
||||
wxvbam.h
|
||||
x11keymap.h
|
||||
xrc/visualboyadvance-m.xpm
|
||||
# Generated files.
|
||||
${VBAM_GENERATED_DIR}/wx//builtin-xrc.h
|
||||
${VBAM_GENERATED_DIR}/wx//builtin-over.h
|
||||
${VBAM_GENERATED_DIR}/wx//cmdhandlers.h
|
||||
${VBAM_GENERATED_DIR}/wx//cmd-evtable.h
|
||||
${VBAM_GENERATED_DIR}/wx//cmdtab.cpp
|
||||
${VBAM_GENERATED_DIR}/wx/builtin-xrc.h
|
||||
${VBAM_GENERATED_DIR}/wx/builtin-over.h
|
||||
${VBAM_GENERATED_DIR}/wx/cmdhandlers.h
|
||||
${VBAM_GENERATED_DIR}/wx/cmd-evtable.h
|
||||
${VBAM_GENERATED_DIR}/wx/cmdtab.cpp
|
||||
)
|
||||
|
||||
if(NOT ZIP_PROGRAM)
|
||||
@@ -182,19 +189,19 @@ add_executable(
|
||||
)
|
||||
|
||||
target_sources(visualboyadvance-m PRIVATE ${VBAM_WX_COMMON} ${VBAM_ICON_PATH})
|
||||
target_include_directories(visualboyadvance-m PRIVATE ${NONSTD_INCLUDE_DIR})
|
||||
target_include_directories(visualboyadvance-m PRIVATE ${NONSTD_INCLUDE_DIR} ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(
|
||||
visualboyadvance-m
|
||||
nonstd-lib
|
||||
vbam-core
|
||||
vbam-components-audio-sdl
|
||||
vbam-components-draw-text
|
||||
vbam-components-filters
|
||||
vbam-components-filters-agb
|
||||
vbam-components-filters-interframe
|
||||
vbam-components-user-config
|
||||
${OPENGL_LIBRARIES}
|
||||
${VBAM_SDL2_LIBS}
|
||||
)
|
||||
|
||||
# adjust link command when making a static binary for gcc
|
||||
@@ -247,7 +254,7 @@ endif()
|
||||
if(WIN32)
|
||||
target_sources(visualboyadvance-m
|
||||
PRIVATE
|
||||
dsound.cpp
|
||||
audio/internal/dsound.cpp
|
||||
wxvbam.rc
|
||||
)
|
||||
target_link_libraries(visualboyadvance-m
|
||||
@@ -311,9 +318,8 @@ target_link_libraries(visualboyadvance-m ${OPENAL_LIBRARY})
|
||||
|
||||
# XAudio2.
|
||||
if(ENABLE_XAUDIO2)
|
||||
target_sources(visualboyadvance-m PRIVATE xaudio2.cpp)
|
||||
else()
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE NO_XAUDIO2)
|
||||
target_sources(visualboyadvance-m PRIVATE audio/internal/xaudio2.cpp)
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_XAUDIO2)
|
||||
endif()
|
||||
|
||||
# Direct3D.
|
||||
@@ -323,10 +329,9 @@ endif()
|
||||
|
||||
# FAudio.
|
||||
if(ENABLE_FAUDIO)
|
||||
find_package(FAudio REQUIRED)
|
||||
target_sources(visualboyadvance-m PRIVATE audio/internal/faudio.cpp)
|
||||
target_link_libraries(visualboyadvance-m FAudio::FAudio)
|
||||
else()
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE NO_FAUDIO)
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_FAUDIO)
|
||||
endif()
|
||||
|
||||
# wxWidgets.
|
||||
@@ -605,13 +610,11 @@ add_custom_command(
|
||||
copy-events.cmake
|
||||
)
|
||||
|
||||
# # Win32 definitions common to all toolchains.
|
||||
# if (WIN32)
|
||||
# add_compile_definitions(wxUSE_GUI=1)
|
||||
# endif()
|
||||
|
||||
set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON})
|
||||
list(APPEND VBAM_LOCALIZABLE_FILES
|
||||
audio/internal/dsound.cpp
|
||||
audio/internal/faudio.cpp
|
||||
audio/internal/xaudio2.cpp
|
||||
autoupdater/autoupdater.h
|
||||
autoupdater/macos/autoupdater.cpp
|
||||
autoupdater/macos/sparkle-wrapper.h
|
||||
@@ -619,11 +622,8 @@ list(APPEND VBAM_LOCALIZABLE_FILES
|
||||
autoupdater/wxmsw/winsparkle-rc.h
|
||||
autoupdater/wxmsw/winsparkle-wrapper.cpp
|
||||
autoupdater/wxmsw/winsparkle-wrapper.h
|
||||
dsound.cpp
|
||||
faudio.cpp
|
||||
widgets/dpi-support.cpp
|
||||
widgets/dpi-support-mac.mm
|
||||
xaudio2.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/core/gba/gbaLink.cpp
|
||||
)
|
||||
|
||||
@@ -780,7 +780,7 @@ if(UPSTREAM_RELEASE AND WIN32)
|
||||
TARGET visualboyadvance-m
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy visualboyadvance-m.exe visualboyadvance-m-unsigned.exe
|
||||
COMMAND ${SIGNTOOL_PROGRAM} sign /f ${cert} /p "vbam3!13" /tr http://timestamp.digicert.com /du https://github.com/visualboyadvance-m/visualboyadvance-m /a visualboyadvance-m.exe
|
||||
COMMAND ${SIGNTOOL_PROGRAM} sign /f ${cert} /fd certHash /td certHash /p "vbam3!13" /tr http://timestamp.digicert.com /du https://github.com/visualboyadvance-m/visualboyadvance-m /a visualboyadvance-m.exe
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
endif()
|
||||
|
76
src/wx/audio/audio.cpp
Normal file
76
src/wx/audio/audio.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "wx/audio/audio.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
|
||||
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
#include "wx/audio/internal/xaudio2.h"
|
||||
#endif
|
||||
|
||||
namespace audio {
|
||||
|
||||
std::vector<AudioDevice> EnumerateAudioDevices(const config::AudioApi& audio_api) {
|
||||
switch (audio_api) {
|
||||
case config::AudioApi::kOpenAL:
|
||||
return audio::internal::GetOpenALDevices();
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
case config::AudioApi::kDirectSound:
|
||||
return audio::internal::GetDirectSoundDevices();
|
||||
#endif
|
||||
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
case config::AudioApi::kXAudio2:
|
||||
return audio::internal::GetXAudio2Devices();
|
||||
#endif
|
||||
|
||||
#if defined(VBAM_ENABLE_FAUDIO)
|
||||
case config::AudioApi::kFAudio:
|
||||
return audio::internal::GetFAudioDevices();
|
||||
#endif
|
||||
|
||||
case config::AudioApi::kLast:
|
||||
default:
|
||||
// This should never happen.
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SoundDriver> CreateSoundDriver(const config::AudioApi& api) {
|
||||
switch (api) {
|
||||
case config::AudioApi::kOpenAL:
|
||||
return audio::internal::CreateOpenALDriver();
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
case config::AudioApi::kDirectSound:
|
||||
return audio::internal::CreateDirectSoundDriver();
|
||||
#endif
|
||||
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
case config::AudioApi::kXAudio2:
|
||||
return audio::internal::CreateXAudio2Driver();
|
||||
#endif
|
||||
|
||||
#if defined(VBAM_ENABLE_FAUDIO)
|
||||
case config::AudioApi::kFAudio:
|
||||
return audio::internal::CreateFAudioDriver();
|
||||
#endif
|
||||
|
||||
case config::AudioApi::kLast:
|
||||
default:
|
||||
// This should never happen.
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace audio
|
30
src/wx/audio/audio.h
Normal file
30
src/wx/audio/audio.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef WX_AUDIO_AUDIO_H_
|
||||
#define WX_AUDIO_AUDIO_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "core/base/sound_driver.h"
|
||||
#include "wx/config/option.h"
|
||||
|
||||
namespace audio {
|
||||
|
||||
// Represents an audio device.
|
||||
struct AudioDevice {
|
||||
// The device user-friendly name.
|
||||
wxString name;
|
||||
// The underlying device ID.
|
||||
wxString id;
|
||||
};
|
||||
|
||||
// Returns the set of audio devices for the given API.
|
||||
std::vector<AudioDevice> EnumerateAudioDevices(const config::AudioApi& api);
|
||||
|
||||
// Creates a sound driver for the given API.
|
||||
std::unique_ptr<SoundDriver> CreateSoundDriver(const config::AudioApi& api);
|
||||
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_AUDIO_H_
|
@@ -1,46 +1,62 @@
|
||||
// Application
|
||||
#include "wx/wxvbam.h"
|
||||
#if !defined(__WXMSW__)
|
||||
#error "This file should only be compiled on Windows"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/internal/dsound.h"
|
||||
|
||||
// DirectSound8
|
||||
#define DIRECTSOUND_VERSION 0x0800
|
||||
#include <Windows.h>
|
||||
#include <mmeapi.h>
|
||||
|
||||
#include <dsound.h>
|
||||
#include <uuids.h>
|
||||
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/translation.h>
|
||||
|
||||
// Internals
|
||||
#include "core/base/sound_driver.h"
|
||||
#include "core/base/system.h"
|
||||
#include "core/gba/gbaGlobals.h"
|
||||
#include "core/gba/gbaSound.h"
|
||||
|
||||
// DirectSound8
|
||||
#define DIRECTSOUND_VERSION 0x0800
|
||||
#include <mmeapi.h>
|
||||
#include <uuids.h>
|
||||
#include <dsound.h>
|
||||
#include "wx/config/option-proxy.h"
|
||||
#include "wx/wxvbam.h"
|
||||
|
||||
extern bool soundBufferLow;
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
class DirectSound : public SoundDriver {
|
||||
private:
|
||||
LPDIRECTSOUND8 pDirectSound; // DirectSound interface
|
||||
LPDIRECTSOUNDBUFFER dsbPrimary; // Primary DirectSound buffer
|
||||
LPDIRECTSOUNDBUFFER dsbSecondary; // Secondary DirectSound buffer
|
||||
LPDIRECTSOUND8 pDirectSound; // DirectSound interface
|
||||
LPDIRECTSOUNDBUFFER dsbPrimary; // Primary DirectSound buffer
|
||||
LPDIRECTSOUNDBUFFER dsbSecondary; // Secondary DirectSound buffer
|
||||
LPDIRECTSOUNDNOTIFY dsbNotify;
|
||||
HANDLE dsbEvent;
|
||||
WAVEFORMATEX wfx; // Primary buffer wave format
|
||||
WAVEFORMATEX wfx; // Primary buffer wave format
|
||||
int soundBufferLen;
|
||||
int soundBufferTotalLen;
|
||||
unsigned int soundNextPosition;
|
||||
|
||||
public:
|
||||
DirectSound();
|
||||
virtual ~DirectSound();
|
||||
~DirectSound() override;
|
||||
|
||||
bool init(long sampleRate); // initialize the primary and secondary sound buffer
|
||||
void setThrottle(unsigned short throttle_); // set game speed
|
||||
void pause(); // pause the secondary sound buffer
|
||||
void reset(); // stop and reset the secondary sound buffer
|
||||
void resume(); // resume the secondary sound buffer
|
||||
void write(uint16_t* finalWave, int length); // write the emulated sound to the secondary sound buffer
|
||||
// SoundDriver implementation.
|
||||
bool init(long sampleRate) override;
|
||||
void pause() override;
|
||||
void reset() override;
|
||||
void resume() override;
|
||||
void write(uint16_t* finalWave, int length) override;
|
||||
void setThrottle(unsigned short throttle_) override;
|
||||
};
|
||||
|
||||
DirectSound::DirectSound()
|
||||
{
|
||||
DirectSound::DirectSound() {
|
||||
pDirectSound = NULL;
|
||||
dsbPrimary = NULL;
|
||||
dsbSecondary = NULL;
|
||||
@@ -50,8 +66,7 @@ DirectSound::DirectSound()
|
||||
soundNextPosition = 0;
|
||||
}
|
||||
|
||||
DirectSound::~DirectSound()
|
||||
{
|
||||
DirectSound::~DirectSound() {
|
||||
if (dsbNotify) {
|
||||
dsbNotify->Release();
|
||||
dsbNotify = NULL;
|
||||
@@ -78,13 +93,13 @@ DirectSound::~DirectSound()
|
||||
}
|
||||
}
|
||||
|
||||
bool DirectSound::init(long sampleRate)
|
||||
{
|
||||
bool DirectSound::init(long sampleRate) {
|
||||
HRESULT hr;
|
||||
DWORD freq;
|
||||
DSBUFFERDESC dsbdesc;
|
||||
int i;
|
||||
hr = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound8, (LPVOID*)&pDirectSound);
|
||||
hr = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound8,
|
||||
(LPVOID*)&pDirectSound);
|
||||
|
||||
if (hr != S_OK) {
|
||||
wxLogError(_("Cannot create Direct Sound %08x"), hr);
|
||||
@@ -93,10 +108,11 @@ bool DirectSound::init(long sampleRate)
|
||||
|
||||
GUID dev;
|
||||
|
||||
if (gopts.audio_dev.empty())
|
||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||
if (audio_device.empty())
|
||||
dev = DSDEVID_DefaultPlayback;
|
||||
else
|
||||
CLSIDFromString(gopts.audio_dev.wc_str(), &dev);
|
||||
CLSIDFromString(audio_device.wc_str(), &dev);
|
||||
|
||||
pDirectSound->Initialize(&dev);
|
||||
|
||||
@@ -105,7 +121,8 @@ bool DirectSound::init(long sampleRate)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FAILED(hr = pDirectSound->SetCooperativeLevel((HWND)wxGetApp().frame->GetHandle(), DSSCL_PRIORITY))) {
|
||||
if (FAILED(hr = pDirectSound->SetCooperativeLevel((HWND)wxGetApp().frame->GetHandle(),
|
||||
DSSCL_PRIORITY))) {
|
||||
wxLogError(_("Cannot SetCooperativeLevel %08x"), hr);
|
||||
return false;
|
||||
}
|
||||
@@ -115,7 +132,8 @@ bool DirectSound::init(long sampleRate)
|
||||
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
||||
dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
|
||||
if (!gopts.dsound_hw_accel) {
|
||||
const bool hw_accel = OPTION(kSoundDSoundHWAccel);
|
||||
if (!hw_accel) {
|
||||
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
||||
}
|
||||
|
||||
@@ -146,9 +164,10 @@ bool DirectSound::init(long sampleRate)
|
||||
// Create secondary sound buffer
|
||||
ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC));
|
||||
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
||||
dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLFREQUENCY;
|
||||
dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY |
|
||||
DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLFREQUENCY;
|
||||
|
||||
if (!gopts.dsound_hw_accel) {
|
||||
if (!hw_accel) {
|
||||
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
||||
}
|
||||
|
||||
@@ -165,7 +184,8 @@ bool DirectSound::init(long sampleRate)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr = dsbSecondary->QueryInterface(IID_IDirectSoundNotify8, (LPVOID*)&dsbNotify))) {
|
||||
if (SUCCEEDED(hr =
|
||||
dsbSecondary->QueryInterface(IID_IDirectSoundNotify8, (LPVOID*)&dsbNotify))) {
|
||||
dsbEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
DSBPOSITIONNOTIFY notify[10];
|
||||
|
||||
@@ -195,7 +215,7 @@ void DirectSound::setThrottle(unsigned short throttle_) {
|
||||
HRESULT hr;
|
||||
|
||||
if (throttle_ == 0)
|
||||
throttle_ = 450; // Close to upper bound on frequency.
|
||||
throttle_ = 450; // Close to upper bound on frequency.
|
||||
|
||||
long freq = soundGetSampleRate();
|
||||
|
||||
@@ -204,8 +224,7 @@ void DirectSound::setThrottle(unsigned short throttle_) {
|
||||
}
|
||||
}
|
||||
|
||||
void DirectSound::pause()
|
||||
{
|
||||
void DirectSound::pause() {
|
||||
LPDIRECTSOUNDBUFFER bufs[] = {dsbPrimary, dsbSecondary};
|
||||
for (auto buf : bufs) {
|
||||
if (buf == NULL)
|
||||
@@ -219,8 +238,7 @@ void DirectSound::pause()
|
||||
}
|
||||
}
|
||||
|
||||
void DirectSound::reset()
|
||||
{
|
||||
void DirectSound::reset() {
|
||||
if (dsbSecondary == NULL)
|
||||
return;
|
||||
|
||||
@@ -229,8 +247,7 @@ void DirectSound::reset()
|
||||
soundNextPosition = 0;
|
||||
}
|
||||
|
||||
void DirectSound::resume()
|
||||
{
|
||||
void DirectSound::resume() {
|
||||
LPDIRECTSOUNDBUFFER bufs[] = {dsbPrimary, dsbSecondary};
|
||||
for (auto buf : bufs) {
|
||||
if (buf == NULL)
|
||||
@@ -240,8 +257,7 @@ void DirectSound::resume()
|
||||
}
|
||||
}
|
||||
|
||||
void DirectSound::write(uint16_t* finalWave, int)
|
||||
{
|
||||
void DirectSound::write(uint16_t* finalWave, int) {
|
||||
if (!pDirectSound)
|
||||
return;
|
||||
|
||||
@@ -260,7 +276,9 @@ void DirectSound::write(uint16_t* finalWave, int)
|
||||
if (!soundPaused) {
|
||||
while (true) {
|
||||
dsbSecondary->GetCurrentPosition(&play, NULL);
|
||||
int BufferLeft = ((soundNextPosition <= play) ? play - soundNextPosition : soundBufferTotalLen - soundNextPosition + play);
|
||||
int BufferLeft = ((soundNextPosition <= play)
|
||||
? play - soundNextPosition
|
||||
: soundBufferTotalLen - soundNextPosition + play);
|
||||
|
||||
if (BufferLeft > soundBufferLen) {
|
||||
if (BufferLeft > soundBufferTotalLen - (soundBufferLen * 3))
|
||||
@@ -285,24 +303,12 @@ void DirectSound::write(uint16_t* finalWave, int)
|
||||
|
||||
// Obtain memory address of write block.
|
||||
// This will be in two parts if the block wraps around.
|
||||
if (DSERR_BUFFERLOST == (hr = dsbSecondary->Lock(
|
||||
soundNextPosition,
|
||||
soundBufferLen,
|
||||
&lpvPtr1,
|
||||
&dwBytes1,
|
||||
&lpvPtr2,
|
||||
&dwBytes2,
|
||||
0))) {
|
||||
if (DSERR_BUFFERLOST == (hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen, &lpvPtr1,
|
||||
&dwBytes1, &lpvPtr2, &dwBytes2, 0))) {
|
||||
// If DSERR_BUFFERLOST is returned, restore and retry lock.
|
||||
dsbSecondary->Restore();
|
||||
hr = dsbSecondary->Lock(
|
||||
soundNextPosition,
|
||||
soundBufferLen,
|
||||
&lpvPtr1,
|
||||
&dwBytes1,
|
||||
&lpvPtr2,
|
||||
&dwBytes2,
|
||||
0);
|
||||
hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen, &lpvPtr1, &dwBytes1, &lpvPtr2,
|
||||
&dwBytes2, 0);
|
||||
}
|
||||
|
||||
soundNextPosition += soundBufferLen;
|
||||
@@ -324,27 +330,33 @@ void DirectSound::write(uint16_t* finalWave, int)
|
||||
}
|
||||
}
|
||||
|
||||
SoundDriver* newDirectSound()
|
||||
{
|
||||
return new DirectSound();
|
||||
}
|
||||
static BOOL CALLBACK DSEnumCB(LPGUID guid, LPCTSTR desc, LPCTSTR /*module*/, LPVOID user) {
|
||||
std::vector<AudioDevice>* devices = static_cast<std::vector<AudioDevice>*>(user);
|
||||
|
||||
struct devnames {
|
||||
wxArrayString *names, *ids;
|
||||
};
|
||||
if (guid == nullptr) {
|
||||
devices->push_back({desc, {}});
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK DSEnumCB(LPGUID guid, LPCTSTR desc, LPCTSTR, LPVOID user)
|
||||
{
|
||||
devnames* dn = (devnames*)user;
|
||||
dn->names->push_back(desc);
|
||||
WCHAR buf[32 + 4 + 2 + 1]; // hex digits + "-" + "{}" + \0
|
||||
StringFromGUID2(*guid, buf, sizeof(buf));
|
||||
dn->ids->push_back(buf);
|
||||
static constexpr size_t kGuidLength = 32 + 4 + 2 + 1; // hex digits + "-" + "{}" + \0
|
||||
std::array<WCHAR, kGuidLength> device_id;
|
||||
StringFromGUID2(*guid, device_id.data(), device_id.size());
|
||||
|
||||
devices->push_back({desc, device_id.data()});
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool GetDSDevices(wxArrayString& names, wxArrayString& ids)
|
||||
{
|
||||
devnames dn = { &names, &ids };
|
||||
return DirectSoundEnumerate(DSEnumCB, (LPVOID)&dn) == DS_OK;
|
||||
} // namespace
|
||||
|
||||
std::vector<AudioDevice> GetDirectSoundDevices() {
|
||||
std::vector<AudioDevice> devices;
|
||||
DirectSoundEnumerateW(DSEnumCB, &devices);
|
||||
return devices;
|
||||
}
|
||||
|
||||
std::unique_ptr<SoundDriver> CreateDirectSoundDriver() {
|
||||
return std::make_unique<DirectSound>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
22
src/wx/audio/internal/dsound.h
Normal file
22
src/wx/audio/internal/dsound.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef WX_AUDIO_INTERNAL_DSOUND_H_
|
||||
#define WX_AUDIO_INTERNAL_DSOUND_H_
|
||||
|
||||
#if !defined(__WXMSW__)
|
||||
#error "This file should only be included on Windows"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
// Returns the set of DirectSound devices.
|
||||
std::vector<AudioDevice> GetDirectSoundDevices();
|
||||
|
||||
// Creates a DirectSound sound driver.
|
||||
std::unique_ptr<SoundDriver> CreateDirectSoundDriver();
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_INTERNAL_DSOUND_H_
|
517
src/wx/audio/internal/faudio.cpp
Normal file
517
src/wx/audio/internal/faudio.cpp
Normal file
@@ -0,0 +1,517 @@
|
||||
#include <wx/string.h>
|
||||
#if !defined(VBAM_ENABLE_FAUDIO)
|
||||
#error "This file should only be compiled if FAudio is enabled"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/internal/faudio.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
// FAudio
|
||||
#include <FAudio.h>
|
||||
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/translation.h>
|
||||
|
||||
#include "core/base/system.h"
|
||||
#include "core/gba/gbaGlobals.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
int FAGetDev(FAudio* fa) {
|
||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||
if (audio_device.empty()) {
|
||||
// Just use the default device.
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t hr;
|
||||
uint32_t dev_count = 0;
|
||||
hr = FAudio_GetDeviceCount(fa, &dev_count);
|
||||
if (hr != 0) {
|
||||
wxLogError(_("FAudio: Enumerating devices failed!"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
FAudioDeviceDetails dd;
|
||||
for (uint32_t i = 0; i < dev_count; i++) {
|
||||
hr = FAudio_GetDeviceDetails(fa, i, &dd);
|
||||
if (hr != 0) {
|
||||
continue;
|
||||
}
|
||||
const wxString device_id(reinterpret_cast<wchar_t*>(dd.DeviceID));
|
||||
if (audio_device == device_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
class FAudio_BufferNotify : public FAudioVoiceCallback {
|
||||
public:
|
||||
// Waits for the buffer end event to be signaled for 10 seconds.
|
||||
// Returns true if the buffer end event was signaled, false if the wait timed out.
|
||||
bool WaitForSignal() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
waiting_for_buffer_end_ = true;
|
||||
const bool was_signaled =
|
||||
buffer_end_cv_.wait_for(lock, std::chrono::seconds(10), [this] { return signaled_; });
|
||||
waiting_for_buffer_end_ = false;
|
||||
signaled_ = false;
|
||||
return was_signaled;
|
||||
}
|
||||
|
||||
FAudio_BufferNotify() {
|
||||
OnBufferEnd = &FAudio_BufferNotify::StaticOnBufferEnd;
|
||||
OnVoiceProcessingPassStart = &FAudio_BufferNotify::StaticOnVoiceProcessingPassStart;
|
||||
OnVoiceProcessingPassEnd = &FAudio_BufferNotify::StaticOnVoiceProcessingPassEnd;
|
||||
OnStreamEnd = &FAudio_BufferNotify::StaticOnStreamEnd;
|
||||
OnBufferStart = &FAudio_BufferNotify::StaticOnBufferStart;
|
||||
OnLoopEnd = &FAudio_BufferNotify::StaticOnLoopEnd;
|
||||
OnVoiceError = &FAudio_BufferNotify::StaticOnVoiceError;
|
||||
}
|
||||
~FAudio_BufferNotify() = default;
|
||||
|
||||
private:
|
||||
// Signals that the buffer end event has occurred.
|
||||
void SignalBufferEnd() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (!waiting_for_buffer_end_) {
|
||||
return;
|
||||
}
|
||||
signaled_ = true;
|
||||
buffer_end_cv_.notify_one();
|
||||
}
|
||||
|
||||
// Protects `waiting_for_buffer_end_` and `signaled_`.
|
||||
std::mutex mutex_;
|
||||
// Used to wait for the buffer end event.
|
||||
std::condition_variable buffer_end_cv_;
|
||||
// Set to true when we are waiting for the buffer end event.
|
||||
// Must be protected by `mutex_`.
|
||||
bool waiting_for_buffer_end_ = false;
|
||||
// Set to true when the buffer end event has been signaled.
|
||||
// Must be protected by `mutex_`.
|
||||
bool signaled_ = false;
|
||||
|
||||
static void StaticOnBufferEnd(FAudioVoiceCallback* callback, void*) {
|
||||
static_cast<FAudio_BufferNotify*>(callback)->SignalBufferEnd();
|
||||
}
|
||||
static void StaticOnVoiceProcessingPassStart(FAudioVoiceCallback*, uint32_t) {}
|
||||
static void StaticOnVoiceProcessingPassEnd(FAudioVoiceCallback*) {}
|
||||
static void StaticOnStreamEnd(FAudioVoiceCallback*) {}
|
||||
static void StaticOnBufferStart(FAudioVoiceCallback*, void*) {}
|
||||
static void StaticOnLoopEnd(FAudioVoiceCallback*, void*) {}
|
||||
static void StaticOnVoiceError(FAudioVoiceCallback*, void*, uint32_t) {}
|
||||
};
|
||||
|
||||
class FAudio_Output : public SoundDriver {
|
||||
public:
|
||||
FAudio_Output();
|
||||
~FAudio_Output();
|
||||
|
||||
void device_change();
|
||||
|
||||
private:
|
||||
void close();
|
||||
|
||||
// SoundDriver implementation.
|
||||
bool init(long sampleRate) override;
|
||||
void pause() override;
|
||||
void reset() override;
|
||||
void resume() override;
|
||||
void write(uint16_t* finalWave, int length) override;
|
||||
void setThrottle(unsigned short throttle_) override;
|
||||
|
||||
bool failed;
|
||||
bool initialized;
|
||||
bool playing;
|
||||
uint32_t freq_;
|
||||
const uint32_t buffer_count_;
|
||||
std::vector<uint8_t> buffers_;
|
||||
int currentBuffer;
|
||||
int sound_buffer_len_;
|
||||
|
||||
volatile bool device_changed;
|
||||
|
||||
FAudio* faud;
|
||||
FAudioMasteringVoice* mVoice; // listener
|
||||
FAudioSourceVoice* sVoice; // sound source
|
||||
FAudioBuffer buf;
|
||||
FAudioVoiceState vState;
|
||||
FAudio_BufferNotify notify; // buffer end notification
|
||||
};
|
||||
|
||||
// Class Implementation
|
||||
FAudio_Output::FAudio_Output() : buffer_count_(OPTION(kSoundBuffers)) {
|
||||
failed = false;
|
||||
initialized = false;
|
||||
playing = false;
|
||||
freq_ = 0;
|
||||
currentBuffer = 0;
|
||||
device_changed = false;
|
||||
faud = nullptr;
|
||||
mVoice = nullptr;
|
||||
sVoice = nullptr;
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
memset(&vState, 0, sizeof(vState));
|
||||
}
|
||||
|
||||
FAudio_Output::~FAudio_Output() {
|
||||
close();
|
||||
}
|
||||
|
||||
void FAudio_Output::close() {
|
||||
initialized = false;
|
||||
|
||||
if (sVoice) {
|
||||
if (playing) {
|
||||
assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0);
|
||||
}
|
||||
|
||||
FAudioVoice_DestroyVoice(sVoice);
|
||||
sVoice = nullptr;
|
||||
}
|
||||
|
||||
if (mVoice) {
|
||||
FAudioVoice_DestroyVoice(mVoice);
|
||||
mVoice = nullptr;
|
||||
}
|
||||
|
||||
if (faud) {
|
||||
FAudio_Release(faud);
|
||||
faud = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FAudio_Output::device_change() {
|
||||
device_changed = true;
|
||||
}
|
||||
|
||||
bool FAudio_Output::init(long sampleRate) {
|
||||
if (failed || initialized)
|
||||
return false;
|
||||
|
||||
uint32_t hr;
|
||||
// Initialize FAudio
|
||||
uint32_t flags = 0;
|
||||
// #ifdef _DEBUG
|
||||
// flags = FAUDIO_DEBUG_ENGINE;
|
||||
// #endif
|
||||
hr = FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR);
|
||||
|
||||
if (hr != 0) {
|
||||
wxLogError(_("The FAudio interface failed to initialize!"));
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
freq_ = sampleRate;
|
||||
// calculate the number of samples per frame first
|
||||
// then multiply it with the size of a sample frame (16 bit * stereo)
|
||||
sound_buffer_len_ = (freq_ / 60) * 4;
|
||||
// create own buffers to store sound data because it must not be
|
||||
// manipulated while the voice plays from it.
|
||||
// +1 because we need one temporary buffer when all others are in use.
|
||||
buffers_.resize((buffer_count_ + 1) * sound_buffer_len_);
|
||||
static const uint16_t kNumChannels = 2;
|
||||
static const uint16_t kBitsPerSample = 16;
|
||||
static const uint16_t kBlockAlign = kNumChannels * (kBitsPerSample / 8);
|
||||
FAudioWaveFormatEx wfx{
|
||||
/*.wFormatTag=*/FAUDIO_FORMAT_PCM,
|
||||
/*.nChannels=*/kNumChannels,
|
||||
/*.nSamplesPerSec=*/freq_,
|
||||
/*.nAvgBytesPerSec=*/freq_ * kBlockAlign,
|
||||
/*.nBlockAlign=*/kNumChannels * (kBitsPerSample / 8),
|
||||
/*.wBitsPerSample=*/kBitsPerSample,
|
||||
/*.cbSize=*/0,
|
||||
};
|
||||
|
||||
// create sound receiver
|
||||
hr = FAudio_CreateMasteringVoice(faud, &mVoice, FAUDIO_DEFAULT_CHANNELS,
|
||||
FAUDIO_DEFAULT_SAMPLERATE, 0, FAGetDev(faud), nullptr);
|
||||
|
||||
if (hr != 0) {
|
||||
wxLogError(_("FAudio: Creating mastering voice failed!"));
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// create sound emitter
|
||||
hr = FAudio_CreateSourceVoice(faud, &sVoice, &wfx, 0, 4.0f, ¬ify, nullptr, nullptr);
|
||||
|
||||
if (hr != 0) {
|
||||
wxLogError(_("FAudio: Creating source voice failed!"));
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OPTION(kSoundUpmix)) {
|
||||
// set up stereo upmixing
|
||||
FAudioDeviceDetails dd{};
|
||||
assert(FAudio_GetDeviceDetails(faud, 0, &dd) == 0);
|
||||
std::vector<float> matrix(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels);
|
||||
|
||||
bool matrixAvailable = true;
|
||||
|
||||
switch (dd.OutputFormat.Format.nChannels) {
|
||||
case 4: // 4.0
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Back L*/ matrix[4] = 1.0000f;
|
||||
matrix[5] = 0.0000f;
|
||||
/*Back R*/ matrix[6] = 0.0000f;
|
||||
matrix[7] = 1.0000f;
|
||||
break;
|
||||
|
||||
case 5: // 5.0
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*Side L*/ matrix[6] = 1.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side R*/ matrix[8] = 0.0000f;
|
||||
matrix[9] = 1.0000f;
|
||||
break;
|
||||
|
||||
case 6: // 5.1
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
break;
|
||||
|
||||
case 7: // 6.1
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
/*Back C*/ matrix[12] = 0.7071f;
|
||||
matrix[13] = 0.7071f;
|
||||
break;
|
||||
|
||||
case 8: // 7.1
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Back L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Back R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
/*Side L*/ matrix[12] = 1.0000f;
|
||||
matrix[13] = 0.0000f;
|
||||
/*Side R*/ matrix[14] = 0.0000f;
|
||||
matrix[15] = 1.0000f;
|
||||
break;
|
||||
|
||||
default:
|
||||
matrixAvailable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (matrixAvailable) {
|
||||
hr = FAudioVoice_SetOutputMatrix(sVoice, nullptr, 2, dd.OutputFormat.Format.nChannels,
|
||||
matrix.data(), FAUDIO_DEFAULT_CHANNELS);
|
||||
assert(hr == 0);
|
||||
}
|
||||
}
|
||||
|
||||
hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||
assert(hr == 0);
|
||||
playing = true;
|
||||
currentBuffer = 0;
|
||||
device_changed = false;
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FAudio_Output::write(uint16_t* finalWave, int) {
|
||||
uint32_t flags = 0;
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
while (true) {
|
||||
if (device_changed) {
|
||||
close();
|
||||
|
||||
if (!init(freq_))
|
||||
return;
|
||||
}
|
||||
|
||||
FAudioSourceVoice_GetState(sVoice, &vState, flags);
|
||||
assert(vState.BuffersQueued <= buffer_count_);
|
||||
|
||||
if (vState.BuffersQueued < buffer_count_) {
|
||||
if (vState.BuffersQueued == 0) {
|
||||
// buffers ran dry
|
||||
if (systemVerbose & VERBOSE_SOUNDOUTPUT) {
|
||||
static unsigned int i = 0;
|
||||
log("FAudio: Buffers were not refilled fast enough (i=%i)\n", i++);
|
||||
}
|
||||
}
|
||||
|
||||
// there is at least one free buffer
|
||||
break;
|
||||
} else {
|
||||
// the maximum number of buffers is currently queued
|
||||
if (!coreOptions.speedup && coreOptions.throttle && !gba_joybus_active) {
|
||||
// wait for one buffer to finish playing
|
||||
if (notify.WaitForSignal()) {
|
||||
device_changed = true;
|
||||
}
|
||||
} else {
|
||||
// drop current audio frame
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy & protect the audio data in own memory area while playing it
|
||||
memcpy(&buffers_[currentBuffer * sound_buffer_len_], finalWave, sound_buffer_len_);
|
||||
buf.AudioBytes = sound_buffer_len_;
|
||||
buf.pAudioData = &buffers_[currentBuffer * sound_buffer_len_];
|
||||
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);
|
||||
}
|
||||
|
||||
void FAudio_Output::pause() {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
if (playing) {
|
||||
[[maybe_unused]] uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||
assert(hr == 0);
|
||||
playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
void FAudio_Output::resume() {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
if (!playing) {
|
||||
[[maybe_unused]] int32_t hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||
assert(hr == 0);
|
||||
playing = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FAudio_Output::reset() {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
if (playing) {
|
||||
[[maybe_unused]] uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||
assert(hr == 0);
|
||||
}
|
||||
|
||||
FAudioSourceVoice_FlushSourceBuffers(sVoice);
|
||||
FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||
playing = true;
|
||||
}
|
||||
|
||||
void FAudio_Output::setThrottle(unsigned short throttle_) {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
if (throttle_ == 0)
|
||||
throttle_ = 100;
|
||||
|
||||
[[maybe_unused]] uint32_t hr =
|
||||
FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_COMMIT_NOW);
|
||||
assert(hr == 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<AudioDevice> GetFAudioDevices() {
|
||||
FAudio* fa = nullptr;
|
||||
uint32_t hr;
|
||||
uint32_t flags = 0;
|
||||
#ifdef _DEBUG
|
||||
flags = FAUDIO_DEBUG_ENGINE;
|
||||
#endif
|
||||
hr = FAudioCreate(&fa, flags, FAUDIO_DEFAULT_PROCESSOR);
|
||||
|
||||
if (hr != 0) {
|
||||
wxLogError(_("The FAudio interface failed to initialize!"));
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t dev_count = 0;
|
||||
hr = FAudio_GetDeviceCount(fa, &dev_count);
|
||||
if (hr != 0) {
|
||||
wxLogError(_("FAudio: Enumerating devices failed!"));
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<AudioDevice> devices;
|
||||
devices.reserve(dev_count + 1);
|
||||
devices.push_back({_("Default device"), wxEmptyString});
|
||||
|
||||
for (uint32_t i = 0; i < dev_count; i++) {
|
||||
FAudioDeviceDetails dd;
|
||||
hr = FAudio_GetDeviceDetails(fa, i, &dd);
|
||||
if (hr != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const wxString display_name(reinterpret_cast<wchar_t*>(dd.DisplayName));
|
||||
const wxString device_id(reinterpret_cast<wchar_t*>(dd.DeviceID));
|
||||
|
||||
devices.push_back({display_name, device_id});
|
||||
}
|
||||
|
||||
FAudio_Release(fa);
|
||||
return devices;
|
||||
}
|
||||
|
||||
std::unique_ptr<SoundDriver> CreateFAudioDriver() {
|
||||
return std::make_unique<FAudio_Output>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
22
src/wx/audio/internal/faudio.h
Normal file
22
src/wx/audio/internal/faudio.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef WX_AUDIO_INTERNAL_FAUDIO_H_
|
||||
#define WX_AUDIO_INTERNAL_FAUDIO_H_
|
||||
|
||||
#if !defined(VBAM_ENABLE_FAUDIO)
|
||||
#error "This file should only be included if FAudio is enabled"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
// Returns the set of FAudio devices.
|
||||
std::vector<AudioDevice> GetFAudioDevices();
|
||||
|
||||
// Creates an FAudio sound driver.
|
||||
std::unique_ptr<SoundDriver> CreateFAudioDriver();
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_INTERNAL_FAUDIO_H_
|
@@ -1,24 +1,52 @@
|
||||
#include "wx/audio/internal/openal.h"
|
||||
|
||||
// === LOGALL writes very detailed informations to vba-trace.log ===
|
||||
//#define LOGALL
|
||||
// #define LOGALL
|
||||
|
||||
#ifndef NO_OAL
|
||||
// on win32 and mac, pointer typedefs only happen with AL_NO_PROTOTYPES
|
||||
// on mac, ALC_NO_PROTOTYPES as well
|
||||
|
||||
// for gopts
|
||||
// also, wx-related
|
||||
#include "wx/wxvbam.h"
|
||||
// #define AL_NO_PROTOTYPES 1
|
||||
|
||||
// Interface
|
||||
#include "core/base/sound_driver.h"
|
||||
// on mac, alc pointer typedefs ony happen for ALC if ALC_NO_PROTOTYPES
|
||||
// unfortunately, there is a bug in the system headers (use of ALCvoid when
|
||||
// void should be used; shame on Apple for introducing this error, and shame
|
||||
// on Creative for making a typedef to void in the first place)
|
||||
// #define ALC_NO_PROTOTYPES 1
|
||||
|
||||
// OpenAL
|
||||
#include "openal.h"
|
||||
#include <al.h>
|
||||
#include <alc.h>
|
||||
|
||||
// Internals
|
||||
#include "core/gba/gbaGlobals.h" // for 'speedup' and 'synchronize'
|
||||
// since the ALC typedefs are broken on Mac:
|
||||
|
||||
#ifdef __WXMAC__
|
||||
typedef ALCcontext*(ALC_APIENTRY* LPALCCREATECONTEXT)(ALCdevice* device, const ALCint* attrlist);
|
||||
typedef ALCboolean(ALC_APIENTRY* LPALCMAKECONTEXTCURRENT)(ALCcontext* context);
|
||||
typedef void(ALC_APIENTRY* LPALCDESTROYCONTEXT)(ALCcontext* context);
|
||||
typedef ALCdevice*(ALC_APIENTRY* LPALCOPENDEVICE)(const ALCchar* devicename);
|
||||
typedef ALCboolean(ALC_APIENTRY* LPALCCLOSEDEVICE)(ALCdevice* device);
|
||||
typedef ALCboolean(ALC_APIENTRY* LPALCISEXTENSIONPRESENT)(ALCdevice* device,
|
||||
const ALCchar* extname);
|
||||
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/gba/gbaGlobals.h"
|
||||
#include "core/gba/gbaSound.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
// Debug
|
||||
#include <assert.h>
|
||||
#define ASSERT_SUCCESS assert(AL_NO_ERROR == alGetError())
|
||||
|
||||
#ifndef LOGALL
|
||||
@@ -27,8 +55,10 @@
|
||||
#undef winlog
|
||||
#endif
|
||||
// https://stackoverflow.com/a/1306690/262458
|
||||
#define winlog(x,...) do {} while(0)
|
||||
#define debugState() //
|
||||
#define winlog(x, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define debugState() //
|
||||
#endif
|
||||
|
||||
struct OPENALFNTABLE;
|
||||
@@ -36,15 +66,15 @@ struct OPENALFNTABLE;
|
||||
class OpenAL : public SoundDriver {
|
||||
public:
|
||||
OpenAL();
|
||||
virtual ~OpenAL();
|
||||
~OpenAL() override;
|
||||
|
||||
static bool GetDevices(wxArrayString& names, wxArrayString& ids);
|
||||
bool init(long sampleRate); // initialize the sound buffer queue
|
||||
void setThrottle(unsigned short throttle_); // set game speed
|
||||
void pause(); // pause the secondary sound buffer
|
||||
void reset(); // stop and reset the secondary sound buffer
|
||||
void resume(); // play/resume the secondary sound buffer
|
||||
void write(uint16_t* finalWave, int length); // write the emulated sound to a sound buffer
|
||||
bool init(long sampleRate) override; // initialize the sound buffer queue
|
||||
void setThrottle(unsigned short throttle_) override; // set game speed
|
||||
void pause() override; // pause the secondary sound buffer
|
||||
void reset() override; // stop and reset the secondary sound buffer
|
||||
void resume() override; // play/resume the secondary sound buffer
|
||||
void write(uint16_t* finalWave,
|
||||
int length) override; // write the emulated sound to a sound buffer
|
||||
|
||||
private:
|
||||
bool initialized;
|
||||
@@ -62,20 +92,18 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
OpenAL::OpenAL()
|
||||
{
|
||||
OpenAL::OpenAL() {
|
||||
initialized = false;
|
||||
buffersLoaded = false;
|
||||
device = NULL;
|
||||
context = NULL;
|
||||
buffer = (ALuint*)malloc(gopts.audio_buffers * sizeof(ALuint));
|
||||
memset(buffer, 0, gopts.audio_buffers * sizeof(ALuint));
|
||||
device = nullptr;
|
||||
context = nullptr;
|
||||
buffer = (ALuint*)malloc(OPTION(kSoundBuffers) * sizeof(ALuint));
|
||||
memset(buffer, 0, OPTION(kSoundBuffers) * sizeof(ALuint));
|
||||
tempBuffer = 0;
|
||||
source = 0;
|
||||
}
|
||||
|
||||
OpenAL::~OpenAL()
|
||||
{
|
||||
OpenAL::~OpenAL() {
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
@@ -85,25 +113,24 @@ OpenAL::~OpenAL()
|
||||
ASSERT_SUCCESS;
|
||||
alDeleteSources(1, &source);
|
||||
ASSERT_SUCCESS;
|
||||
alDeleteBuffers(gopts.audio_buffers, buffer);
|
||||
alDeleteBuffers(OPTION(kSoundBuffers), buffer);
|
||||
ASSERT_SUCCESS;
|
||||
free(buffer);
|
||||
alcMakeContextCurrent(NULL);
|
||||
alcMakeContextCurrent(nullptr);
|
||||
// Wine incorrectly returns ALC_INVALID_VALUE
|
||||
// and then fails the rest of these functions as well
|
||||
// so there will be a leak under Wine, but that's a bug in Wine, not
|
||||
// this code
|
||||
//ASSERT_SUCCESS;
|
||||
// ASSERT_SUCCESS;
|
||||
alcDestroyContext(context);
|
||||
//ASSERT_SUCCESS;
|
||||
// ASSERT_SUCCESS;
|
||||
alcCloseDevice(device);
|
||||
//ASSERT_SUCCESS;
|
||||
alGetError(); // reset error state
|
||||
// ASSERT_SUCCESS;
|
||||
alGetError(); // reset error state
|
||||
}
|
||||
|
||||
#ifdef LOGALL
|
||||
void OpenAL::debugState()
|
||||
{
|
||||
void OpenAL::debugState() {
|
||||
ALint value = 0;
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &value);
|
||||
ASSERT_SUCCESS;
|
||||
@@ -112,28 +139,27 @@ void OpenAL::debugState()
|
||||
winlog(" State: ");
|
||||
|
||||
switch (value) {
|
||||
case AL_INITIAL:
|
||||
winlog("AL_INITIAL\n");
|
||||
break;
|
||||
case AL_INITIAL:
|
||||
winlog("AL_INITIAL\n");
|
||||
break;
|
||||
|
||||
case AL_PLAYING:
|
||||
winlog("AL_PLAYING\n");
|
||||
break;
|
||||
case AL_PLAYING:
|
||||
winlog("AL_PLAYING\n");
|
||||
break;
|
||||
|
||||
case AL_PAUSED:
|
||||
winlog("AL_PAUSED\n");
|
||||
break;
|
||||
case AL_PAUSED:
|
||||
winlog("AL_PAUSED\n");
|
||||
break;
|
||||
|
||||
case AL_STOPPED:
|
||||
winlog("AL_STOPPED\n");
|
||||
break;
|
||||
case AL_STOPPED:
|
||||
winlog("AL_STOPPED\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
winlog("!unknown!\n");
|
||||
break;
|
||||
default:
|
||||
winlog("!unknown!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
alGetSourcei(source, AL_BUFFERS_QUEUED, &value);
|
||||
ASSERT_SUCCESS;
|
||||
winlog(" Buffers in queue: %i\n", value);
|
||||
@@ -143,23 +169,32 @@ void OpenAL::debugState()
|
||||
}
|
||||
#endif
|
||||
|
||||
bool OpenAL::init(long sampleRate)
|
||||
{
|
||||
bool OpenAL::init(long sampleRate) {
|
||||
winlog("OpenAL::init\n");
|
||||
assert(initialized == false);
|
||||
|
||||
if (!gopts.audio_dev.empty()) {
|
||||
device = alcOpenDevice(gopts.audio_dev.utf8_str());
|
||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||
if (!audio_device.empty()) {
|
||||
device = alcOpenDevice(audio_device.utf8_str());
|
||||
if (device == nullptr) {
|
||||
// Might be the default device. Try again.
|
||||
OPTION(kSoundAudioDevice) = wxEmptyString;
|
||||
device = alcOpenDevice(nullptr);
|
||||
}
|
||||
} else {
|
||||
device = alcOpenDevice(NULL);
|
||||
device = alcOpenDevice(nullptr);
|
||||
}
|
||||
|
||||
assert(device != NULL);
|
||||
context = alcCreateContext(device, NULL);
|
||||
assert(context != NULL);
|
||||
if (!device) {
|
||||
wxLogError(_("OpenAL: Failed to open audio device"));
|
||||
return false;
|
||||
}
|
||||
|
||||
context = alcCreateContext(device, nullptr);
|
||||
assert(context != nullptr);
|
||||
ALCboolean retVal = alcMakeContextCurrent(context);
|
||||
assert(ALC_TRUE == retVal);
|
||||
alGenBuffers(gopts.audio_buffers, buffer);
|
||||
alGenBuffers(OPTION(kSoundBuffers), buffer);
|
||||
ASSERT_SUCCESS;
|
||||
alGenSources(1, &source);
|
||||
ASSERT_SUCCESS;
|
||||
@@ -182,8 +217,7 @@ void OpenAL::setThrottle(unsigned short throttle_) {
|
||||
ASSERT_SUCCESS;
|
||||
}
|
||||
|
||||
void OpenAL::resume()
|
||||
{
|
||||
void OpenAL::resume() {
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
@@ -205,8 +239,7 @@ void OpenAL::resume()
|
||||
debugState();
|
||||
}
|
||||
|
||||
void OpenAL::pause()
|
||||
{
|
||||
void OpenAL::pause() {
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
@@ -228,8 +261,7 @@ void OpenAL::pause()
|
||||
debugState();
|
||||
}
|
||||
|
||||
void OpenAL::reset()
|
||||
{
|
||||
void OpenAL::reset() {
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
@@ -251,9 +283,8 @@ void OpenAL::reset()
|
||||
debugState();
|
||||
}
|
||||
|
||||
void OpenAL::write(uint16_t* finalWave, int length)
|
||||
{
|
||||
(void)length; // unused param
|
||||
void OpenAL::write(uint16_t* finalWave, int length) {
|
||||
(void)length; // unused param
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
@@ -266,14 +297,14 @@ void OpenAL::write(uint16_t* finalWave, int length)
|
||||
// ==initial buffer filling==
|
||||
winlog(" initial buffer filling\n");
|
||||
|
||||
for (int i = 0; i < gopts.audio_buffers; i++) {
|
||||
for (int i = 0; i < OPTION(kSoundBuffers); i++) {
|
||||
// Filling the buffers explicitly with silence would be cleaner,
|
||||
// but the very first sample is usually silence anyway.
|
||||
alBufferData(buffer[i], AL_FORMAT_STEREO16, finalWave, soundBufferLen, freq);
|
||||
ASSERT_SUCCESS;
|
||||
}
|
||||
|
||||
alSourceQueueBuffers(source, gopts.audio_buffers, buffer);
|
||||
alSourceQueueBuffers(source, OPTION(kSoundBuffers), buffer);
|
||||
ASSERT_SUCCESS;
|
||||
buffersLoaded = true;
|
||||
} else {
|
||||
@@ -282,7 +313,7 @@ void OpenAL::write(uint16_t* finalWave, int length)
|
||||
alGetSourcei(source, AL_BUFFERS_PROCESSED, &nBuffersProcessed);
|
||||
ASSERT_SUCCESS;
|
||||
|
||||
if (nBuffersProcessed == gopts.audio_buffers) {
|
||||
if (nBuffersProcessed == OPTION(kSoundBuffers)) {
|
||||
// we only want to know about it when we are emulating at full speed or faster:
|
||||
if ((coreOptions.throttle >= 100) || (coreOptions.throttle == 0)) {
|
||||
if (systemVerbose & VERBOSE_SOUNDOUTPUT) {
|
||||
@@ -331,37 +362,39 @@ void OpenAL::write(uint16_t* finalWave, int length)
|
||||
}
|
||||
}
|
||||
|
||||
SoundDriver* newOpenAL()
|
||||
{
|
||||
winlog("newOpenAL\n");
|
||||
return new OpenAL();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool GetOALDevices(wxArrayString& names, wxArrayString& ids)
|
||||
{
|
||||
return OpenAL::GetDevices(names, ids);
|
||||
}
|
||||
std::vector<AudioDevice> GetOpenALDevices() {
|
||||
std::vector<AudioDevice> devices;
|
||||
|
||||
bool OpenAL::GetDevices(wxArrayString& names, wxArrayString& ids)
|
||||
{
|
||||
#ifdef ALC_DEVICE_SPECIFIER
|
||||
|
||||
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT") == AL_FALSE)
|
||||
if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT") == AL_FALSE) {
|
||||
// this extension isn't critical to OpenAL operating
|
||||
return true;
|
||||
return devices;
|
||||
}
|
||||
|
||||
const char* devs = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||
const char* devs = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
|
||||
|
||||
while (*devs) {
|
||||
names.push_back(wxString(devs, wxConvLibc));
|
||||
ids.push_back(names[names.size() - 1]);
|
||||
const wxString device_name(devs, wxConvLibc);
|
||||
devices.push_back({device_name, device_name});
|
||||
devs += strlen(devs) + 1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
devices.push_back({_("Default device"), wxEmptyString});
|
||||
|
||||
#endif
|
||||
|
||||
// should work anyway, but must always use default driver
|
||||
return true;
|
||||
return devices;
|
||||
}
|
||||
|
||||
#endif
|
||||
std::unique_ptr<SoundDriver> CreateOpenALDriver() {
|
||||
winlog("newOpenAL\n");
|
||||
return std::make_unique<OpenAL>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
18
src/wx/audio/internal/openal.h
Normal file
18
src/wx/audio/internal/openal.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef WX_AUDIO_INTERNAL_OPENAL_H_
|
||||
#define WX_AUDIO_INTERNAL_OPENAL_H_
|
||||
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
// Returns the set of OpenAL devices.
|
||||
std::vector<AudioDevice> GetOpenALDevices();
|
||||
|
||||
// Creates an OpenAL sound driver.
|
||||
std::unique_ptr<SoundDriver> CreateOpenALDriver();
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_INTERNAL_OPENAL_H_
|
@@ -1,88 +1,70 @@
|
||||
#ifndef NO_XAUDIO2
|
||||
#if !defined(VBAM_ENABLE_XAUDIO2)
|
||||
#error "This file should only be compiled if XAudio2 is enabled"
|
||||
#endif
|
||||
|
||||
// Application
|
||||
#include "wx/wxvbam.h"
|
||||
#include <stdio.h>
|
||||
#include "wx/audio/internal/xaudio2.h"
|
||||
|
||||
// Interface
|
||||
#include "core/base/sound_driver.h"
|
||||
#include <cstdio>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// MMDevice API
|
||||
#include <mmdeviceapi.h>
|
||||
|
||||
// XAudio2
|
||||
#if _MSC_VER
|
||||
#include <xaudio2.legacy.h>
|
||||
#else
|
||||
#include <XAudio2.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// MMDevice API
|
||||
#include <mmdeviceapi.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/translation.h>
|
||||
|
||||
// Internals
|
||||
#include "core/base/system.h" // for systemMessage()
|
||||
#include "core/base/sound_driver.h"
|
||||
#include "core/base/system.h" // for systemMessage()
|
||||
#include "core/gba/gbaGlobals.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
|
||||
int GetXA2Devices(IXAudio2* xa, wxArrayString* names, wxArrayString* ids,
|
||||
const wxString* match)
|
||||
{
|
||||
HRESULT hr;
|
||||
UINT32 dev_count = 0;
|
||||
hr = xa->GetDeviceCount(&dev_count);
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
if (hr != S_OK) {
|
||||
wxLogError(_("XAudio2: Enumerating devices failed!"));
|
||||
return true;
|
||||
} else {
|
||||
XAUDIO2_DEVICE_DETAILS dd;
|
||||
namespace {
|
||||
|
||||
for (UINT32 i = 0; i < dev_count; i++) {
|
||||
hr = xa->GetDeviceDetails(i, &dd);
|
||||
|
||||
if (hr != S_OK) {
|
||||
continue;
|
||||
} else {
|
||||
if (ids) {
|
||||
ids->push_back(dd.DeviceID);
|
||||
names->push_back(dd.DisplayName);
|
||||
} else if (*match == dd.DeviceID)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
int XA2GetDev(IXAudio2* xa) {
|
||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||
if (audio_device.empty()) {
|
||||
// Just use the default device.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool GetXA2Devices(wxArrayString& names, wxArrayString& ids)
|
||||
{
|
||||
HRESULT hr;
|
||||
IXAudio2* xa = NULL;
|
||||
hr = XAudio2Create(&xa, 0);
|
||||
|
||||
uint32_t hr;
|
||||
uint32_t dev_count = 0;
|
||||
hr = xa->GetDeviceCount(&dev_count);
|
||||
if (hr != S_OK) {
|
||||
wxLogError(_("The XAudio2 interface failed to initialize!"));
|
||||
wxLogError(_("XAudio2: Enumerating devices failed!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
GetXA2Devices(xa, &names, &ids, NULL);
|
||||
xa->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
static int XA2GetDev(IXAudio2* xa)
|
||||
{
|
||||
if (gopts.audio_dev.empty())
|
||||
return 0;
|
||||
else {
|
||||
int ret = GetXA2Devices(xa, NULL, NULL, &gopts.audio_dev);
|
||||
return ret < 0 ? 0 : ret;
|
||||
for (UINT32 i = 0; i < dev_count; i++) {
|
||||
XAUDIO2_DEVICE_DETAILS dd;
|
||||
hr = xa->GetDeviceDetails(i, &dd);
|
||||
if (hr != S_OK) {
|
||||
continue;
|
||||
}
|
||||
const wxString device_id(reinterpret_cast<wchar_t*>(dd.DeviceID));
|
||||
if (audio_device == device_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
class XAudio2_Output;
|
||||
|
||||
static void xaudio2_device_changed(XAudio2_Output*);
|
||||
void xaudio2_device_changed(XAudio2_Output*);
|
||||
|
||||
class XAudio2_Device_Notifier : public IMMNotificationClient {
|
||||
volatile LONG registered;
|
||||
@@ -94,28 +76,14 @@ class XAudio2_Device_Notifier : public IMMNotificationClient {
|
||||
std::vector<XAudio2_Output*> instances;
|
||||
|
||||
public:
|
||||
XAudio2_Device_Notifier()
|
||||
: registered(0)
|
||||
{
|
||||
InitializeCriticalSection(&lock);
|
||||
}
|
||||
~XAudio2_Device_Notifier()
|
||||
{
|
||||
DeleteCriticalSection(&lock);
|
||||
}
|
||||
XAudio2_Device_Notifier() : registered(0) { InitializeCriticalSection(&lock); }
|
||||
~XAudio2_Device_Notifier() { DeleteCriticalSection(&lock); }
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
ULONG STDMETHODCALLTYPE AddRef() { return 1; }
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
ULONG STDMETHODCALLTYPE Release() { return 1; }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface)
|
||||
{
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface) {
|
||||
if (IID_IUnknown == riid) {
|
||||
*ppvInterface = (IUnknown*)this;
|
||||
} else if (__uuidof(IMMNotificationClient) == riid) {
|
||||
@@ -128,8 +96,7 @@ public:
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole, LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole, LPCWSTR pwstrDeviceId) {
|
||||
if (flow == eRender && last_device.compare(pwstrDeviceId) != 0) {
|
||||
last_device = pwstrDeviceId;
|
||||
EnterCriticalSection(&lock);
|
||||
@@ -149,11 +116,11 @@ public:
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) { return S_OK; }
|
||||
|
||||
void do_register(XAudio2_Output* p_instance)
|
||||
{
|
||||
void do_register(XAudio2_Output* p_instance) {
|
||||
if (InterlockedIncrement(®istered) == 1) {
|
||||
pEnumerator = NULL;
|
||||
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
|
||||
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
|
||||
__uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
pEnumerator->RegisterEndpointNotificationCallback(this);
|
||||
@@ -165,8 +132,7 @@ public:
|
||||
LeaveCriticalSection(&lock);
|
||||
}
|
||||
|
||||
void do_unregister(XAudio2_Output* p_instance)
|
||||
{
|
||||
void do_unregister(XAudio2_Output* p_instance) {
|
||||
if (InterlockedDecrement(®istered) == 0) {
|
||||
if (pEnumerator) {
|
||||
pEnumerator->UnregisterEndpointNotificationCallback(this);
|
||||
@@ -193,22 +159,19 @@ class XAudio2_BufferNotify : public IXAudio2VoiceCallback {
|
||||
public:
|
||||
HANDLE hBufferEndEvent;
|
||||
|
||||
XAudio2_BufferNotify()
|
||||
{
|
||||
XAudio2_BufferNotify() {
|
||||
hBufferEndEvent = NULL;
|
||||
hBufferEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
assert(hBufferEndEvent != NULL);
|
||||
}
|
||||
|
||||
~XAudio2_BufferNotify()
|
||||
{
|
||||
~XAudio2_BufferNotify() {
|
||||
CloseHandle(hBufferEndEvent);
|
||||
hBufferEndEvent = NULL;
|
||||
}
|
||||
|
||||
STDMETHOD_(void, OnBufferEnd)
|
||||
(void*)
|
||||
{
|
||||
(void*) {
|
||||
assert(hBufferEndEvent != NULL);
|
||||
SetEvent(hBufferEndEvent);
|
||||
}
|
||||
@@ -229,29 +192,24 @@ public:
|
||||
};
|
||||
|
||||
// Class Declaration
|
||||
class XAudio2_Output
|
||||
: public SoundDriver {
|
||||
class XAudio2_Output : public SoundDriver {
|
||||
public:
|
||||
XAudio2_Output();
|
||||
~XAudio2_Output();
|
||||
~XAudio2_Output() override;
|
||||
|
||||
// Initialization
|
||||
bool init(long sampleRate);
|
||||
|
||||
// Sound Data Feed
|
||||
void write(uint16_t* finalWave, int length);
|
||||
|
||||
// Play Control
|
||||
void pause();
|
||||
void resume();
|
||||
void reset();
|
||||
void close();
|
||||
void device_change();
|
||||
|
||||
// Configuration Changes
|
||||
void setThrottle(unsigned short throttle);
|
||||
|
||||
private:
|
||||
void close();
|
||||
|
||||
// SoundDriver implementation.
|
||||
bool init(long sampleRate) override;
|
||||
void pause() override;
|
||||
void reset() override;
|
||||
void resume() override;
|
||||
void write(uint16_t* finalWave, int length) override;
|
||||
void setThrottle(unsigned short throttle_) override;
|
||||
|
||||
bool failed;
|
||||
bool initialized;
|
||||
bool playing;
|
||||
@@ -264,21 +222,20 @@ private:
|
||||
volatile bool device_changed;
|
||||
|
||||
IXAudio2* xaud;
|
||||
IXAudio2MasteringVoice* mVoice; // listener
|
||||
IXAudio2SourceVoice* sVoice; // sound source
|
||||
IXAudio2MasteringVoice* mVoice; // listener
|
||||
IXAudio2SourceVoice* sVoice; // sound source
|
||||
XAUDIO2_BUFFER buf;
|
||||
XAUDIO2_VOICE_STATE vState;
|
||||
XAudio2_BufferNotify notify; // buffer end notification
|
||||
XAudio2_BufferNotify notify; // buffer end notification
|
||||
};
|
||||
|
||||
// Class Implementation
|
||||
XAudio2_Output::XAudio2_Output()
|
||||
{
|
||||
XAudio2_Output::XAudio2_Output() {
|
||||
failed = false;
|
||||
initialized = false;
|
||||
playing = false;
|
||||
freq = 0;
|
||||
bufferCount = gopts.audio_buffers;
|
||||
bufferCount = OPTION(kSoundBuffers);
|
||||
buffers = NULL;
|
||||
currentBuffer = 0;
|
||||
device_changed = false;
|
||||
@@ -290,14 +247,12 @@ XAudio2_Output::XAudio2_Output()
|
||||
g_notifier.do_register(this);
|
||||
}
|
||||
|
||||
XAudio2_Output::~XAudio2_Output()
|
||||
{
|
||||
XAudio2_Output::~XAudio2_Output() {
|
||||
g_notifier.do_unregister(this);
|
||||
close();
|
||||
}
|
||||
|
||||
void XAudio2_Output::close()
|
||||
{
|
||||
void XAudio2_Output::close() {
|
||||
initialized = false;
|
||||
|
||||
if (sVoice) {
|
||||
@@ -326,13 +281,11 @@ void XAudio2_Output::close()
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2_Output::device_change()
|
||||
{
|
||||
void XAudio2_Output::device_change() {
|
||||
device_changed = true;
|
||||
}
|
||||
|
||||
bool XAudio2_Output::init(long sampleRate)
|
||||
{
|
||||
bool XAudio2_Output::init(long sampleRate) {
|
||||
if (failed || initialized)
|
||||
return false;
|
||||
|
||||
@@ -363,13 +316,8 @@ bool XAudio2_Output::init(long sampleRate)
|
||||
wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8);
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
// create sound receiver
|
||||
hr = xaud->CreateMasteringVoice(
|
||||
&mVoice,
|
||||
XAUDIO2_DEFAULT_CHANNELS,
|
||||
XAUDIO2_DEFAULT_SAMPLERATE,
|
||||
0,
|
||||
XA2GetDev(xaud),
|
||||
NULL);
|
||||
hr = xaud->CreateMasteringVoice(&mVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE,
|
||||
0, XA2GetDev(xaud), NULL);
|
||||
|
||||
if (hr != S_OK) {
|
||||
wxLogError(_("XAudio2: Creating mastering voice failed!"));
|
||||
@@ -386,7 +334,7 @@ bool XAudio2_Output::init(long sampleRate)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gopts.upmix) {
|
||||
if (OPTION(kSoundUpmix)) {
|
||||
// set up stereo upmixing
|
||||
XAUDIO2_DEVICE_DETAILS dd;
|
||||
ZeroMemory(&dd, sizeof(dd));
|
||||
@@ -401,89 +349,89 @@ bool XAudio2_Output::init(long sampleRate)
|
||||
bool matrixAvailable = true;
|
||||
|
||||
switch (dd.OutputFormat.Format.nChannels) {
|
||||
case 4: // 4.0
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Back L*/ matrix[4] = 1.0000f;
|
||||
matrix[5] = 0.0000f;
|
||||
/*Back R*/ matrix[6] = 0.0000f;
|
||||
matrix[7] = 1.0000f;
|
||||
break;
|
||||
case 4: // 4.0
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Back L*/ matrix[4] = 1.0000f;
|
||||
matrix[5] = 0.0000f;
|
||||
/*Back R*/ matrix[6] = 0.0000f;
|
||||
matrix[7] = 1.0000f;
|
||||
break;
|
||||
|
||||
case 5: // 5.0
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*Side L*/ matrix[6] = 1.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side R*/ matrix[8] = 0.0000f;
|
||||
matrix[9] = 1.0000f;
|
||||
break;
|
||||
case 5: // 5.0
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*Side L*/ matrix[6] = 1.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side R*/ matrix[8] = 0.0000f;
|
||||
matrix[9] = 1.0000f;
|
||||
break;
|
||||
|
||||
case 6: // 5.1
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
break;
|
||||
case 6: // 5.1
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
break;
|
||||
|
||||
case 7: // 6.1
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
/*Back C*/ matrix[12] = 0.7071f;
|
||||
matrix[13] = 0.7071f;
|
||||
break;
|
||||
case 7: // 6.1
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
/*Back C*/ matrix[12] = 0.7071f;
|
||||
matrix[13] = 0.7071f;
|
||||
break;
|
||||
|
||||
case 8: // 7.1
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Back L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Back R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
/*Side L*/ matrix[12] = 1.0000f;
|
||||
matrix[13] = 0.0000f;
|
||||
/*Side R*/ matrix[14] = 0.0000f;
|
||||
matrix[15] = 1.0000f;
|
||||
break;
|
||||
case 8: // 7.1
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Back L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Back R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
/*Side L*/ matrix[12] = 1.0000f;
|
||||
matrix[13] = 0.0000f;
|
||||
/*Side R*/ matrix[14] = 0.0000f;
|
||||
matrix[15] = 1.0000f;
|
||||
break;
|
||||
|
||||
default:
|
||||
matrixAvailable = false;
|
||||
break;
|
||||
default:
|
||||
matrixAvailable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (matrixAvailable) {
|
||||
@@ -504,8 +452,7 @@ bool XAudio2_Output::init(long sampleRate)
|
||||
return true;
|
||||
}
|
||||
|
||||
void XAudio2_Output::write(uint16_t* finalWave, int)
|
||||
{
|
||||
void XAudio2_Output::write(uint16_t* finalWave, int) {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
@@ -550,13 +497,12 @@ void XAudio2_Output::write(uint16_t* finalWave, int)
|
||||
buf.AudioBytes = soundBufferLen;
|
||||
buf.pAudioData = &buffers[currentBuffer * soundBufferLen];
|
||||
currentBuffer++;
|
||||
currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer
|
||||
HRESULT hr = sVoice->SubmitSourceBuffer(&buf); // send buffer to queue
|
||||
currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer
|
||||
HRESULT hr = sVoice->SubmitSourceBuffer(&buf); // send buffer to queue
|
||||
assert(hr == S_OK);
|
||||
}
|
||||
|
||||
void XAudio2_Output::pause()
|
||||
{
|
||||
void XAudio2_Output::pause() {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
@@ -567,8 +513,7 @@ void XAudio2_Output::pause()
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2_Output::resume()
|
||||
{
|
||||
void XAudio2_Output::resume() {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
@@ -579,8 +524,7 @@ void XAudio2_Output::resume()
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2_Output::reset()
|
||||
{
|
||||
void XAudio2_Output::reset() {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
@@ -594,8 +538,7 @@ void XAudio2_Output::reset()
|
||||
playing = true;
|
||||
}
|
||||
|
||||
void XAudio2_Output::setThrottle(unsigned short throttle_)
|
||||
{
|
||||
void XAudio2_Output::setThrottle(unsigned short throttle_) {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
@@ -606,14 +549,50 @@ void XAudio2_Output::setThrottle(unsigned short throttle_)
|
||||
assert(hr == S_OK);
|
||||
}
|
||||
|
||||
void xaudio2_device_changed(XAudio2_Output* instance)
|
||||
{
|
||||
void xaudio2_device_changed(XAudio2_Output* instance) {
|
||||
instance->device_change();
|
||||
}
|
||||
|
||||
SoundDriver* newXAudio2_Output()
|
||||
{
|
||||
return new XAudio2_Output();
|
||||
} // namespace
|
||||
|
||||
std::vector<AudioDevice> GetXAudio2Devices() {
|
||||
HRESULT hr;
|
||||
IXAudio2* xa = nullptr;
|
||||
hr = XAudio2Create(&xa, 0);
|
||||
|
||||
if (hr != S_OK) {
|
||||
wxLogError(_("The XAudio2 interface failed to initialize!"));
|
||||
return {};
|
||||
}
|
||||
|
||||
UINT32 dev_count = 0;
|
||||
hr = xa->GetDeviceCount(&dev_count);
|
||||
if (hr != S_OK) {
|
||||
wxLogError(_("XAudio2: Enumerating devices failed!"));
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<AudioDevice> devices;
|
||||
devices.reserve(dev_count + 1);
|
||||
devices.push_back({_("Default device"), wxEmptyString});
|
||||
|
||||
for (UINT32 i = 0; i < dev_count; i++) {
|
||||
XAUDIO2_DEVICE_DETAILS dd;
|
||||
hr = xa->GetDeviceDetails(i, &dd);
|
||||
|
||||
if (hr != S_OK) {
|
||||
continue;
|
||||
}
|
||||
devices.push_back({dd.DisplayName, dd.DeviceID});
|
||||
}
|
||||
|
||||
xa->Release();
|
||||
return devices;
|
||||
}
|
||||
|
||||
#endif // #ifndef NO_XAUDIO2
|
||||
std::unique_ptr<SoundDriver> CreateXAudio2Driver() {
|
||||
return std::make_unique<XAudio2_Output>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
22
src/wx/audio/internal/xaudio2.h
Normal file
22
src/wx/audio/internal/xaudio2.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef WX_AUDIO_INTERNAL_XAUDIO2_H_
|
||||
#define WX_AUDIO_INTERNAL_XAUDIO2_H_
|
||||
|
||||
#if !defined(VBAM_ENABLE_FAUDIO)
|
||||
#error "This file should only be included if FAudio is enabled"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
// Returns the set of XAudio2 devices.
|
||||
std::vector<AudioDevice> GetXAudio2Devices();
|
||||
|
||||
// Creates an XAudio2 sound driver.
|
||||
std::unique_ptr<SoundDriver> CreateXAudio2Driver();
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_INTERNAL_XAUDIO2_H_
|
@@ -547,17 +547,17 @@ wxThread::ExitCode BackgroundInput::CheckKeyboard()
|
||||
// virtual key "i" is pressed
|
||||
if ((bits & 0x8000) && (previousState[i] & 0x8000) == 0) {
|
||||
if (handler && !wxWindow::FindFocus()) {
|
||||
wxKeyEvent ev(wxEVT_KEY_DOWN);
|
||||
ev.m_keyCode = xKeySym;
|
||||
handler->AddPendingEvent(ev);
|
||||
wxKeyEvent* event = new wxKeyEvent(wxEVT_KEY_DOWN);
|
||||
event->m_keyCode = xKeySym;
|
||||
handler->QueueEvent(event);
|
||||
}
|
||||
}
|
||||
// virtual key "i" is released
|
||||
else if (((bits & 0x8000) == 0) && (previousState[i] & 0x8000)) {
|
||||
if (handler && !wxWindow::FindFocus()) {
|
||||
wxKeyEvent ev(wxEVT_KEY_UP);
|
||||
ev.m_keyCode = xKeySym;
|
||||
handler->AddPendingEvent(ev);
|
||||
wxKeyEvent* event = new wxKeyEvent(wxEVT_KEY_UP);
|
||||
event->m_keyCode = xKeySym;
|
||||
handler->QueueEvent(event);
|
||||
}
|
||||
}
|
||||
previousState[i] = bits;
|
||||
|
@@ -1184,7 +1184,7 @@ EVT_HANDLER(AllowKeyboardBackgroundInput, "Allow keyboard background input (togg
|
||||
disableKeyboardBackgroundInput();
|
||||
if (OPTION(kUIAllowKeyboardBackgroundInput)) {
|
||||
if (panel && panel->panel) {
|
||||
enableKeyboardBackgroundInput(panel->panel->GetWindow());
|
||||
enableKeyboardBackgroundInput(panel->panel->GetWindow()->GetEventHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1686,30 +1686,13 @@ EVT_HANDLER(ToggleSound, "Enable/disable all sound channels")
|
||||
|
||||
EVT_HANDLER(IncreaseVolume, "Increase volume")
|
||||
{
|
||||
gopts.sound_vol += 5;
|
||||
OPTION(kSoundVolume) += 5;
|
||||
|
||||
if (gopts.sound_vol > 200)
|
||||
gopts.sound_vol = 200;
|
||||
|
||||
update_opts();
|
||||
soundSetVolume((float)gopts.sound_vol / 100.0);
|
||||
wxString msg;
|
||||
msg.Printf(_("Volume: %d %%"), gopts.sound_vol);
|
||||
systemScreenMessage(msg);
|
||||
}
|
||||
|
||||
EVT_HANDLER(DecreaseVolume, "Decrease volume")
|
||||
{
|
||||
gopts.sound_vol -= 5;
|
||||
|
||||
if (gopts.sound_vol < 0)
|
||||
gopts.sound_vol = 0;
|
||||
|
||||
update_opts();
|
||||
soundSetVolume((float)gopts.sound_vol / 100.0);
|
||||
wxString msg;
|
||||
msg.Printf(_("Volume: %d %%"), gopts.sound_vol);
|
||||
systemScreenMessage(msg);
|
||||
OPTION(kSoundVolume) -= 5;
|
||||
}
|
||||
|
||||
EVT_HANDLER_MASK(NextFrame, "Next Frame", CMDEN_GB | CMDEN_GBA)
|
||||
@@ -1731,7 +1714,7 @@ EVT_HANDLER_MASK(Disassemble, "Disassemble...", CMDEN_GB | CMDEN_GBA)
|
||||
|
||||
EVT_HANDLER(Logging, "Logging...")
|
||||
{
|
||||
wxDialog* dlg = wxGetApp().frame->logdlg;
|
||||
wxDialog* dlg = wxGetApp().frame->logdlg.get();
|
||||
dlg->SetWindowStyle(wxCAPTION | wxRESIZE_BORDER);
|
||||
dlg->Show();
|
||||
dlg->Raise();
|
||||
@@ -2174,45 +2157,13 @@ EVT_HANDLER_MASK(ChangeIFB, "Change Interframe Blending", CMDEN_NREC_ANY)
|
||||
|
||||
EVT_HANDLER_MASK(SoundConfigure, "Sound options...", CMDEN_NREC_ANY)
|
||||
{
|
||||
int oqual = gopts.sound_qual, oapi = gopts.audio_api;
|
||||
bool oupmix = gopts.upmix, ohw = gopts.dsound_hw_accel;
|
||||
wxString odev = gopts.audio_dev;
|
||||
wxDialog* dlg = GetXRCDialog("SoundConfig");
|
||||
|
||||
if (ShowModal(dlg) != wxID_OK)
|
||||
if (ShowModal(GetXRCDialog("SoundConfig")) != wxID_OK)
|
||||
return;
|
||||
|
||||
switch (panel->game_type()) {
|
||||
case IMAGE_UNKNOWN:
|
||||
break;
|
||||
|
||||
case IMAGE_GB:
|
||||
gb_effects_config.echo = (float)gopts.gb_echo / 100.0;
|
||||
gb_effects_config.stereo = (float)gopts.gb_stereo / 100.0;
|
||||
gbSoundSetSampleRate(!gopts.sound_qual ? 48000 : 44100 / (1 << (gopts.sound_qual - 1)));
|
||||
break;
|
||||
|
||||
case IMAGE_GBA:
|
||||
soundSetSampleRate(!gopts.sound_qual ? 48000 : 44100 / (1 << (gopts.sound_qual - 1)));
|
||||
soundFiltering = (float)gopts.gba_sound_filter / 100.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
// changing sample rate causes driver reload, so no explicit reload needed
|
||||
if (oqual == gopts.sound_qual &&
|
||||
// otherwise reload if API changes
|
||||
(oapi != gopts.audio_api || odev != gopts.audio_dev ||
|
||||
// or init-only options
|
||||
(oapi == AUD_XAUDIO2 && oupmix != gopts.upmix) || (oapi == AUD_FAUDIO && oupmix != gopts.upmix) || (oapi == AUD_DIRECTSOUND && ohw != gopts.dsound_hw_accel))) {
|
||||
soundShutdown();
|
||||
|
||||
if (!soundInit()) {
|
||||
wxLogError(_("Could not initialize the sound driver!"));
|
||||
}
|
||||
}
|
||||
|
||||
soundSetVolume((float)gopts.sound_vol / 100.0);
|
||||
update_opts();
|
||||
// No point in observing these since they can only be set in this dialog.
|
||||
gb_effects_config.echo = (float)OPTION(kSoundGBEcho) / 100.0;
|
||||
gb_effects_config.stereo = (float)OPTION(kSoundGBStereo) / 100.0;
|
||||
soundFiltering = (float)OPTION(kSoundGBAFiltering) / 100.0f;
|
||||
}
|
||||
|
||||
EVT_HANDLER(EmulatorDirectories, "Directories...")
|
||||
@@ -2222,44 +2173,17 @@ EVT_HANDLER(EmulatorDirectories, "Directories...")
|
||||
|
||||
EVT_HANDLER(JoypadConfigure, "Joypad options...")
|
||||
{
|
||||
joy.PollAllJoysticks();
|
||||
|
||||
auto frame = wxGetApp().frame;
|
||||
bool joy_timer = frame->IsJoyPollTimerRunning();
|
||||
|
||||
if (!joy_timer) {
|
||||
frame->StartJoyPollTimer();
|
||||
}
|
||||
|
||||
if (ShowModal(GetXRCDialog("JoypadConfig")) == wxID_OK) {
|
||||
update_joypad_opts();
|
||||
}
|
||||
|
||||
if (!joy_timer) {
|
||||
frame->StopJoyPollTimer();
|
||||
}
|
||||
|
||||
SetJoystick();
|
||||
}
|
||||
|
||||
EVT_HANDLER(Customize, "Customize UI...")
|
||||
{
|
||||
wxDialog* dlg = GetXRCDialog("AccelConfig");
|
||||
joy.PollAllJoysticks();
|
||||
|
||||
auto frame = wxGetApp().frame;
|
||||
bool joy_timer = frame->IsJoyPollTimerRunning();
|
||||
|
||||
if (!joy_timer) frame->StartJoyPollTimer();
|
||||
|
||||
if (ShowModal(dlg) == wxID_OK) {
|
||||
if (ShowModal(GetXRCDialog("AccelConfig")) == wxID_OK) {
|
||||
update_shortcut_opts();
|
||||
ResetMenuAccelerators();
|
||||
}
|
||||
|
||||
if (!joy_timer) frame->StopJoyPollTimer();
|
||||
|
||||
SetJoystick();
|
||||
}
|
||||
|
||||
#ifndef NO_ONLINEUPDATES
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "wx/opts.h"
|
||||
#include "wx/strutils.h"
|
||||
#include "wx/wxlogdebug.h"
|
||||
|
||||
namespace config {
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#ifndef VBAM_WX_CONFIG_GAME_CONTROL_H_
|
||||
#define VBAM_WX_CONFIG_GAME_CONTROL_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
@@ -77,47 +77,26 @@ static const std::array<wxString, kNbRenderMethods> kRenderMethodStrings = {
|
||||
#endif
|
||||
};
|
||||
|
||||
// This enum must be kept in sync with the one in wxvbam.h
|
||||
// TODO: These 2 enums should be unified and a validator created for this enum.
|
||||
// TODO: DirectSound and XAudio2 should only be used on Windows.
|
||||
enum class AudioApi {
|
||||
kSdl = 0,
|
||||
kOpenAL,
|
||||
kDirectSound,
|
||||
kXAudio2,
|
||||
kFaudio,
|
||||
|
||||
// Do not add anything under here.
|
||||
kLast,
|
||||
};
|
||||
constexpr size_t kNbAudioApis = static_cast<size_t>(AudioApi::kLast);
|
||||
|
||||
// These MUST follow the same order as the definitions of the enum above.
|
||||
// Adding an option without adding to this array will result in a compiler
|
||||
// error since kNbAudioApis is automatically updated.
|
||||
static const std::array<wxString, kNbAudioApis> kAudioApiStrings = {
|
||||
"sdl",
|
||||
"openal",
|
||||
#if defined(__WXMSW__)
|
||||
"directsound",
|
||||
#endif
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
"xaudio2",
|
||||
#endif
|
||||
#if defined(VBAM_ENABLE_FAUDIO)
|
||||
"faudio",
|
||||
#endif
|
||||
};
|
||||
|
||||
enum class SoundQuality {
|
||||
k48kHz = 0,
|
||||
k44kHz,
|
||||
k22kHz,
|
||||
k11kHz,
|
||||
|
||||
// Do not add anything under here.
|
||||
kLast,
|
||||
};
|
||||
constexpr size_t kNbSoundQualities = static_cast<size_t>(SoundQuality::kLast);
|
||||
|
||||
// These MUST follow the same order as the definitions of the enum above.
|
||||
// Adding an option without adding to this array will result in a compiler
|
||||
// error since kNbSoundQualities is automatically updated.
|
||||
static const std::array<wxString, kNbSoundQualities> kSoundQualityStrings = {
|
||||
static const std::array<wxString, kNbSoundRate> kAudioRateStrings = {
|
||||
"48",
|
||||
"44",
|
||||
"22",
|
||||
@@ -176,9 +155,11 @@ std::array<Option, kNbOptions>& Option::All() {
|
||||
|
||||
/// GBA
|
||||
bool gba_lcd_filter = false;
|
||||
#ifndef NO_LINK
|
||||
bool link_auto = false;
|
||||
bool link_hacks = true;
|
||||
bool link_proto = false;
|
||||
#endif
|
||||
wxString gba_rom_dir;
|
||||
|
||||
/// Core
|
||||
@@ -211,6 +192,7 @@ std::array<Option, kNbOptions>& Option::All() {
|
||||
|
||||
/// Joypad
|
||||
uint32_t default_stick = 1;
|
||||
bool sdl_game_controller_mode = true;
|
||||
|
||||
/// Geometry
|
||||
bool fullscreen = false;
|
||||
@@ -225,9 +207,25 @@ std::array<Option, kNbOptions>& Option::All() {
|
||||
bool allow_joystick_background_input = true;
|
||||
|
||||
/// Sound
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
AudioApi audio_api = AudioApi::kXAudio2;
|
||||
#else
|
||||
AudioApi audio_api = AudioApi::kOpenAL;
|
||||
#endif
|
||||
wxString audio_dev;
|
||||
// 10 fixes stuttering on mac with openal, as opposed to 5
|
||||
// also should be better for modern hardware in general
|
||||
int32_t audio_buffers = 10;
|
||||
int32_t gba_sound_filtering = 50;
|
||||
bool gb_declicking = true;
|
||||
int32_t gb_echo = 20;
|
||||
bool gb_effects_config_enabled = false;
|
||||
int32_t gb_stereo = 15;
|
||||
bool gb_effects_config_surround = false;
|
||||
AudioRate sound_quality = AudioRate::k44kHz;
|
||||
bool dsound_hw_accel = false;
|
||||
bool upmix = false;
|
||||
int32_t volume = 100;
|
||||
};
|
||||
static OwnedOptions g_owned_opts;
|
||||
|
||||
@@ -293,6 +291,7 @@ std::array<Option, kNbOptions>& Option::All() {
|
||||
Option(OptionID::kJoy),
|
||||
Option(OptionID::kJoyAutofireThrottle, &gopts.autofire_rate, 1, 1000),
|
||||
Option(OptionID::kJoyDefault, &g_owned_opts.default_stick, 1, 4),
|
||||
Option(OptionID::kSDLGameControllerMode, &g_owned_opts.sdl_game_controller_mode),
|
||||
|
||||
/// Keyboard
|
||||
Option(OptionID::kKeyboard),
|
||||
@@ -350,19 +349,21 @@ std::array<Option, kNbOptions>& Option::All() {
|
||||
Option(OptionID::kUISuspendScreenSaver, &gopts.suspend_screensaver),
|
||||
|
||||
/// Sound
|
||||
Option(OptionID::kSoundAudioAPI, &gopts.audio_api),
|
||||
Option(OptionID::kSoundAudioDevice, &gopts.audio_dev),
|
||||
Option(OptionID::kSoundBuffers, &gopts.audio_buffers, 2, 10),
|
||||
Option(OptionID::kSoundAudioAPI, &g_owned_opts.audio_api),
|
||||
Option(OptionID::kSoundAudioDevice, &g_owned_opts.audio_dev),
|
||||
Option(OptionID::kSoundBuffers, &g_owned_opts.audio_buffers, 2, 10),
|
||||
Option(OptionID::kSoundEnable, &gopts.sound_en, 0, 0x30f),
|
||||
Option(OptionID::kSoundGBAFiltering, &gopts.gba_sound_filter, 0, 100),
|
||||
Option(OptionID::kSoundGBAFiltering, &g_owned_opts.gba_sound_filtering, 0, 100),
|
||||
Option(OptionID::kSoundGBAInterpolation, &g_gbaSoundInterpolation),
|
||||
Option(OptionID::kSoundGBDeclicking, &g_owned_opts.gb_declicking),
|
||||
Option(OptionID::kSoundGBEcho, &gopts.gb_echo, 0, 100),
|
||||
Option(OptionID::kSoundGBEcho, &g_owned_opts.gb_echo, 0, 100),
|
||||
Option(OptionID::kSoundGBEnableEffects, &g_owned_opts.gb_effects_config_enabled),
|
||||
Option(OptionID::kSoundGBStereo, &gopts.gb_stereo, 0, 100),
|
||||
Option(OptionID::kSoundGBStereo, &g_owned_opts.gb_stereo, 0, 100),
|
||||
Option(OptionID::kSoundGBSurround, &g_owned_opts.gb_effects_config_surround),
|
||||
Option(OptionID::kSoundQuality, &gopts.sound_qual),
|
||||
Option(OptionID::kSoundVolume, &gopts.sound_vol, 0, 200),
|
||||
Option(OptionID::kSoundAudioRate, &g_owned_opts.sound_quality),
|
||||
Option(OptionID::kSoundDSoundHWAccel, &g_owned_opts.dsound_hw_accel),
|
||||
Option(OptionID::kSoundUpmix, &g_owned_opts.upmix),
|
||||
Option(OptionID::kSoundVolume, &g_owned_opts.volume, 0, 200),
|
||||
};
|
||||
// clang-format on
|
||||
return g_all_opts;
|
||||
@@ -375,8 +376,7 @@ namespace internal {
|
||||
// error since kNbOptions is automatically updated.
|
||||
const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||
/// Display
|
||||
OptionData{"Display/Bilinear", "Bilinear",
|
||||
_("Use bilinear filter with 3d renderer")},
|
||||
OptionData{"Display/Bilinear", "Bilinear", _("Use bilinear filter with 3d renderer")},
|
||||
OptionData{"Display/Filter", "", _("Full-screen filter to apply")},
|
||||
OptionData{"Display/FilterPlugin", "", _("Filter plugin library")},
|
||||
OptionData{"Display/IFB", "", _("Interframe blending function")},
|
||||
@@ -386,20 +386,14 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||
OptionData{"Display/RenderMethod", "",
|
||||
_("Render method; if unsupported, simple method will be used")},
|
||||
OptionData{"Display/Scale", "", _("Default scale factor")},
|
||||
OptionData{"Display/Stretch", "RetainAspect",
|
||||
_("Retain aspect ratio when resizing")},
|
||||
OptionData{"Display/Stretch", "RetainAspect", _("Retain aspect ratio when resizing")},
|
||||
|
||||
/// GB
|
||||
OptionData{"GB/BiosFile", "",
|
||||
_("BIOS file to use for Game Boy, if enabled")},
|
||||
OptionData{"GB/ColorOption", "GBColorOption",
|
||||
_("Game Boy color enhancement, if enabled")},
|
||||
OptionData{"GB/ColorizerHack", "ColorizerHack",
|
||||
_("Enable DX Colorization Hacks")},
|
||||
OptionData{"GB/LCDFilter", "GBLcdFilter",
|
||||
_("Apply LCD filter, if enabled")},
|
||||
OptionData{"GB/GBCBiosFile", "",
|
||||
_("BIOS file to use for Game Boy Color, if enabled")},
|
||||
OptionData{"GB/BiosFile", "", _("BIOS file to use for Game Boy, if enabled")},
|
||||
OptionData{"GB/ColorOption", "GBColorOption", _("Game Boy color enhancement, if enabled")},
|
||||
OptionData{"GB/ColorizerHack", "ColorizerHack", _("Enable DX Colorization Hacks")},
|
||||
OptionData{"GB/LCDFilter", "GBLcdFilter", _("Apply LCD filter, if enabled")},
|
||||
OptionData{"GB/GBCBiosFile", "", _("BIOS file to use for Game Boy Color, if enabled")},
|
||||
OptionData{"GB/Palette0", "",
|
||||
_("The default palette, as 8 comma-separated 4-digit hex "
|
||||
"integers (rgb555).")},
|
||||
@@ -415,8 +409,7 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||
_("Automatically save printouts as screen captures with -print "
|
||||
"suffix")},
|
||||
OptionData{"GB/ROMDir", "", _("Directory to look for ROM files")},
|
||||
OptionData{"GB/GBCROMDir", "",
|
||||
_("Directory to look for Game Boy Color ROM files")},
|
||||
OptionData{"GB/GBCROMDir", "", _("Directory to look for Game Boy Color ROM files")},
|
||||
|
||||
/// GBA
|
||||
OptionData{"GBA/BiosFile", "", _("BIOS file to use, if enabled")},
|
||||
@@ -438,8 +431,7 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||
},
|
||||
OptionData{"GBA/LinkHost", "", _("Default network link client host")},
|
||||
OptionData{"GBA/ServerIP", "", _("Default network link server IP to bind")},
|
||||
OptionData{"GBA/LinkPort", "",
|
||||
_("Default network link port (server and client)")},
|
||||
OptionData{"GBA/LinkPort", "", _("Default network link port (server and client)")},
|
||||
OptionData{"GBA/LinkProto", "LinkProto", _("Default network protocol")},
|
||||
OptionData{"GBA/LinkTimeout", "LinkTimeout", _("Link timeout (ms)")},
|
||||
OptionData{"GBA/LinkType", "LinkType", _("Link cable type")},
|
||||
@@ -447,8 +439,7 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||
OptionData{"GBA/ROMDir", "", _("Directory to look for ROM files")},
|
||||
|
||||
/// General
|
||||
OptionData{"General/AutoLoadLastState", "",
|
||||
_("Automatically load last saved state")},
|
||||
OptionData{"General/AutoLoadLastState", "", _("Automatically load last saved state")},
|
||||
OptionData{"General/BatteryDir", "",
|
||||
_("Directory to store game save files (relative paths are "
|
||||
"relative to ROM; blank is config dir)")},
|
||||
@@ -474,10 +465,10 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||
"Button is one of Up, Down, Left, Right, A, B, L, R, Select, "
|
||||
"Start, MotionUp, MotionDown, MotionLeft, MotionRight, AutoA, "
|
||||
"AutoB, Speed, Capture, GS")},
|
||||
OptionData{"Joypad/AutofireThrottle", "",
|
||||
_("The autofire toggle period, in frames (1/60 s)")},
|
||||
OptionData{"Joypad/Default", "",
|
||||
_("The number of the stick to use in single-player mode")},
|
||||
OptionData{"Joypad/AutofireThrottle", "", _("The autofire toggle period, in frames (1/60 s)")},
|
||||
OptionData{"Joypad/Default", "", _("The number of the stick to use in single-player mode")},
|
||||
OptionData{"Joypad/SDLGameControllerMode", "SDLGameControllerMode",
|
||||
_("Whether to enable SDL GameController mode")},
|
||||
|
||||
/// Keyboard
|
||||
OptionData{"Keyboard/*", "",
|
||||
@@ -486,10 +477,8 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||
"pressed, the command <cmd> is executed.")},
|
||||
|
||||
/// Core
|
||||
OptionData{"preferences/agbPrint", "AGBPrinter",
|
||||
_("Enable AGB debug print")},
|
||||
OptionData{"preferences/autoFrameSkip", "FrameSkipAuto",
|
||||
_("Auto skip frames")},
|
||||
OptionData{"preferences/agbPrint", "AGBPrinter", _("Enable AGB debug print")},
|
||||
OptionData{"preferences/autoFrameSkip", "FrameSkipAuto", _("Auto skip frames")},
|
||||
OptionData{"preferences/autoPatch", "ApplyPatches",
|
||||
_("Apply IPS / UPS / IPF patches if found")},
|
||||
OptionData{"preferences/autoSaveLoadCheatList", "",
|
||||
@@ -500,41 +489,32 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||
_("Automatically enable border for Super Game Boy games"),
|
||||
},
|
||||
OptionData{"preferences/borderOn", "", _("Always enable border")},
|
||||
OptionData{"preferences/captureFormat", "",
|
||||
_("Screen capture file format")},
|
||||
OptionData{"preferences/captureFormat", "", _("Screen capture file format")},
|
||||
OptionData{"preferences/cheatsEnabled", "", _("Enable cheats")},
|
||||
OptionData{"preferences/disableStatus", "NoStatusMsg",
|
||||
_("Disable on-screen status messages")},
|
||||
OptionData{"preferences/disableStatus", "NoStatusMsg", _("Disable on-screen status messages")},
|
||||
OptionData{"preferences/emulatorType", "", _("Type of system to emulate")},
|
||||
OptionData{"preferences/flashSize", "",
|
||||
_("Flash size 0 = 64 KB 1 = 128 KB")},
|
||||
OptionData{"preferences/flashSize", "", _("Flash size 0 = 64 KB 1 = 128 KB")},
|
||||
OptionData{"preferences/frameSkip", "FrameSkip",
|
||||
_("Skip frames. Values are 0-9 or -1 to skip automatically "
|
||||
"based on time.")},
|
||||
OptionData{"preferences/gbPaletteOption", "", _("The palette to use")},
|
||||
OptionData{"preferences/gbPrinter", "Printer",
|
||||
_("Enable printer emulation")},
|
||||
OptionData{"preferences/gbPrinter", "Printer", _("Enable printer emulation")},
|
||||
OptionData{"preferences/gdbBreakOnLoad", "DebugGDBBreakOnLoad",
|
||||
_("Break into GDB after loading the game.")},
|
||||
OptionData{"preferences/gdbPort", "DebugGDBPort",
|
||||
_("Port to connect GDB to")},
|
||||
OptionData{"preferences/gdbPort", "DebugGDBPort", _("Port to connect GDB to")},
|
||||
#ifndef NO_LINK
|
||||
OptionData{"preferences/LinkNumPlayers", "",
|
||||
_("Number of players in network")},
|
||||
OptionData{"preferences/LinkNumPlayers", "", _("Number of players in network")},
|
||||
#endif
|
||||
OptionData{"preferences/maxScale", "",
|
||||
_("Maximum scale factor (0 = no limit)")},
|
||||
OptionData{"preferences/maxScale", "", _("Maximum scale factor (0 = no limit)")},
|
||||
OptionData{"preferences/pauseWhenInactive", "PauseWhenInactive",
|
||||
_("Pause game when main window loses focus")},
|
||||
OptionData{"preferences/rtcEnabled", "RTC",
|
||||
_("Enable RTC (vba-over.ini override is rtcEnabled")},
|
||||
OptionData{"preferences/saveType", "",
|
||||
_("Native save (\"battery\") hardware type")},
|
||||
OptionData{"preferences/saveType", "", _("Native save (\"battery\") hardware type")},
|
||||
OptionData{"preferences/showSpeed", "", _("Show speed indicator")},
|
||||
OptionData{"preferences/showSpeedTransparent", "Transparent",
|
||||
_("Draw on-screen messages transparently")},
|
||||
OptionData{"preferences/skipBios", "SkipIntro",
|
||||
_("Skip BIOS initialization")},
|
||||
OptionData{"preferences/skipBios", "SkipIntro", _("Skip BIOS initialization")},
|
||||
OptionData{"preferences/skipSaveGameCheats", "",
|
||||
_("Do not overwrite cheat list when loading state")},
|
||||
OptionData{"preferences/skipSaveGameBattery", "",
|
||||
@@ -549,54 +529,45 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||
"throttle)")},
|
||||
OptionData{"preferences/speedupThrottleFrameSkip", "",
|
||||
_("Use frame skip for speedup throttle")},
|
||||
OptionData{"preferences/useBiosGB", "BootRomGB",
|
||||
_("Use the specified BIOS file for Game Boy")},
|
||||
OptionData{"preferences/useBiosGBA", "BootRomEn",
|
||||
_("Use the specified BIOS file")},
|
||||
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",
|
||||
_("Use the specified BIOS file for Game Boy Color")},
|
||||
OptionData{"preferences/vsync", "VSync", _("Wait for vertical sync")},
|
||||
|
||||
/// Geometry
|
||||
OptionData{"geometry/fullScreen", "Fullscreen",
|
||||
_("Enter fullscreen mode at startup")},
|
||||
OptionData{"geometry/fullScreen", "Fullscreen", _("Enter fullscreen mode at startup")},
|
||||
OptionData{"geometry/isMaximized", "Maximized", _("Window maximized")},
|
||||
OptionData{"geometry/windowHeight", "Height",
|
||||
_("Window height at startup")},
|
||||
OptionData{"geometry/windowHeight", "Height", _("Window height at startup")},
|
||||
OptionData{"geometry/windowWidth", "Width", _("Window width at startup")},
|
||||
OptionData{"geometry/windowX", "X", _("Window axis X position at startup")},
|
||||
OptionData{"geometry/windowY", "Y", _("Window axis Y position at startup")},
|
||||
|
||||
/// UI
|
||||
OptionData{"ui/allowKeyboardBackgroundInput",
|
||||
"AllowKeyboardBackgroundInput",
|
||||
OptionData{"ui/allowKeyboardBackgroundInput", "AllowKeyboardBackgroundInput",
|
||||
_("Capture key events while on background")},
|
||||
OptionData{"ui/allowJoystickBackgroundInput",
|
||||
"AllowJoystickBackgroundInput",
|
||||
OptionData{"ui/allowJoystickBackgroundInput", "AllowJoystickBackgroundInput",
|
||||
_("Capture joy events while on background")},
|
||||
OptionData{"ui/hideMenuBar", "HideMenuBar", _("Hide menu bar when mouse is inactive")},
|
||||
OptionData{"ui/suspendScreenSaver", "SuspendScreenSaver", _("Suspend screensaver when game is running")},
|
||||
OptionData{"ui/suspendScreenSaver", "SuspendScreenSaver",
|
||||
_("Suspend screensaver when game is running")},
|
||||
|
||||
/// Sound
|
||||
OptionData{"Sound/AudioAPI", "",
|
||||
_("Sound API; if unsupported, default API will be used")},
|
||||
OptionData{"Sound/AudioDevice", "",
|
||||
_("Device ID of chosen audio device for chosen driver")},
|
||||
OptionData{"Sound/AudioAPI", "", _("Sound API; if unsupported, default API will be used")},
|
||||
OptionData{"Sound/AudioDevice", "", _("Device ID of chosen audio device for chosen driver")},
|
||||
OptionData{"Sound/Buffers", "", _("Number of sound buffers")},
|
||||
OptionData{"Sound/Enable", "", _("Bit mask of sound channels to enable")},
|
||||
OptionData{"Sound/GBAFiltering", "",
|
||||
_("Game Boy Advance sound filtering (%)")},
|
||||
OptionData{"Sound/GBAFiltering", "", _("Game Boy Advance sound filtering (%)")},
|
||||
OptionData{"Sound/GBAInterpolation", "GBASoundInterpolation",
|
||||
_("Game Boy Advance sound interpolation")},
|
||||
OptionData{"Sound/GBDeclicking", "GBDeclicking",
|
||||
_("Game Boy sound declicking")},
|
||||
OptionData{"Sound/GBDeclicking", "GBDeclicking", _("Game Boy sound declicking")},
|
||||
OptionData{"Sound/GBEcho", "", _("Game Boy echo effect (%)")},
|
||||
OptionData{"Sound/GBEnableEffects", "GBEnhanceSound",
|
||||
_("Enable Game Boy sound effects")},
|
||||
OptionData{"Sound/GBEnableEffects", "GBEnhanceSound", _("Enable Game Boy sound effects")},
|
||||
OptionData{"Sound/GBStereo", "", _("Game Boy stereo effect (%)")},
|
||||
OptionData{"Sound/GBSurround", "GBSurround",
|
||||
_("Game Boy surround sound effect (%)")},
|
||||
OptionData{"Sound/GBSurround", "GBSurround", _("Game Boy surround sound effect (%)")},
|
||||
OptionData{"Sound/Quality", "", _("Sound sample rate (kHz)")},
|
||||
OptionData{"Sound/DSoundHWAccel", "DSoundHWAccel", _("Use DirectSound hardware acceleration")},
|
||||
OptionData{"Sound/Upmix", "Upmix", _("Upmix stereo to surround")},
|
||||
OptionData{"Sound/Volume", "", _("Sound volume (%)")},
|
||||
|
||||
// Last. This should never be used, it actually maps to OptionID::kLast.
|
||||
@@ -642,14 +613,16 @@ wxString RenderMethodToString(const RenderMethod& value) {
|
||||
return kRenderMethodStrings[size_value];
|
||||
}
|
||||
|
||||
wxString AudioApiToString(int value) {
|
||||
assert(value >= 0 && static_cast<size_t>(value) < kNbAudioApis);
|
||||
return kAudioApiStrings[value];
|
||||
wxString AudioApiToString(const AudioApi& value) {
|
||||
const size_t size_value = static_cast<size_t>(value);
|
||||
assert(size_value < kNbAudioApis);
|
||||
return kAudioApiStrings[size_value];
|
||||
}
|
||||
|
||||
wxString SoundQualityToString(int value) {
|
||||
assert(value >= 0 && static_cast<size_t>(value) < kNbSoundQualities);
|
||||
return kSoundQualityStrings[value];
|
||||
wxString AudioRateToString(const AudioRate& value) {
|
||||
const size_t size_value = static_cast<size_t>(value);
|
||||
assert(size_value < kNbSoundRate);
|
||||
return kAudioRateStrings[size_value];
|
||||
}
|
||||
|
||||
Filter StringToFilter(const wxString& config_name, const wxString& input) {
|
||||
@@ -712,7 +685,7 @@ RenderMethod StringToRenderMethod(const wxString& config_name,
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
int StringToAudioApi(const wxString& config_name, const wxString& input_) {
|
||||
AudioApi StringToAudioApi(const wxString& config_name, const wxString& input) {
|
||||
static std::map<wxString, AudioApi> kStringToAudioApi;
|
||||
if (kStringToAudioApi.empty()) {
|
||||
for (size_t i = 0; i < kNbAudioApis; i++) {
|
||||
@@ -722,45 +695,32 @@ int StringToAudioApi(const wxString& config_name, const wxString& input_) {
|
||||
assert(kStringToAudioApi.size() == kNbAudioApis);
|
||||
}
|
||||
|
||||
wxString input = input_;
|
||||
|
||||
// sdl has been removed, rewrite to new default
|
||||
if (input == "sdl") {
|
||||
#ifdef __WXMSW__
|
||||
input = "xaudio2";
|
||||
#else
|
||||
input = "openal";
|
||||
#endif
|
||||
}
|
||||
|
||||
const auto iter = kStringToAudioApi.find(input);
|
||||
if (iter == kStringToAudioApi.end()) {
|
||||
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
|
||||
input, config_name,
|
||||
AllEnumValuesForType(Option::Type::kAudioApi));
|
||||
return 0;
|
||||
return AudioApi::kOpenAL;
|
||||
}
|
||||
return static_cast<int>(iter->second);
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
int StringToSoundQuality(const wxString& config_name, const wxString& input) {
|
||||
static std::map<wxString, SoundQuality> kStringToSoundQuality;
|
||||
AudioRate StringToSoundQuality(const wxString& config_name, const wxString& input) {
|
||||
static std::map<wxString, AudioRate> kStringToSoundQuality;
|
||||
if (kStringToSoundQuality.empty()) {
|
||||
for (size_t i = 0; i < kNbSoundQualities; i++) {
|
||||
kStringToSoundQuality.emplace(kSoundQualityStrings[i],
|
||||
static_cast<SoundQuality>(i));
|
||||
for (size_t i = 0; i < kNbSoundRate; i++) {
|
||||
kStringToSoundQuality.emplace(kAudioRateStrings[i], static_cast<AudioRate>(i));
|
||||
}
|
||||
assert(kStringToSoundQuality.size() == kNbSoundQualities);
|
||||
assert(kStringToSoundQuality.size() == kNbSoundRate);
|
||||
}
|
||||
|
||||
const auto iter = kStringToSoundQuality.find(input);
|
||||
if (iter == kStringToSoundQuality.end()) {
|
||||
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
|
||||
input, config_name,
|
||||
AllEnumValuesForType(Option::Type::kSoundQuality));
|
||||
return 0;
|
||||
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"), input, config_name,
|
||||
AllEnumValuesForType(Option::Type::kAudioRate));
|
||||
return AudioRate::k44kHz;
|
||||
}
|
||||
return static_cast<int>(iter->second);
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
wxString AllEnumValuesForType(Option::Type type) {
|
||||
@@ -785,9 +745,9 @@ wxString AllEnumValuesForType(Option::Type type) {
|
||||
AllEnumValuesForArray(kAudioApiStrings);
|
||||
return kAllAudioApiValues;
|
||||
}
|
||||
case Option::Type::kSoundQuality: {
|
||||
case Option::Type::kAudioRate: {
|
||||
static const wxString kAllSoundQualityValues =
|
||||
AllEnumValuesForArray(kSoundQualityStrings);
|
||||
AllEnumValuesForArray(kAudioRateStrings);
|
||||
return kAllSoundQualityValues;
|
||||
}
|
||||
|
||||
@@ -807,7 +767,7 @@ wxString AllEnumValuesForType(Option::Type type) {
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
int MaxForType(Option::Type type) {
|
||||
size_t MaxForType(Option::Type type) {
|
||||
switch (type) {
|
||||
case Option::Type::kFilter:
|
||||
return kNbFilters;
|
||||
@@ -817,8 +777,8 @@ int MaxForType(Option::Type type) {
|
||||
return kNbRenderMethods;
|
||||
case Option::Type::kAudioApi:
|
||||
return kNbAudioApis;
|
||||
case Option::Type::kSoundQuality:
|
||||
return kNbSoundQualities;
|
||||
case Option::Type::kAudioRate:
|
||||
return kNbSoundRate;
|
||||
|
||||
// We don't use default here to explicitly trigger a compiler warning
|
||||
// when adding a new value.
|
||||
|
@@ -27,18 +27,18 @@ nonstd::optional<OptionID> StringToOptionId(const wxString& input);
|
||||
wxString FilterToString(const Filter& value);
|
||||
wxString InterframeToString(const Interframe& value);
|
||||
wxString RenderMethodToString(const RenderMethod& value);
|
||||
wxString AudioApiToString(int value);
|
||||
wxString SoundQualityToString(int value);
|
||||
wxString AudioApiToString(const AudioApi& value);
|
||||
wxString AudioRateToString(const AudioRate& value);
|
||||
Filter StringToFilter(const wxString& config_name, const wxString& input);
|
||||
Interframe StringToInterframe(const wxString& config_name, const wxString& input);
|
||||
RenderMethod StringToRenderMethod(const wxString& config_name, const wxString& input);
|
||||
int StringToAudioApi(const wxString& config_name, const wxString& input);
|
||||
int StringToSoundQuality(const wxString& config_name, const wxString& input);
|
||||
AudioApi StringToAudioApi(const wxString& config_name, const wxString& input);
|
||||
AudioRate StringToSoundQuality(const wxString& config_name, const wxString& input);
|
||||
|
||||
wxString AllEnumValuesForType(Option::Type type);
|
||||
|
||||
// Max value for enum types.
|
||||
int MaxForType(Option::Type type);
|
||||
size_t MaxForType(Option::Type type);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace config
|
||||
|
@@ -61,6 +61,7 @@ enum class OptionID {
|
||||
kJoy,
|
||||
kJoyAutofireThrottle,
|
||||
kJoyDefault,
|
||||
kSDLGameControllerMode,
|
||||
|
||||
/// Keyboard
|
||||
kKeyboard,
|
||||
@@ -129,7 +130,9 @@ enum class OptionID {
|
||||
kSoundGBEnableEffects,
|
||||
kSoundGBStereo,
|
||||
kSoundGBSurround,
|
||||
kSoundQuality,
|
||||
kSoundAudioRate,
|
||||
kSoundDSoundHWAccel,
|
||||
kSoundUpmix,
|
||||
kSoundVolume,
|
||||
|
||||
// Do not add anything under here.
|
||||
|
@@ -65,6 +65,7 @@ static constexpr std::array<Option::Type, kNbOptions> kOptionsTypes = {
|
||||
/*kJoy*/ Option::Type::kNone,
|
||||
/*kJoyAutofireThrottle*/ Option::Type::kInt,
|
||||
/*kJoyDefault*/ Option::Type::kUnsigned,
|
||||
/*kSDLGameControllerMode*/ Option::Type::kBool,
|
||||
|
||||
/// Keyboard
|
||||
/*kKeyboard*/ Option::Type::kNone,
|
||||
@@ -133,7 +134,9 @@ static constexpr std::array<Option::Type, kNbOptions> kOptionsTypes = {
|
||||
/*kSoundGBEnableEffects*/ Option::Type::kBool,
|
||||
/*kSoundGBStereo*/ Option::Type::kInt,
|
||||
/*kSoundGBSurround*/ Option::Type::kBool,
|
||||
/*kSoundQuality*/ Option::Type::kSoundQuality,
|
||||
/*kSoundAudioRate*/ Option::Type::kAudioRate,
|
||||
/*kSoundDSoundHWAccel*/ Option::Type::kBool,
|
||||
/*kSoundUpmix*/ Option::Type::kBool,
|
||||
/*kSoundVolume*/ Option::Type::kInt,
|
||||
};
|
||||
|
||||
@@ -207,6 +210,22 @@ public:
|
||||
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:
|
||||
@@ -312,6 +331,44 @@ private:
|
||||
Option* option_;
|
||||
};
|
||||
|
||||
template <OptionID ID>
|
||||
class OptionProxy<
|
||||
ID,
|
||||
typename std::enable_if<kOptionsTypes[static_cast<size_t>(ID)] ==
|
||||
Option::Type::kAudioApi>::type> {
|
||||
public:
|
||||
OptionProxy() : option_(Option::ByID(ID)) {}
|
||||
~OptionProxy() = default;
|
||||
|
||||
AudioApi Get() const { return option_->GetAudioApi(); }
|
||||
bool Set(AudioApi value) { return option_->SetAudioApi(value); }
|
||||
|
||||
bool operator=(AudioApi value) { return Set(value); }
|
||||
operator AudioApi() const { return Get(); }
|
||||
|
||||
private:
|
||||
Option* option_;
|
||||
};
|
||||
|
||||
template <OptionID ID>
|
||||
class OptionProxy<
|
||||
ID,
|
||||
typename std::enable_if<kOptionsTypes[static_cast<size_t>(ID)] ==
|
||||
Option::Type::kAudioRate>::type> {
|
||||
public:
|
||||
OptionProxy() : option_(Option::ByID(ID)) {}
|
||||
~OptionProxy() = default;
|
||||
|
||||
AudioRate Get() const { return option_->GetAudioRate(); }
|
||||
bool Set(AudioRate value) { return option_->SetAudioRate(value); }
|
||||
|
||||
bool operator=(AudioRate value) { return Set(value); }
|
||||
operator AudioRate() const { return Get(); }
|
||||
|
||||
private:
|
||||
Option* option_;
|
||||
};
|
||||
|
||||
} // namespace config
|
||||
|
||||
#endif // VBAM_WX_CONFIG_OPTION_PROXY_H_
|
||||
|
@@ -17,8 +17,7 @@ namespace config {
|
||||
|
||||
// static
|
||||
Option* Option::ByName(const wxString& config_name) {
|
||||
nonstd::optional<OptionID> option_id =
|
||||
internal::StringToOptionId(config_name);
|
||||
nonstd::optional<OptionID> option_id = internal::StringToOptionId(config_name);
|
||||
if (!option_id) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -33,8 +32,7 @@ Option* Option::ByID(OptionID id) {
|
||||
|
||||
Option::~Option() = default;
|
||||
|
||||
Option::Observer::Observer(OptionID option_id)
|
||||
: option_(Option::ByID(option_id)) {
|
||||
Option::Observer::Observer(OptionID option_id) : option_(Option::ByID(option_id)) {
|
||||
assert(option_);
|
||||
option_->AddObserver(this);
|
||||
}
|
||||
@@ -44,11 +42,9 @@ Option::Observer::~Observer() {
|
||||
|
||||
Option::Option(OptionID id)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(),
|
||||
min_(),
|
||||
@@ -59,11 +55,9 @@ Option::Option(OptionID id)
|
||||
|
||||
Option::Option(OptionID id, bool* option)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(),
|
||||
@@ -74,11 +68,9 @@ Option::Option(OptionID id, bool* option)
|
||||
|
||||
Option::Option(OptionID id, double* option, double min, double max)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(min),
|
||||
@@ -92,11 +84,9 @@ Option::Option(OptionID id, double* option, double min, double max)
|
||||
|
||||
Option::Option(OptionID id, int32_t* option, int32_t min, int32_t max)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(min),
|
||||
@@ -110,11 +100,9 @@ Option::Option(OptionID id, int32_t* option, int32_t min, int32_t max)
|
||||
|
||||
Option::Option(OptionID id, uint32_t* option, uint32_t min, uint32_t max)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(min),
|
||||
@@ -128,11 +116,9 @@ Option::Option(OptionID id, uint32_t* option, uint32_t min, uint32_t max)
|
||||
|
||||
Option::Option(OptionID id, wxString* option)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(),
|
||||
@@ -143,74 +129,74 @@ Option::Option(OptionID id, wxString* option)
|
||||
|
||||
Option::Option(OptionID id, Filter* option)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(0),
|
||||
max_(internal::MaxForType(type_)) {
|
||||
min_(),
|
||||
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
|
||||
assert(id != OptionID::Last);
|
||||
assert(is_filter());
|
||||
}
|
||||
|
||||
Option::Option(OptionID id, Interframe* option)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(0),
|
||||
max_(internal::MaxForType(type_)) {
|
||||
min_(),
|
||||
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
|
||||
assert(id != OptionID::Last);
|
||||
assert(is_interframe());
|
||||
}
|
||||
|
||||
Option::Option(OptionID id, RenderMethod* option)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(0),
|
||||
max_(internal::MaxForType(type_)) {
|
||||
min_(),
|
||||
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
|
||||
assert(id != OptionID::Last);
|
||||
assert(is_render_method());
|
||||
}
|
||||
|
||||
Option::Option(OptionID id, int* option)
|
||||
Option::Option(OptionID id, AudioApi* option)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(0),
|
||||
max_(internal::MaxForType(type_)) {
|
||||
min_(),
|
||||
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
|
||||
assert(id != OptionID::Last);
|
||||
assert(is_audio_api() || is_sound_quality());
|
||||
assert(is_audio_api());
|
||||
}
|
||||
|
||||
// Validate the initial value.
|
||||
SetEnumInt(*option);
|
||||
Option::Option(OptionID id, AudioRate* option)
|
||||
: id_(id),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(),
|
||||
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
|
||||
assert(id != OptionID::Last);
|
||||
assert(is_audio_rate());
|
||||
}
|
||||
|
||||
Option::Option(OptionID id, uint16_t* option)
|
||||
: id_(id),
|
||||
config_name_(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||
ux_helper_(wxGetTranslation(
|
||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||
value_(option),
|
||||
min_(),
|
||||
@@ -259,6 +245,16 @@ RenderMethod Option::GetRenderMethod() const {
|
||||
return *(nonstd::get<RenderMethod*>(value_));
|
||||
}
|
||||
|
||||
AudioApi Option::GetAudioApi() const {
|
||||
assert(is_audio_api());
|
||||
return *(nonstd::get<AudioApi*>(value_));
|
||||
}
|
||||
|
||||
AudioRate Option::GetAudioRate() const {
|
||||
assert(is_audio_rate());
|
||||
return *(nonstd::get<AudioRate*>(value_));
|
||||
}
|
||||
|
||||
wxString Option::GetEnumString() const {
|
||||
switch (type_) {
|
||||
case Option::Type::kFilter:
|
||||
@@ -268,10 +264,9 @@ wxString Option::GetEnumString() const {
|
||||
case Option::Type::kRenderMethod:
|
||||
return internal::RenderMethodToString(GetRenderMethod());
|
||||
case Option::Type::kAudioApi:
|
||||
return internal::AudioApiToString(*(nonstd::get<int32_t*>(value_)));
|
||||
case Option::Type::kSoundQuality:
|
||||
return internal::SoundQualityToString(
|
||||
*(nonstd::get<int32_t*>(value_)));
|
||||
return internal::AudioApiToString(GetAudioApi());
|
||||
case Option::Type::kAudioRate:
|
||||
return internal::AudioRateToString(GetAudioRate());
|
||||
|
||||
// We don't use default here to explicitly trigger a compiler warning
|
||||
// when adding a new value.
|
||||
@@ -303,9 +298,8 @@ wxString Option::GetGbPaletteString() const {
|
||||
|
||||
wxString palette_string;
|
||||
uint16_t const* value = nonstd::get<uint16_t*>(value_);
|
||||
palette_string.Printf("%04X,%04X,%04X,%04X,%04X,%04X,%04X,%04X", value[0],
|
||||
value[1], value[2], value[3], value[4], value[5],
|
||||
value[6], value[7]);
|
||||
palette_string.Printf("%04X,%04X,%04X,%04X,%04X,%04X,%04X,%04X", value[0], value[1], value[2],
|
||||
value[3], value[4], value[5], value[6], value[7]);
|
||||
return palette_string;
|
||||
}
|
||||
|
||||
@@ -322,12 +316,9 @@ bool Option::SetBool(bool value) {
|
||||
bool Option::SetDouble(double value) {
|
||||
assert(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, config_name_, nonstd::get<double>(min_),
|
||||
nonstd::get<double>(max_));
|
||||
if (value < nonstd::get<double>(min_) || value > nonstd::get<double>(max_)) {
|
||||
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), value,
|
||||
config_name_, nonstd::get<double>(min_), nonstd::get<double>(max_));
|
||||
return false;
|
||||
}
|
||||
*nonstd::get<double*>(value_) = value;
|
||||
@@ -340,12 +331,9 @@ bool Option::SetDouble(double value) {
|
||||
bool Option::SetInt(int32_t value) {
|
||||
assert(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, config_name_, nonstd::get<int32_t>(min_),
|
||||
nonstd::get<int32_t>(max_));
|
||||
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,
|
||||
config_name_, nonstd::get<int32_t>(min_), nonstd::get<int32_t>(max_));
|
||||
return false;
|
||||
}
|
||||
*nonstd::get<int32_t*>(value_) = value;
|
||||
@@ -358,12 +346,9 @@ bool Option::SetInt(int32_t value) {
|
||||
bool Option::SetUnsigned(uint32_t value) {
|
||||
assert(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, config_name_, nonstd::get<uint32_t>(min_),
|
||||
nonstd::get<uint32_t>(max_));
|
||||
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,
|
||||
config_name_, nonstd::get<uint32_t>(min_), nonstd::get<uint32_t>(max_));
|
||||
return false;
|
||||
}
|
||||
*nonstd::get<uint32_t*>(value_) = value;
|
||||
@@ -385,7 +370,7 @@ bool Option::SetString(const wxString& value) {
|
||||
|
||||
bool Option::SetFilter(const Filter& value) {
|
||||
assert(is_filter());
|
||||
assert(value != Filter::kLast);
|
||||
assert(value < Filter::kLast);
|
||||
const Filter old_value = GetFilter();
|
||||
*nonstd::get<Filter*>(value_) = value;
|
||||
if (old_value != value) {
|
||||
@@ -396,7 +381,7 @@ bool Option::SetFilter(const Filter& value) {
|
||||
|
||||
bool Option::SetInterframe(const Interframe& value) {
|
||||
assert(is_interframe());
|
||||
assert(value != Interframe::kLast);
|
||||
assert(value < Interframe::kLast);
|
||||
const Interframe old_value = GetInterframe();
|
||||
*nonstd::get<Interframe*>(value_) = value;
|
||||
if (old_value != value) {
|
||||
@@ -407,7 +392,7 @@ bool Option::SetInterframe(const Interframe& value) {
|
||||
|
||||
bool Option::SetRenderMethod(const RenderMethod& value) {
|
||||
assert(is_render_method());
|
||||
assert(value != RenderMethod::kLast);
|
||||
assert(value < RenderMethod::kLast);
|
||||
const RenderMethod old_value = GetRenderMethod();
|
||||
*nonstd::get<RenderMethod*>(value_) = value;
|
||||
if (old_value != value) {
|
||||
@@ -416,21 +401,40 @@ bool Option::SetRenderMethod(const RenderMethod& value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Option::SetAudioApi(const AudioApi& value) {
|
||||
assert(is_audio_api());
|
||||
assert(value < AudioApi::kLast);
|
||||
const AudioApi old_value = GetAudioApi();
|
||||
*nonstd::get<AudioApi*>(value_) = value;
|
||||
if (old_value != value) {
|
||||
CallObservers();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Option::SetAudioRate(const AudioRate& value) {
|
||||
assert(is_audio_rate());
|
||||
assert(value < AudioRate::kLast);
|
||||
const AudioRate old_value = GetAudioRate();
|
||||
*nonstd::get<AudioRate*>(value_) = value;
|
||||
if (old_value != value) {
|
||||
CallObservers();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Option::SetEnumString(const wxString& value) {
|
||||
switch (type_) {
|
||||
case Option::Type::kFilter:
|
||||
return SetFilter(internal::StringToFilter(config_name_, value));
|
||||
case Option::Type::kInterframe:
|
||||
return SetInterframe(
|
||||
internal::StringToInterframe(config_name_, value));
|
||||
return SetInterframe(internal::StringToInterframe(config_name_, value));
|
||||
case Option::Type::kRenderMethod:
|
||||
return SetRenderMethod(
|
||||
internal::StringToRenderMethod(config_name_, value));
|
||||
return SetRenderMethod(internal::StringToRenderMethod(config_name_, value));
|
||||
case Option::Type::kAudioApi:
|
||||
return SetEnumInt(internal::StringToAudioApi(config_name_, value));
|
||||
case Option::Type::kSoundQuality:
|
||||
return SetEnumInt(
|
||||
internal::StringToSoundQuality(config_name_, value));
|
||||
return SetAudioApi(internal::StringToAudioApi(config_name_, value));
|
||||
case Option::Type::kAudioRate:
|
||||
return SetAudioRate(internal::StringToSoundQuality(config_name_, value));
|
||||
|
||||
// We don't use default here to explicitly trigger a compiler warning
|
||||
// when adding a new value.
|
||||
@@ -448,23 +452,6 @@ bool Option::SetEnumString(const wxString& value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Option::SetEnumInt(int value) {
|
||||
assert(is_audio_api() || is_sound_quality());
|
||||
int32_t old_value = *nonstd::get<int32_t*>(value_);
|
||||
if (value < nonstd::get<int32_t>(min_) ||
|
||||
value > nonstd::get<int32_t>(max_)) {
|
||||
wxLogWarning(_("Invalid value %d for option %s; valid values are %s"),
|
||||
value, config_name_,
|
||||
internal::AllEnumValuesForType(type_));
|
||||
return false;
|
||||
}
|
||||
*nonstd::get<int32_t*>(value_) = value;
|
||||
if (old_value != value) {
|
||||
CallObservers();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Option::SetGbPalette(const std::array<uint16_t, 8>& value) {
|
||||
assert(is_gb_palette());
|
||||
|
||||
@@ -536,6 +523,12 @@ uint32_t Option::GetUnsignedMax() const {
|
||||
return nonstd::get<uint32_t>(max_);
|
||||
}
|
||||
|
||||
size_t Option::GetEnumMax() const {
|
||||
assert(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());
|
||||
const int old_value = static_cast<int>(GetFilter());
|
||||
@@ -575,7 +568,7 @@ wxString Option::ToHelperString() const {
|
||||
case Option::Type::kInterframe:
|
||||
case Option::Type::kRenderMethod:
|
||||
case Option::Type::kAudioApi:
|
||||
case Option::Type::kSoundQuality:
|
||||
case Option::Type::kAudioRate:
|
||||
helper_string.Append(" (");
|
||||
helper_string.Append(internal::AllEnumValuesForType(type_));
|
||||
helper_string.Append(")");
|
||||
|
@@ -75,8 +75,36 @@ enum class RenderMethod {
|
||||
// Do not add anything under here.
|
||||
kLast,
|
||||
};
|
||||
static constexpr size_t kNbRenderMethods =
|
||||
static_cast<size_t>(RenderMethod::kLast);
|
||||
static constexpr size_t kNbRenderMethods = static_cast<size_t>(RenderMethod::kLast);
|
||||
|
||||
// Values for kAudioApi.
|
||||
enum class AudioApi {
|
||||
kOpenAL,
|
||||
#if defined(__WXMSW__)
|
||||
kDirectSound,
|
||||
#endif // __WXMSW__
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
kXAudio2,
|
||||
#endif // VBAM_ENABLE_XAUDIO2
|
||||
#if defined(VBAM_ENABLE_FAUDIO)
|
||||
kFAudio,
|
||||
#endif // VBAM_ENABLE_FAUDIO
|
||||
|
||||
// Do not add anything under here.
|
||||
kLast,
|
||||
};
|
||||
static constexpr size_t kNbAudioApis = static_cast<size_t>(AudioApi::kLast);
|
||||
|
||||
enum class AudioRate {
|
||||
k48kHz = 0,
|
||||
k44kHz,
|
||||
k22kHz,
|
||||
k11kHz,
|
||||
|
||||
// Do not add anything under here.
|
||||
kLast,
|
||||
};
|
||||
static constexpr size_t kNbSoundRate = static_cast<size_t>(AudioRate::kLast);
|
||||
|
||||
// This is incremented whenever we want to change a default value between
|
||||
// release versions. The option update code is in load_opts.
|
||||
@@ -108,7 +136,7 @@ public:
|
||||
kInterframe,
|
||||
kRenderMethod,
|
||||
kAudioApi,
|
||||
kSoundQuality,
|
||||
kAudioRate,
|
||||
kGbPalette,
|
||||
};
|
||||
|
||||
@@ -165,11 +193,11 @@ public:
|
||||
bool is_interframe() const { return type() == Type::kInterframe; }
|
||||
bool is_render_method() const { return type() == Type::kRenderMethod; }
|
||||
bool is_audio_api() const { return type() == Type::kAudioApi; }
|
||||
bool is_sound_quality() const { return type() == Type::kSoundQuality; }
|
||||
bool is_audio_rate() const { return type() == Type::kAudioRate; }
|
||||
bool is_gb_palette() const { return type() == Type::kGbPalette; }
|
||||
|
||||
// Returns a reference to the stored data. Will assert on type mismatch.
|
||||
// Only enum types can use through GetEnumString().
|
||||
// Only enum types can use GetEnumString().
|
||||
bool GetBool() const;
|
||||
double GetDouble() const;
|
||||
int32_t GetInt() const;
|
||||
@@ -178,6 +206,8 @@ public:
|
||||
Filter GetFilter() const;
|
||||
Interframe GetInterframe() const;
|
||||
RenderMethod GetRenderMethod() const;
|
||||
AudioApi GetAudioApi() const;
|
||||
AudioRate GetAudioRate() const;
|
||||
wxString GetEnumString() const;
|
||||
std::array<uint16_t, 8> GetGbPalette() const;
|
||||
wxString GetGbPaletteString() const;
|
||||
@@ -193,6 +223,8 @@ public:
|
||||
bool SetFilter(const Filter& value);
|
||||
bool SetInterframe(const Interframe& value);
|
||||
bool SetRenderMethod(const RenderMethod& value);
|
||||
bool SetAudioApi(const AudioApi& value);
|
||||
bool SetAudioRate(const AudioRate& value);
|
||||
bool SetEnumString(const wxString& value);
|
||||
bool SetGbPalette(const std::array<uint16_t, 8>& value);
|
||||
bool SetGbPaletteString(const wxString& value);
|
||||
@@ -204,6 +236,7 @@ public:
|
||||
int32_t GetIntMax() const;
|
||||
uint32_t GetUnsignedMin() const;
|
||||
uint32_t GetUnsignedMax() const;
|
||||
size_t GetEnumMax() const;
|
||||
|
||||
// Special convenience modifiers.
|
||||
void NextFilter();
|
||||
@@ -226,12 +259,11 @@ private:
|
||||
Option(OptionID id, Filter* option);
|
||||
Option(OptionID id, Interframe* option);
|
||||
Option(OptionID id, RenderMethod* option);
|
||||
Option(OptionID id, AudioApi* option);
|
||||
Option(OptionID id, AudioRate* option);
|
||||
Option(OptionID id, int* option);
|
||||
Option(OptionID id, uint16_t* option);
|
||||
|
||||
// Helper method for enums not fully converted yet.
|
||||
bool SetEnumInt(int value);
|
||||
|
||||
// Observer.
|
||||
void AddObserver(Observer* observer);
|
||||
void RemoveObserver(Observer* observer);
|
||||
@@ -259,11 +291,16 @@ private:
|
||||
Filter*,
|
||||
Interframe*,
|
||||
RenderMethod*,
|
||||
AudioApi*,
|
||||
AudioRate*,
|
||||
uint16_t*>
|
||||
value_;
|
||||
|
||||
// Technically, `uint64_t` is only needed for 64 bits targets, as `size_t`.
|
||||
// However, `size_t` is the same as `uint32_t` on 32 bits targets, resulting
|
||||
// in a compiler error if we use `size_t` here.
|
||||
const nonstd::variant<nonstd::monostate, double, int32_t, uint32_t> min_;
|
||||
const nonstd::variant<nonstd::monostate, double, int32_t, uint32_t> max_;
|
||||
const nonstd::variant<nonstd::monostate, double, int32_t, uint32_t, uint64_t> max_;
|
||||
};
|
||||
|
||||
} // namespace config
|
||||
|
@@ -87,16 +87,6 @@ int Shortcuts::CommandForInput(const UserInput& input) const {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
std::set<wxJoystick> Shortcuts::Joysticks() const {
|
||||
std::set<wxJoystick> output;
|
||||
for (const auto& iter : command_to_inputs_) {
|
||||
for (const UserInput& user_input : iter.second) {
|
||||
output.insert(user_input.joystick());
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
Shortcuts Shortcuts::Clone() const {
|
||||
return Shortcuts(this->command_to_inputs_, this->input_to_command_, this->disabled_defaults_);
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
#include "wx/config/user-input.h"
|
||||
|
||||
// by default, only 9 recent items
|
||||
// wxWidgets only goes up to `wxID_FILE9` but we want 10 recent files.
|
||||
#define wxID_FILE10 (wxID_FILE9 + 1)
|
||||
|
||||
namespace config {
|
||||
@@ -45,9 +45,6 @@ public:
|
||||
// Returns the command currently assigned to `input` or nullptr if none.
|
||||
int CommandForInput(const UserInput& input) const;
|
||||
|
||||
// Returns the set of joysticks used by shortcuts.
|
||||
std::set<wxJoystick> Joysticks() const;
|
||||
|
||||
// Returns a copy of this object. This can be an expensive operation and
|
||||
// should only be used to modify the currently active shortcuts
|
||||
// configuration.
|
||||
|
@@ -8,7 +8,6 @@
|
||||
#include <wx/translation.h>
|
||||
|
||||
#include "wx/strutils.h"
|
||||
#include "wx/wxutil.h"
|
||||
|
||||
namespace config {
|
||||
|
||||
@@ -202,26 +201,26 @@ UserInput StringToUserInput(const wxString& string) {
|
||||
if (kAxisRegex.Matches(remainder)) {
|
||||
kAxisRegex.GetMatch(&start, &length, 1);
|
||||
const int key = StringToInt(remainder.Mid(start, length));
|
||||
const int mod =
|
||||
remainder[start + length] == '+' ? wxJoyControl::AxisPlus : wxJoyControl::AxisMinus;
|
||||
return UserInput(key, mod, joy);
|
||||
const JoyControl control =
|
||||
remainder[start + length] == '+' ? JoyControl::AxisPlus : JoyControl::AxisMinus;
|
||||
return UserInput(key, control, JoyId(joy - 1));
|
||||
}
|
||||
if (kButtonRegex.Matches(remainder)) {
|
||||
kButtonRegex.GetMatch(&start, &length, 1);
|
||||
const int key = StringToInt(remainder.Mid(start, length));
|
||||
return UserInput(key, wxJoyControl::Button, joy);
|
||||
return UserInput(key, JoyControl::Button, JoyId(joy - 1));
|
||||
}
|
||||
if (kHatRegex.Matches(remainder)) {
|
||||
kHatRegex.GetMatch(&start, &length, 1);
|
||||
const int key = StringToInt(remainder.Mid(start, length));
|
||||
if (kHatRegex.GetMatch(remainder, 3).Length()) {
|
||||
return UserInput(key, wxJoyControl::HatNorth, joy);
|
||||
return UserInput(key, JoyControl::HatNorth, JoyId(joy - 1));
|
||||
} else if (kHatRegex.GetMatch(remainder, 4).Length()) {
|
||||
return UserInput(key, wxJoyControl::HatSouth, joy);
|
||||
return UserInput(key, JoyControl::HatSouth, JoyId(joy - 1));
|
||||
} else if (kHatRegex.GetMatch(remainder, 5).Length()) {
|
||||
return UserInput(key, wxJoyControl::HatEast, joy);
|
||||
return UserInput(key, JoyControl::HatEast, JoyId(joy - 1));
|
||||
} else if (kHatRegex.GetMatch(remainder, 6).Length()) {
|
||||
return UserInput(key, wxJoyControl::HatWest, joy);
|
||||
return UserInput(key, JoyControl::HatWest, JoyId(joy - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,14 +266,47 @@ UserInput StringToUserInput(const wxString& string) {
|
||||
|
||||
} // namespace
|
||||
|
||||
UserInput::UserInput(const wxKeyEvent& event)
|
||||
: UserInput(Device::Keyboard, event.GetModifiers(), getKeyboardKeyCode(event), 0) {}
|
||||
// static
|
||||
JoyId JoyId::Invalid() {
|
||||
static constexpr int kInvalidSdlIndex = -1;
|
||||
return JoyId(kInvalidSdlIndex);
|
||||
}
|
||||
|
||||
UserInput::UserInput(const wxJoyEvent& event)
|
||||
wxString JoyId::ToString() {
|
||||
return wxString::Format("Joy%d", sdl_index_ + 1);
|
||||
}
|
||||
|
||||
bool JoyId::operator==(const JoyId& other) const {
|
||||
return sdl_index_ == other.sdl_index_;
|
||||
}
|
||||
bool JoyId::operator!=(const JoyId& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
bool JoyId::operator<(const JoyId& other) const {
|
||||
return sdl_index_ < other.sdl_index_;
|
||||
}
|
||||
bool JoyId::operator<=(const JoyId& other) const {
|
||||
return !(*this > other);
|
||||
}
|
||||
bool JoyId::operator>(const JoyId& other) const {
|
||||
return other < *this;
|
||||
}
|
||||
bool JoyId::operator>=(const JoyId& other) const {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
JoyId::JoyId(int sdl_index) : sdl_index_(sdl_index) {}
|
||||
|
||||
UserInput::UserInput(uint8_t control_index, JoyControl control, JoyId joystick)
|
||||
: UserInput(Device::Joystick,
|
||||
event.control(),
|
||||
event.control_index(),
|
||||
event.joystick().player_index()) {}
|
||||
static_cast<int>(control),
|
||||
control_index,
|
||||
joystick.sdl_index_ + 1) {}
|
||||
|
||||
UserInput::UserInput(wxKeyCode key, wxKeyModifier mod)
|
||||
: UserInput(Device::Keyboard, mod, key, 0) {}
|
||||
|
||||
UserInput::UserInput(char c, wxKeyModifier mod) : UserInput(Device::Keyboard, mod, c, 0) {}
|
||||
|
||||
// static
|
||||
std::set<UserInput> UserInput::FromConfigString(const wxString& string) {
|
||||
@@ -315,26 +347,26 @@ wxString UserInput::ToConfigString() const {
|
||||
return KeyboardInputToConfigString(mod_, key_);
|
||||
case Device::Joystick:
|
||||
wxString key;
|
||||
switch (mod_) {
|
||||
case wxJoyControl::AxisPlus:
|
||||
switch (static_cast<JoyControl>(mod_)) {
|
||||
case JoyControl::AxisPlus:
|
||||
key = wxString::Format(("Axis%d+"), key_);
|
||||
break;
|
||||
case wxJoyControl::AxisMinus:
|
||||
case JoyControl::AxisMinus:
|
||||
key = wxString::Format(("Axis%d-"), key_);
|
||||
break;
|
||||
case wxJoyControl::Button:
|
||||
case JoyControl::Button:
|
||||
key = wxString::Format(("Button%d"), key_);
|
||||
break;
|
||||
case wxJoyControl::HatNorth:
|
||||
case JoyControl::HatNorth:
|
||||
key = wxString::Format(("Hat%dN"), key_);
|
||||
break;
|
||||
case wxJoyControl::HatSouth:
|
||||
case JoyControl::HatSouth:
|
||||
key = wxString::Format(("Hat%dS"), key_);
|
||||
break;
|
||||
case wxJoyControl::HatWest:
|
||||
case JoyControl::HatWest:
|
||||
key = wxString::Format(("Hat%dW"), key_);
|
||||
break;
|
||||
case wxJoyControl::HatEast:
|
||||
case JoyControl::HatEast:
|
||||
key = wxString::Format(("Hat%dE"), key_);
|
||||
break;
|
||||
}
|
||||
|
@@ -1,14 +1,56 @@
|
||||
#ifndef VBAM_WX_CONFIG_USER_INPUT_H_
|
||||
#define VBAM_WX_CONFIG_USER_INPUT_H_
|
||||
|
||||
#include <wx/event.h>
|
||||
#include <wx/string.h>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
|
||||
#include "wx/widgets/sdljoy.h"
|
||||
#include <wx/event.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
namespace config {
|
||||
|
||||
// Forward declaration.
|
||||
class UserInput;
|
||||
|
||||
// One of the possible joystick controls.
|
||||
enum class JoyControl {
|
||||
AxisPlus = 0,
|
||||
AxisMinus,
|
||||
Button,
|
||||
HatNorth,
|
||||
HatSouth,
|
||||
HatWest,
|
||||
HatEast,
|
||||
Last = HatEast
|
||||
};
|
||||
|
||||
// Abstraction for a single joystick. In the current implementation, this
|
||||
// encapsulates an `sdl_index_`.
|
||||
class JoyId {
|
||||
public:
|
||||
static JoyId Invalid();
|
||||
|
||||
explicit JoyId(int sdl_index);
|
||||
virtual ~JoyId() = default;
|
||||
|
||||
wxString ToString();
|
||||
|
||||
bool operator==(const JoyId& other) const;
|
||||
bool operator!=(const JoyId& other) const;
|
||||
bool operator<(const JoyId& other) const;
|
||||
bool operator<=(const JoyId& other) const;
|
||||
bool operator>(const JoyId& other) const;
|
||||
bool operator>=(const JoyId& other) const;
|
||||
|
||||
private:
|
||||
JoyId() = delete;
|
||||
|
||||
int sdl_index_;
|
||||
|
||||
friend class UserInput;
|
||||
};
|
||||
|
||||
// Abstraction for a user input, which can come from a keyboard or a joystick.
|
||||
// This class implements comparison operators so it can be used in sets and as
|
||||
// a key in maps.
|
||||
@@ -34,14 +76,15 @@ public:
|
||||
// Invalid UserInput, mainly used for comparison.
|
||||
UserInput() : UserInput(Device::Invalid, 0, 0, 0) {}
|
||||
|
||||
// Constructor from a wxKeyEvent.
|
||||
UserInput(const wxKeyEvent& event);
|
||||
// Constructor for a joystick input.
|
||||
UserInput(uint8_t control_index, JoyControl control, JoyId joystick);
|
||||
|
||||
// Constructor from a wxJoyEvent.
|
||||
UserInput(const wxJoyEvent& event);
|
||||
// Constructors for a keyboard input.
|
||||
UserInput(wxKeyCode key, wxKeyModifier mod = wxMOD_NONE);
|
||||
UserInput(char c, wxKeyModifier mod = wxMOD_NONE);
|
||||
|
||||
// TODO: Remove this once all uses have been removed.
|
||||
UserInput(int key, int mod = 0, int joy = 0)
|
||||
explicit UserInput(int key, int mod = 0, int joy = 0)
|
||||
: UserInput(joy == 0 ? Device::Keyboard : Device::Joystick,
|
||||
mod,
|
||||
key,
|
||||
@@ -55,14 +98,27 @@ public:
|
||||
// Converts to a localized string for display.
|
||||
wxString ToLocalizedString() const;
|
||||
|
||||
wxJoystick joystick() const { return joystick_; }
|
||||
JoyId joystick() const { return joystick_; }
|
||||
constexpr bool is_valid() const { return device_ != Device::Invalid; }
|
||||
constexpr operator bool() const { return is_valid(); }
|
||||
|
||||
bool is_joystick() const { return device_ == Device::Joystick; }
|
||||
bool is_keyboard() const { return device_ == Device::Keyboard; }
|
||||
|
||||
int key() const { return key_; }
|
||||
int mod() const { return mod_; }
|
||||
unsigned joy() const { return joy_; }
|
||||
|
||||
JoyControl joy_control() const {
|
||||
assert(is_joystick());
|
||||
return static_cast<JoyControl>(mod_);
|
||||
}
|
||||
|
||||
wxKeyCode key_code() const {
|
||||
assert(is_keyboard());
|
||||
return static_cast<wxKeyCode>(key_);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const UserInput& other) const {
|
||||
return device_ == other.device_ && mod_ == other.mod_ &&
|
||||
key_ == other.key_ && joy_ == other.joy_;
|
||||
@@ -98,14 +154,14 @@ public:
|
||||
private:
|
||||
UserInput(Device device, int mod, int key, unsigned joy)
|
||||
: device_(device),
|
||||
joystick_(joy == 0 ? wxJoystick::Invalid()
|
||||
: wxJoystick::FromLegacyPlayerIndex(joy)),
|
||||
joystick_(joy == 0 ? JoyId::Invalid()
|
||||
: JoyId(joy - 1)),
|
||||
mod_(mod),
|
||||
key_(key),
|
||||
joy_(joy) {}
|
||||
|
||||
Device device_;
|
||||
wxJoystick joystick_;
|
||||
JoyId joystick_;
|
||||
int mod_;
|
||||
int key_;
|
||||
unsigned joy_;
|
||||
|
@@ -5,12 +5,10 @@
|
||||
#include <wx/listbox.h>
|
||||
#include <wx/menu.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
|
||||
#include "wx/config/shortcuts.h"
|
||||
#include "wx/config/user-input.h"
|
||||
#include "wx/dialogs/validated-child.h"
|
||||
#include "wx/opts.h"
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
#include "wx/widgets/user-input-ctrl.h"
|
||||
#include "wx/wxvbam.h"
|
||||
|
||||
@@ -132,21 +130,16 @@ AccelConfig* AccelConfig::NewInstance(wxWindow* parent, wxMenuBar* menu, wxMenu*
|
||||
}
|
||||
|
||||
AccelConfig::AccelConfig(wxWindow* parent, wxMenuBar* menu, wxMenu* recents)
|
||||
: wxDialog(), keep_on_top_styler_(this) {
|
||||
assert(parent);
|
||||
: BaseDialog(parent, "AccelConfig") {
|
||||
assert(menu);
|
||||
|
||||
// Load the dialog XML.
|
||||
const bool success = wxXmlResource::Get()->LoadDialog(this, parent, "AccelConfig");
|
||||
assert(success);
|
||||
|
||||
// Loads the various dialog elements.
|
||||
tree_ = GetValidatedChild<wxTreeCtrl>(this, "Commands");
|
||||
current_keys_ = GetValidatedChild<wxListBox>(this, "Current");
|
||||
assign_button_ = GetValidatedChild(this, "Assign");
|
||||
remove_button_ = GetValidatedChild(this, "Remove");
|
||||
key_input_ = GetValidatedChild<widgets::UserInputCtrl>(this, "Shortcut");
|
||||
currently_assigned_label_ = GetValidatedChild<wxControl>(this, "AlreadyThere");
|
||||
tree_ = GetValidatedChild<wxTreeCtrl>("Commands");
|
||||
current_keys_ = GetValidatedChild<wxListBox>("Current");
|
||||
assign_button_ = GetValidatedChild("Assign");
|
||||
remove_button_ = GetValidatedChild("Remove");
|
||||
key_input_ = GetValidatedChild<widgets::UserInputCtrl>("Shortcut");
|
||||
currently_assigned_label_ = GetValidatedChild<wxControl>("AlreadyThere");
|
||||
|
||||
// Configure the key input.
|
||||
key_input_->MoveBeforeInTabOrder(assign_button_);
|
||||
|
@@ -3,11 +3,10 @@
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/treectrl.h>
|
||||
|
||||
#include "wx/config/shortcuts.h"
|
||||
#include "wx/widgets/keep-on-top-styler.h"
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
|
||||
// Forward declarations.
|
||||
class wxControl;
|
||||
@@ -23,7 +22,7 @@ class UserInputCtrl;
|
||||
namespace dialogs {
|
||||
|
||||
// Manages the shortcuts editor dialog.
|
||||
class AccelConfig : public wxDialog {
|
||||
class AccelConfig : public BaseDialog {
|
||||
public:
|
||||
static AccelConfig* NewInstance(wxWindow* parent, wxMenuBar* menu_bar, wxMenu* recents);
|
||||
|
||||
@@ -74,8 +73,6 @@ private:
|
||||
|
||||
config::Shortcuts config_shortcuts_;
|
||||
int selected_command_ = 0;
|
||||
|
||||
const widgets::KeepOnTopStyler keep_on_top_styler_;
|
||||
};
|
||||
|
||||
} // namespace dialogs
|
||||
|
137
src/wx/dialogs/base-dialog.cpp
Normal file
137
src/wx/dialogs/base-dialog.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <wx/persist.h>
|
||||
#include <wx/persist/toplevel.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
|
||||
#include "wx/config/option-proxy.h"
|
||||
#include "wx/widgets/utils.h"
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
namespace {
|
||||
|
||||
// Due to the way wxWidgets handles initialization, we need to use a custom
|
||||
// persistent object to save and restore the dialog's position. In particular,
|
||||
// when the dialog is constructed, the the main window is not (yet) in its final
|
||||
// position. This means we need to differentiate between 2 states here:
|
||||
// 1. The dialog was registered for the first time and never shown.
|
||||
// We don't want to save the position in this case.
|
||||
// 2. The dialog was registered and shown at least once.
|
||||
// We want to save the position in this case. However, on restore, we need
|
||||
// to check we are not drawing out of bounds.
|
||||
//
|
||||
// We can't use the built-in wxPersistenceManager for this, as it doesn't allow
|
||||
// us to differentiate between these 2 states.
|
||||
class PersistentBaseDialog : public wxPersistentWindow<BaseDialog> {
|
||||
public:
|
||||
PersistentBaseDialog(BaseDialog* window) : wxPersistentWindow<BaseDialog>(window) {}
|
||||
|
||||
private:
|
||||
// wxPersistentObject implementation.
|
||||
wxString GetKind() const override { return "Dialog"; }
|
||||
|
||||
void Save() const override {
|
||||
if (!dialog_shown_) {
|
||||
// Do not update the position if the dialog was not shown.
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool Restore() override {
|
||||
dialog_shown_ = true;
|
||||
wxRect dialog_rect(0, 0, 0, 0);
|
||||
if (!this->RestoreValue("x", &dialog_rect.x)) {
|
||||
return false;
|
||||
};
|
||||
if (!this->RestoreValue("y", &dialog_rect.y)) {
|
||||
return false;
|
||||
};
|
||||
if (!this->RestoreValue("width", &dialog_rect.width)) {
|
||||
return false;
|
||||
};
|
||||
if (!this->RestoreValue("height", &dialog_rect.height)) {
|
||||
return false;
|
||||
};
|
||||
|
||||
this->Get()->SetSize(dialog_rect);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dialog_shown_ = false;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
wxDialog* BaseDialog::LoadDialog(wxWindow* parent, const wxString& xrc_file) {
|
||||
assert(parent);
|
||||
return new BaseDialog(parent, xrc_file);
|
||||
}
|
||||
|
||||
BaseDialog::BaseDialog(wxWindow* parent, const wxString& xrc_file)
|
||||
: wxDialog(), keep_on_top_styler_(this) {
|
||||
#if !wxCHECK_VERSION(3, 1, 0)
|
||||
// This needs to be set before loading any element on the window. This also
|
||||
// has no effect since wx 3.1.0, where it became the default.
|
||||
this->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
|
||||
#endif
|
||||
|
||||
[[maybe_unused]] const bool success = wxXmlResource::Get()->LoadDialog(this, parent, xrc_file);
|
||||
assert(success);
|
||||
|
||||
// Bind the event handler.
|
||||
this->Bind(wxEVT_SHOW, &BaseDialog::OnBaseDialogShow, this);
|
||||
|
||||
wxPersistenceManager::Get().Register(this, new PersistentBaseDialog(this));
|
||||
}
|
||||
|
||||
wxWindow* BaseDialog::GetValidatedChild(const wxString& name) const {
|
||||
wxWindow* window = this->FindWindow(name);
|
||||
assert(window);
|
||||
return window;
|
||||
}
|
||||
|
||||
void BaseDialog::OnBaseDialogShow(wxShowEvent& event) {
|
||||
if (event.IsShown()) {
|
||||
// Restore the dialog saved position.
|
||||
if (wxPersistenceManager::Get().Restore(this)) {
|
||||
// Ensure we are not restoring the dialog out of bounds.
|
||||
if (!widgets::GetDisplayRect().Intersects(this->GetRect())) {
|
||||
this->RepositionDialog();
|
||||
}
|
||||
} else {
|
||||
// First-time use.
|
||||
this->RepositionDialog();
|
||||
}
|
||||
|
||||
// Do not run this again.
|
||||
this->Unbind(wxEVT_SHOW, &BaseDialog::OnBaseDialogShow, this);
|
||||
}
|
||||
|
||||
// Let the event propagate.
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void BaseDialog::RepositionDialog() {
|
||||
// Re-position the dialog slightly to the bottom-right of the parent.
|
||||
const wxWindow* parent = this->GetParent();
|
||||
wxPoint parent_pos;
|
||||
if (parent) {
|
||||
parent_pos = parent->GetPosition();
|
||||
} else {
|
||||
parent_pos = wxPoint(OPTION(kGeomWindowX), OPTION(kGeomWindowY));
|
||||
}
|
||||
const wxPoint dialog_pos = parent_pos + wxPoint(40, 40);
|
||||
this->SetPosition(dialog_pos);
|
||||
}
|
||||
|
||||
} // namespace dialogs
|
42
src/wx/dialogs/base-dialog.h
Normal file
42
src/wx/dialogs/base-dialog.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef VBAM_WX_DIALOGS_BASE_DIALOG_H_
|
||||
#define VBAM_WX_DIALOGS_BASE_DIALOG_H_
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/event.h>
|
||||
|
||||
#include "wx/widgets/keep-on-top-styler.h"
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
class BaseDialog : public wxDialog {
|
||||
public:
|
||||
static wxDialog* LoadDialog(wxWindow* parent, const wxString& xrc_file);
|
||||
|
||||
~BaseDialog() override = default;
|
||||
|
||||
protected:
|
||||
BaseDialog(wxWindow* parent, const wxString& xrc_file);
|
||||
|
||||
// Helper function to assert on the returned value.
|
||||
wxWindow* GetValidatedChild(const wxString& name) const;
|
||||
|
||||
template <class T>
|
||||
T* GetValidatedChild(const wxString& name) const {
|
||||
T* child = wxDynamicCast(this->GetValidatedChild(name), T);
|
||||
assert(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
private:
|
||||
// Handler for the wxEVT_SHOW event.
|
||||
void OnBaseDialogShow(wxShowEvent& event);
|
||||
|
||||
// Repositions the dialog to the bottom-right of the parent.
|
||||
void RepositionDialog();
|
||||
|
||||
const widgets::KeepOnTopStyler keep_on_top_styler_;
|
||||
};
|
||||
|
||||
} // namespace dialogs
|
||||
|
||||
#endif // VBAM_WX_DIALOGS_BASE_DIALOG_H_
|
@@ -2,9 +2,7 @@
|
||||
|
||||
#include <wx/filepicker.h>
|
||||
|
||||
#include <wx/xrc/xmlres.h>
|
||||
|
||||
#include "wx/dialogs/validated-child.h"
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
#include "wx/widgets/option-validator.h"
|
||||
|
||||
namespace dialogs {
|
||||
@@ -58,29 +56,23 @@ DirectoriesConfig* DirectoriesConfig::NewInstance(wxWindow* parent) {
|
||||
return new DirectoriesConfig(parent);
|
||||
}
|
||||
|
||||
DirectoriesConfig::DirectoriesConfig(wxWindow* parent)
|
||||
: wxDialog(), keep_on_top_styler_(this) {
|
||||
#if !wxCHECK_VERSION(3, 1, 0)
|
||||
// This needs to be set before loading any element on the window. This also
|
||||
// has no effect since wx 3.1.0, where it became the default.
|
||||
this->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
|
||||
#endif
|
||||
wxXmlResource::Get()->LoadDialog(this, parent, "DirectoriesConfig");
|
||||
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "GBARoms"),
|
||||
DirectoriesConfig::DirectoriesConfig(wxWindow* parent) : BaseDialog(parent, "DirectoriesConfig") {
|
||||
// clang-format off
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("GBARoms"),
|
||||
config::OptionID::kGBAROMDir);
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "GBRoms"),
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("GBRoms"),
|
||||
config::OptionID::kGBROMDir);
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "GBCRoms"),
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("GBCRoms"),
|
||||
config::OptionID::kGBGBCROMDir);
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "BatSaves"),
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("BatSaves"),
|
||||
config::OptionID::kGenBatteryDir);
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "StateSaves"),
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("StateSaves"),
|
||||
config::OptionID::kGenStateDir);
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "Screenshots"),
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("Screenshots"),
|
||||
config::OptionID::kGenScreenshotDir);
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "Recordings"),
|
||||
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("Recordings"),
|
||||
config::OptionID::kGenRecordingDir);
|
||||
// clang-format on
|
||||
|
||||
this->Fit();
|
||||
}
|
||||
|
@@ -1,14 +1,12 @@
|
||||
#ifndef VBAM_WX_DIALOGS_DIRECTORIES_CONFIG_H_
|
||||
#define VBAM_WX_DIALOGS_DIRECTORIES_CONFIG_H_
|
||||
|
||||
#include <wx/dialog.h>
|
||||
|
||||
#include "wx/widgets/keep-on-top-styler.h"
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
// Manages the directories configuration dialog.
|
||||
class DirectoriesConfig : public wxDialog {
|
||||
class DirectoriesConfig : public BaseDialog {
|
||||
public:
|
||||
static DirectoriesConfig* NewInstance(wxWindow* parent);
|
||||
~DirectoriesConfig() override = default;
|
||||
@@ -18,8 +16,6 @@ private:
|
||||
// static method. This is because this class is destroyed when its
|
||||
// owner, `parent` is destroyed. This prevents accidental deletion.
|
||||
DirectoriesConfig(wxWindow* parent);
|
||||
|
||||
const widgets::KeepOnTopStyler keep_on_top_styler_;
|
||||
};
|
||||
|
||||
} // namespace dialogs
|
||||
|
@@ -16,7 +16,7 @@
|
||||
#include "wx/config/option-id.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
#include "wx/config/option.h"
|
||||
#include "wx/dialogs/validated-child.h"
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
#include "wx/rpi.h"
|
||||
#include "wx/widgets/option-validator.h"
|
||||
#include "wx/widgets/render-plugin.h"
|
||||
@@ -98,7 +98,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(selection) > config::kNbFilters) {
|
||||
if (static_cast<size_t>(selection) >= option()->GetEnumMax()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(selection) > config::kNbInterframes) {
|
||||
if (static_cast<size_t>(selection) >= option()->GetEnumMax()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ DisplayConfig* DisplayConfig::NewInstance(wxWindow* parent) {
|
||||
}
|
||||
|
||||
DisplayConfig::DisplayConfig(wxWindow* parent)
|
||||
: wxDialog(),
|
||||
: BaseDialog(parent, "DisplayConfig"),
|
||||
filter_observer_(config::OptionID::kDispFilter,
|
||||
std::bind(&DisplayConfig::OnFilterChanged,
|
||||
this,
|
||||
@@ -233,77 +233,69 @@ DisplayConfig::DisplayConfig(wxWindow* parent)
|
||||
interframe_observer_(config::OptionID::kDispIFB,
|
||||
std::bind(&DisplayConfig::OnInterframeChanged,
|
||||
this,
|
||||
std::placeholders::_1)),
|
||||
keep_on_top_styler_(this) {
|
||||
#if !wxCHECK_VERSION(3, 1, 0)
|
||||
// This needs to be set before loading any element on the window. This also
|
||||
// has no effect since wx 3.1.0, where it became the default.
|
||||
this->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
|
||||
#endif
|
||||
wxXmlResource::Get()->LoadDialog(this, parent, "DisplayConfig");
|
||||
|
||||
std::placeholders::_1)) {
|
||||
// Speed
|
||||
GetValidatedChild(this, "FrameSkip")
|
||||
GetValidatedChild("FrameSkip")
|
||||
->SetValidator(
|
||||
widgets::OptionSpinCtrlValidator(config::OptionID::kPrefFrameSkip));
|
||||
widgets::OptionIntValidator(config::OptionID::kPrefFrameSkip));
|
||||
|
||||
// On-Screen Display
|
||||
GetValidatedChild(this, "SpeedIndicator")
|
||||
GetValidatedChild("SpeedIndicator")
|
||||
->SetValidator(
|
||||
widgets::OptionChoiceValidator(config::OptionID::kPrefShowSpeed));
|
||||
|
||||
// Zoom
|
||||
GetValidatedChild(this, "DefaultScale")->SetValidator(ScaleValidator());
|
||||
GetValidatedChild("DefaultScale")->SetValidator(ScaleValidator());
|
||||
|
||||
// this was a choice, but I'd rather not have to make an off-by-one
|
||||
// validator just for this, and spinctrl is good enough.
|
||||
GetValidatedChild(this, "MaxScale")
|
||||
GetValidatedChild("MaxScale")
|
||||
->SetValidator(wxGenericValidator(&gopts.max_scale));
|
||||
|
||||
// Basic
|
||||
GetValidatedChild(this, "OutputSimple")
|
||||
GetValidatedChild("OutputSimple")
|
||||
->SetValidator(RenderValidator(config::RenderMethod::kSimple));
|
||||
|
||||
#if defined(__WXMAC__)
|
||||
GetValidatedChild(this, "OutputQuartz2D")
|
||||
GetValidatedChild("OutputQuartz2D")
|
||||
->SetValidator(RenderValidator(config::RenderMethod::kQuartz2d));
|
||||
#else
|
||||
GetValidatedChild(this, "OutputQuartz2D")->Hide();
|
||||
GetValidatedChild("OutputQuartz2D")->Hide();
|
||||
#endif
|
||||
|
||||
#ifdef NO_OGL
|
||||
GetValidatedChild(this, "OutputOpenGL")->Hide();
|
||||
GetValidatedChild("OutputOpenGL")->Hide();
|
||||
#elif defined(HAVE_WAYLAND_SUPPORT) && !defined(HAVE_WAYLAND_EGL)
|
||||
// wxGLCanvas segfaults on Wayland before wx 3.2.
|
||||
if (IsWayland()) {
|
||||
GetValidatedChild(this, "OutputOpenGL")->Hide();
|
||||
GetValidatedChild("OutputOpenGL")->Hide();
|
||||
} else {
|
||||
GetValidatedChild(this, "OutputOpenGL")
|
||||
GetValidatedChild("OutputOpenGL")
|
||||
->SetValidator(RenderValidator(config::RenderMethod::kOpenGL));
|
||||
}
|
||||
#else
|
||||
GetValidatedChild(this, "OutputOpenGL")
|
||||
GetValidatedChild("OutputOpenGL")
|
||||
->SetValidator(RenderValidator(config::RenderMethod::kOpenGL));
|
||||
#endif // NO_OGL
|
||||
|
||||
#if defined(__WXMSW__) && !defined(NO_D3D)
|
||||
// Enable the Direct3D option on Windows.
|
||||
GetValidatedChild(this, "OutputDirect3D")
|
||||
GetValidatedChild("OutputDirect3D")
|
||||
->SetValidator(RenderValidator(config::RenderMethod::kDirect3d));
|
||||
#else
|
||||
GetValidatedChild(this, "OutputDirect3D")->Hide();
|
||||
GetValidatedChild("OutputDirect3D")->Hide();
|
||||
#endif
|
||||
|
||||
filter_selector_ = GetValidatedChild<wxChoice>(this, "Filter");
|
||||
filter_selector_ = GetValidatedChild<wxChoice>("Filter");
|
||||
filter_selector_->SetValidator(FilterValidator());
|
||||
filter_selector_->Bind(wxEVT_CHOICE, &DisplayConfig::UpdatePlugin, this,
|
||||
GetId());
|
||||
|
||||
// These are filled and/or hidden at dialog load time.
|
||||
plugin_label_ = GetValidatedChild<wxControl>(this, "PluginLab");
|
||||
plugin_selector_ = GetValidatedChild<wxChoice>(this, "Plugin");
|
||||
plugin_label_ = GetValidatedChild<wxControl>("PluginLab");
|
||||
plugin_selector_ = GetValidatedChild<wxChoice>("Plugin");
|
||||
|
||||
interframe_selector_ = GetValidatedChild<wxChoice>(this, "IFB");
|
||||
interframe_selector_ = GetValidatedChild<wxChoice>("IFB");
|
||||
interframe_selector_->SetValidator(InterframeValidator());
|
||||
|
||||
Bind(wxEVT_SHOW, &DisplayConfig::OnDialogShowEvent, this, GetId());
|
||||
|
@@ -1,11 +1,8 @@
|
||||
#ifndef VBAM_WX_DIALOGS_DISPLAY_CONFIG_H_
|
||||
#define VBAM_WX_DIALOGS_DISPLAY_CONFIG_H_
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/event.h>
|
||||
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
#include "wx/config/option-observer.h"
|
||||
#include "wx/widgets/keep-on-top-styler.h"
|
||||
|
||||
// Forward declarations.
|
||||
class wxChoice;
|
||||
@@ -19,7 +16,7 @@ class Option;
|
||||
namespace dialogs {
|
||||
|
||||
// Manages the display configuration dialog.
|
||||
class DisplayConfig : public wxDialog {
|
||||
class DisplayConfig : public BaseDialog {
|
||||
public:
|
||||
static DisplayConfig* NewInstance(wxWindow* parent);
|
||||
~DisplayConfig() override = default;
|
||||
@@ -58,7 +55,6 @@ private:
|
||||
wxChoice* interframe_selector_;
|
||||
const config::OptionsObserver filter_observer_;
|
||||
const config::OptionsObserver interframe_observer_;
|
||||
const widgets::KeepOnTopStyler keep_on_top_styler_;
|
||||
};
|
||||
|
||||
} // namespace dialogs
|
||||
|
@@ -15,8 +15,9 @@
|
||||
|
||||
#include "wx/config/option-observer.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
#include "wx/dialogs/validated-child.h"
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
#include "wx/widgets/option-validator.h"
|
||||
#include "wx/widgets/utils.h"
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
@@ -221,23 +222,22 @@ private:
|
||||
|
||||
GBPalettePanelData::GBPalettePanelData(wxPanel* panel, size_t palette_id)
|
||||
: wxClientData(),
|
||||
default_selector_(GetValidatedChild<wxChoice>(panel, "DefaultPalette")),
|
||||
option_id_(static_cast<config::OptionID>(
|
||||
static_cast<size_t>(config::OptionID::kGBPalette0) + 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);
|
||||
|
||||
default_selector_->Bind(
|
||||
wxEVT_CHOICE, &GBPalettePanelData::OnDefaultPaletteSelected, this);
|
||||
|
||||
GetValidatedChild<wxCheckBox>(panel, "UsePalette")
|
||||
->SetValidator(widgets::OptionSelectedValidator(
|
||||
config::OptionID::kPrefGBPaletteOption, palette_id));
|
||||
widgets::GetValidatedChild<wxCheckBox>(panel, "UsePalette")
|
||||
->SetValidator(
|
||||
widgets::OptionSelectedValidator(config::OptionID::kPrefGBPaletteOption, palette_id));
|
||||
|
||||
for (size_t i = 0; i < colour_pickers_.size(); i++) {
|
||||
wxColourPickerCtrl* colour_picker =
|
||||
GetValidatedChild<wxColourPickerCtrl>(
|
||||
panel, wxString::Format("Color%zu", i));
|
||||
widgets::GetValidatedChild<wxColourPickerCtrl>(panel, wxString::Format("Color%zu", i));
|
||||
colour_pickers_[i] = colour_picker;
|
||||
|
||||
// Update the internal palette reference on colour change.
|
||||
@@ -247,7 +247,7 @@ GBPalettePanelData::GBPalettePanelData(wxPanel* panel, size_t palette_id)
|
||||
colour_picker->GetId());
|
||||
}
|
||||
|
||||
GetValidatedChild(panel, "Reset")
|
||||
widgets::GetValidatedChild(panel, "Reset")
|
||||
->Bind(wxEVT_BUTTON, &GBPalettePanelData::OnPaletteReset, this);
|
||||
}
|
||||
|
||||
@@ -317,39 +317,27 @@ GameBoyConfig* GameBoyConfig::NewInstance(wxWindow* parent) {
|
||||
return new GameBoyConfig(parent);
|
||||
}
|
||||
|
||||
GameBoyConfig::GameBoyConfig(wxWindow* parent)
|
||||
: wxDialog(), keep_on_top_styler_(this) {
|
||||
#if !wxCHECK_VERSION(3, 1, 0)
|
||||
// This needs to be set before loading any element on the window. This also
|
||||
// has no effect since wx 3.1.0, where it became the default.
|
||||
this->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
|
||||
#endif
|
||||
wxXmlResource::Get()->LoadDialog(this, parent, "GameBoyConfig");
|
||||
|
||||
GameBoyConfig::GameBoyConfig(wxWindow* parent) : BaseDialog(parent, "GameBoyConfig") {
|
||||
// System and Peripherals.
|
||||
GetValidatedChild(this, "System")
|
||||
->SetValidator(widgets::OptionChoiceValidator(
|
||||
config::OptionID::kPrefEmulatorType));
|
||||
GetValidatedChild("System")->SetValidator(
|
||||
widgets::OptionChoiceValidator(config::OptionID::kPrefEmulatorType));
|
||||
|
||||
// "Display borders" corresponds to 2 variables.
|
||||
GetValidatedChild(this, "Borders")->SetValidator(BorderSelectorValidator());
|
||||
GetValidatedChild("Borders")->SetValidator(BorderSelectorValidator());
|
||||
|
||||
// GB BIOS ROM
|
||||
GetValidatedChild(this, "GBBiosPicker")
|
||||
->SetValidator(BIOSPickerValidator(
|
||||
config::OptionID::kGBBiosFile,
|
||||
GetValidatedChild<wxStaticText>(this, "GBBiosLabel")));
|
||||
GetValidatedChild("GBBiosPicker")
|
||||
->SetValidator(BIOSPickerValidator(config::OptionID::kGBBiosFile,
|
||||
GetValidatedChild<wxStaticText>("GBBiosLabel")));
|
||||
|
||||
// GBC BIOS ROM
|
||||
GetValidatedChild(this, "GBCBiosPicker")
|
||||
->SetValidator(BIOSPickerValidator(
|
||||
config::OptionID::kGBGBCBiosFile,
|
||||
GetValidatedChild<wxStaticText>(this, "GBCBiosLabel")));
|
||||
GetValidatedChild("GBCBiosPicker")
|
||||
->SetValidator(BIOSPickerValidator(config::OptionID::kGBGBCBiosFile,
|
||||
GetValidatedChild<wxStaticText>("GBCBiosLabel")));
|
||||
|
||||
for (size_t i = 0; i < kNbPalettes; i++) {
|
||||
// All of the wxPanel logic is handled in its client object.
|
||||
wxPanel* panel =
|
||||
GetValidatedChild<wxPanel>(this, wxString::Format("cp%zu", i));
|
||||
wxPanel* panel = GetValidatedChild<wxPanel>(wxString::Format("cp%zu", i));
|
||||
GBPalettePanelData* palette_data = new GBPalettePanelData(panel, i);
|
||||
|
||||
// `panel` takes ownership of `palette_data` here.
|
||||
|
@@ -2,14 +2,13 @@
|
||||
#define VBAM_WX_DIALOGS_GAME_BOY_CONFIG_H_
|
||||
|
||||
#include <wx/clrpicker.h>
|
||||
#include <wx/dialog.h>
|
||||
|
||||
#include "wx/widgets/keep-on-top-styler.h"
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
// Manages the Game Boy configuration dialog.
|
||||
class GameBoyConfig : public wxDialog {
|
||||
class GameBoyConfig : public BaseDialog {
|
||||
public:
|
||||
static GameBoyConfig* NewInstance(wxWindow* parent);
|
||||
~GameBoyConfig() override = default;
|
||||
@@ -19,8 +18,6 @@ private:
|
||||
// static method. This is because this class is destroyed when its
|
||||
// owner, `parent` is destroyed. This prevents accidental deletion.
|
||||
GameBoyConfig(wxWindow* parent);
|
||||
|
||||
const widgets::KeepOnTopStyler keep_on_top_styler_;
|
||||
};
|
||||
|
||||
} // namespace dialogs
|
||||
|
@@ -5,8 +5,8 @@
|
||||
|
||||
#include "core/base/sizes.h"
|
||||
#include "core/gb/gb.h"
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
#include "wx/dialogs/game-maker.h"
|
||||
#include "wx/dialogs/validated-child.h"
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
@@ -171,14 +171,7 @@ GbRomInfo* GbRomInfo::NewInstance(wxWindow* parent) {
|
||||
return new GbRomInfo(parent);
|
||||
}
|
||||
|
||||
GbRomInfo::GbRomInfo(wxWindow* parent) : wxDialog(), keep_on_top_styler_(this) {
|
||||
#if !wxCHECK_VERSION(3, 1, 0)
|
||||
// This needs to be set before loading any element on the window. This also
|
||||
// has no effect since wx 3.1.0, where it became the default.
|
||||
this->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
|
||||
#endif
|
||||
wxXmlResource::Get()->LoadDialog(this, parent, "GBROMInfo");
|
||||
|
||||
GbRomInfo::GbRomInfo(wxWindow* parent) : BaseDialog(parent, "GBROMInfo") {
|
||||
Bind(wxEVT_SHOW, &GbRomInfo::OnDialogShowEvent, this);
|
||||
}
|
||||
|
||||
@@ -191,23 +184,23 @@ void GbRomInfo::OnDialogShowEvent(wxShowEvent& event) {
|
||||
}
|
||||
|
||||
// Populate the dialog.
|
||||
GetValidatedChild(this, "Title")->SetLabel(g_gbCartData.title());
|
||||
GetValidatedChild(this, "MakerCode")->SetLabel(g_gbCartData.maker_code());
|
||||
GetValidatedChild(this, "MakerName")->SetLabel(GetGameMakerName(g_gbCartData.maker_code()));
|
||||
GetValidatedChild(this, "CartridgeType")->SetLabel(GetCartType());
|
||||
GetValidatedChild(this, "SGBCode")->SetLabel(GetCartSGBFlag());
|
||||
GetValidatedChild(this, "CGBCode")->SetLabel(GetCartCGBFlag());
|
||||
GetValidatedChild(this, "ROMSize")->SetLabel(GetCartRomSize());
|
||||
GetValidatedChild(this, "RAMSize")->SetLabel(GetCartRamSize());
|
||||
GetValidatedChild(this, "DestCode")->SetLabel(GetCartDestinationCode());
|
||||
GetValidatedChild(this, "LicCode")
|
||||
GetValidatedChild("Title")->SetLabel(g_gbCartData.title());
|
||||
GetValidatedChild("MakerCode")->SetLabel(g_gbCartData.maker_code());
|
||||
GetValidatedChild("MakerName")->SetLabel(GetGameMakerName(g_gbCartData.maker_code()));
|
||||
GetValidatedChild("CartridgeType")->SetLabel(GetCartType());
|
||||
GetValidatedChild("SGBCode")->SetLabel(GetCartSGBFlag());
|
||||
GetValidatedChild("CGBCode")->SetLabel(GetCartCGBFlag());
|
||||
GetValidatedChild("ROMSize")->SetLabel(GetCartRomSize());
|
||||
GetValidatedChild("RAMSize")->SetLabel(GetCartRamSize());
|
||||
GetValidatedChild("DestCode")->SetLabel(GetCartDestinationCode());
|
||||
GetValidatedChild("LicCode")
|
||||
->SetLabel(wxString::Format("%02X", g_gbCartData.old_licensee_code()));
|
||||
GetValidatedChild(this, "Version")
|
||||
GetValidatedChild("Version")
|
||||
->SetLabel(wxString::Format("%02X", g_gbCartData.version_flag()));
|
||||
GetValidatedChild(this, "HeaderChecksum")
|
||||
GetValidatedChild("HeaderChecksum")
|
||||
->SetLabel(wxString::Format(_("%02X (Actual: %02X)"), g_gbCartData.header_checksum(),
|
||||
g_gbCartData.actual_header_checksum()));
|
||||
GetValidatedChild(this, "CartridgeChecksum")
|
||||
GetValidatedChild("CartridgeChecksum")
|
||||
->SetLabel(wxString::Format(_("%04X (Actual: %04X)"), g_gbCartData.global_checksum(),
|
||||
g_gbCartData.actual_global_checksum()));
|
||||
|
||||
|
@@ -1,13 +1,11 @@
|
||||
#ifndef VBAM_WX_DIALOGS_GB_ROM_INFO_H_
|
||||
#define VBAM_WX_DIALOGS_GB_ROM_INFO_H_
|
||||
|
||||
#include <wx/dialog.h>
|
||||
|
||||
#include "wx/widgets/keep-on-top-styler.h"
|
||||
#include "wx/dialogs/base-dialog.h"
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
class GbRomInfo : public wxDialog {
|
||||
class GbRomInfo : public BaseDialog {
|
||||
public:
|
||||
static GbRomInfo* NewInstance(wxWindow* parent);
|
||||
~GbRomInfo() override = default;
|
||||
@@ -20,8 +18,6 @@ private:
|
||||
|
||||
// Handler for the wxEVT_SHOW event.
|
||||
void OnDialogShowEvent(wxShowEvent& event);
|
||||
|
||||
const widgets::KeepOnTopStyler keep_on_top_styler_;
|
||||
};
|
||||
|
||||
} // namespace dialogs
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user