From f975809e3c9df6c581ab3fd865278b955c1a1484 Mon Sep 17 00:00:00 2001 From: Andy Vandijck Date: Sun, 4 May 2025 11:39:45 +0200 Subject: [PATCH] Update to SDL3 APIs, fix bpps, add SDL audio/video, add Metal video, fix Quartz drawing Implement SDL3 for the SDL port if the CMake definition ENABLE_SDL3 is set. Implement setting the color depth for both wxWidgets and the SDL port. Fix the coloring issues in the SDL port for OpenGL. Implement 8 bit color support. Update and fix the wx port SDL sound driver. Implement SDL video in the wx port. Silence GL deprecations on macOS. Add SDL renderer selection. Fix filter for bit depths other than 32 bit. Fix GDK backend for Linux. Add Metal renderer for macOS. Fix Quartz drawing. --- CMakeLists.txt | 10 + cmake/CMakeDetermineMetalCompiler.cmake | 85 ++ cmake/CMakeMetalCompiler.cmake.in | 27 + cmake/CMakeMetalInformation.cmake | 85 ++ cmake/CMakeTestMetalCompiler.cmake | 78 ++ cmake/CheckLanguage.cmake | 169 +++ cmake/Dependencies.cmake | 38 +- cmake/MetalShaderSupport.cmake | 53 + cmake/Options.cmake | 15 + cmake/Toolchain-msvc.cmake | 2 + installdeps | 2 +- po/wxvbam/bg.po | 8 + po/wxvbam/br.po | 8 + po/wxvbam/cs.po | 8 + po/wxvbam/de.po | 8 + po/wxvbam/el.po | 8 + po/wxvbam/en_US.po | 8 + po/wxvbam/es.po | 8 + po/wxvbam/es_419.po | 8 + po/wxvbam/es_CO.po | 8 + po/wxvbam/es_PE.po | 8 + po/wxvbam/es_US.po | 8 + po/wxvbam/fr.po | 8 + po/wxvbam/fr_FR.po | 8 + po/wxvbam/gl.po | 8 + po/wxvbam/he_IL.po | 8 + po/wxvbam/hu.po | 8 + po/wxvbam/hu_HU.po | 8 + po/wxvbam/id.po | 8 + po/wxvbam/it_IT.po | 8 + po/wxvbam/ja.po | 8 + po/wxvbam/ja_JP.po | 8 + po/wxvbam/ko.po | 8 + po/wxvbam/ko_KR.po | 8 + po/wxvbam/ms_MY.po | 8 + po/wxvbam/nb.po | 8 + po/wxvbam/nl.po | 8 + po/wxvbam/pl.po | 8 + po/wxvbam/pl_PL.po | 8 + po/wxvbam/pt_BR.po | 8 + po/wxvbam/pt_PT.po | 8 + po/wxvbam/ru_RU.po | 8 + po/wxvbam/sv.po | 8 + po/wxvbam/tr.po | 8 + po/wxvbam/uk.po | 8 + po/wxvbam/ur_PK.po | 8 + po/wxvbam/zh_CN.po | 8 + src/components/av_recording/av_recording.cpp | 6 + src/components/filters/filters.h | 6 +- src/components/filters_agb/filters_agb.cpp | 17 + .../filters_interframe/interframe.cpp | 167 +++ .../filters_interframe/interframe.h | 8 + src/core/base/image_util.cpp | 57 + src/core/base/image_util.h | 4 +- src/core/base/internal/memgzio.c | 6 +- src/core/base/system.h | 1 + src/core/gb/gb.cpp | 35 + src/core/gb/gbDis.cpp | 10 +- src/core/gb/gbDis.h | 2 +- src/core/gb/gbSGB.cpp | 25 +- src/core/gba/gba.cpp | 32 + src/core/gba/gbaCheats.cpp | 16 +- src/core/gba/gbaLink.cpp | 16 +- src/core/gba/gbaRemote.cpp | 492 +++---- src/core/test/fake_core.cpp | 3 +- src/libretro/Makefile | 8 +- src/libretro/libretro.cpp | 12 +- src/libretro/link.T | 5 + src/sdl/CMakeLists.txt | 22 +- src/sdl/ConfigManager.cpp | 35 +- src/sdl/SDL.cpp | 823 ++++++++--- src/sdl/audio_sdl.cpp | 89 +- src/sdl/audio_sdl.h | 25 +- src/sdl/debugger.cpp | 14 +- src/sdl/dictionary.c | 10 +- src/sdl/filters.cpp | 69 +- src/sdl/iniparser.c | 14 +- src/sdl/inputSDL.cpp | 110 +- src/sdl/inputSDL.h | 6 +- src/wx/AAPLShaderTypes.h | 42 + src/wx/AAPLShaders.metal | 76 ++ src/wx/CMakeLists.txt | 56 +- src/wx/audio/audio.cpp | 7 + src/wx/audio/internal/openal.cpp | 2 +- src/wx/audio/internal/sdl.cpp | 381 ++++++ src/wx/audio/internal/sdl.h | 18 + src/wx/cmdevents.cpp | 8 + src/wx/config/internal/option-internal.cpp | 19 +- src/wx/config/option-id.h | 2 + src/wx/config/option-proxy.h | 2 + src/wx/config/option-test.cpp | 3 + src/wx/config/option.h | 5 + src/wx/dialogs/display-config.cpp | 126 +- src/wx/dialogs/display-config.h | 4 + src/wx/dialogs/sound-config.cpp | 8 +- src/wx/drawing.h | 95 ++ src/wx/gfxviewers.cpp | 33 +- src/wx/macsupport.mm | 422 +++++- src/wx/panel.cpp | 1203 ++++++++++++++--- src/wx/sys.cpp | 6 + src/wx/viewers.cpp | 2 +- src/wx/widgets/sdl-poller.cpp | 131 +- src/wx/widgets/sdl-poller.h | 4 + src/wx/wxvbam.cpp | 26 +- src/wx/xrc/DisplayConfig.xrc | 58 + src/wx/xrc/SoundConfig.xrc | 7 + third_party/include/stb/stb_image_write.h | 2 +- 107 files changed, 5023 insertions(+), 722 deletions(-) create mode 100644 cmake/CMakeDetermineMetalCompiler.cmake create mode 100644 cmake/CMakeMetalCompiler.cmake.in create mode 100644 cmake/CMakeMetalInformation.cmake create mode 100644 cmake/CMakeTestMetalCompiler.cmake create mode 100644 cmake/CheckLanguage.cmake create mode 100644 cmake/MetalShaderSupport.cmake create mode 100644 src/libretro/link.T create mode 100644 src/wx/AAPLShaderTypes.h create mode 100644 src/wx/AAPLShaders.metal create mode 100644 src/wx/audio/internal/sdl.cpp create mode 100644 src/wx/audio/internal/sdl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c690c26d..209f9e22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,16 @@ set(CMAKE_C_STANDARD_REQUIRED True) project(VBA-M C CXX) +if(APPLE) + include(CheckLanguage) + include(MetalShaderSupport) + + check_language(Metal) + if(CMAKE_Metal_COMPILER) + enable_language(Metal) + endif() +endif() + include(CTest) include(FetchContent) include(GNUInstallDirs) diff --git a/cmake/CMakeDetermineMetalCompiler.cmake b/cmake/CMakeDetermineMetalCompiler.cmake new file mode 100644 index 00000000..5ced536e --- /dev/null +++ b/cmake/CMakeDetermineMetalCompiler.cmake @@ -0,0 +1,85 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENCE.txt or https://cmake.org/licensing for details. + +# CMakeDetermine(LANG)Compiler.cmake -> this should find the compiler for LANG and configure CMake(LANG)Compiler.cmake.in + +include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake) + +if(NOT CMAKE_Metal_COMPILER_NAMES) + set(CMAKE_Metal_COMPILER_NAMES metal) +endif() + +if("${CMAKE_GENERATOR}" STREQUAL "Xcode") + set(CMAKE_Metal_COMPILER_XCODE_TYPE sourcecode.metal) + + execute_process(COMMAND xcrun --find metal + OUTPUT_VARIABLE _xcrun_out OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE _xcrun_err RESULT_VARIABLE _xcrun_result + ) + + if(_xcrun_result EQUAL 0 AND EXISTS "${_xcrun_out}") + set(CMAKE_Metal_COMPILER "${_xcrun_out}") + else() + _cmake_find_compiler_path(Metal) + endif() +else() + if(CMAKE_Metal_COMPILER) + _cmake_find_compiler_path(Metal) + else() + set(CMAKE_Metal_COMPILER_INIT NOTFOUND) + + if(NOT $ENV{METALC} STREQUAL "") + get_filename_component(CMAKE_Metal_COMPILER_INIT $ENV{METALC} PROGRAM PROGRAM_ARGS CMAKE_Metal_FLAGS_ENV_INIT) + if(CMAKE_Metal_FLAGS_ENV_INIT) + set(CMAKE_Metal_COMPILER_ARG1 "${CMAKE_Metal_FLAGS_ENV_INIT}" CACHE STRING "Arguments to the Metal compiler") + endif() + if(NOT EXISTS ${CMAKE_Metal_COMPILER_INIT}) + message(FATAL_ERROR "Could not find compiler set in environment variable METALC\n$ENV{METALC}.\n${CMAKE_Metal_COMPILER_INIT}") + endif() + endif() + + if(NOT CMAKE_Metal_COMPILER_INIT) + set(CMAKE_Metal_COMPILER_LIST metal ${_CMAKE_TOOLCHAIN_PREFIX}metal) + endif() + + _cmake_find_compiler(Metal) + endif() + + mark_as_advanced(CMAKE_Metal_COMPILER) +endif() + +# For Metal we need to explicitly query the version. +if(CMAKE_Metal_COMPILER AND NOT CMAKE_Metal_COMPILER_VERSION) + execute_process( + COMMAND "${CMAKE_Metal_COMPILER}" --version + OUTPUT_VARIABLE output ERROR_VARIABLE output + RESULT_VARIABLE result + TIMEOUT 10 + ) + message(CONFIGURE_LOG + "Running the Metal compiler: \"${CMAKE_Metal_COMPILER}\" --version\n" + "${output}\n" + ) + + if(output MATCHES [[metal version ([0-9]+\.[0-9]+(\.[0-9]+)?)]]) + set(CMAKE_Metal_COMPILER_VERSION "${CMAKE_MATCH_1}") + if(NOT CMAKE_Metal_COMPILER_ID) + set(CMAKE_Metal_COMPILER_ID "Apple") + endif() + endif() +endif() + +if(NOT _CMAKE_TOOLCHAIN_LOCATION) + get_filename_component(_CMAKE_TOOLCHAIN_LOCATION "${CMAKE_Metal_COMPILER}" PATH) +endif () + +set(_CMAKE_PROCESSING_LANGUAGE "Metal") +include(CMakeFindBinUtils) +unset(_CMAKE_PROCESSING_LANGUAGE) + +configure_file( + ${CMAKE_CURRENT_LIST_DIR}/CMakeMetalCompiler.cmake.in + ${CMAKE_PLATFORM_INFO_DIR}/CMakeMetalCompiler.cmake +) + +set(CMAKE_Metal_COMPILER_ENV_VAR "METALC") diff --git a/cmake/CMakeMetalCompiler.cmake.in b/cmake/CMakeMetalCompiler.cmake.in new file mode 100644 index 00000000..d636af4b --- /dev/null +++ b/cmake/CMakeMetalCompiler.cmake.in @@ -0,0 +1,27 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENCE.txt or https://cmake.org/licensing for details. + +# CMake(LANG)Compiler.cmake.in -> used by CMakeDetermine(LANG)Compiler.cmake +# This file is used to store compiler information and is copied down into try +# compile directories so that try compiles do not need to re-determine and test +# the LANG + +set(CMAKE_Metal_COMPILER "@CMAKE_Metal_COMPILER@") +set(CMAKE_Metal_COMPILER_ID "@CMAKE_Metal_COMPILER_ID@") +set(CMAKE_Metal_COMPILER_VERSION "@CMAKE_Metal_COMPILER_VERSION@") + +set(CMAKE_Metal_COMPILER_LOADED 1) +set(CMAKE_Metal_COMPILER_WORKS "@CMAKE_Metal_COMPILER_WORKS@") + +set(CMAKE_Metal_COMPILER_ENV_VAR "METALC") + +set(CMAKE_Metal_COMPILER_ID_RUN "@CMAKE_Metal_COMPILER_ID_RUN@") +set(CMAKE_Metal_SOURCE_FILE_EXTENSIONS metal) +set(CMAKE_Metal_OUTPUT_EXTENSION ".air") +set(CMAKE_STATIC_LIBRARY_PREFIX_Metal "") +set(CMAKE_STATIC_LIBRARY_SUFFIX_Metal ".metal-ar") +set(CMAKE_SHARED_LIBRARY_PREFIX_Metal "") +set(CMAKE_SHARED_LIBRARY_SUFFIX_Metal ".metallib") +set(CMAKE_SHARED_MODULE_PREFIX_Metal "") +set(CMAKE_SHARED_MODULE_SUFFIX_Metal ".metallib") +set(CMAKE_EXECUTABLE_SUFFIX_Metal ".metallib") diff --git a/cmake/CMakeMetalInformation.cmake b/cmake/CMakeMetalInformation.cmake new file mode 100644 index 00000000..7593895b --- /dev/null +++ b/cmake/CMakeMetalInformation.cmake @@ -0,0 +1,85 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENCE.txt or https://cmake.org/licensing for details. + +# CMake(LANG)Information.cmake -> set up rule variables for LANG : +# CMAKE_(LANG)_CREATE_SHARED_LIBRARY +# CMAKE_(LANG)_CREATE_SHARED_MODULE +# CMAKE_(LANG)_CREATE_STATIC_LIBRARY +# CMAKE_(LANG)_COMPILE_OBJECT +# CMAKE_(LANG)_LINK_EXECUTABLE + +include(CMakeCommonLanguageInclude) + +set(CMAKE_Metal_FLAGS_INIT "-ffast-math") +set(CMAKE_Metal_FLAGS_DEBUG_INIT "-gline-tables-only -frecord-sources") +set(CMAKE_Metal_FLAGS_RELWITHDEBINFO_INIT "-gline-tables-only -frecord-sources") + +cmake_initialize_per_config_variable(CMAKE_Metal_FLAGS "Flags used by the Metal compiler") + +set(CMAKE_INCLUDE_FLAG_Metal "-I ") +set(CMAKE_Metal_COMPILER_ARG1 "") +set(CMAKE_Metal_DEFINE_FLAG -D) +set(CMAKE_Metal_FRAMEWORK_SEARCH_FLAG "-F ") +set(CMAKE_Metal_LIBRARY_PATH_FLAG "-L ") +set(CMAKE_Metal_SYSROOT_FLAG "-isysroot") +set(CMAKE_Metal_COMPILE_OPTIONS_TARGET "-target ") +set(CMAKE_DEPFILE_FLAGS_Metal "-MMD -MT dependencies -MF ") + +if(CMAKE_GENERATOR MATCHES "Makefiles") + set(CMAKE_Metal_DEPFILE_FORMAT gcc) + set(CMAKE_Metal_DEPENDS_USE_COMPILER TRUE) +endif() + +set(CMAKE_Metal_COMPILER_PREDEFINES_COMMAND "${CMAKE_Metal_COMPILER}") +if(CMAKE_Metal_COMPILER_TARGET) + list(APPEND CMAKE_Metal_COMPILER_PREDEFINES_COMMAND "-target" "${CMAKE_Metal_COMPILER_TARGET}") +endif() + +# now define the following rule variables + +# CMAKE_Metal_CREATE_SHARED_LIBRARY +# CMAKE_Metal_CREATE_SHARED_MODULE +# CMAKE_Metal_COMPILE_OBJECT +# CMAKE_Metal_LINK_EXECUTABLE + +# variables supplied by the generator at use time +# +# the target without the suffix +# +# +# +# +# + +# Metal compiler information +# +# +# +# + +if(NOT CMAKE_Metal_COMPILE_OBJECT) + set(CMAKE_Metal_COMPILE_OBJECT + " -c -o " + ) +endif() + +if(NOT CMAKE_Metal_CREATE_SHARED_LIBRARY) + set(CMAKE_Metal_CREATE_SHARED_LIBRARY + " -o " + ) +endif() + +if(NOT CMAKE_Metal_CREATE_SHARED_MODULE) + set(CMAKE_Metal_CREATE_SHARED_MODULE + "${CMAKE_Metal_CREATE_SHARED_LIBRARY}" + ) +endif() + +if(NOT CMAKE_Metal_LINK_EXECUTABLE) + # Metal shaders don't really have "executables", but we need this for the try_compile to work properly, so we'll just have it output a metallib file + set(CMAKE_Metal_LINK_EXECUTABLE + " -o " + ) +endif() + +set(CMAKE_Metal_INFORMATION_LOADED 1) diff --git a/cmake/CMakeTestMetalCompiler.cmake b/cmake/CMakeTestMetalCompiler.cmake new file mode 100644 index 00000000..0014553e --- /dev/null +++ b/cmake/CMakeTestMetalCompiler.cmake @@ -0,0 +1,78 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENCE.txt or https://cmake.org/licensing for details. + +# CMakeTest(LANG)Compiler.cmake -> test the compiler and set: +# SET(CMAKE_(LANG)_COMPILER_WORKS 1 CACHE INTERNAL "") + +if(CMAKE_Metal_COMPILER_FORCED) + # The compiler configuration was forced by the user. + # Assume the user has configured all compiler information. + set(CMAKE_Metal_COMPILER_WORKS TRUE) + return() +endif() + +include(CMakeTestCompilerCommon) + +if("${CMAKE_GENERATOR}" STREQUAL "Xcode") + if(XCODE_VERSION VERSION_GREATER 7.0) + set(CMAKE_Metal_COMPILER_WORKS 1) + endif() +endif() + +# This file is used by EnableLanguage in cmGlobalGenerator to +# determine that that selected Metal compiler can actually compile +# and link the most basic of programs. If not, a fatal error +# is set and cmake stops processing commands and will not generate +# any makefiles or projects. +if(NOT CMAKE_Metal_COMPILER_WORKS) + PrintTestCompilerStatus("Metal") + __TestCompiler_setTryCompileTargetType() + + string(CONCAT __TestCompiler_testMetalCompilerSource + "#ifndef __METAL_VERSION__\n" + "# error \"The CMAKE_Metal_COMPILER is not a Metal compiler\"\n" + "#endif\n" + "#import \n" + "using namespace metal;\n" + ) + + # Clear result from normal variable. + unset(CMAKE_Metal_COMPILER_WORKS) + + # Puts test result in cache variable. + try_compile(CMAKE_Metal_COMPILER_WORKS + SOURCE_FROM_VAR testMetalCompiler.metal __TestCompiler_testMetalCompilerSource + OUTPUT_VARIABLE __CMAKE_Metal_COMPILER_OUTPUT + ) + unset(__TestCompiler_testMetalCompilerSource) + + # Move result from cache to normal variable. + set(CMAKE_Metal_COMPILER_WORKS ${CMAKE_Metal_COMPILER_WORKS}) + unset(CMAKE_Metal_COMPILER_WORKS CACHE) + __TestCompiler_restoreTryCompileTargetType() + set(METAL_TEST_WAS_RUN 1) +endif() + +if(NOT CMAKE_Metal_COMPILER_WORKS) + PrintTestCompilerResult(CHECK_FAIL "broken") + string(REPLACE "\n" "\n " _output "${__CMAKE_Metal_COMPILER_OUTPUT}") + message(FATAL_ERROR "The Metal compiler\n \"${CMAKE_Metal_COMPILER}\"\n" + "is not able to compile a simple test program.\nIt fails " + "with the following output:\n ${_output}\n\n" + "CMake will not be able to correctly generate this project." + ) +else() + if(METAL_TEST_WAS_RUN) + PrintTestCompilerResult(CHECK_PASS "works") + endif() + + # Re-configure to save learned information. + configure_file( + ${CMAKE_CURRENT_LIST_DIR}/CMakeMetalCompiler.cmake.in + ${CMAKE_PLATFORM_INFO_DIR}/CMakeMetalCompiler.cmake + @ONLY + ) + include(${CMAKE_PLATFORM_INFO_DIR}/CMakeMetalCompiler.cmake) +endif() + +unset(__CMAKE_Metal_COMPILER_OUTPUT) diff --git a/cmake/CheckLanguage.cmake b/cmake/CheckLanguage.cmake new file mode 100644 index 00000000..e212e3aa --- /dev/null +++ b/cmake/CheckLanguage.cmake @@ -0,0 +1,169 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENCE.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +CheckLanguage +------------- + +Check whether a language can be enabled by the :command:`enable_language` +or :command:`project` commands: + +.. command:: check_language + + .. code-block:: cmake + + check_language() + + Try enabling language ```` in a test project and record results + in the cache: + + :variable:`CMAKE__COMPILER` + If the language can be enabled, this variable is set to the compiler + that was found. If the language cannot be enabled, this variable is + set to ``NOTFOUND``. + + If this variable is already set, either explicitly or cached by + a previous call, the check is skipped. + + :variable:`CMAKE__HOST_COMPILER` + This variable is set when ```` is ``CUDA`` or ``HIP``. + + If the check detects an explicit host compiler that is required for + compilation, this variable will be set to that compiler. + If the check detects that no explicit host compiler is needed, + this variable will be cleared. + + If this variable is already set, its value is preserved only if + :variable:`CMAKE__COMPILER` is also set. + Otherwise, the check runs and overwrites + :variable:`CMAKE__HOST_COMPILER` with a new result. + Note that :variable:`CMAKE__HOST_COMPILER` documents it should + not be set without also setting + :variable:`CMAKE__COMPILER` to a NVCC compiler. + + :variable:`CMAKE__PLATFORM ` + This variable is set to the detected GPU platform when ```` is ``HIP``. + + If the variable is already set its value is always preserved. Only compatible values + will be considered for :variable:`CMAKE__COMPILER`. + +For example: + +.. code-block:: cmake + + check_language(Fortran) + if(CMAKE_Fortran_COMPILER) + enable_language(Fortran) + else() + message(STATUS "No Fortran support") + endif() +#]=======================================================================] + +# This file has been modified to take into account the CMAKE_MODULES path when trying to build the test project +# Ref https://gitlab.kitware.com/cmake/cmake/-/issues/26020 +# This was merged in to CMake 3.30.0, so we only need this for older versions + +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.30) + include(${CMAKE_ROOT}/Modules/CheckLanguage.cmake) +else() + include_guard(GLOBAL) + + block(SCOPE_FOR POLICIES) + cmake_policy(SET CMP0126 NEW) + + macro(check_language lang) + if(NOT DEFINED CMAKE_${lang}_COMPILER) + set(_desc "Looking for a ${lang} compiler") + message(CHECK_START "${_desc}") + file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang}) + + set(extra_compiler_variables) + if("${lang}" MATCHES "^(CUDA|HIP)$" AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") + set(extra_compiler_variables "set(CMAKE_${lang}_HOST_COMPILER \\\"\${CMAKE_${lang}_HOST_COMPILER}\\\")") + endif() + + if("${lang}" STREQUAL "HIP") + list(APPEND extra_compiler_variables "set(CMAKE_${lang}_PLATFORM \\\"\${CMAKE_${lang}_PLATFORM}\\\")") + endif() + + list(TRANSFORM extra_compiler_variables PREPEND "\"") + list(TRANSFORM extra_compiler_variables APPEND "\\n\"") + list(JOIN extra_compiler_variables "\n " extra_compiler_variables) + + set(_cl_content + "cmake_minimum_required(VERSION ${CMAKE_VERSION}) + set(CMAKE_MODULE_PATH \"${CMAKE_MODULE_PATH}\") + project(Check${lang} ${lang}) + file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\" + \"set(CMAKE_${lang}_COMPILER \\\"\${CMAKE_${lang}_COMPILER}\\\")\\n\" + ${extra_compiler_variables} + )" + ) + + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang}/CMakeLists.txt" + "${_cl_content}") + if(CMAKE_GENERATOR_INSTANCE) + set(_D_CMAKE_GENERATOR_INSTANCE "-DCMAKE_GENERATOR_INSTANCE:INTERNAL=${CMAKE_GENERATOR_INSTANCE}") + else() + set(_D_CMAKE_GENERATOR_INSTANCE "") + endif() + if(CMAKE_GENERATOR MATCHES "^(Xcode$|Green Hills MULTI$|Visual Studio)") + set(_D_CMAKE_MAKE_PROGRAM "") + else() + set(_D_CMAKE_MAKE_PROGRAM "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}") + endif() + if(CMAKE_TOOLCHAIN_FILE) + set(_D_CMAKE_TOOLCHAIN_FILE "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE}") + else() + set(_D_CMAKE_TOOLCHAIN_FILE "") + endif() + if(CMAKE_${lang}_PLATFORM) + set(_D_CMAKE_LANG_PLATFORM "-DCMAKE_${lang}_PLATFORM:STRING=${CMAKE_${lang}_PLATFORM}") + else() + set(_D_CMAKE_LANG_PLATFORM "") + endif() + execute_process( + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang} + COMMAND ${CMAKE_COMMAND} . -G ${CMAKE_GENERATOR} + -A "${CMAKE_GENERATOR_PLATFORM}" + -T "${CMAKE_GENERATOR_TOOLSET}" + ${_D_CMAKE_GENERATOR_INSTANCE} + ${_D_CMAKE_MAKE_PROGRAM} + ${_D_CMAKE_TOOLCHAIN_FILE} + ${_D_CMAKE_LANG_PLATFORM} + OUTPUT_VARIABLE _cl_output + ERROR_VARIABLE _cl_output + RESULT_VARIABLE _cl_result + ) + include(${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang}/result.cmake OPTIONAL) + if(CMAKE_${lang}_COMPILER AND "${_cl_result}" STREQUAL "0") + message(CONFIGURE_LOG + "${_desc} passed with the following output:\n" + "${_cl_output}\n") + set(_CHECK_COMPILER_STATUS CHECK_PASS) + else() + set(CMAKE_${lang}_COMPILER NOTFOUND) + set(_CHECK_COMPILER_STATUS CHECK_FAIL) + message(CONFIGURE_LOG + "${_desc} failed with the following output:\n" + "${_cl_output}\n") + endif() + message(${_CHECK_COMPILER_STATUS} "${CMAKE_${lang}_COMPILER}") + set(CMAKE_${lang}_COMPILER "${CMAKE_${lang}_COMPILER}" CACHE FILEPATH "${lang} compiler") + mark_as_advanced(CMAKE_${lang}_COMPILER) + + if(CMAKE_${lang}_HOST_COMPILER) + message(STATUS "Looking for a ${lang} host compiler - ${CMAKE_${lang}_HOST_COMPILER}") + set(CMAKE_${lang}_HOST_COMPILER "${CMAKE_${lang}_HOST_COMPILER}" CACHE FILEPATH "${lang} host compiler") + mark_as_advanced(CMAKE_${lang}_HOST_COMPILER) + endif() + + if(CMAKE_${lang}_PLATFORM) + set(CMAKE_${lang}_PLATFORM "${CMAKE_${lang}_PLATFORM}" CACHE STRING "${lang} platform") + mark_as_advanced(CMAKE_${lang}_PLATFORM) + endif() + endif() + endmacro() + + endblock() +endif() diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index ba9a6821..be5216a7 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -11,11 +11,23 @@ if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) set(OpenGL_GL_PREFERENCE LEGACY) endif() -find_package(OpenGL REQUIRED) -find_package(SDL2 REQUIRED) +if(NOT DISABLE_OPENGL) + find_package(OpenGL) -# Add libsamplerate to SDL2 with vcpkg -unset(SDL2_LIBRARY_TEMP) + if(NOT OpenGL_FOUND) + set(CMAKE_C_FLAGS "-DNO_OPENGL -DNO_OGL ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-DNO_OPENGL -DNO_OGL ${CMAKE_CXX_FLAGS}") + endif() +endif() + +if(ENABLE_SDL3) + find_package(SDL3 REQUIRED) +else() + find_package(SDL2 REQUIRED) +endif() + +# Add libsamplerate to SDL3 with vcpkg +unset(SDL_LIBRARY_TEMP) if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg") if(WIN32) unset(arch_suffix) @@ -28,16 +40,24 @@ if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg") 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) + SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} ${installed_prefix}/lib/samplerate.lib) else() - SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} -lsamplerate) + SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} -lsamplerate) endif() endif() -if(VBAM_STATIC) - set(VBAM_SDL2_LIBS SDL2::SDL2-static ${SDL2_LIBRARY_TEMP}) +if(ENABLE_SDL3) + if(VBAM_STATIC) + set(VBAM_SDL_LIBS SDL3::SDL3-static ${SDL_LIBRARY_TEMP}) + else() + set(VBAM_SDL_LIBS SDL3::SDL3 ${SDL_LIBRARY_TEMP}) + endif() else() - set(VBAM_SDL2_LIBS SDL2::SDL2 ${SDL2_LIBRARY_TEMP}) + if(VBAM_STATIC) + set(VBAM_SDL_LIBS SDL2::SDL2-static ${SDL_LIBRARY_TEMP}) + else() + set(VBAM_SDL_LIBS SDL2::SDL2 ${SDL_LIBRARY_TEMP}) + endif() endif() if(ENABLE_FFMPEG) diff --git a/cmake/MetalShaderSupport.cmake b/cmake/MetalShaderSupport.cmake new file mode 100644 index 00000000..00213860 --- /dev/null +++ b/cmake/MetalShaderSupport.cmake @@ -0,0 +1,53 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file LICENCE.txt or https://cmake.org/licensing for details. + +function(add_metal_shader_library TARGET) + cmake_parse_arguments(PARSE_ARGV 1 _amsl + "" + "STANDARD" + "" + ) + + add_library(${TARGET} MODULE ${_amsl_UNPARSED_ARGUMENTS}) + + set_target_properties(${TARGET} PROPERTIES + DEBUG_POSTFIX "" + XCODE_PRODUCT_TYPE com.apple.product-type.metal-library + XCODE_ATTRIBUTE_MTL_FAST_MATH "YES" + XCODE_ATTRIBUTE_MTL_ENABLE_DEBUG_INFO[variant=Debug] "INCLUDE_SOURCE" + XCODE_ATTRIBUTE_MTL_ENABLE_DEBUG_INFO[variant=RelWithDebInfo] "INCLUDE_SOURCE" + XCODE_ATTRIBUTE_MTL_HEADER_SEARCH_PATHS "$(HEADER_SEARCH_PATHS)" + ) + + if(_amsl_STANDARD AND _amsl_STANDARD MATCHES "metal([0-9]+)\.([0-9]+)") + target_compile_options(${TARGET} + PRIVATE "-std=${_amsl_STANDARD}" + ) + + set_target_properties(${TARGET} PROPERTIES + XCODE_ATTRIBUTE_MTL_LANGUAGE_REVISION "Metal${CMAKE_MATCH_1}${CMAKE_MATCH_2}" + ) + endif() +endfunction() + +function(target_embed_metal_shader_libraries TARGET) + cmake_parse_arguments(PARSE_ARGV 1 _temsl + "" + "" + "" + ) + + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.28 AND ${CMAKE_GENERATOR} STREQUAL "Xcode") + set_target_properties(${TARGET} PROPERTIES + XCODE_EMBED_RESOURCES "${_temsl_UNPARSED_ARGUMENTS}" + ) + else() + foreach(SHADERLIB IN LISTS _temsl_UNPARSED_ARGUMENTS) + add_dependencies(${TARGET} ${SHADERLIB}) + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "$" "$/Resources/$" + VERBATIM + ) + endforeach() + endif() +endfunction() diff --git a/cmake/Options.cmake b/cmake/Options.cmake index e0ca3e93..d04286d5 100644 --- a/cmake/Options.cmake +++ b/cmake/Options.cmake @@ -9,15 +9,28 @@ else() endif() set(ENABLE_SDL_DEFAULT ${BUILD_DEFAULT}) + if(WIN32 OR APPLE) set(ENABLE_SDL_DEFAULT OFF) endif() +option(ENABLE_SDL3 "Use SDL3" OFF) +option(DISABLE_OPENGL "Disable OpenGL" OFF) option(ENABLE_SDL "Build the SDL port" ${ENABLE_SDL_DEFAULT}) option(ENABLE_WX "Build the wxWidgets port" ${BUILD_DEFAULT}) option(ENABLE_DEBUGGER "Enable the debugger" ON) option(ENABLE_ASAN "Enable -fsanitize=address by default. Requires debug build with GCC/Clang" OFF) +if(ENABLE_SDL3) + set(CMAKE_C_FLAGS "-DENABLE_SDL3 ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-DENABLE_SDL3 ${CMAKE_CXX_FLAGS}") +endif() + +if(DISABLE_OPENGL) + set(CMAKE_C_FLAGS "-DNO_OPENGL -DNO_OGL ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-DNO_OPENGL -DNO_OGL ${CMAKE_CXX_FLAGS}") +endif() + # Static linking set(VBAM_STATIC_DEFAULT OFF) if(VCPKG_TARGET_TRIPLET MATCHES -static OR CMAKE_TOOLCHAIN_FILE MATCHES "mxe|-static") @@ -30,6 +43,8 @@ option(VBAM_STATIC "Try to link all libraries statically" ${VBAM_STATIC_DEFAULT} if(VBAM_STATIC) set(SDL2_STATIC ON) + set(SDL3_STATIC ON) + set(SFML_STATIC_LIBRARIES ON) set(FFMPEG_STATIC ON) set(OPENAL_STATIC ON) set_property(GLOBAL PROPERTY LINK_SEARCH_START_STATIC ON) diff --git a/cmake/Toolchain-msvc.cmake b/cmake/Toolchain-msvc.cmake index c2f39ab1..4700bfd3 100644 --- a/cmake/Toolchain-msvc.cmake +++ b/cmake/Toolchain-msvc.cmake @@ -68,6 +68,8 @@ if(CMAKE_VERSION VERSION_LESS "3.25") endif() endif() +add_compile_options($<$:/std:c++17>) + set(CMAKE_RC_FLAGS "-c65001 /DWIN32" CACHE STRING "" FORCE) # We need to explicitly set all of these to override the CMake defaults. diff --git a/installdeps b/installdeps index 55437a6c..0d42d32f 100755 --- a/installdeps +++ b/installdeps @@ -1244,4 +1244,4 @@ EOF main "$@" -# vim:et sw=4: +# vim:et sw=4: \ No newline at end of file diff --git a/po/wxvbam/bg.po b/po/wxvbam/bg.po index 9b5fc201..64f8c40a 100644 --- a/po/wxvbam/bg.po +++ b/po/wxvbam/bg.po @@ -361,6 +361,14 @@ msgstr "Няма намерени използваеми добавки в/ъв msgid "Plugin" msgstr "Добавка" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Битове на пиксел" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Битове на пиксел:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/br.po b/po/wxvbam/br.po index 5d892ed8..e0bd5c6f 100644 --- a/po/wxvbam/br.po +++ b/po/wxvbam/br.po @@ -361,6 +361,14 @@ msgstr "N'eo ket bet kavet an astenn rpi implijadus e-barzh %s" msgid "Plugin" msgstr "Astenn" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits por pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits por pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/cs.po b/po/wxvbam/cs.po index 9cbd81d7..87ecf19a 100644 --- a/po/wxvbam/cs.po +++ b/po/wxvbam/cs.po @@ -365,6 +365,14 @@ msgstr "V %s nenalezeny žádné použitelné zásuvné moduly rpi" msgid "Plugin" msgstr "Zásuvný modul" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Počet bitů na pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Počet bitů na pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/de.po b/po/wxvbam/de.po index e5b3c38b..7d4673b4 100644 --- a/po/wxvbam/de.po +++ b/po/wxvbam/de.po @@ -378,6 +378,14 @@ msgstr "Keine anwendbaren RPI-Plugins in %s gefunden" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits pro Pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits pro Pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/el.po b/po/wxvbam/el.po index e0b1ac3d..fec692e3 100644 --- a/po/wxvbam/el.po +++ b/po/wxvbam/el.po @@ -362,6 +362,14 @@ msgstr "Δεν βρέθηκαν χρήσιμες επεκτάσεις rpi στο msgid "Plugin" msgstr "Επέκταση" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits ανά pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits ανά pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/en_US.po b/po/wxvbam/en_US.po index b773420b..cf8009b1 100644 --- a/po/wxvbam/en_US.po +++ b/po/wxvbam/en_US.po @@ -1560,6 +1560,14 @@ msgstr "" msgid "Using interframe blending: %s" msgstr "" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits per pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits per pixel:" + #: dialogs/game-boy-config.cpp:142 xrc/GameBoyAdvanceConfig.xrc:122 #: xrc/GameBoyConfig.xrc:138 xrc/GameBoyConfig.xrc:159 msgid "(None)" diff --git a/po/wxvbam/es.po b/po/wxvbam/es.po index 27b470c8..9ad54d93 100644 --- a/po/wxvbam/es.po +++ b/po/wxvbam/es.po @@ -382,6 +382,14 @@ msgstr "No se encontraron complementos rpi usables en %s" msgid "Plugin" msgstr "Complemento" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits por píxel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits por píxel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/es_419.po b/po/wxvbam/es_419.po index 8dcf2bd4..1129bbd0 100644 --- a/po/wxvbam/es_419.po +++ b/po/wxvbam/es_419.po @@ -374,6 +374,14 @@ msgstr "No usable rpi plugins found in %s" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits por píxel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits por píxel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/es_CO.po b/po/wxvbam/es_CO.po index 82da39fa..c499b254 100644 --- a/po/wxvbam/es_CO.po +++ b/po/wxvbam/es_CO.po @@ -1553,6 +1553,14 @@ msgstr "No usable rpi plugins found in %s" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits per píxel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits per píxel:" + #: dialogs/display-config.cpp:417 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/es_PE.po b/po/wxvbam/es_PE.po index 1777bc20..4c91defd 100644 --- a/po/wxvbam/es_PE.po +++ b/po/wxvbam/es_PE.po @@ -1549,6 +1549,14 @@ msgstr "No usable rpi plugins found in %s" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits per píxel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits per píxel:" + #: dialogs/display-config.cpp:417 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/es_US.po b/po/wxvbam/es_US.po index 74cc8b63..0d67ee4a 100644 --- a/po/wxvbam/es_US.po +++ b/po/wxvbam/es_US.po @@ -1549,6 +1549,14 @@ msgstr "No usable rpi plugins found in %s" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits per píxel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits per píxel:" + #: dialogs/display-config.cpp:417 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/fr.po b/po/wxvbam/fr.po index 0f5de9f5..56dfe973 100644 --- a/po/wxvbam/fr.po +++ b/po/wxvbam/fr.po @@ -1563,6 +1563,14 @@ msgstr "Extension rpi utilisable non trouvée dans %s" msgid "Plugin" msgstr "Extension" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits par pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits par pixel:" + #: dialogs/display-config.cpp:417 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/fr_FR.po b/po/wxvbam/fr_FR.po index 7ef65fdf..42538986 100644 --- a/po/wxvbam/fr_FR.po +++ b/po/wxvbam/fr_FR.po @@ -369,6 +369,14 @@ msgstr "Aucun plugin RPI utilisable n'a été trouvé dans %s" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits par pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits par pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/gl.po b/po/wxvbam/gl.po index 8f52aeda..12b6ab9c 100644 --- a/po/wxvbam/gl.po +++ b/po/wxvbam/gl.po @@ -364,6 +364,14 @@ msgstr "Non se atoparon complementos rpi aproveitábeis en %s" msgid "Plugin" msgstr "Complemento" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits por píxel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits por píxel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/he_IL.po b/po/wxvbam/he_IL.po index e7edf9c4..fedea7a9 100644 --- a/po/wxvbam/he_IL.po +++ b/po/wxvbam/he_IL.po @@ -362,6 +362,14 @@ msgstr "לא נמצא תוסף rpi הניתן לשימוש ב-%s" msgid "Plugin" msgstr "תוסף" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "סיביות לפיקסל" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "סיביות לפיקסל:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/hu.po b/po/wxvbam/hu.po index 2beb60d8..7d4bc123 100644 --- a/po/wxvbam/hu.po +++ b/po/wxvbam/hu.po @@ -1549,6 +1549,14 @@ msgstr "A %s helyen nincs használható RPI bővítmény" msgid "Plugin" msgstr "Bővítmény" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bit per pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bit per pixel:" + #: dialogs/display-config.cpp:417 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/hu_HU.po b/po/wxvbam/hu_HU.po index 38a37010..fe36c56a 100644 --- a/po/wxvbam/hu_HU.po +++ b/po/wxvbam/hu_HU.po @@ -363,6 +363,14 @@ msgstr "A %s helyen nincs használható RPI bővítmény" msgid "Plugin" msgstr "Bővítmény" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bit per pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bit per pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/id.po b/po/wxvbam/id.po index ecfd101d..b279947b 100644 --- a/po/wxvbam/id.po +++ b/po/wxvbam/id.po @@ -366,6 +366,14 @@ msgstr "Tidak menemukan plugin rpi yang dapat digunakan di %s" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bit per piksel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bit per piksel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/it_IT.po b/po/wxvbam/it_IT.po index 7837cb0f..ea69ff0e 100644 --- a/po/wxvbam/it_IT.po +++ b/po/wxvbam/it_IT.po @@ -378,6 +378,14 @@ msgstr "Non sono stati trovati plugin rpi utilizzabili in %s" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bit per pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bit per pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/ja.po b/po/wxvbam/ja.po index fb7b51aa..416f7563 100644 --- a/po/wxvbam/ja.po +++ b/po/wxvbam/ja.po @@ -370,6 +370,14 @@ msgstr "%s に使用可能なrpiプラグインが見つかりませんでした msgid "Plugin" msgstr "プラグイン" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "ピクセルあたりのビット数" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "ピクセルあたりのビット数:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/ja_JP.po b/po/wxvbam/ja_JP.po index bc770afc..3e8ddb9d 100644 --- a/po/wxvbam/ja_JP.po +++ b/po/wxvbam/ja_JP.po @@ -362,6 +362,14 @@ msgstr "" msgid "Plugin" msgstr "" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/ko.po b/po/wxvbam/ko.po index c3089936..b1464816 100644 --- a/po/wxvbam/ko.po +++ b/po/wxvbam/ko.po @@ -1552,6 +1552,14 @@ msgstr "%s에서 사용가능한 rpi 플러그인을 찾을 수 없음" msgid "Plugin" msgstr "플러그인" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "픽셀당 비트" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "픽셀당 비트:" + #: dialogs/display-config.cpp:417 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/ko_KR.po b/po/wxvbam/ko_KR.po index 7c6fd02d..cff7de67 100644 --- a/po/wxvbam/ko_KR.po +++ b/po/wxvbam/ko_KR.po @@ -365,6 +365,14 @@ msgstr "%s에서 사용가능한 rpi 플러그인을 찾을 수 없음" msgid "Plugin" msgstr "플러그인" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "픽셀당 비트" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "픽셀당 비트:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/ms_MY.po b/po/wxvbam/ms_MY.po index 74fc3129..2cd31e8c 100644 --- a/po/wxvbam/ms_MY.po +++ b/po/wxvbam/ms_MY.po @@ -361,6 +361,14 @@ msgstr "Tiada pemalam rpi boleh guna ditemui dalam %s" msgid "Plugin" msgstr "Pemalam" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bit setiap piksel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bit setiap piksel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/nb.po b/po/wxvbam/nb.po index 77ece357..2b6684a3 100644 --- a/po/wxvbam/nb.po +++ b/po/wxvbam/nb.po @@ -364,6 +364,14 @@ msgstr "Ingen brukbare rpi plugins funnet i %s" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits per piksel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits per piksel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/nl.po b/po/wxvbam/nl.po index 7c8ebb06..8313fb8f 100644 --- a/po/wxvbam/nl.po +++ b/po/wxvbam/nl.po @@ -365,6 +365,14 @@ msgstr "Geen bruikbare rpi plugin gevonden in %s" msgid "Plugin" msgstr "Plug-in" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits per pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits per pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/pl.po b/po/wxvbam/pl.po index 3f58ae1c..34546e54 100644 --- a/po/wxvbam/pl.po +++ b/po/wxvbam/pl.po @@ -1552,6 +1552,14 @@ msgstr "Nie znaleziono użytecznych wtyczek rpi w %s" msgid "Plugin" msgstr "Wtyczka" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bity na piksel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bity na piksel:" + #: dialogs/display-config.cpp:417 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/pl_PL.po b/po/wxvbam/pl_PL.po index 2aa9a64d..168c33a0 100644 --- a/po/wxvbam/pl_PL.po +++ b/po/wxvbam/pl_PL.po @@ -361,6 +361,14 @@ msgstr "Nie znaleziono użytecznych wtyczek rpi w %s" msgid "Plugin" msgstr "Wtyczka" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bity na piksel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bity na piksel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/pt_BR.po b/po/wxvbam/pt_BR.po index c900ce0d..2c31aa57 100644 --- a/po/wxvbam/pt_BR.po +++ b/po/wxvbam/pt_BR.po @@ -390,6 +390,14 @@ msgstr "Nenhum plugin rpi usável foi encontrado em %s" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits por pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits por pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/pt_PT.po b/po/wxvbam/pt_PT.po index 9884c9c1..d937d6b1 100644 --- a/po/wxvbam/pt_PT.po +++ b/po/wxvbam/pt_PT.po @@ -362,6 +362,14 @@ msgstr "" msgid "Plugin" msgstr "" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bits por pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bits por pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/ru_RU.po b/po/wxvbam/ru_RU.po index 51d43226..ea370a5a 100644 --- a/po/wxvbam/ru_RU.po +++ b/po/wxvbam/ru_RU.po @@ -377,6 +377,14 @@ msgstr "Не найдено подходящих rpi-плагинов в %s" msgid "Plugin" msgstr "Плагин" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Бит на пиксель" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Бит на пиксель:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/sv.po b/po/wxvbam/sv.po index 4a203a90..73f1fa77 100644 --- a/po/wxvbam/sv.po +++ b/po/wxvbam/sv.po @@ -369,6 +369,14 @@ msgstr "Inga användbara rpi plugins funna i %s" msgid "Plugin" msgstr "Plugin" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Bitar per pixel" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Bitar per pixel:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/tr.po b/po/wxvbam/tr.po index 7a364e35..fbefab02 100644 --- a/po/wxvbam/tr.po +++ b/po/wxvbam/tr.po @@ -370,6 +370,14 @@ msgstr "%s'da kullanılabilir bir rpi eklentisi bulunamadı" msgid "Plugin" msgstr "Eklenti" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Piksel başına bit sayısı" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Piksel başına bit sayısı:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/uk.po b/po/wxvbam/uk.po index cef95429..5bde265e 100644 --- a/po/wxvbam/uk.po +++ b/po/wxvbam/uk.po @@ -362,6 +362,14 @@ msgstr "Не знайдено придатних rpi-плаґінів у %s." msgid "Plugin" msgstr "Плаґін" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "Біт на піксель" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "Біт на піксель:" + #: dialogs/display-config.cpp:407 #, c-format msgid "Using pixel filter: %s" diff --git a/po/wxvbam/ur_PK.po b/po/wxvbam/ur_PK.po index a10ef019..b42457ec 100644 --- a/po/wxvbam/ur_PK.po +++ b/po/wxvbam/ur_PK.po @@ -1046,6 +1046,14 @@ msgstr "" msgid "Invalid value for Default magnification." msgstr "" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "" + #: dialogs/display-config.cpp:331 xrc/DisplayConfig.xrc:86 #: xrc/DisplayConfig.xrc:136 xrc/DisplayConfig.xrc:222 #: xrc/GameBoyAdvanceConfig.xrc:32 xrc/GameBoyAdvanceConfig.xrc:204 diff --git a/po/wxvbam/zh_CN.po b/po/wxvbam/zh_CN.po index cceee086..0a1c916f 100644 --- a/po/wxvbam/zh_CN.po +++ b/po/wxvbam/zh_CN.po @@ -360,6 +360,14 @@ msgstr "浏览" msgid "Invalid value for Default magnification." msgstr "无效的默认放大倍数值。" +#: xrc/DisplayConfig.xrc +msgid "Bit Depth" +msgstr "每像素位数" + +#: xrc/DisplayConfig.xrc +msgid "Bit Depth:" +msgstr "每像素位数:" + #: dialogs/display-config.cpp:331 xrc/DisplayConfig.xrc:86 #: xrc/DisplayConfig.xrc:136 xrc/DisplayConfig.xrc:222 #: xrc/GameBoyAdvanceConfig.xrc:32 xrc/GameBoyAdvanceConfig.xrc:204 diff --git a/src/components/av_recording/av_recording.cpp b/src/components/av_recording/av_recording.cpp index 3070f84d..2f05b788 100644 --- a/src/components/av_recording/av_recording.cpp +++ b/src/components/av_recording/av_recording.cpp @@ -226,6 +226,9 @@ recording::MediaRet recording::MediaRecorder::setup_video_stream_info(int width, { switch (depth) { + case 8: + pixfmt = AV_PIX_FMT_RGB8; + break; case 16: // FIXME: test & make endian-neutral pixfmt = AV_PIX_FMT_RGB565LE; @@ -250,6 +253,9 @@ recording::MediaRet recording::MediaRecorder::setup_video_stream_info(int width, linesize = pixsize * width; switch (pixsize) { + case 1: + tbord = 1; rbord = 2; + break; case 2: // 16-bit: 2 @ right, 1 @ top tbord = 1; rbord = 2; diff --git a/src/components/filters/filters.h b/src/components/filters/filters.h index 16f0f819..4097834b 100644 --- a/src/components/filters/filters.h +++ b/src/components/filters/filters.h @@ -45,11 +45,11 @@ void lq2x(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, // in any case, they are worthless, since all renderers do "simple" or // better by default void Simple2x32(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, int w, int h); -void Simple2x(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, int w, int h); +void Simple2x16(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, int w, int h); void Simple3x32(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, int w, int h); -void Simple3x(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, int w, int h); +void Simple3x16(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, int w, int h); void Simple4x32(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, int w, int h); -void Simple4x(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, int w, int h); +void Simple4x16(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, int w, int h); // note: 16-bit input for asm version only! void hq3x32(uint8_t* src, uint32_t spitch, uint8_t*, uint8_t* dst, uint32_t dstp, int w, int h); // this takes 32-bit input diff --git a/src/components/filters_agb/filters_agb.cpp b/src/components/filters_agb/filters_agb.cpp index 6f29df72..e5eacc6e 100644 --- a/src/components/filters_agb/filters_agb.cpp +++ b/src/components/filters_agb/filters_agb.cpp @@ -5,6 +5,7 @@ extern int systemRedShift; extern int systemGreenShift; extern int systemBlueShift; +extern uint8_t systemColorMap8[0x10000]; extern uint16_t systemColorMap16[0x10000]; extern uint32_t systemColorMap32[0x10000]; @@ -27,6 +28,13 @@ inline void swap(short& a, short& b) void gbafilter_update_colors(bool lcd) { switch (systemColorDepth) { + case 8: { + for (int i = 0; i < 0x10000; i++) { + systemColorMap8[i] = (uint8_t)((((i & 0x1f) << 3) & 0xE0) | + ((((i & 0x3e0) >> 5) << 0) & 0x1C) | + ((((i & 0x7c00) >> 10) >> 3) & 0x3)); + } + } break; case 16: { for (int i = 0; i < 0x10000; i++) { systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | @@ -237,6 +245,15 @@ void gbafilter_pad(uint8_t* buf, int count) void UpdateSystemColorMaps(int lcd) { switch(systemColorDepth) { + case 8: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap8[i] = (((i & 0x1f) << systemRedShift) & 0xE0) | + ((((i & 0x3e0) >> 5) << systemGreenShift) & 0x1C) | + ((((i & 0x7c00) >> 10) << systemBlueShift) & 0x3); + } + } + break; case 16: { for(int i = 0; i < 0x10000; i++) { diff --git a/src/components/filters_interframe/interframe.cpp b/src/components/filters_interframe/interframe.cpp index f46c7be1..7970f43c 100644 --- a/src/components/filters_interframe/interframe.cpp +++ b/src/components/filters_interframe/interframe.cpp @@ -159,6 +159,51 @@ static void SmartIB_MMX(uint8_t *srcPtr, uint32_t srcPitch, int width, int start } #endif +void SmartIB8(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height) +{ + (void)width; // unused param + if(frm1 == NULL) { + InterframeFilterInit(); + } + + uint16_t colorMask = ~RGB_LOW_BITS_MASK; + + uint8_t *src0 = (uint8_t *)srcPtr + starty * srcPitch; + uint8_t *src1 = (uint8_t *)frm1 + srcPitch * starty; + uint8_t *src2 = (uint8_t *)frm2 + srcPitch * starty; + uint8_t *src3 = (uint8_t *)frm3 + srcPitch * starty; + + int sPitch = srcPitch; + + int pos = 0; + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) { + uint16_t color = src0[pos] == 0xff ? 0x7fff : ((src0[pos] & 0xe0) << 7) | ((src0[pos] & 0x1c) << 5) | ((src0[pos] & 0x3) << 3); + uint16_t color2 = src1[pos] == 0xff ? 0x7fff : ((src1[pos] & 0xe0) << 7) | ((src1[pos] & 0x1c) << 5) | ((src1[pos] & 0x3) << 3); + uint16_t color_dst = ((color & colorMask) >> 1) + ((color2 & colorMask) >> 1); + src0[pos] = + (src1[pos] != src2[pos]) && + (src3[pos] != color) && + ((color == src2[pos]) || (src1[pos] == src3[pos])) + ? (uint8_t)(((color_dst >> 7) & 0xe0) | ((color_dst >> 5) & 0x1c) | ((color_dst >> 3) & 0x3)) : + (uint8_t)(((color >> 7) & 0xe0) | ((color >> 5) & 0x1c) | ((color >> 3) & 0x3)); + src3[pos] = (uint8_t)(((color >> 7) & 0xe0) | ((color >> 5) & 0x1c) | ((color >> 3) & 0x3)); /* oldest buffer now holds newest frame */ + pos++; + } + + /* Swap buffers around */ + uint8_t *temp = frm1; + frm1 = frm3; + frm3 = frm2; + frm2 = temp; + +} + +void SmartIB8(uint8_t *srcPtr, uint32_t srcPitch, int width, int height) +{ + SmartIB8(srcPtr, srcPitch, width, 0, height); +} + void SmartIB(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height) { (void)width; // unused param @@ -207,6 +252,64 @@ void SmartIB(uint8_t *srcPtr, uint32_t srcPitch, int width, int height) SmartIB(srcPtr, srcPitch, width, 0, height); } +void SmartIB24(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height) +{ + (void)width; // unused param + if(frm1 == NULL) { + InterframeFilterInit(); + } + + uint8_t colorMask = 0xfe; + + uint8_t *src0 = (uint8_t *)srcPtr + starty * srcPitch / 3; + uint8_t *src1 = (uint8_t *)frm1 + srcPitch * starty / 3; + uint8_t *src2 = (uint8_t *)frm2 + srcPitch * starty / 3; + uint8_t *src3 = (uint8_t *)frm3 + srcPitch * starty / 3; + + int sPitch = srcPitch / 3; + + int pos = 0; + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) { + uint8_t color = src0[pos]; + uint8_t color2 = src0[pos+1]; + uint8_t color3 = src0[pos+2]; + src0[pos] = + (src1[pos] != src2[pos]) && + (src3[pos] != color) && + ((color == src2[pos]) || (src1[pos] == src3[pos])) + ? (((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1)) : + color; + src0[pos+1] = + (src1[pos+1] != src2[pos+1]) && + (src3[pos+1] != color2) && + ((color2 == src2[pos+1]) || (src1[pos+1] == src3[pos+1])) + ? (((color2 & colorMask) >> 1) + ((src1[pos+1] & colorMask) >> 1)) : + color2; + src0[pos+2] = + (src1[pos+2] != src2[pos+2]) && + (src3[pos+2] != color3) && + ((color3 == src2[pos+2]) || (src1[pos+1] == src3[pos+2])) + ? (((color3 & colorMask) >> 1) + ((src1[pos+2] & colorMask) >> 1)) : + color3; + src3[pos] = color; /* oldest buffer now holds newest frame */ + src3[pos+1] = color2; /* oldest buffer now holds newest frame */ + src3[pos+2] = color3; /* oldest buffer now holds newest frame */ + pos += 3; + } + + /* Swap buffers around */ + uint8_t *temp = frm1; + frm1 = frm3; + frm3 = frm2; + frm2 = temp; +} + +void SmartIB24(uint8_t *srcPtr, uint32_t srcPitch, int width, int height) +{ + SmartIB24(srcPtr, srcPitch, width, 0, height); +} + #ifdef MMX static void SmartIB32_MMX(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height) { @@ -443,6 +546,70 @@ static void MotionBlurIB_MMX(uint8_t *srcPtr, uint32_t srcPitch, int width, int } #endif +void MotionBlurIB24(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height) +{ + (void)width; // unused param + if(frm1 == NULL) { + InterframeFilterInit(); + } + + uint8_t colorMask = 0xfe; + + uint8_t *src0 = (uint8_t *)srcPtr + starty * srcPitch / 3; + uint8_t *src1 = (uint8_t *)frm1 + starty * srcPitch / 3; + int sPitch = srcPitch / 3; + + int pos = 0; + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) { + uint8_t color = src0[pos]; + uint8_t color2 = src0[pos+1]; + uint8_t color3 = src0[pos+2]; + src0[pos] = ((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1); + src0[pos+1] = ((color2 & colorMask) >> 1) + ((src1[pos+1] & colorMask) >> 1); + src0[pos+2] = ((color3 & colorMask) >> 1) + ((src1[pos+2] & colorMask) >> 1); + src1[pos] = color; + src1[pos+1] = color2; + src1[pos+2] = color3; + pos += 3; + } +} + +void MotionBlurIB24(uint8_t *srcPtr, uint32_t srcPitch, int width, int height) +{ + MotionBlurIB24(srcPtr, srcPitch, width, 0, height); +} + +void MotionBlurIB8(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height) +{ + (void)width; // unused param + if(frm1 == NULL) { + InterframeFilterInit(); + } + + uint16_t colorMask = ~RGB_LOW_BITS_MASK; + + uint8_t *src0 = (uint8_t *)srcPtr + starty * srcPitch; + uint8_t *src1 = (uint8_t *)frm1 + starty * srcPitch; + int sPitch = srcPitch; + + int pos = 0; + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) { + uint16_t color = src0[pos] == 0xff ? 0x7fff : ((src0[pos] & 0xe0) << 7) | ((src0[pos] & 0x1c) << 5) | ((src0[pos] & 0x3) << 3); + uint16_t color2 = src1[pos] == 0xff ? 0x7fff : ((src1[pos] & 0xe0) << 7) | ((src1[pos] & 0x1c) << 5) | ((src1[pos] & 0x3) << 3); + uint16_t color_dst = ((color & colorMask) >> 1) + ((color2 & colorMask) >> 1); + src0[pos] = (uint8_t)(((color_dst >> 7) & 0xe0) | ((color_dst >> 5) & 0x1c) | ((color_dst >> 3) & 0x3)); + src1[pos] = (uint8_t)(((color >> 7) & 0xe0) | ((color >> 5) & 0x1c) | ((color >> 3) & 0x3)); + pos++; + } +} + +void MotionBlurIB8(uint8_t *srcPtr, uint32_t srcPitch, int width, int height) +{ + MotionBlurIB8(srcPtr, srcPitch, width, 0, height); +} + void MotionBlurIB(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height) { (void)width; // unused param diff --git a/src/components/filters_interframe/interframe.h b/src/components/filters_interframe/interframe.h index 830ec067..1f37e607 100644 --- a/src/components/filters_interframe/interframe.h +++ b/src/components/filters_interframe/interframe.h @@ -14,8 +14,12 @@ void InterframeCleanup(); // all 4 are MMX-accelerated if enabled void SmartIB(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height); +void SmartIB8(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height); +void SmartIB24(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height); void SmartIB32(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height); void MotionBlurIB(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height); +void MotionBlurIB8(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height); +void MotionBlurIB24(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height); void MotionBlurIB32(uint8_t *srcPtr, uint32_t srcPitch, int width, int starty, int height); #ifdef MMX @@ -27,8 +31,12 @@ static void MotionBlurIB32_MMX(uint8_t *srcPtr, uint32_t srcPitch, int width, in //Options for if start is 0 void SmartIB(uint8_t *srcPtr, uint32_t srcPitch, int width, int height); +void SmartIB8(uint8_t *srcPtr, uint32_t srcPitch, int width, int height); +void SmartIB24(uint8_t *srcPtr, uint32_t srcPitch, int width, int height); void SmartIB32(uint8_t *srcPtr, uint32_t srcPitch, int width, int height); void MotionBlurIB(uint8_t *srcPtr, uint32_t srcPitch, int width, int height); +void MotionBlurIB8(uint8_t *srcPtr, uint32_t srcPitch, int width, int height); +void MotionBlurIB24(uint8_t *srcPtr, uint32_t srcPitch, int width, int height); void MotionBlurIB32(uint8_t *srcPtr, uint32_t srcPitch, int width, int height); #endif //VBAM_COMPONENTS_FILTERS_INTERFRAME_INTERFRAME_H_ diff --git a/src/core/base/image_util.cpp b/src/core/base/image_util.cpp index 375c0f0d..ce6c8edd 100644 --- a/src/core/base/image_util.cpp +++ b/src/core/base/image_util.cpp @@ -10,6 +10,8 @@ extern "C" { #include "core/base/system.h" #include "core/base/message.h" +bool no_border = false; + bool utilWritePNGFile(const char* fileName, int w, int h, uint8_t* pix) { static constexpr size_t kNumChannels = 3; uint8_t* writeBuffer = new uint8_t[w * h * kNumChannels]; @@ -20,6 +22,27 @@ bool utilWritePNGFile(const char* fileName, int w, int h, uint8_t* pix) { int sizeY = h; switch (systemColorDepth) { + case 8: { + uint8_t* pixU8 = (uint8_t*)pix + (w); + for (int y = 0; y < sizeY; y++) { + for (int x = 0; x < sizeX; x++, pixU8++) { + // White color fix + if (*pixU8 == 0xff) { + *b++ = 0xff; + *b++ = 0xff; + *b++ = 0xff; + } else { + *b++ = (((*pixU8 >> 5) & 0x7) << 5); + *b++ = (((*pixU8 >> 2) & 0x7) << 5); + *b++ = ((*pixU8 & 0x3) << 6); + } + } + + if (no_border == false) { + pixU8 += 2; + } + } + } break; case 16: { uint16_t* p = (uint16_t*)(pix + (w + 2) * 2); // skip first black line for (int y = 0; y < sizeY; y++) { @@ -125,6 +148,40 @@ bool utilWriteBMPFile(const char* fileName, int w, int h, uint8_t* pix) { int sizeY = h; switch (systemColorDepth) { + case 8: { + uint8_t* pixU8 = 0; + if (no_border == false) { + pixU8 = (uint8_t*)pix + ((w + 2) * (h)); + } else { + pixU8 = (uint8_t*)pix + ((w) * (h)); + } + + for (int y = 0; y < sizeY; y++) { + for (int x = 0; x < sizeX; x++, pixU8++) { + // White color fix + if (*pixU8 == 0xff) { + *b++ = 0xff; + *b++ = 0xff; + *b++ = 0xff; + } else { + *b++ = ((*pixU8 & 0x3) << 6); + *b++ = (((*pixU8 >> 2) & 0x7) << 5); + *b++ = (((*pixU8 >> 5) & 0x7) << 5); + } + } + + if (no_border == false) { + pixU8++; + pixU8++; + pixU8 -= 2 * (w + 2); + } else { + pixU8 -= 2 * (w); + } + + fwrite(writeBuffer, 1, 3 * w, fp); + b = writeBuffer; + } + } break; case 16: { uint16_t* p = (uint16_t*)(pix + (w + 2) * (h) * 2); // skip first black line for (int y = 0; y < sizeY; y++) { diff --git a/src/core/base/image_util.h b/src/core/base/image_util.h index c8f6354b..ae6d3921 100644 --- a/src/core/base/image_util.h +++ b/src/core/base/image_util.h @@ -10,4 +10,6 @@ bool utilWritePNGFile(const char*, int, int, uint8_t*); bool utilWriteBMPFile(const char*, int, int, uint8_t*); -#endif // VBAM_CORE_BASE_IMAGE_UTIL_H_ \ No newline at end of file +extern bool no_border; + +#endif // VBAM_CORE_BASE_IMAGE_UTIL_H_ diff --git a/src/core/base/internal/memgzio.c b/src/core/base/internal/memgzio.c index ce36d6c9..e94409cc 100644 --- a/src/core/base/internal/memgzio.c +++ b/src/core/base/internal/memgzio.c @@ -20,6 +20,10 @@ #include "core/base/internal/memgzio.h" +#if __STDC_WANT_SECURE_LIB__ +#define vsnprintf vsprintf_s +#endif + #ifndef local #define local static #endif @@ -213,7 +217,7 @@ local int memPrintf(MEMFILE *f, const char *format, ...) int len; va_start(list, format); - len = vsprintf(buffer, format, list); + len = vsnprintf(buffer, sizeof(buffer), format, list); va_end(list); return (int)memWrite(buffer, 1, len, f); diff --git a/src/core/base/system.h b/src/core/base/system.h index fa4b2762..00924e0c 100644 --- a/src/core/base/system.h +++ b/src/core/base/system.h @@ -110,6 +110,7 @@ extern void systemFrame(); extern void systemGbBorderOn(); extern void (*dbgOutput)(const char* s, uint32_t addr); extern void (*dbgSignal)(int sig, int number); +extern uint8_t systemColorMap8[0x10000]; extern uint16_t systemColorMap16[0x10000]; extern uint32_t systemColorMap32[0x10000]; extern uint16_t systemGbPalette[24]; diff --git a/src/core/gb/gb.cpp b/src/core/gb/gb.cpp index d7e91459..87ac58a8 100644 --- a/src/core/gb/gb.cpp +++ b/src/core/gb/gb.cpp @@ -3887,6 +3887,41 @@ int gbGetNextEvent(int _clockTicks) void gbDrawLine() { switch (systemColorDepth) { + case 8: { +#ifdef __LIBRETRO__ + uint8_t* dest = (uint8_t*)g_pix + gbBorderLineSkip * (register_LY + gbBorderRowSkip) + + gbBorderColumnSkip; +#else + uint8_t* dest = (uint8_t*)g_pix + (gbBorderLineSkip + 2) * (register_LY + gbBorderRowSkip + 1) + + gbBorderColumnSkip; +#endif + for (size_t x = 0; x < kGBWidth;) { + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + *dest++ = systemColorMap8[gbLineMix[x++]]; + } + if (gbBorderOn) + dest += gbBorderColumnSkip; +#ifndef __LIBRETRO__ + *dest++ = 0; // for filters that read one pixel more +#endif + } break; case 16: { #ifdef __LIBRETRO__ uint16_t* dest = (uint16_t*)g_pix + gbBorderLineSkip * (register_LY + gbBorderRowSkip) diff --git a/src/core/gb/gbDis.cpp b/src/core/gb/gbDis.cpp index 8061357b..e176799a 100644 --- a/src/core/gb/gbDis.cpp +++ b/src/core/gb/gbDis.cpp @@ -4,6 +4,10 @@ #include "core/gb/gbGlobals.h" +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#endif + typedef struct { uint8_t mask; uint8_t value; @@ -132,12 +136,12 @@ static char* addStr(char* p, const char* s) return p; } -int gbDis(char* buffer, uint16_t address) +int gbDis(char* buffer, int buflen, uint16_t address) { char* p = buffer; uint16_t instr = 1; uint16_t addr = address; - sprintf(p, "%04x ", address); + snprintf(p, buflen, "%04x ", address); p += 12; uint8_t opcode = GB_READ(address); @@ -183,7 +187,7 @@ int gbDis(char* buffer, uint16_t address) disp = GB_READ(address); if (disp >= 0) *p++ = '+'; - p += sprintf(p, "%d", disp); + p += snprintf(p, buflen, "%d", disp); instr++; break; case 'd': diff --git a/src/core/gb/gbDis.h b/src/core/gb/gbDis.h index 3603db9d..a0c3d9c1 100644 --- a/src/core/gb/gbDis.h +++ b/src/core/gb/gbDis.h @@ -3,6 +3,6 @@ #include -int gbDis(char*, uint16_t); +int gbDis(char* buffer, int buflen, uint16_t address); #endif // VBAM_CORE_GB_GBDIS_H_ diff --git a/src/core/gb/gbSGB.cpp b/src/core/gb/gbSGB.cpp index e084b848..c2741d88 100644 --- a/src/core/gb/gbSGB.cpp +++ b/src/core/gb/gbSGB.cpp @@ -54,6 +54,11 @@ inline void gbSgbDraw16Bit(uint16_t* p, uint16_t v) *p = systemColorMap16[v]; } +inline void gbSgbDraw8Bit(uint8_t* p, uint16_t v) +{ + *p = systemColorMap8[v]; +} + void gbSgbReset() { gbSgbPacketTimeout = 0; @@ -112,6 +117,18 @@ void gbSgbShutdown() void gbSgbFillScreen(uint16_t color) { switch (systemColorDepth) { + case 8: { + for (int y = 0; y < 144; y++) { +#ifdef __LIBRETRO__ + int yLine = (y + gbBorderRowSkip) * gbBorderLineSkip + gbBorderColumnSkip; +#else + int yLine = (y + gbBorderRowSkip + 1) * (gbBorderLineSkip + 2) + gbBorderColumnSkip; +#endif + uint8_t* dest = (uint8_t*)g_pix + yLine; + for (int x = 0; x < 160; x++) + gbSgbDraw8Bit(dest++, color); + } + } break; case 16: { for (int y = 0; y < 144; y++) { #ifdef __LIBRETRO__ @@ -198,7 +215,7 @@ void gbSgbDrawBorderTile(int x, int y, int tile, int attr) uint32_t* dest32 = (uint32_t*)g_pix + ((y + 1) * (256 + 1)) + x; #endif uint8_t* dest8 = (uint8_t*)g_pix + ((y * 256) + x) * 3; - + uint8_t* dest8b = (uint8_t*)g_pix + ((y * 256) + x); uint8_t* tileAddress = &gbSgbBorderChar[tile * 32]; uint8_t* tileAddress2 = &gbSgbBorderChar[tile * 32 + 16]; @@ -258,6 +275,12 @@ void gbSgbDrawBorderTile(int x, int y, int tile, int attr) } switch (systemColorDepth) { + case 8: +#ifdef __LIBRETRO__ + gbSgbDraw8Bit(dest8b + yyy * 256 + xxx, cc); +#else + gbSgbDraw8Bit(dest8b + yyy * (256 + 2) + xxx, cc); +#endif case 16: #ifdef __LIBRETRO__ gbSgbDraw16Bit(dest + yyy * 256 + xxx, cc); diff --git a/src/core/gba/gba.cpp b/src/core/gba/gba.cpp index 6551ca90..09a45e7b 100644 --- a/src/core/gba/gba.cpp +++ b/src/core/gba/gba.cpp @@ -3958,6 +3958,38 @@ void CPULoop(int ticks) if (frameCount >= framesToSkip) { (*renderLine)(); switch (systemColorDepth) { + case 8: { +#ifdef __LIBRETRO__ + uint8_t* dest = (uint8_t*)g_pix + 240 * VCOUNT; +#else + uint8_t* dest = (uint8_t*)g_pix + 242 * (VCOUNT + 1); +#endif + for (int x = 0; x < 240;) { + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap8[g_lineMix[x++] & 0xFFFF]; + } +// for filters that read past the screen +#ifndef __LIBRETRO__ + *dest++ = 0; +#endif + } break; case 16: { #ifdef __LIBRETRO__ uint16_t* dest = (uint16_t*)g_pix + 240 * VCOUNT; diff --git a/src/core/gba/gbaCheats.cpp b/src/core/gba/gbaCheats.cpp index cbfc3aa3..63e48ed1 100644 --- a/src/core/gba/gbaCheats.cpp +++ b/src/core/gba/gbaCheats.cpp @@ -9,6 +9,10 @@ #include "core/gba/gbaInline.h" #include "core/gba/gbaGlobals.h" +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#endif + /** * Gameshark code types: (based on AR v1.0) * @@ -2615,15 +2619,15 @@ void cheatsReadGame(gzFile file, int version) if (!cheatsList[i].codestring[0]) { switch (cheatsList[i].size) { case 0: - sprintf(cheatsList[i].codestring, "%08x:%02x", cheatsList[i].address, + snprintf(cheatsList[i].codestring, sizeof(cheatsList[i].codestring), "%08x:%02x", cheatsList[i].address, cheatsList[i].value); break; case 1: - sprintf(cheatsList[i].codestring, "%08x:%04x", cheatsList[i].address, + snprintf(cheatsList[i].codestring, sizeof(cheatsList[i].codestring), "%08x:%04x", cheatsList[i].address, cheatsList[i].value); break; case 2: - sprintf(cheatsList[i].codestring, "%08x:%08x", cheatsList[i].address, + snprintf(cheatsList[i].codestring, sizeof(cheatsList[i].codestring), "%08x:%08x", cheatsList[i].address, cheatsList[i].value); break; } @@ -2759,15 +2763,15 @@ bool cheatsLoadCheatList(const char* file) if (!cheatsList[i].codestring[0]) { switch (cheatsList[i].size) { case 0: - sprintf(cheatsList[i].codestring, "%08x:%02x", cheatsList[i].address, + snprintf(cheatsList[i].codestring, sizeof(cheatsList[i].codestring), "%08x:%02x", cheatsList[i].address, cheatsList[i].value); break; case 1: - sprintf(cheatsList[i].codestring, "%08x:%04x", cheatsList[i].address, + snprintf(cheatsList[i].codestring, sizeof(cheatsList[i].codestring), "%08x:%04x", cheatsList[i].address, cheatsList[i].value); break; case 2: - sprintf(cheatsList[i].codestring, "%08x:%08x", cheatsList[i].address, + snprintf(cheatsList[i].codestring, sizeof(cheatsList[i].codestring), "%08x:%08x", cheatsList[i].address, cheatsList[i].value); break; } diff --git a/src/core/gba/gbaLink.cpp b/src/core/gba/gbaLink.cpp index a8687948..b7779c21 100644 --- a/src/core/gba/gbaLink.cpp +++ b/src/core/gba/gbaLink.cpp @@ -38,8 +38,12 @@ #include "core/gba/internal/gbaSockClient.h" #ifdef _MSC_VER +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#else #define snprintf _snprintf #endif +#endif #ifdef UPDATE_REG #undef UPDATE_REG @@ -58,9 +62,9 @@ const char* MakeInstanceFilename(const char* Input) free(result); } - result = (char*)malloc(strlen(Input) + 3); + result = (char*)malloc(strlen(Input) + 4); char* p = strrchr((char*)Input, '.'); - sprintf(result, "%.*s-%d.%s", (int)(p - Input), Input, vbaid + 1, p + 1); + snprintf(result, strlen(Input) + 3, "%.*s-%d.%s", (int)(p - Input), Input, vbaid + 1, p + 1); return result; } @@ -852,7 +856,7 @@ void CableServer::Recv(void) } if (inbuffer[1] == -32) { char message[30]; - sprintf(message, _("Player %d disconnected."), i + 2); + snprintf(message, sizeof(message), _("Player %d disconnected."), i + 2); systemScreenMessage(message); outbuffer[0] = 4; outbuffer[1] = -32; @@ -916,7 +920,7 @@ bool CableServer::RecvGB(void) if (inbuffer[1] == -32) { char message[30]; - sprintf(message, _("Player %d disconnected."), i + 2); + snprintf(message, sizeof(message), _("Player %d disconnected."), i + 2); systemScreenMessage(message); for (i = 1; i < lanlink.numslaves; i++) { tcpsocket[i].disconnect(); @@ -1570,7 +1574,7 @@ void RFUServer::Recv(void) sf::Socket::Status status = tcpsocket[i + 1].receive(packet); if (status == sf::Socket::Status::Disconnected) { char message[30]; - sprintf(message, _("Player %d disconnected."), i + 1); + snprintf(message, sizeof(message), _("Player %d disconnected."), i + 1); systemScreenMessage(message); //tcpsocket[i + 1].disconnect(); //CloseLink(); @@ -2902,7 +2906,7 @@ static void UpdateCableIPC(int) if (f < (1 << transfer_direction) - 1) linkmem->numgbas = transfer_direction - 1; char message[30]; - sprintf(message, _("Player %d disconnected."), transfer_direction - 1); + snprintf(message, sizeof(message), _("Player %d disconnected."), transfer_direction - 1); systemScreenMessage(message); } transfer_direction = linkmem->trgbas + 1; diff --git a/src/core/gba/gbaRemote.cpp b/src/core/gba/gbaRemote.cpp index 70524460..bcb8cbad 100644 --- a/src/core/gba/gbaRemote.cpp +++ b/src/core/gba/gbaRemote.cpp @@ -48,6 +48,10 @@ #include "core/gba/gbaRemote.h" #include "core/gba/internal/gbaBreakpoint.h" +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#endif + extern int emulating; extern void CPUUpdateCPSR(); @@ -251,11 +255,11 @@ void printBreakRegList(bool verbose) if (regBreakCounter[i][k]) { if (!anyPrint) { { - sprintf(monbuf, "Register breakpoint list:\n"); + snprintf(monbuf, sizeof(monbuf), "Register breakpoint list:\n"); monprintf(monbuf); } { - sprintf(monbuf, "-------------------------\n"); + snprintf(monbuf, sizeof(monbuf), "-------------------------\n"); monprintf(monbuf); } anyPrint = true; @@ -263,37 +267,37 @@ void printBreakRegList(bool verbose) struct regBreak* tmp = breakRegList[i * 4 + k]; for (int j = 0; j < regBreakCounter[i][k]; j++) { if (tmp->flags & 8) { - sprintf(monbuf, "No. %d:\tBreak if (signed)%s %08x\n", j, flagsToOP[tmp->flags & 7], tmp->intVal); + snprintf(monbuf, sizeof(monbuf), "No. %d:\tBreak if (signed)%s %08x\n", j, flagsToOP[tmp->flags & 7], tmp->intVal); monprintf(monbuf); } else { - sprintf(monbuf, "No. %d:\tBreak if %s %08x\n", j, flagsToOP[tmp->flags], tmp->intVal); + snprintf(monbuf, sizeof(monbuf), "No. %d:\tBreak if %s %08x\n", j, flagsToOP[tmp->flags], tmp->intVal); monprintf(monbuf); } tmp = tmp->next; } { - sprintf(monbuf, "-------------------------\n"); + snprintf(monbuf, sizeof(monbuf), "-------------------------\n"); monprintf(monbuf); } } else { if (verbose) { if (!anyPrint) { { - sprintf(monbuf, "Register breakpoint list:\n"); + snprintf(monbuf, sizeof(monbuf), "Register breakpoint list:\n"); monprintf(monbuf); } { - sprintf(monbuf, "-------------------------\n"); + snprintf(monbuf, sizeof(monbuf), "-------------------------\n"); monprintf(monbuf); } anyPrint = true; } { - sprintf(monbuf, "No breaks on r%d.\n", i); + snprintf(monbuf, sizeof(monbuf), "No breaks on r%d.\n", i); monprintf(monbuf); } { - sprintf(monbuf, "-------------------------\n"); + snprintf(monbuf, sizeof(monbuf), "-------------------------\n"); monprintf(monbuf); } } @@ -302,7 +306,7 @@ void printBreakRegList(bool verbose) } if (!verbose && !anyPrint) { { - sprintf(monbuf, "No Register breaks found.\n"); + snprintf(monbuf, sizeof(monbuf), "No Register breaks found.\n"); monprintf(monbuf); } } @@ -350,7 +354,7 @@ void debuggerDontBreak(int n, char** args) debuggerNoBreakpointList[i] = address; debuggerNumOfDontBreak++; { - sprintf(monbuf, "Added Don't Break at %08x\n", address); + snprintf(monbuf, sizeof(monbuf), "Added Don't Break at %08x\n", address); monprintf(monbuf); } } else @@ -363,7 +367,7 @@ void debuggerDontBreakClear(int n, char** args) if (n == 1) { debuggerNumOfDontBreak = 0; { - sprintf(monbuf, "Cleared Don't Break list.\n"); + snprintf(monbuf, sizeof(monbuf), "Cleared Don't Break list.\n"); monprintf(monbuf); } } else @@ -382,7 +386,7 @@ void debuggerDumpLoad(int n, char** args) if (!dexp_eval(args[2], &address)) { { - sprintf(monbuf, "Invalid expression in address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n"); monprintf(monbuf); } return; @@ -391,7 +395,7 @@ void debuggerDumpLoad(int n, char** args) f = fopen(file, "rb"); if (f == NULL) { { - sprintf(monbuf, "Error opening file.\n"); + snprintf(monbuf, sizeof(monbuf), "Error opening file.\n"); monprintf(monbuf); } return; @@ -425,14 +429,14 @@ void debuggerDumpSave(int n, char** args) file = args[1]; if (!dexp_eval(args[2], &address)) { { - sprintf(monbuf, "Invalid expression in address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n"); monprintf(monbuf); } return; } if (!dexp_eval(args[3], &size)) { { - sprintf(monbuf, "Invalid expression in size"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in size"); monprintf(monbuf); } return; @@ -441,7 +445,7 @@ void debuggerDumpSave(int n, char** args) f = fopen(file, "wb"); if (f == NULL) { { - sprintf(monbuf, "Error opening file.\n"); + snprintf(monbuf, sizeof(monbuf), "Error opening file.\n"); monprintf(monbuf); } return; @@ -464,7 +468,7 @@ void debuggerEditByte(int n, char** args) uint32_t value; if (!dexp_eval(args[1], &address)) { { - sprintf(monbuf, "Invalid expression in address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n"); monprintf(monbuf); } return; @@ -472,7 +476,7 @@ void debuggerEditByte(int n, char** args) for (int i = 2; i < n; i++) { if (!dexp_eval(args[i], &value)) { { - sprintf(monbuf, "Invalid expression in %d value.Ignored.\n", (i - 1)); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in %d value.Ignored.\n", (i - 1)); monprintf(monbuf); } } @@ -490,14 +494,14 @@ void debuggerEditHalfWord(int n, char** args) uint32_t value; if (!dexp_eval(args[1], &address)) { { - sprintf(monbuf, "Invalid expression in address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n"); monprintf(monbuf); } return; } if (address & 1) { { - sprintf(monbuf, "Error: address must be half-word aligned\n"); + snprintf(monbuf, sizeof(monbuf), "Error: address must be half-word aligned\n"); monprintf(monbuf); } return; @@ -505,7 +509,7 @@ void debuggerEditHalfWord(int n, char** args) for (int i = 2; i < n; i++) { if (!dexp_eval(args[i], &value)) { { - sprintf(monbuf, "Invalid expression in %d value.Ignored.\n", (i - 1)); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in %d value.Ignored.\n", (i - 1)); monprintf(monbuf); } } @@ -523,14 +527,14 @@ void debuggerEditWord(int n, char** args) uint32_t value; if (!dexp_eval(args[1], &address)) { { - sprintf(monbuf, "Invalid expression in address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n"); monprintf(monbuf); } return; } if (address & 3) { { - sprintf(monbuf, "Error: address must be word aligned\n"); + snprintf(monbuf, sizeof(monbuf), "Error: address must be word aligned\n"); monprintf(monbuf); } return; @@ -538,7 +542,7 @@ void debuggerEditWord(int n, char** args) for (int i = 2; i < n; i++) { if (!dexp_eval(args[i], &value)) { { - sprintf(monbuf, "Invalid expression in %d value.Ignored.\n", (i - 1)); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in %d value.Ignored.\n", (i - 1)); monprintf(monbuf); } } @@ -572,12 +576,12 @@ bool debuggerBreakOnRegisterCondition(uint8_t registerName, uint32_t compareVal, typeName = "unknown"; } { - sprintf(monbuf, "Breakpoint on R%02d : %08x is %s register content (%08x)\n", registerName, compareVal, typeName, regVal); + snprintf(monbuf, sizeof(monbuf), "Breakpoint on R%02d : %08x is %s register content (%08x)\n", registerName, compareVal, typeName, regVal); monprintf(monbuf); } if (debuggerInDB(armState ? reg[15].I - 4 : reg[15].I - 2)) { { - sprintf(monbuf, "But this address is marked not to break, so skipped\n"); + snprintf(monbuf, sizeof(monbuf), "But this address is marked not to break, so skipped\n"); monprintf(monbuf); } return false; @@ -616,21 +620,21 @@ void debuggerEditRegister(int n, char** args) uint32_t val; if (r > 16) { { - sprintf(monbuf, "Error: Register must be valid (0-16)\n"); + snprintf(monbuf, sizeof(monbuf), "Error: Register must be valid (0-16)\n"); monprintf(monbuf); } return; } if (!dexp_eval(args[2], &val)) { { - sprintf(monbuf, "Invalid expression in value.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in value.\n"); monprintf(monbuf); } return; } reg[r].I = val; { - sprintf(monbuf, "R%02d=%08X\n", r, val); + snprintf(monbuf, sizeof(monbuf), "R%02d=%08X\n", r, val); monprintf(monbuf); } } else @@ -643,12 +647,12 @@ void debuggerEval(int n, char** args) uint32_t result = 0; if (dexp_eval(args[1], &result)) { { - sprintf(monbuf, " =$%08X\n", result); + snprintf(monbuf, sizeof(monbuf), " =$%08X\n", result); monprintf(monbuf); } } else { { - sprintf(monbuf, "Invalid expression\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression\n"); monprintf(monbuf); } } @@ -664,20 +668,20 @@ void debuggerFillByte(int n, char** args) uint32_t reps; if (!dexp_eval(args[1], &address)) { { - sprintf(monbuf, "Invalid expression in address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n"); monprintf(monbuf); } return; } if (!dexp_eval(args[2], &value)) { { - sprintf(monbuf, "Invalid expression in value.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in value.\n"); monprintf(monbuf); } } if (!dexp_eval(args[3], &reps)) { { - sprintf(monbuf, "Invalid expression in repetition number.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n"); monprintf(monbuf); } } @@ -697,24 +701,24 @@ void debuggerFillHalfWord(int n, char** args) uint32_t reps; if (!dexp_eval(args[1], &address)) { { - sprintf(monbuf, "Invalid expression in address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n"); monprintf(monbuf); } return; } /* if(address & 1) { - { sprintf(monbuf, "Error: address must be halfword aligned\n"); monprintf(monbuf); } + { snprintf(monbuf, sizeof(monbuf), "Error: address must be halfword aligned\n"); monprintf(monbuf); } return; }*/ if (!dexp_eval(args[2], &value)) { { - sprintf(monbuf, "Invalid expression in value.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in value.\n"); monprintf(monbuf); } } if (!dexp_eval(args[3], &reps)) { { - sprintf(monbuf, "Invalid expression in repetition number.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n"); monprintf(monbuf); } } @@ -734,24 +738,24 @@ void debuggerFillWord(int n, char** args) uint32_t reps; if (!dexp_eval(args[1], &address)) { { - sprintf(monbuf, "Invalid expression in address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n"); monprintf(monbuf); } return; } /* if(address & 3) { - { sprintf(monbuf, "Error: address must be word aligned\n"); monprintf(monbuf); } + { snprintf(monbuf, sizeof(monbuf), "Error: address must be word aligned\n"); monprintf(monbuf); } return; }*/ if (!dexp_eval(args[2], &value)) { { - sprintf(monbuf, "Invalid expression in value.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in value.\n"); monprintf(monbuf); } } if (!dexp_eval(args[3], &reps)) { { - sprintf(monbuf, "Invalid expression in repetition number.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n"); monprintf(monbuf); } } @@ -876,7 +880,7 @@ void debuggerDoSearch() break; }; default: { - sprintf(monbuf, "Search completed.\n"); + snprintf(monbuf, sizeof(monbuf), "Search completed.\n"); monprintf(monbuf); } SearchLength = 0; @@ -898,7 +902,7 @@ void debuggerDoSearch() if (p == SearchLength) { { - sprintf(monbuf, "Search result (%d): %08x\n", count + SearchResults, AddressToGBA(start)); + snprintf(monbuf, sizeof(monbuf), "Search result (%d): %08x\n", count + SearchResults, AddressToGBA(start)); monprintf(monbuf); } count++; @@ -923,7 +927,7 @@ void debuggerFindText(int n, char** args) SearchResults = 0; if (!dexp_eval(args[1], &SearchStart)) { { - sprintf(monbuf, "Invalid expression.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression.\n"); monprintf(monbuf); } return; @@ -940,7 +944,7 @@ void debuggerFindText(int n, char** args) if (SearchLength > 64) { { - sprintf(monbuf, "Entered string (length: %d) is longer than 64 bytes and was cut.\n", SearchLength); + snprintf(monbuf, sizeof(monbuf), "Entered string (length: %d) is longer than 64 bytes and was cut.\n", SearchLength); monprintf(monbuf); } SearchLength = 64; @@ -958,7 +962,7 @@ void debuggerFindHex(int n, char** args) SearchResults = 0; if (!dexp_eval(args[1], &SearchStart)) { { - sprintf(monbuf, "Invalid expression.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression.\n"); monprintf(monbuf); } return; @@ -975,7 +979,7 @@ void debuggerFindHex(int n, char** args) }; if (SearchLength & 1) { - sprintf(monbuf, "Unaligned bytecount: %d,5. Last digit (%c) cut.\n", SearchLength / 2, SearchHex[SearchLength - 1]); + snprintf(monbuf, sizeof(monbuf), "Unaligned bytecount: %d,5. Last digit (%c) cut.\n", SearchLength / 2, SearchHex[SearchLength - 1]); monprintf(monbuf); } @@ -983,7 +987,7 @@ void debuggerFindHex(int n, char** args) if (SearchLength > 64) { { - sprintf(monbuf, "Entered string (length: %d) is longer than 64 bytes and was cut.\n", SearchLength); + snprintf(monbuf, sizeof(monbuf), "Entered string (length: %d) is longer than 64 bytes and was cut.\n", SearchLength); monprintf(monbuf); } SearchLength = 64; @@ -1006,7 +1010,7 @@ void debuggerFindResume(int n, char** args) if ((n == 1) || (n == 2)) { if (SearchLength == 0) { { - sprintf(monbuf, "Error: No search in progress. Start a search with ft or fh.\n"); + snprintf(monbuf, sizeof(monbuf), "Error: No search in progress. Start a search with ft or fh.\n"); monprintf(monbuf); } debuggerUsage("fr"); @@ -1035,7 +1039,7 @@ void debuggerCopyByte(int n, char** args) if (n == 5) { if (!dexp_eval(args[4], &reps)) { { - sprintf(monbuf, "Invalid expression in repetition number.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n"); monprintf(monbuf); } } @@ -1043,21 +1047,21 @@ void debuggerCopyByte(int n, char** args) if (n > 3) { if (!dexp_eval(args[3], &number)) { { - sprintf(monbuf, "Invalid expression in number of copy units.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in number of copy units.\n"); monprintf(monbuf); } } } if (!dexp_eval(args[1], &source)) { { - sprintf(monbuf, "Invalid expression in source address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in source address.\n"); monprintf(monbuf); } return; } if (!dexp_eval(args[2], &dest)) { { - sprintf(monbuf, "Invalid expression in destination address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in destination address.\n"); monprintf(monbuf); } } @@ -1083,7 +1087,7 @@ void debuggerCopyHalfWord(int n, char** args) if (n == 5) { if (!dexp_eval(args[4], &reps)) { { - sprintf(monbuf, "Invalid expression in repetition number.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n"); monprintf(monbuf); } } @@ -1091,7 +1095,7 @@ void debuggerCopyHalfWord(int n, char** args) if (n > 3) { if (!dexp_eval(args[3], &number)) { { - sprintf(monbuf, "Invalid expression in number of copy units.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in number of copy units.\n"); monprintf(monbuf); } } @@ -1099,14 +1103,14 @@ void debuggerCopyHalfWord(int n, char** args) } if (!dexp_eval(args[1], &source)) { { - sprintf(monbuf, "Invalid expression in source address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in source address.\n"); monprintf(monbuf); } return; } if (!dexp_eval(args[2], &dest)) { { - sprintf(monbuf, "Invalid expression in destination address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in destination address.\n"); monprintf(monbuf); } } @@ -1132,7 +1136,7 @@ void debuggerCopyWord(int n, char** args) if (n == 5) { if (!dexp_eval(args[4], &reps)) { { - sprintf(monbuf, "Invalid expression in repetition number.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n"); monprintf(monbuf); } } @@ -1140,7 +1144,7 @@ void debuggerCopyWord(int n, char** args) if (n > 3) { if (!dexp_eval(args[3], &number)) { { - sprintf(monbuf, "Invalid expression in number of copy units.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in number of copy units.\n"); monprintf(monbuf); } } @@ -1148,14 +1152,14 @@ void debuggerCopyWord(int n, char** args) } if (!dexp_eval(args[1], &source)) { { - sprintf(monbuf, "Invalid expression in source address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in source address.\n"); monprintf(monbuf); } return; } if (!dexp_eval(args[2], &dest)) { { - sprintf(monbuf, "Invalid expression in destination address.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression in destination address.\n"); monprintf(monbuf); } } @@ -1171,71 +1175,71 @@ void debuggerCopyWord(int n, char** args) void debuggerIoVideo() { { - sprintf(monbuf, "DISPCNT = %04x\n", DISPCNT); + snprintf(monbuf, sizeof(monbuf), "DISPCNT = %04x\n", DISPCNT); monprintf(monbuf); } { - sprintf(monbuf, "DISPSTAT = %04x\n", DISPSTAT); + snprintf(monbuf, sizeof(monbuf), "DISPSTAT = %04x\n", DISPSTAT); monprintf(monbuf); } { - sprintf(monbuf, "VCOUNT = %04x\n", VCOUNT); + snprintf(monbuf, sizeof(monbuf), "VCOUNT = %04x\n", VCOUNT); monprintf(monbuf); } { - sprintf(monbuf, "BG0CNT = %04x\n", BG0CNT); + snprintf(monbuf, sizeof(monbuf), "BG0CNT = %04x\n", BG0CNT); monprintf(monbuf); } { - sprintf(monbuf, "BG1CNT = %04x\n", BG1CNT); + snprintf(monbuf, sizeof(monbuf), "BG1CNT = %04x\n", BG1CNT); monprintf(monbuf); } { - sprintf(monbuf, "BG2CNT = %04x\n", BG2CNT); + snprintf(monbuf, sizeof(monbuf), "BG2CNT = %04x\n", BG2CNT); monprintf(monbuf); } { - sprintf(monbuf, "BG3CNT = %04x\n", BG3CNT); + snprintf(monbuf, sizeof(monbuf), "BG3CNT = %04x\n", BG3CNT); monprintf(monbuf); } { - sprintf(monbuf, "WIN0H = %04x\n", WIN0H); + snprintf(monbuf, sizeof(monbuf), "WIN0H = %04x\n", WIN0H); monprintf(monbuf); } { - sprintf(monbuf, "WIN0V = %04x\n", WIN0V); + snprintf(monbuf, sizeof(monbuf), "WIN0V = %04x\n", WIN0V); monprintf(monbuf); } { - sprintf(monbuf, "WIN1H = %04x\n", WIN1H); + snprintf(monbuf, sizeof(monbuf), "WIN1H = %04x\n", WIN1H); monprintf(monbuf); } { - sprintf(monbuf, "WIN1V = %04x\n", WIN1V); + snprintf(monbuf, sizeof(monbuf), "WIN1V = %04x\n", WIN1V); monprintf(monbuf); } { - sprintf(monbuf, "WININ = %04x\n", WININ); + snprintf(monbuf, sizeof(monbuf), "WININ = %04x\n", WININ); monprintf(monbuf); } { - sprintf(monbuf, "WINOUT = %04x\n", WINOUT); + snprintf(monbuf, sizeof(monbuf), "WINOUT = %04x\n", WINOUT); monprintf(monbuf); } { - sprintf(monbuf, "MOSAIC = %04x\n", MOSAIC); + snprintf(monbuf, sizeof(monbuf), "MOSAIC = %04x\n", MOSAIC); monprintf(monbuf); } { - sprintf(monbuf, "BLDMOD = %04x\n", BLDMOD); + snprintf(monbuf, sizeof(monbuf), "BLDMOD = %04x\n", BLDMOD); monprintf(monbuf); } { - sprintf(monbuf, "COLEV = %04x\n", COLEV); + snprintf(monbuf, sizeof(monbuf), "COLEV = %04x\n", COLEV); monprintf(monbuf); } { - sprintf(monbuf, "COLY = %04x\n", COLY); + snprintf(monbuf, sizeof(monbuf), "COLY = %04x\n", COLY); monprintf(monbuf); } } @@ -1243,83 +1247,83 @@ void debuggerIoVideo() void debuggerIoVideo2() { { - sprintf(monbuf, "BG0HOFS = %04x\n", BG0HOFS); + snprintf(monbuf, sizeof(monbuf), "BG0HOFS = %04x\n", BG0HOFS); monprintf(monbuf); } { - sprintf(monbuf, "BG0VOFS = %04x\n", BG0VOFS); + snprintf(monbuf, sizeof(monbuf), "BG0VOFS = %04x\n", BG0VOFS); monprintf(monbuf); } { - sprintf(monbuf, "BG1HOFS = %04x\n", BG1HOFS); + snprintf(monbuf, sizeof(monbuf), "BG1HOFS = %04x\n", BG1HOFS); monprintf(monbuf); } { - sprintf(monbuf, "BG1VOFS = %04x\n", BG1VOFS); + snprintf(monbuf, sizeof(monbuf), "BG1VOFS = %04x\n", BG1VOFS); monprintf(monbuf); } { - sprintf(monbuf, "BG2HOFS = %04x\n", BG2HOFS); + snprintf(monbuf, sizeof(monbuf), "BG2HOFS = %04x\n", BG2HOFS); monprintf(monbuf); } { - sprintf(monbuf, "BG2VOFS = %04x\n", BG2VOFS); + snprintf(monbuf, sizeof(monbuf), "BG2VOFS = %04x\n", BG2VOFS); monprintf(monbuf); } { - sprintf(monbuf, "BG3HOFS = %04x\n", BG3HOFS); + snprintf(monbuf, sizeof(monbuf), "BG3HOFS = %04x\n", BG3HOFS); monprintf(monbuf); } { - sprintf(monbuf, "BG3VOFS = %04x\n", BG3VOFS); + snprintf(monbuf, sizeof(monbuf), "BG3VOFS = %04x\n", BG3VOFS); monprintf(monbuf); } { - sprintf(monbuf, "BG2PA = %04x\n", BG2PA); + snprintf(monbuf, sizeof(monbuf), "BG2PA = %04x\n", BG2PA); monprintf(monbuf); } { - sprintf(monbuf, "BG2PB = %04x\n", BG2PB); + snprintf(monbuf, sizeof(monbuf), "BG2PB = %04x\n", BG2PB); monprintf(monbuf); } { - sprintf(monbuf, "BG2PC = %04x\n", BG2PC); + snprintf(monbuf, sizeof(monbuf), "BG2PC = %04x\n", BG2PC); monprintf(monbuf); } { - sprintf(monbuf, "BG2PD = %04x\n", BG2PD); + snprintf(monbuf, sizeof(monbuf), "BG2PD = %04x\n", BG2PD); monprintf(monbuf); } { - sprintf(monbuf, "BG2X = %08x\n", (BG2X_H << 16) | BG2X_L); + snprintf(monbuf, sizeof(monbuf), "BG2X = %08x\n", (BG2X_H << 16) | BG2X_L); monprintf(monbuf); } { - sprintf(monbuf, "BG2Y = %08x\n", (BG2Y_H << 16) | BG2Y_L); + snprintf(monbuf, sizeof(monbuf), "BG2Y = %08x\n", (BG2Y_H << 16) | BG2Y_L); monprintf(monbuf); } { - sprintf(monbuf, "BG3PA = %04x\n", BG3PA); + snprintf(monbuf, sizeof(monbuf), "BG3PA = %04x\n", BG3PA); monprintf(monbuf); } { - sprintf(monbuf, "BG3PB = %04x\n", BG3PB); + snprintf(monbuf, sizeof(monbuf), "BG3PB = %04x\n", BG3PB); monprintf(monbuf); } { - sprintf(monbuf, "BG3PC = %04x\n", BG3PC); + snprintf(monbuf, sizeof(monbuf), "BG3PC = %04x\n", BG3PC); monprintf(monbuf); } { - sprintf(monbuf, "BG3PD = %04x\n", BG3PD); + snprintf(monbuf, sizeof(monbuf), "BG3PD = %04x\n", BG3PD); monprintf(monbuf); } { - sprintf(monbuf, "BG3X = %08x\n", (BG3X_H << 16) | BG3X_L); + snprintf(monbuf, sizeof(monbuf), "BG3X = %08x\n", (BG3X_H << 16) | BG3X_L); monprintf(monbuf); } { - sprintf(monbuf, "BG3Y = %08x\n", (BG3Y_H << 16) | BG3Y_L); + snprintf(monbuf, sizeof(monbuf), "BG3Y = %08x\n", (BG3Y_H << 16) | BG3Y_L); monprintf(monbuf); } } @@ -1327,51 +1331,51 @@ void debuggerIoVideo2() void debuggerIoDMA() { { - sprintf(monbuf, "DM0SAD = %08x\n", (DM0SAD_H << 16) | DM0SAD_L); + snprintf(monbuf, sizeof(monbuf), "DM0SAD = %08x\n", (DM0SAD_H << 16) | DM0SAD_L); monprintf(monbuf); } { - sprintf(monbuf, "DM0DAD = %08x\n", (DM0DAD_H << 16) | DM0DAD_L); + snprintf(monbuf, sizeof(monbuf), "DM0DAD = %08x\n", (DM0DAD_H << 16) | DM0DAD_L); monprintf(monbuf); } { - sprintf(monbuf, "DM0CNT = %08x\n", (DM0CNT_H << 16) | DM0CNT_L); + snprintf(monbuf, sizeof(monbuf), "DM0CNT = %08x\n", (DM0CNT_H << 16) | DM0CNT_L); monprintf(monbuf); } { - sprintf(monbuf, "DM1SAD = %08x\n", (DM1SAD_H << 16) | DM1SAD_L); + snprintf(monbuf, sizeof(monbuf), "DM1SAD = %08x\n", (DM1SAD_H << 16) | DM1SAD_L); monprintf(monbuf); } { - sprintf(monbuf, "DM1DAD = %08x\n", (DM1DAD_H << 16) | DM1DAD_L); + snprintf(monbuf, sizeof(monbuf), "DM1DAD = %08x\n", (DM1DAD_H << 16) | DM1DAD_L); monprintf(monbuf); } { - sprintf(monbuf, "DM1CNT = %08x\n", (DM1CNT_H << 16) | DM1CNT_L); + snprintf(monbuf, sizeof(monbuf), "DM1CNT = %08x\n", (DM1CNT_H << 16) | DM1CNT_L); monprintf(monbuf); } { - sprintf(monbuf, "DM2SAD = %08x\n", (DM2SAD_H << 16) | DM2SAD_L); + snprintf(monbuf, sizeof(monbuf), "DM2SAD = %08x\n", (DM2SAD_H << 16) | DM2SAD_L); monprintf(monbuf); } { - sprintf(monbuf, "DM2DAD = %08x\n", (DM2DAD_H << 16) | DM2DAD_L); + snprintf(monbuf, sizeof(monbuf), "DM2DAD = %08x\n", (DM2DAD_H << 16) | DM2DAD_L); monprintf(monbuf); } { - sprintf(monbuf, "DM2CNT = %08x\n", (DM2CNT_H << 16) | DM2CNT_L); + snprintf(monbuf, sizeof(monbuf), "DM2CNT = %08x\n", (DM2CNT_H << 16) | DM2CNT_L); monprintf(monbuf); } { - sprintf(monbuf, "DM3SAD = %08x\n", (DM3SAD_H << 16) | DM3SAD_L); + snprintf(monbuf, sizeof(monbuf), "DM3SAD = %08x\n", (DM3SAD_H << 16) | DM3SAD_L); monprintf(monbuf); } { - sprintf(monbuf, "DM3DAD = %08x\n", (DM3DAD_H << 16) | DM3DAD_L); + snprintf(monbuf, sizeof(monbuf), "DM3DAD = %08x\n", (DM3DAD_H << 16) | DM3DAD_L); monprintf(monbuf); } { - sprintf(monbuf, "DM3CNT = %08x\n", (DM3CNT_H << 16) | DM3CNT_L); + snprintf(monbuf, sizeof(monbuf), "DM3CNT = %08x\n", (DM3CNT_H << 16) | DM3CNT_L); monprintf(monbuf); } } @@ -1379,35 +1383,35 @@ void debuggerIoDMA() void debuggerIoTimer() { { - sprintf(monbuf, "TM0D = %04x\n", TM0D); + snprintf(monbuf, sizeof(monbuf), "TM0D = %04x\n", TM0D); monprintf(monbuf); } { - sprintf(monbuf, "TM0CNT = %04x\n", TM0CNT); + snprintf(monbuf, sizeof(monbuf), "TM0CNT = %04x\n", TM0CNT); monprintf(monbuf); } { - sprintf(monbuf, "TM1D = %04x\n", TM1D); + snprintf(monbuf, sizeof(monbuf), "TM1D = %04x\n", TM1D); monprintf(monbuf); } { - sprintf(monbuf, "TM1CNT = %04x\n", TM1CNT); + snprintf(monbuf, sizeof(monbuf), "TM1CNT = %04x\n", TM1CNT); monprintf(monbuf); } { - sprintf(monbuf, "TM2D = %04x\n", TM2D); + snprintf(monbuf, sizeof(monbuf), "TM2D = %04x\n", TM2D); monprintf(monbuf); } { - sprintf(monbuf, "TM2CNT = %04x\n", TM2CNT); + snprintf(monbuf, sizeof(monbuf), "TM2CNT = %04x\n", TM2CNT); monprintf(monbuf); } { - sprintf(monbuf, "TM3D = %04x\n", TM3D); + snprintf(monbuf, sizeof(monbuf), "TM3D = %04x\n", TM3D); monprintf(monbuf); } { - sprintf(monbuf, "TM3CNT = %04x\n", TM3CNT); + snprintf(monbuf, sizeof(monbuf), "TM3CNT = %04x\n", TM3CNT); monprintf(monbuf); } } @@ -1415,19 +1419,19 @@ void debuggerIoTimer() void debuggerIoMisc() { { - sprintf(monbuf, "P1 = %04x\n", P1); + snprintf(monbuf, sizeof(monbuf), "P1 = %04x\n", P1); monprintf(monbuf); } { - sprintf(monbuf, "IE = %04x\n", IE); + snprintf(monbuf, sizeof(monbuf), "IE = %04x\n", IE); monprintf(monbuf); } { - sprintf(monbuf, "IF = %04x\n", IF); + snprintf(monbuf, sizeof(monbuf), "IF = %04x\n", IF); monprintf(monbuf); } { - sprintf(monbuf, "IME = %04x\n", IME); + snprintf(monbuf, sizeof(monbuf), "IME = %04x\n", IME); monprintf(monbuf); } } @@ -1449,7 +1453,7 @@ void debuggerIo(int n, char** args) else if (!strcmp(args[1], "misc")) debuggerIoMisc(); else { - sprintf(monbuf, "Unrecognized option %s\n", args[1]); + snprintf(monbuf, sizeof(monbuf), "Unrecognized option %s\n", args[1]); monprintf(monbuf); } } @@ -1490,7 +1494,7 @@ void debuggerReadCharTable(int n, char** args) if (n == 2) { if (!canUseTbl) { { - sprintf(monbuf, "Cannot operate over character table, as it was disabled.\n"); + snprintf(monbuf, sizeof(monbuf), "Cannot operate over character table, as it was disabled.\n"); monprintf(monbuf); } return; @@ -1498,7 +1502,7 @@ void debuggerReadCharTable(int n, char** args) if (strcmp(args[1], "none") == 0) { freeWordSymbol(); { - sprintf(monbuf, "Cleared table. Reverted to ASCII.\n"); + snprintf(monbuf, sizeof(monbuf), "Cleared table. Reverted to ASCII.\n"); monprintf(monbuf); } return; @@ -1506,7 +1510,7 @@ void debuggerReadCharTable(int n, char** args) FILE* tlb = fopen(args[1], "r"); if (!tlb) { { - sprintf(monbuf, "Could not open specified file. Abort.\n"); + snprintf(monbuf, sizeof(monbuf), "Could not open specified file. Abort.\n"); monprintf(monbuf); } return; @@ -1560,7 +1564,7 @@ void printCharGroup(uint32_t addr, bool useAscii) int j; if (c) { { - sprintf(monbuf, "%s", c); + snprintf(monbuf, sizeof(monbuf), "%s", c); monprintf(monbuf); } j = strlen(c); @@ -1569,14 +1573,14 @@ void printCharGroup(uint32_t addr, bool useAscii) } while (j < largestSymbol) { { - sprintf(monbuf, " "); + snprintf(monbuf, sizeof(monbuf), " "); monprintf(monbuf); } j++; } } else { { - sprintf(monbuf, "%c", ASCII(debuggerReadByte(addr + i))); + snprintf(monbuf, sizeof(monbuf), "%c", ASCII(debuggerReadByte(addr + i))); monprintf(monbuf); } } @@ -1590,25 +1594,25 @@ void debuggerMemoryByte(int n, char** args) if (!dexp_eval(args[1], &addr)) { { - sprintf(monbuf, "Invalid expression\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression\n"); monprintf(monbuf); } return; } for (int loop = 0; loop < 16; loop++) { { - sprintf(monbuf, "%08x ", addr); + snprintf(monbuf, sizeof(monbuf), "%08x ", addr); monprintf(monbuf); } for (int j = 0; j < 16; j++) { { - sprintf(monbuf, "%02x ", debuggerReadByte(addr + j)); + snprintf(monbuf, sizeof(monbuf), "%02x ", debuggerReadByte(addr + j)); monprintf(monbuf); } } printCharGroup(addr, true); { - sprintf(monbuf, "\n"); + snprintf(monbuf, sizeof(monbuf), "\n"); monprintf(monbuf); } addr += 16; @@ -1624,7 +1628,7 @@ void debuggerMemoryHalfWord(int n, char** args) if (!dexp_eval(args[1], &addr)) { { - sprintf(monbuf, "Invalid expression\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression\n"); monprintf(monbuf); } return; @@ -1634,18 +1638,18 @@ void debuggerMemoryHalfWord(int n, char** args) for (int loop = 0; loop < 16; loop++) { { - sprintf(monbuf, "%08x ", addr); + snprintf(monbuf, sizeof(monbuf), "%08x ", addr); monprintf(monbuf); } for (int j = 0; j < 16; j += 2) { { - sprintf(monbuf, "%02x%02x ", debuggerReadByte(addr + j + 1), debuggerReadByte(addr + j)); + snprintf(monbuf, sizeof(monbuf), "%02x%02x ", debuggerReadByte(addr + j + 1), debuggerReadByte(addr + j)); monprintf(monbuf); } } printCharGroup(addr, true); { - sprintf(monbuf, "\n"); + snprintf(monbuf, sizeof(monbuf), "\n"); monprintf(monbuf); } addr += 16; @@ -1660,7 +1664,7 @@ void debuggerMemoryWord(int n, char** args) uint32_t addr = 0; if (!dexp_eval(args[1], &addr)) { { - sprintf(monbuf, "Invalid expression\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression\n"); monprintf(monbuf); } return; @@ -1668,18 +1672,18 @@ void debuggerMemoryWord(int n, char** args) addr = addr & 0xfffffffc; for (int loop = 0; loop < 16; loop++) { { - sprintf(monbuf, "%08x ", addr); + snprintf(monbuf, sizeof(monbuf), "%08x ", addr); monprintf(monbuf); } for (int j = 0; j < 16; j += 4) { { - sprintf(monbuf, "%02x%02x%02x%02x ", debuggerReadByte(addr + j + 3), debuggerReadByte(addr + j + 2), debuggerReadByte(addr + j + 1), debuggerReadByte(addr + j)); + snprintf(monbuf, sizeof(monbuf), "%02x%02x%02x%02x ", debuggerReadByte(addr + j + 3), debuggerReadByte(addr + j + 2), debuggerReadByte(addr + j + 1), debuggerReadByte(addr + j)); monprintf(monbuf); } } printCharGroup(addr, true); { - sprintf(monbuf, "\n"); + snprintf(monbuf, sizeof(monbuf), "\n"); monprintf(monbuf); } addr += 16; @@ -1695,7 +1699,7 @@ void debuggerStringRead(int n, char** args) if (!dexp_eval(args[1], &addr)) { { - sprintf(monbuf, "Invalid expression\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression\n"); monprintf(monbuf); } return; @@ -1706,31 +1710,31 @@ void debuggerStringRead(int n, char** args) if (useWordSymbol) { if (isTerminator[slot]) { { - sprintf(monbuf, "\n"); + snprintf(monbuf, sizeof(monbuf), "\n"); monprintf(monbuf); } return; } else if (isNewline[slot]) { { - sprintf(monbuf, "\n"); + snprintf(monbuf, sizeof(monbuf), "\n"); monprintf(monbuf); } } else if (isTab[slot]) { { - sprintf(monbuf, "\t"); + snprintf(monbuf, sizeof(monbuf), "\t"); monprintf(monbuf); } } else { if (wordSymbol[slot]) { { - sprintf(monbuf, "%s", wordSymbol[slot]); + snprintf(monbuf, sizeof(monbuf), "%s", wordSymbol[slot]); monprintf(monbuf); } } } } else { { - sprintf(monbuf, "%c", ASCII(slot)); + snprintf(monbuf, sizeof(monbuf), "%c", ASCII(slot)); monprintf(monbuf); } } @@ -1742,23 +1746,23 @@ void debuggerStringRead(int n, char** args) void debuggerRegisters(int, char**) { { - sprintf(monbuf, "R00=%08x R04=%08x R08=%08x R12=%08x\n", reg[0].I, reg[4].I, reg[8].I, reg[12].I); + snprintf(monbuf, sizeof(monbuf), "R00=%08x R04=%08x R08=%08x R12=%08x\n", reg[0].I, reg[4].I, reg[8].I, reg[12].I); monprintf(monbuf); } { - sprintf(monbuf, "R01=%08x R05=%08x R09=%08x R13=%08x\n", reg[1].I, reg[5].I, reg[9].I, reg[13].I); + snprintf(monbuf, sizeof(monbuf), "R01=%08x R05=%08x R09=%08x R13=%08x\n", reg[1].I, reg[5].I, reg[9].I, reg[13].I); monprintf(monbuf); } { - sprintf(monbuf, "R02=%08x R06=%08x R10=%08x R14=%08x\n", reg[2].I, reg[6].I, reg[10].I, reg[14].I); + snprintf(monbuf, sizeof(monbuf), "R02=%08x R06=%08x R10=%08x R14=%08x\n", reg[2].I, reg[6].I, reg[10].I, reg[14].I); monprintf(monbuf); } { - sprintf(monbuf, "R03=%08x R07=%08x R11=%08x R15=%08x\n", reg[3].I, reg[7].I, reg[11].I, reg[15].I); + snprintf(monbuf, sizeof(monbuf), "R03=%08x R07=%08x R11=%08x R15=%08x\n", reg[3].I, reg[7].I, reg[11].I, reg[15].I); monprintf(monbuf); } { - sprintf(monbuf, "CPSR=%08x (%c%c%c%c%c%c%c Mode: %02x)\n", + snprintf(monbuf, sizeof(monbuf), "CPSR=%08x (%c%c%c%c%c%c%c Mode: %02x)\n", reg[16].I, (N_FLAG ? 'N' : '.'), (Z_FLAG ? 'Z' : '.'), @@ -1776,7 +1780,7 @@ void debuggerExecuteCommands(int n, char** args) { if (n == 1) { { - sprintf(monbuf, "%s requires at least one pathname to execute.", args[0]); + snprintf(monbuf, sizeof(monbuf), "%s requires at least one pathname to execute.", args[0]); monprintf(monbuf); } return; @@ -1795,7 +1799,7 @@ void debuggerExecuteCommands(int n, char** args) } } } else { - sprintf(monbuf, "Could not open %s. Will not be executed.\n", args[0]); + snprintf(monbuf, sizeof(monbuf), "Could not open %s. Will not be executed.\n", args[0]); monprintf(monbuf); } @@ -1826,13 +1830,13 @@ void debuggerSetRadix(int argc, char** argv) default: error = true; { - sprintf(monbuf, "Unknown radix %d. Valid values are 8, 10 and 16.\n", r); + snprintf(monbuf, sizeof(monbuf), "Unknown radix %d. Valid values are 8, 10 and 16.\n", r); monprintf(monbuf); } break; } if (!error) { - sprintf(monbuf, "Radix set to %d\n", r); + snprintf(monbuf, sizeof(monbuf), "Radix set to %d\n", r); monprintf(monbuf); } } @@ -1854,11 +1858,11 @@ void debuggerSymbols(int argc, char** argv) matchStr = argv[1]; } { - sprintf(monbuf, "Symbol Value Size Type \n"); + snprintf(monbuf, sizeof(monbuf), "Symbol Value Size Type \n"); monprintf(monbuf); } { - sprintf(monbuf, "-------------------- ------- -------- -------\n"); + snprintf(monbuf, sizeof(monbuf), "-------------------- ------- -------- -------\n"); monprintf(monbuf); } const char* s = NULL; @@ -1883,7 +1887,7 @@ void debuggerSymbols(int argc, char** argv) break; } { - sprintf(monbuf, "%-20s %08x %08x %-7s\n", s, value, size, ts); + snprintf(monbuf, sizeof(monbuf), "%-20s %08x %08x %-7s\n", s, value, size, ts); monprintf(monbuf); } } @@ -1912,7 +1916,7 @@ void debuggerVar(int n, char** args) if (n < 4) { { - sprintf(monbuf, "No expression specified.\n"); + snprintf(monbuf, sizeof(monbuf), "No expression specified.\n"); monprintf(monbuf); } return; @@ -1920,7 +1924,7 @@ void debuggerVar(int n, char** args) if (!dexp_eval(args[3], &val)) { { - sprintf(monbuf, "Invalid expression.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression.\n"); monprintf(monbuf); } return; @@ -1928,7 +1932,7 @@ void debuggerVar(int n, char** args) dexp_setVar(args[2], val); { - sprintf(monbuf, "%s = $%08x\n", args[2], val); + snprintf(monbuf, sizeof(monbuf), "%s = $%08x\n", args[2], val); monprintf(monbuf); } return; @@ -1942,7 +1946,7 @@ void debuggerVar(int n, char** args) if (strcmp(args[1], "save") == 0) { if (n < 3) { { - sprintf(monbuf, "No file specified.\n"); + snprintf(monbuf, sizeof(monbuf), "No file specified.\n"); monprintf(monbuf); } return; @@ -1954,7 +1958,7 @@ void debuggerVar(int n, char** args) if (strcmp(args[1], "load") == 0) { if (n < 3) { { - sprintf(monbuf, "No file specified.\n"); + snprintf(monbuf, sizeof(monbuf), "No file specified.\n"); monprintf(monbuf); } return; @@ -1964,7 +1968,7 @@ void debuggerVar(int n, char** args) } { - sprintf(monbuf, "Unrecognized sub-command.\n"); + snprintf(monbuf, sizeof(monbuf), "Unrecognized sub-command.\n"); monprintf(monbuf); } } @@ -1980,7 +1984,7 @@ bool debuggerBreakOnExecution(uint32_t address, uint8_t state) return false; { - sprintf(monbuf, "Breakpoint (on %s) address %08x\n", (armState ? "ARM" : "Thumb"), address); + snprintf(monbuf, sizeof(monbuf), "Breakpoint (on %s) address %08x\n", (armState ? "ARM" : "Thumb"), address); monprintf(monbuf); } debugger = true; @@ -2109,7 +2113,7 @@ void debuggerBreakRegister(int n, char** args) { if (n != 3) { { - sprintf(monbuf, "Incorrect usage of breg. Correct usage is breg {flag} {value}\n"); + snprintf(monbuf, sizeof(monbuf), "Incorrect usage of breg. Correct usage is breg {flag} {value}\n"); monprintf(monbuf); } printFlagHelp(); @@ -2120,7 +2124,7 @@ void debuggerBreakRegister(int n, char** args) uint32_t value; if (!dexp_eval(args[2], &value)) { { - sprintf(monbuf, "Invalid expression.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression.\n"); monprintf(monbuf); } return; @@ -2128,7 +2132,7 @@ void debuggerBreakRegister(int n, char** args) if (flag != 0) { addBreakRegToList(reg, flag, value); { - sprintf(monbuf, "Added breakpoint on register R%02d, value %08x\n", reg, value); + snprintf(monbuf, sizeof(monbuf), "Added breakpoint on register R%02d, value %08x\n", reg, value); monprintf(monbuf); } } @@ -2142,14 +2146,14 @@ void debuggerBreakRegisterClear(int n, char** args) if (r >= 0) { clearParticularRegListBreaks(r); { - sprintf(monbuf, "Cleared all Register breakpoints for %s.\n", args[0]); + snprintf(monbuf, sizeof(monbuf), "Cleared all Register breakpoints for %s.\n", args[0]); monprintf(monbuf); } } } else { clearBreakRegList(); { - sprintf(monbuf, "Cleared all Register breakpoints.\n"); + snprintf(monbuf, sizeof(monbuf), "Cleared all Register breakpoints.\n"); monprintf(monbuf); } } @@ -2159,7 +2163,7 @@ void debuggerBreakRegisterDelete(int n, char** args) { if (n < 2) { { - sprintf(monbuf, "Illegal use of Break register delete:\n Correct usage requires .\n"); + snprintf(monbuf, sizeof(monbuf), "Illegal use of Break register delete:\n Correct usage requires .\n"); monprintf(monbuf); } return; @@ -2167,7 +2171,7 @@ void debuggerBreakRegisterDelete(int n, char** args) int r = getRegisterNumber(args[0]); if ((r < 0) || (r > 16)) { { - sprintf(monbuf, "Could not find a correct register number:\n Correct usage requires .\n"); + snprintf(monbuf, sizeof(monbuf), "Could not find a correct register number:\n Correct usage requires .\n"); monprintf(monbuf); } return; @@ -2175,14 +2179,14 @@ void debuggerBreakRegisterDelete(int n, char** args) uint32_t num; if (!dexp_eval(args[1], &num)) { { - sprintf(monbuf, "Could not parse the breakpoint number:\n Correct usage requires .\n"); + snprintf(monbuf, sizeof(monbuf), "Could not parse the breakpoint number:\n Correct usage requires .\n"); monprintf(monbuf); } return; } deleteFromBreakRegList(r, num); { - sprintf(monbuf, "Deleted Breakpoint %d of regsiter %s.\n", num, args[0]); + snprintf(monbuf, sizeof(monbuf), "Deleted Breakpoint %d of regsiter %s.\n", num, args[0]); monprintf(monbuf); } } @@ -2379,20 +2383,20 @@ void printCondition(struct ConditionalBreakNode* toPrint) const char* secondType = typeMapping[(toPrint->exp_type_flags >> 4) & 0x7]; const char* operand = compareFlagMapping[toPrint->cond_flags & 0x7]; { - sprintf(monbuf, "%s %s %s%s %s %s", firstType, toPrint->address, + snprintf(monbuf, sizeof(monbuf), "%s %s %s%s %s %s", firstType, toPrint->address, ((toPrint->cond_flags & 8) ? "s" : ""), operand, secondType, toPrint->value); monprintf(monbuf); } if (toPrint->next) { { - sprintf(monbuf, " &&\n\t\t"); + snprintf(monbuf, sizeof(monbuf), " &&\n\t\t"); monprintf(monbuf); } printCondition(toPrint->next); } else { { - sprintf(monbuf, "\n"); + snprintf(monbuf, sizeof(monbuf), "\n"); monprintf(monbuf); } return; @@ -2404,11 +2408,11 @@ void printConditionalBreak(struct ConditionalBreak* toPrint, bool printAddress) { if (toPrint) { if (printAddress) { - sprintf(monbuf, "At %08x, ", toPrint->break_address); + snprintf(monbuf, sizeof(monbuf), "At %08x, ", toPrint->break_address); monprintf(monbuf); } if (toPrint->type_flags & 0xf0) { - sprintf(monbuf, "Break Always on"); + snprintf(monbuf, sizeof(monbuf), "Break Always on"); monprintf(monbuf); } bool hasPrevCond = false; @@ -2416,13 +2420,13 @@ void printConditionalBreak(struct ConditionalBreak* toPrint, bool printAddress) while (flgs != 0) { if (toPrint->type_flags & flgs) { if (hasPrevCond) { - sprintf(monbuf, ","); + snprintf(monbuf, sizeof(monbuf), ","); monprintf(monbuf); } for (int i = 0; i < 9; i++) { if (breakFlagMapping[i].value == flgs) { { - sprintf(monbuf, "\t%s", breakFlagMapping[i].mapping); + snprintf(monbuf, sizeof(monbuf), "\t%s", breakFlagMapping[i].mapping); monprintf(monbuf); } hasPrevCond = true; @@ -2432,26 +2436,26 @@ void printConditionalBreak(struct ConditionalBreak* toPrint, bool printAddress) flgs = flgs >> 1; if ((flgs == 0x8) && (toPrint->type_flags & 0xf)) { { - sprintf(monbuf, "\n\t\tBreak conditional on"); + snprintf(monbuf, sizeof(monbuf), "\n\t\tBreak conditional on"); monprintf(monbuf); } hasPrevCond = false; } } { - sprintf(monbuf, "\n"); + snprintf(monbuf, sizeof(monbuf), "\n"); monprintf(monbuf); } if (toPrint->type_flags & 0xf && toPrint->firstCond) { { - sprintf(monbuf, "With conditions:\n\t\t"); + snprintf(monbuf, sizeof(monbuf), "With conditions:\n\t\t"); monprintf(monbuf); } printCondition(toPrint->firstCond); } else if (toPrint->type_flags & 0xf) { //should not happen { - sprintf(monbuf, "No conditions detected, but conditional. Assumed always by default.\n"); + snprintf(monbuf, sizeof(monbuf), "No conditions detected, but conditional. Assumed always by default.\n"); monprintf(monbuf); } } @@ -2465,18 +2469,18 @@ void printAllConditionals() if (conditionals[i] != NULL) { { - sprintf(monbuf, "Address range 0x%02x000000 breaks:\n", i); + snprintf(monbuf, sizeof(monbuf), "Address range 0x%02x000000 breaks:\n", i); monprintf(monbuf); } { - sprintf(monbuf, "-------------------------\n"); + snprintf(monbuf, sizeof(monbuf), "-------------------------\n"); monprintf(monbuf); } struct ConditionalBreak* base = conditionals[i]; int count = 1; uint32_t lastAddress = base->break_address; { - sprintf(monbuf, "Address %08x\n-------------------------\n", lastAddress); + snprintf(monbuf, sizeof(monbuf), "Address %08x\n-------------------------\n", lastAddress); monprintf(monbuf); } while (base) { @@ -2484,16 +2488,16 @@ void printAllConditionals() lastAddress = base->break_address; count = 1; { - sprintf(monbuf, "-------------------------\n"); + snprintf(monbuf, sizeof(monbuf), "-------------------------\n"); monprintf(monbuf); } { - sprintf(monbuf, "Address %08x\n-------------------------\n", lastAddress); + snprintf(monbuf, sizeof(monbuf), "Address %08x\n-------------------------\n", lastAddress); monprintf(monbuf); } } { - sprintf(monbuf, "No.%d\t-->\t", count); + snprintf(monbuf, sizeof(monbuf), "No.%d\t-->\t", count); monprintf(monbuf); } printConditionalBreak(base, false); @@ -2513,12 +2517,12 @@ uint8_t printConditionalsFromAddress(uint32_t address) if (address == base->break_address) { if (count == 1) { { - sprintf(monbuf, "Address %08x\n-------------------------\n", address); + snprintf(monbuf, sizeof(monbuf), "Address %08x\n-------------------------\n", address); monprintf(monbuf); } } { - sprintf(monbuf, "No.%d\t-->\t", count); + snprintf(monbuf, sizeof(monbuf), "No.%d\t-->\t", count); monprintf(monbuf); } printConditionalBreak(base, false); @@ -2531,7 +2535,7 @@ uint8_t printConditionalsFromAddress(uint32_t address) } if (count == 1) { { - sprintf(monbuf, "None\n"); + snprintf(monbuf, sizeof(monbuf), "None\n"); monprintf(monbuf); } } @@ -2559,22 +2563,22 @@ void printAllFlagConditionals(uint8_t flag, bool orMode) if (actualCount == 1) { if (isCondStart) { { - sprintf(monbuf, "Address range 0x%02x000000 breaks:\n", i); + snprintf(monbuf, sizeof(monbuf), "Address range 0x%02x000000 breaks:\n", i); monprintf(monbuf); } { - sprintf(monbuf, "-------------------------\n"); + snprintf(monbuf, sizeof(monbuf), "-------------------------\n"); monprintf(monbuf); } isCondStart = false; } { - sprintf(monbuf, "Address %08x\n-------------------------\n", lastAddress); + snprintf(monbuf, sizeof(monbuf), "Address %08x\n-------------------------\n", lastAddress); monprintf(monbuf); } } { - sprintf(monbuf, "No.%d\t-->\t", count); + snprintf(monbuf, sizeof(monbuf), "No.%d\t-->\t", count); monprintf(monbuf); } printConditionalBreak(base, false); @@ -2608,22 +2612,22 @@ void printAllFlagConditionalsWithAddress(uint32_t address, uint8_t flag, bool or if (actualCount == 1) { if (isCondStart) { { - sprintf(monbuf, "Address range 0x%02x000000 breaks:\n", i); + snprintf(monbuf, sizeof(monbuf), "Address range 0x%02x000000 breaks:\n", i); monprintf(monbuf); } { - sprintf(monbuf, "-------------------------\n"); + snprintf(monbuf, sizeof(monbuf), "-------------------------\n"); monprintf(monbuf); } isCondStart = false; } { - sprintf(monbuf, "Address %08x\n-------------------------\n", lastAddress); + snprintf(monbuf, sizeof(monbuf), "Address %08x\n-------------------------\n", lastAddress); monprintf(monbuf); } } { - sprintf(monbuf, "No.%d\t-->\t", count); + snprintf(monbuf, sizeof(monbuf), "No.%d\t-->\t", count); monprintf(monbuf); } printConditionalBreak(base, false); @@ -2666,14 +2670,14 @@ void deleteBreak(uint32_t address, uint8_t flags, char** expression, int howToDe uint32_t number = 0; if (!dexp_eval(expression[0], &number)) { { - sprintf(monbuf, "Invalid expression for number format.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression for number format.\n"); monprintf(monbuf); } return; } removeFlagFromConditionalBreakNo(address, (uint8_t)number, (flags | (flags >> 4))); { - sprintf(monbuf, "Removed all specified breaks from %08x.\n", address); + snprintf(monbuf, sizeof(monbuf), "Removed all specified breaks from %08x.\n", address); monprintf(monbuf); } return; @@ -2681,14 +2685,14 @@ void deleteBreak(uint32_t address, uint8_t flags, char** expression, int howToDe removeConditionalWithAddressAndFlag(address, flags, applyOr); removeConditionalWithAddressAndFlag(address, flags << 4, applyOr); { - sprintf(monbuf, "Removed all specified breaks from %08x.\n", address); + snprintf(monbuf, sizeof(monbuf), "Removed all specified breaks from %08x.\n", address); monprintf(monbuf); } } else { removeConditionalWithAddressAndFlag(address, flags, applyOr); removeConditionalWithAddressAndFlag(address, flags << 4, applyOr); { - sprintf(monbuf, "Removed all specified breaks from %08x.\n", address); + snprintf(monbuf, sizeof(monbuf), "Removed all specified breaks from %08x.\n", address); monprintf(monbuf); } } @@ -2706,7 +2710,7 @@ void clearBreaks(uint32_t address, uint8_t flags, char** expression, int howToCl removeConditionalWithFlag(flags << 4, false); } { - sprintf(monbuf, "Cleared all requested breaks.\n"); + snprintf(monbuf, sizeof(monbuf), "Cleared all requested breaks.\n"); monprintf(monbuf); } } @@ -2721,7 +2725,7 @@ void listBreaks(uint32_t address, uint8_t flags, char** expression, int howToLis printAllFlagConditionals(flags, true); } { - sprintf(monbuf, "\n"); + snprintf(monbuf, sizeof(monbuf), "\n"); monprintf(monbuf); } } @@ -2758,7 +2762,7 @@ void executeBreakCommands(int n, char** cmd) if (command[2] == '0') { if (n <= 0) { { - sprintf(monbuf, "Invalid break command.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid break command.\n"); monprintf(monbuf); } free(command); @@ -2786,7 +2790,7 @@ void executeBreakCommands(int n, char** cmd) if (command[4] == '0') { if (n <= 0) { { - sprintf(monbuf, "Invalid break command.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid break command.\n"); monprintf(monbuf); } free(command); @@ -2872,7 +2876,7 @@ void executeBreakCommands(int n, char** cmd) if ((n >= 1) && (operation != clearBreaks)) { if (!dexp_eval(cmd[0], &address)) { { - sprintf(monbuf, "Invalid expression for address format.\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid expression for address format.\n"); monprintf(monbuf); } return; @@ -2894,22 +2898,22 @@ void executeBreakCommands(int n, char** cmd) } } else if (!hasAddress && (operation == deleteBreak)) { { - sprintf(monbuf, "Delete breakpoint operation requires at least one address;\n"); + snprintf(monbuf, sizeof(monbuf), "Delete breakpoint operation requires at least one address;\n"); monprintf(monbuf); } { - sprintf(monbuf, "Usage: break [type] delete [address] no.[number] --> Deletes breakpoint [number] of [address].\n"); + snprintf(monbuf, sizeof(monbuf), "Usage: break [type] delete [address] no.[number] --> Deletes breakpoint [number] of [address].\n"); monprintf(monbuf); } - //{ sprintf(monbuf, "Usage: [delete Operand] [address] End [address] --> Deletes range between [address] and [end]\n"); monprintf(monbuf); } + //{ snprintf(monbuf, sizeof(monbuf), "Usage: [delete Operand] [address] End [address] --> Deletes range between [address] and [end]\n"); monprintf(monbuf); } { - sprintf(monbuf, "Usage: break [type] delete [address]\n --> Deletes all breakpoints of [type] on [address]."); + snprintf(monbuf, sizeof(monbuf), "Usage: break [type] delete [address]\n --> Deletes all breakpoints of [type] on [address]."); monprintf(monbuf); } return; } else if (!hasAddress && (operation == makeBreak)) { { - sprintf(monbuf, "Can only create breakpoints if an address is provided"); + snprintf(monbuf, sizeof(monbuf), "Can only create breakpoints if an address is provided"); monprintf(monbuf); } //print usage here @@ -2953,19 +2957,19 @@ void debuggerDisable(int n, char** args) if (strcmp(args[3 - n], "breg")) { enableRegBreak = false; { - sprintf(monbuf, "Break on register disabled.\n"); + snprintf(monbuf, sizeof(monbuf), "Break on register disabled.\n"); monprintf(monbuf); } } else if (strcmp(args[3 - n], "tbl")) { canUseTbl = false; useWordSymbol = false; { - sprintf(monbuf, "Symbol table disabled.\n"); + snprintf(monbuf, sizeof(monbuf), "Symbol table disabled.\n"); monprintf(monbuf); } } else { { - sprintf(monbuf, "Invalid command. Only tbl and breg are accepted as commands\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid command. Only tbl and breg are accepted as commands\n"); monprintf(monbuf); } return; @@ -2989,19 +2993,19 @@ void debuggerEnable(int n, char** args) if (strcmp(args[3 - n], "breg")) { enableRegBreak = true; { - sprintf(monbuf, "Break on register enabled.\n"); + snprintf(monbuf, sizeof(monbuf), "Break on register enabled.\n"); monprintf(monbuf); } } else if (strcmp(args[3 - n], "tbl")) { canUseTbl = true; useWordSymbol = thereIsATable; { - sprintf(monbuf, "Symbol table enabled.\n"); + snprintf(monbuf, sizeof(monbuf), "Symbol table enabled.\n"); monprintf(monbuf); } } else { { - sprintf(monbuf, "Invalid command. Only tbl and breg are accepted as commands\n"); + snprintf(monbuf, sizeof(monbuf), "Invalid command. Only tbl and breg are accepted as commands\n"); monprintf(monbuf); } return; @@ -3231,7 +3235,7 @@ void debuggerUsage(const char* cmd) for (int i = 0;; i++) { if (debuggerCommands[i].name) { if (!strcmp(debuggerCommands[i].name, cmd)) { - sprintf(monbuf, "%s %s\t%s\n", + snprintf(monbuf, sizeof(monbuf), "%s %s\t%s\n", debuggerCommands[i].name, debuggerCommands[i].syntax ? debuggerCommands[i].syntax : "", debuggerCommands[i].help); @@ -3240,7 +3244,7 @@ void debuggerUsage(const char* cmd) } } else { { - sprintf(monbuf, "Unrecognized command '%s'.", cmd); + snprintf(monbuf, sizeof(monbuf), "Unrecognized command '%s'.", cmd); monprintf(monbuf); } break; @@ -3256,14 +3260,14 @@ void debuggerHelp(int n, char** args) for (int i = 0;; i++) { if (debuggerCommands[i].name) { { - sprintf(monbuf, "%-10s%s\n", debuggerCommands[i].name, debuggerCommands[i].help); + snprintf(monbuf, sizeof(monbuf), "%-10s%s\n", debuggerCommands[i].name, debuggerCommands[i].help); monprintf(monbuf); } } else break; } { - sprintf(monbuf, "%-10s%s\n", "break", "Breakpoint commands"); + snprintf(monbuf, sizeof(monbuf), "%-10s%s\n", "break", "Breakpoint commands"); monprintf(monbuf); } } @@ -3369,7 +3373,7 @@ void dbgExecute(char* toRun) for (int j = 0;; j++) { if (debuggerCommands[j].name == NULL) { { - sprintf(monbuf, "Unrecognized command %s. Type h for help.\n", commands[0]); + snprintf(monbuf, sizeof(monbuf), "Unrecognized command %s. Type h for help.\n", commands[0]); monprintf(monbuf); } return; @@ -3590,7 +3594,7 @@ void remoteOutput(const char* s, uint32_t addr) if (s) { char c = *s++; while (c) { - sprintf(d, "%02x", c); + snprintf(d, sizeof(buffer), "%02x", c); d += 2; c = *s++; } @@ -3598,7 +3602,7 @@ void remoteOutput(const char* s, uint32_t addr) char c = debuggerReadByte(addr); addr++; while (c) { - sprintf(d, "%02x", c); + snprintf(d, sizeof(buffer), "%02x", c); d += 2; c = debuggerReadByte(addr); addr++; @@ -3611,19 +3615,19 @@ void remoteOutput(const char* s, uint32_t addr) void remoteSendSignal() { char buffer[1024]; - sprintf(buffer, "S%02x", remoteSignal); + snprintf(buffer, sizeof(buffer), "S%02x", remoteSignal); remotePutPacket(buffer); } void remoteSendStatus() { char buffer[1024]; - sprintf(buffer, "T%02x", remoteSignal); + snprintf(buffer, sizeof(buffer), "T%02x", remoteSignal); char* s = buffer; s += 3; for (int i = 0; i < 15; i++) { uint32_t v = reg[i].I; - sprintf(s, "%02x:%02x%02x%02x%02x;", i, + snprintf(s, sizeof(buffer), "%02x:%02x%02x%02x%02x;", i, (v & 255), (v >> 8) & 255, (v >> 16) & 255, @@ -3631,14 +3635,14 @@ void remoteSendStatus() s += 12; } uint32_t v = armNextPC; - sprintf(s, "0f:%02x%02x%02x%02x;", (v & 255), + snprintf(s, sizeof(buffer), "0f:%02x%02x%02x%02x;", (v & 255), (v >> 8) & 255, (v >> 16) & 255, (v >> 24) & 255); s += 12; CPUUpdateCPSR(); v = reg[16].I; - sprintf(s, "19:%02x%02x%02x%02x;", (v & 255), + snprintf(s, sizeof(buffer), "19:%02x%02x%02x%02x;", (v & 255), (v >> 8) & 255, (v >> 16) & 255, (v >> 24) & 255); @@ -3715,7 +3719,7 @@ void remoteMemoryRead(char* p) char* s = buffer; for (int i = 0; i < count; i++) { uint8_t b = debuggerReadByte(address); - sprintf(s, "%02x", b); + snprintf(s, (count*2), "%02x", b); address++; s += 2; } @@ -3989,7 +3993,7 @@ void remoteReadRegister(char* p) char buffer[1024]; char* s = buffer; uint32_t v = reg[r].I; - sprintf(s, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255, + snprintf(s, sizeof(buffer), "%02x%02x%02x%02x", v & 255, (v >> 8) & 255, (v >> 16) & 255, (v >> 24) & 255); remotePutPacket(buffer); } @@ -4004,29 +4008,29 @@ void remoteReadRegisters(char* p) // regular registers for (i = 0; i < 15; i++) { uint32_t v = reg[i].I; - sprintf(s, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255, + snprintf(s, sizeof(buffer), "%02x%02x%02x%02x", v & 255, (v >> 8) & 255, (v >> 16) & 255, (v >> 24) & 255); s += 8; } // PC uint32_t pc = armNextPC; - sprintf(s, "%02x%02x%02x%02x", pc & 255, (pc >> 8) & 255, + snprintf(s, sizeof(buffer) - 8, "%02x%02x%02x%02x", pc & 255, (pc >> 8) & 255, (pc >> 16) & 255, (pc >> 24) & 255); s += 8; // floating point registers (24-bit) for (i = 0; i < 8; i++) { - sprintf(s, "000000000000000000000000"); + snprintf(s, sizeof(buffer) - 16, "000000000000000000000000"); s += 24; } // FP status register - sprintf(s, "00000000"); + snprintf(s, sizeof(buffer) - 40, "00000000"); s += 8; // CPSR CPUUpdateCPSR(); uint32_t v = reg[16].I; - sprintf(s, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255, + snprintf(s, sizeof(buffer) - 48, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255, (v >> 16) & 255, (v >> 24) & 255); s += 8; *s = 0; diff --git a/src/core/test/fake_core.cpp b/src/core/test/fake_core.cpp index 56f81662..4d8e9e11 100644 --- a/src/core/test/fake_core.cpp +++ b/src/core/test/fake_core.cpp @@ -79,6 +79,7 @@ void systemGbBorderOn() {} void (*dbgOutput)(const char* s, uint32_t addr); void (*dbgSignal)(int sig, int number); +uint8_t systemColorMap8[0x10000]; uint16_t systemColorMap16[0x10000]; uint32_t systemColorMap32[0x10000]; uint16_t systemGbPalette[24]; @@ -91,4 +92,4 @@ int systemFrameSkip; int systemSaveUpdateCounter; int systemSpeed; -int emulating = 0; \ No newline at end of file +int emulating = 0; diff --git a/src/libretro/Makefile b/src/libretro/Makefile index dcd5f20c..320eb442 100644 --- a/src/libretro/Makefile +++ b/src/libretro/Makefile @@ -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,--version-script=$(LIBRETRO_DIR)/link.T -Wl,--no-undefined TILED_RENDERING=1 # Classic Platforms #################### @@ -222,7 +222,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 @@ -344,7 +344,7 @@ else ifeq ($(platform), switch) else ifneq (,$(findstring armv,$(platform))) TARGET := $(TARGET_NAME)_libretro.so - SHARED := -shared -Wl,--no-undefined + SHARED := -shared -Wl,--version-script=$(LIBRETRO_DIR)/link.T -Wl,--no-undefined TILED_RENDERING=1 fpic := -fPIC ifneq (,$(findstring cortexa8,$(platform))) @@ -533,7 +533,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 diff --git a/src/libretro/libretro.cpp b/src/libretro/libretro.cpp index c8411ffc..749e6b48 100644 --- a/src/libretro/libretro.cpp +++ b/src/libretro/libretro.cpp @@ -27,6 +27,11 @@ #include "core/gba/gbaRtc.h" #include "core/gba/gbaSound.h" +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#define vsnprintf vsprintf_s +#endif + #define FRAMERATE (16777216.0 / 280896.0) // 59.73 #define SAMPLERATE 32768.0 @@ -68,6 +73,7 @@ static IMAGE_TYPE type = IMAGE_UNKNOWN; static bool libretro_supports_bitmasks = false; // global vars +uint8_t systemColorMap8[0x10000]; uint16_t systemColorMap16[0x10000]; uint32_t systemColorMap32[0x10000]; int RGB_LOW_BITS_MASK = 0x821; // used for 16bit inter-frame filters @@ -1526,7 +1532,7 @@ void retro_cheat_set(unsigned index, bool enabled, const char* code) int i = 0; codeLine = (char *)calloc(codeLineSize, sizeof(char)); - sprintf(name, "cheat_%d", index); + snprintf(name, sizeof(name), "cheat_%d", index); for (cursor = 0;; cursor++) { if (ISHEXDEC) { codeLine[codePos++] = toupper(code[cursor]); @@ -1791,7 +1797,7 @@ void systemMessage(const char* fmt, ...) char buffer[256]; va_list ap; va_start(ap, fmt); - vsprintf(buffer, fmt, ap); + vsnprintf(buffer, sizeof(buffer), fmt, ap); if (log_cb) log_cb(RETRO_LOG_INFO, "%s\n", buffer); va_end(ap); @@ -1802,7 +1808,7 @@ void systemMessage(int, const char* fmt, ...) char buffer[256]; va_list ap; va_start(ap, fmt); - vsprintf(buffer, fmt, ap); + vsnprintf(buffer, sizeof(buffer), fmt, ap); if (log_cb) log_cb(RETRO_LOG_INFO, "%s\n", buffer); va_end(ap); diff --git a/src/libretro/link.T b/src/libretro/link.T new file mode 100644 index 00000000..78f657b3 --- /dev/null +++ b/src/libretro/link.T @@ -0,0 +1,5 @@ +{ + global: retro_*; + local: *; +}; + diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index 8dcd0e9e..d98adf02 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -40,9 +40,15 @@ if(ENABLE_LIRC) set(LIRC_CLIENT_LIBRARY lirc_client) endif() -target_include_directories(vbam - PRIVATE ${SDL2_INCLUDE_DIRS} -) +if(ENABLE_SDL3) + target_include_directories(vbam + PRIVATE ${SDL3_INCLUDE_DIRS} + ) +else() + target_include_directories(vbam + PRIVATE ${SDL2_INCLUDE_DIRS} + ) +endif() target_link_libraries(vbam vbam-core @@ -52,12 +58,16 @@ target_link_libraries(vbam vbam-components-filters-interframe vbam-components-user-config ${OPENGL_LIBRARIES} - ${VBAM_SDL2_LIBS} + ${VBAM_SDL_LIBS} nonstd-lib ) if(WIN32) - target_link_libraries(vbam ${SDL2_LIBRARY} ${SDL2MAIN_LIBRARY}) + if(ENABLE_SDL3) + target_link_libraries(vbam ${SDL3_LIBRARY}) + else() + target_link_libraries(vbam ${SDL2_LIBRARY} ${SDL2MAIN_LIBRARY}) + endif() endif() if(ENABLE_LIRC) @@ -66,7 +76,7 @@ if(ENABLE_LIRC) endif() if(WIN32) - target_link_libraries(vbam wsock32 ws2_32 winmm version imm32 ${SDL2MAIN_LIBRARY}) + target_link_libraries(vbam wsock32 ws2_32 winmm version imm32) endif() # Installation scripts. diff --git a/src/sdl/ConfigManager.cpp b/src/sdl/ConfigManager.cpp index a4374a3d..ba07d44c 100644 --- a/src/sdl/ConfigManager.cpp +++ b/src/sdl/ConfigManager.cpp @@ -16,6 +16,10 @@ #include #include +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#endif + #define getcwd _getcwd #define stat _stat #define mkdir(X,Y) (_mkdir(X)) @@ -138,6 +142,8 @@ int rewindTimer = 0; int showSpeed; int showSpeedTransparent; +int userColorDepth = 0; + const char* preparedCheatCodes[MAX_CHEATS]; // allow up to 100 IPS/UPS/PPF patches given on commandline @@ -168,6 +174,7 @@ struct option argOptions[] = { { "capture-format", required_argument, 0, OPT_CAPTURE_FORMAT }, { "cheat", required_argument, 0, OPT_CHEAT }, { "cheats-enabled", no_argument, &coreOptions.cheatsEnabled, 1 }, + { "color-depth", required_argument, 0, 'z'}, { "color-option", no_argument, 0, OPT_GB_COLOR_OPTION }, { "config", required_argument, 0, 'c' }, { "cpu-disable-sfx", no_argument, &coreOptions.cpuDisableSfx, 1 }, @@ -457,7 +464,7 @@ const char* FindConfigFile(const char *name) mkdir(fullDir, 0755); if (fullDir) { - sprintf(path, "%s%c%s", fullDir, kFileSep, name); + snprintf(path, sizeof(path), "%s%c%s", fullDir, kFileSep, name); if (FileExists(path)) { return path; @@ -467,7 +474,7 @@ const char* FindConfigFile(const char *name) #ifdef _WIN32 char *home = getenv("USERPROFILE"); if (home != NULL) { - sprintf(path, "%s%c%s", home, kFileSep, name); + snprintf(path, "%s%c%s", home, kFileSep, name); if (FileExists(path)) { return path; @@ -484,10 +491,10 @@ const char* FindConfigFile(const char *name) char *tok = strtok(buffer, PATH_SEP); while (tok) { - sprintf(env_path, "%s%c%s", tok, kFileSep, EXE_NAME); + snprintf(env_path, 4096, "%s%c%s", tok, kFileSep, EXE_NAME); if (FileExists(env_path)) { static char path2[2048]; - sprintf(path2, "%s%c%s", tok, kFileSep, name); + snprintf(path2, sizeof(path2), "%s%c%s", tok, kFileSep, name); if (FileExists(path2)) { return path2; } @@ -502,7 +509,7 @@ const char* FindConfigFile(const char *name) char *p = strrchr(buffer, kFileSep); if (p) { *p = 0; - sprintf(path, "%s%c%s", buffer, kFileSep, name); + snprintf(path, sizeof(path), "%s%c%s", buffer, kFileSep, name); if (FileExists(path)) { return path; @@ -510,13 +517,13 @@ const char* FindConfigFile(const char *name) } } #else // ! _WIN32 - sprintf(path, "%s%c%s", PKGDATADIR, kFileSep, name); + snprintf(path, sizeof(path), "%s%c%s", PKGDATADIR, kFileSep, name); if (FileExists(path)) { return path; } - sprintf(path, "%s%c%s", SYSCONF_INSTALL_DIR, kFileSep, name); + snprintf(path, sizeof(path), "%s%c%s", SYSCONF_INSTALL_DIR, kFileSep, name); if (FileExists(path)) { return path; @@ -745,6 +752,20 @@ int ReadOpts(int argc, char ** argv) case 'F': fullScreen = 1; break; + case 'z': + if (optarg != NULL) { + userColorDepth = atoi(optarg); + + if ((userColorDepth != 8) && (userColorDepth != 16) && (userColorDepth != 24) && (userColorDepth != 32)) { + fprintf(stderr, "Wrong color depth (%d bit)\n", userColorDepth); + userColorDepth = 0; + } else { + log("Set color depth to %d bit\n", userColorDepth); + } + } else { + userColorDepth = 0; + } + break; case 'f': if (optarg) { filter = (Filter)atoi(optarg); diff --git a/src/sdl/SDL.cpp b/src/sdl/SDL.cpp index 88f0f770..c89d9e24 100644 --- a/src/sdl/SDL.cpp +++ b/src/sdl/SDL.cpp @@ -33,18 +33,17 @@ #include #define getcwd _getcwd -#define snprintf sprintf #define stat _stat #define access _access #ifndef W_OK - #define W_OK 2 +#define W_OK 2 #endif #define mkdir(X,Y) (_mkdir(X)) // from: https://www.linuxquestions.org/questions/programming-9/porting-to-win32-429334/ #ifndef S_ISDIR - #define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR) +#define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR) #endif #endif // _WIN32 @@ -72,7 +71,9 @@ #endif // defined(_WIN32) +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) #if defined(__APPLE__) +#define GL_SILENCE_DEPRECATION 1 #include #include @@ -85,14 +86,29 @@ #include #endif // defined(__APPLE__) +#endif +#ifdef ENABLE_SDL3 +#include + +#ifndef CONFIG_IDF_TARGET +#include +#endif +#else #include +#endif #if defined(VBAM_ENABLE_LIRC) #include #include #endif +#ifdef CONFIG_IDF_TARGET +#define CONFIG_RGB565 1 +#define CONFIG_16BIT 1 +#define NO_OPENGL 1 +#endif + #include "components/draw_text/draw_text.h" #include "components/filters_agb/filters_agb.h" #include "components/user_config/user_config.h" @@ -115,6 +131,10 @@ #include "sdl/filters.h" #include "sdl/inputSDL.h" +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#endif + // from: https://stackoverflow.com/questions/7608714/why-is-my-pointer-not-null-after-free #define freeSafe(ptr) free(ptr); ptr = NULL; @@ -125,6 +145,7 @@ extern void remoteStubSignal(int, int); extern void remoteOutput(const char*, uint32_t); extern void remoteSetProtocol(int); extern void remoteSetPort(int); +extern int userColorDepth; struct CoreOptions coreOptions; @@ -199,6 +220,7 @@ int emulating = 0; int RGB_LOW_BITS_MASK = 0x821; uint32_t systemColorMap32[0x10000]; uint16_t systemColorMap16[0x10000]; +uint8_t systemColorMap8[0x10000]; uint16_t systemGbPalette[24]; char filename[2048]; @@ -278,7 +300,7 @@ static void sdlChangeVolume(float d) if (fabs(newVolume - oldVolume) > 0.001) { char tmp[32]; - sprintf(tmp, "Volume: %i%%", (int)(newVolume * 100.0 + 0.5)); + snprintf(tmp, sizeof(tmp), "Volume: %i%%", (int)(newVolume * 100.0 + 0.5)); systemScreenMessage(tmp); soundSetVolume(newVolume); } @@ -302,7 +324,7 @@ void StartLirc(void) fprintf(stdout, "Success\n"); //read the config file char LIRCConfigLoc[2048]; - sprintf(LIRCConfigLoc, "%s%c%s", homeConfigDir, kFileSep, "lircrc"); + snprintf(LIRCConfigLoc, sizeof(LIRCConfigLoc), "%s%c%s", homeConfigDir, kFileSep, "lircrc"); fprintf(stdout, "LIRC Config file:"); if (lirc_readconfig(LIRCConfigLoc, &LIRCConfigInfo, NULL) == 0) { //check vbam dir for lircrc @@ -417,7 +439,7 @@ FILE* sdlFindFile(const char* name) if (strlen(homeDataDir)) { fprintf(stdout, "Searching home directory: %s\n", homeDataDir); - sprintf(path, "%s%c%s", homeDataDir, kFileSep, name); + snprintf(path, sizeof(path), "%s%c%s", homeDataDir, kFileSep, name); f = fopen(path, "r"); if (f != NULL) return f; @@ -427,7 +449,7 @@ FILE* sdlFindFile(const char* name) char* profileDir = getenv("USERPROFILE"); if (profileDir != NULL) { fprintf(stdout, "Searching user profile directory: %s\n", profileDir); - sprintf(path, "%s%c%s", profileDir, kFileSep, name); + snprintf(path, sizeof(path), "%s%c%s", profileDir, kFileSep, name); f = fopen(path, "r"); if (f != NULL) return f; @@ -438,17 +460,17 @@ FILE* sdlFindFile(const char* name) if (path != NULL) { fprintf(stdout, "Searching PATH\n"); - strncpy(buffer, path, sizeof(buffer)); + strcpy(buffer, path); buffer[sizeof(buffer) - 1] = 0; char* tok = strtok(buffer, PATH_SEP); while (tok) { - sprintf(path, "%s%c%s", tok, kFileSep, EXE_NAME); + snprintf(path, sizeof(path), "%s%c%s", tok, kFileSep, EXE_NAME); f = fopen(path, "r"); if (f != NULL) { char path2[2048]; fclose(f); - sprintf(path2, "%s%c%s", tok, kFileSep, name); + snprintf(path2, sizeof(path2), "%s%c%s", tok, kFileSep, name); f = fopen(path2, "r"); if (f != NULL) { fprintf(stdout, "Found at %s\n", path2); @@ -465,7 +487,7 @@ FILE* sdlFindFile(const char* name) char* p = strrchr(buffer, kFileSep); if (p) { *p = 0; - sprintf(path, "%s%c%s", buffer, kFileSep, name); + snprintf(path, sizeof(path), "%s%c%s", buffer, kFileSep, name); f = fopen(path, "r"); if (f != NULL) return f; @@ -473,13 +495,13 @@ FILE* sdlFindFile(const char* name) } #else // ! _WIN32 fprintf(stdout, "Searching data directory: %s\n", PKGDATADIR); - sprintf(path, "%s%c%s", PKGDATADIR, kFileSep, name); + snprintf(path, sizeof(path), "%s%c%s", PKGDATADIR, kFileSep, name); f = fopen(path, "r"); if (f != NULL) return f; fprintf(stdout, "Searching system config directory: %s\n", SYSCONF_INSTALL_DIR); - sprintf(path, "%s%c%s", SYSCONF_INSTALL_DIR, kFileSep, name); + snprintf(path, sizeof(path), "%s%c%s", SYSCONF_INSTALL_DIR, kFileSep, name); f = fopen(path, "r"); if (f != NULL) return f; @@ -488,6 +510,7 @@ FILE* sdlFindFile(const char* name) return NULL; } +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) static void sdlOpenGLScaleWithAspect(int w, int h) { float screenAspect = (float)sizeX / sizeY, @@ -528,8 +551,25 @@ static void sdlOpenGLVideoResize() textureSize = (int)pow(2.0f, n); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize, textureSize, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (systemColorDepth == 8) + { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureSize, textureSize, 0, + GL_RGB, GL_UNSIGNED_BYTE_3_3_2, NULL); + } else if (systemColorDepth == 16) { +#ifdef CONFIG_RGB565 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureSize, textureSize, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureSize, textureSize, 0, + GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL); +#endif + } else if (systemColorDepth == 24) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureSize, textureSize, 0, + GL_RGB, GL_UNSIGNED_BYTE, NULL); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureSize, textureSize, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + } glClear(GL_COLOR_BUFFER_BIT); @@ -556,6 +596,7 @@ void sdlOpenGLInit(int w, int h) sdlOpenGLVideoResize(); } +#endif static void sdlApplyPerImagePreferences() { @@ -667,11 +708,11 @@ static char* sdlStateName(int num) char *gameFile = sdlGetFilename(filename); if (saveDir) - sprintf(stateName, "%s%c%s%d.sgm", saveDir, kFileSep, gameFile, num + 1); + snprintf(stateName, sizeof(stateName), "%s%c%s%d.sgm", saveDir, kFileSep, gameFile, num + 1); else if (access(gameDir, W_OK) == 0) - sprintf(stateName, "%s%c%s%d.sgm", gameDir, kFileSep, gameFile, num + 1); + snprintf(stateName, sizeof(stateName), "%s%c%s%d.sgm", gameDir, kFileSep, gameFile, num + 1); else - sprintf(stateName, "%s%c%s%d.sgm", homeDataDir, kFileSep, gameFile, num + 1); + snprintf(stateName, sizeof(stateName), "%s%c%s%d.sgm", homeDataDir, kFileSep, gameFile, num + 1); freeSafe(gameDir); freeSafe(gameFile); @@ -689,10 +730,10 @@ void sdlWriteState(int num) // now we reuse the stateName buffer - 2048 bytes fit in a lot if (num == SLOT_POS_LOAD_BACKUP) { - sprintf(stateName, "Current state backed up to %d", num + 1); + snprintf(stateName, 2048, "Current state backed up to %d", num + 1); systemScreenMessage(stateName); } else if (num >= 0) { - sprintf(stateName, "Wrote state %d", num + 1); + snprintf(stateName, 2048, "Wrote state %d", num + 1); systemScreenMessage(stateName); } @@ -708,11 +749,11 @@ void sdlReadState(int num) emulator.emuReadState(stateName); if (num == SLOT_POS_LOAD_BACKUP) { - sprintf(stateName, "Last load UNDONE"); + snprintf(stateName, 2048, "Last load UNDONE"); } else if (num == SLOT_POS_SAVE_BACKUP) { - sprintf(stateName, "Last save UNDONE"); + snprintf(stateName, 2048, "Last save UNDONE"); } else { - sprintf(stateName, "Loaded state %d", num + 1); + snprintf(stateName, 2048, "Loaded state %d", num + 1); } systemScreenMessage(stateName); @@ -770,11 +811,11 @@ void sdlWriteBattery() char *gameFile = sdlGetFilename(filename); if (batteryDir) - sprintf(buffer, "%s%c%s.sav", batteryDir, kFileSep, gameFile); + snprintf(buffer, sizeof(buffer), "%s%c%s.sav", batteryDir, kFileSep, gameFile); else if (access(gameDir, W_OK) == 0) - sprintf(buffer, "%s%c%s.sav", gameDir, kFileSep, gameFile); + snprintf(buffer, sizeof(buffer), "%s%c%s.sav", gameDir, kFileSep, gameFile); else - sprintf(buffer, "%s%c%s.sav", homeDataDir, kFileSep, gameFile); + snprintf(buffer, sizeof(buffer), "%s%c%s.sav", homeDataDir, kFileSep, gameFile); bool result = emulator.emuWriteBattery(buffer); @@ -792,11 +833,11 @@ void sdlReadBattery() char *gameFile = sdlGetFilename(filename); if (batteryDir) - sprintf(buffer, "%s%c%s.sav", batteryDir, kFileSep, gameFile); + snprintf(buffer, sizeof(buffer), "%s%c%s.sav", batteryDir, kFileSep, gameFile); else if (access(gameDir, W_OK) == 0) - sprintf(buffer, "%s%c%s.sav", gameDir, kFileSep, gameFile); + snprintf(buffer, sizeof(buffer), "%s%c%s.sav", gameDir, kFileSep, gameFile); else - sprintf(buffer, "%s%c%s.sav", homeDataDir, kFileSep, gameFile); + snprintf(buffer, sizeof(buffer), "%s%c%s.sav", homeDataDir, kFileSep, gameFile); bool result = emulator.emuReadBattery(buffer); @@ -810,10 +851,16 @@ void sdlReadBattery() void sdlReadDesktopVideoMode() { if (window) { +#ifdef ENABLE_SDL3 + const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(SDL_GetDisplayForWindow(window)); + desktopWidth = dm->w; + desktopHeight = dm->h; +#else SDL_DisplayMode dm; SDL_GetDesktopDisplayMode(SDL_GetWindowDisplayIndex(window), &dm); desktopWidth = dm.w; desktopHeight = dm.h; +#endif } } @@ -824,27 +871,81 @@ static void sdlResizeVideo() destWidth = filter_enlarge * sizeX; destHeight = filter_enlarge * sizeY; +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (openGL) { free(filterPix); filterPix = (uint8_t*)calloc(1, (systemColorDepth >> 3) * destWidth * destHeight); sdlOpenGLVideoResize(); } +#endif +#ifdef ENABLE_SDL3 + if (surface) + SDL_DestroySurface(surface); +#else if (surface) SDL_FreeSurface(surface); +#endif + if (texture) SDL_DestroyTexture(texture); +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (!openGL) { - surface = SDL_CreateRGBSurface(0, destWidth, destHeight, 32, - 0x00FF0000, 0x0000FF00, - 0x000000FF, 0xFF000000); - texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, - destWidth, destHeight); +#endif + if (systemColorDepth == 8) + { +#ifdef ENABLE_SDL3 + surface = SDL_CreateSurface(destWidth, destHeight, SDL_GetPixelFormatForMasks(8, 0xE0, 0x1C, 0x03, 0x00)); + texture = SDL_CreateTexture(renderer, SDL_GetPixelFormatForMasks(8, 0xE0, 0x1C, 0x03, 0x00), SDL_TEXTUREACCESS_STREAMING, destWidth, destHeight); +#else + surface = SDL_CreateRGBSurface(0, destWidth, destHeight, 8, 0xE0, 0x1C, 0x03, 0x00); + texture = SDL_CreateTexture(renderer, SDL_MasksToPixelFormatEnum(8, 0xE0, 0x1C, 0x03, 0x00), SDL_TEXTUREACCESS_STREAMING, destWidth, destHeight); +#endif + } else if (systemColorDepth == 16) { +#ifdef CONFIG_RGB565 +#ifdef ENABLE_SDL3 + surface = SDL_CreateSurface(destWidth, destHeight, SDL_GetPixelFormatForMasks(16, 0xF800, 0x07E0, 0x001F, 0x0000)); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, destWidth, destHeight); +#else + surface = SDL_CreateRGBSurface(0, destWidth, destHeight, 16, 0xF800, 0x07E0, 0x001F, 0x0000); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, destWidth, destHeight); +#endif +#else +#ifdef ENABLE_SDL3 + surface = SDL_CreateSurface(destWidth, destHeight, SDL_GetPixelFormatForMasks(16, 0x7C00, 0x03E0, 0x001F, 0x0000)); + texture = SDL_CreateTexture(renderer, SDL_GetPixelFormatForMasks(16, 0x7C00, 0x03E0, 0x001F, 0x0000), SDL_TEXTUREACCESS_STREAMING, destWidth, destHeight); +#else + surface = SDL_CreateRGBSurface(0, destWidth, destHeight, 16, 0x7C00, 0x03E0, 0x001F, 0x0000); + texture = SDL_CreateTexture(renderer, SDL_MasksToPixelFormatEnum(16, 0x7C00, 0x03E0, 0x001F, 0x0000), SDL_TEXTUREACCESS_STREAMING, destWidth, destHeight); +#endif +#endif + } else if (systemColorDepth == 24) { +#ifdef ENABLE_SDL3 + surface = SDL_CreateSurface(destWidth, destHeight, SDL_GetPixelFormatForMasks(24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000)); + texture = SDL_CreateTexture(renderer, SDL_GetPixelFormatForMasks(24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000), SDL_TEXTUREACCESS_STREAMING, destWidth, destHeight); +#else + surface = SDL_CreateRGBSurface(0, destWidth, destHeight, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000); + texture = SDL_CreateTexture(renderer, SDL_MasksToPixelFormatEnum(24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000), SDL_TEXTUREACCESS_STREAMING, destWidth, destHeight); +#endif + } else { +#ifdef ENABLE_SDL3 + surface = SDL_CreateSurface(destWidth, destHeight, SDL_GetPixelFormatForMasks(32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000)); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_XRGB8888, SDL_TEXTUREACCESS_STREAMING, destWidth, destHeight); +#else + surface = SDL_CreateRGBSurface(0, destWidth, destHeight, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_XRGB8888, SDL_TEXTUREACCESS_STREAMING, destWidth, destHeight); +#endif + } +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) } +#endif +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (!openGL && surface == NULL) { +#else + if (surface == NULL) { +#endif systemMessage(0, "Failed to set video mode"); SDL_Quit(); exit(-1); @@ -856,17 +957,31 @@ void sdlInitVideo() int flags; int screenWidth; int screenHeight; + int window_width, window_height, render_width, render_height; +#ifdef ENABLE_SDL3 + SDL_RendererLogicalPresentation representation; + bool makes_sense = false; +#else + SDL_bool makes_sense = SDL_FALSE; +#endif + uint32_t rmask, gmask, bmask; +#ifndef ENABLE_SDL3 + SDL_RendererInfo render_info; +#endif filter_enlarge = getFilterEnlargeFactor(filter); destWidth = filter_enlarge * sizeX; destHeight = filter_enlarge * sizeY; - flags = fullScreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0; + flags = fullScreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE; + +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (openGL) { SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); flags |= SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; } +#endif screenWidth = destWidth; screenHeight = destHeight; @@ -875,24 +990,132 @@ void sdlInitVideo() SDL_DestroyWindow(window); if (renderer) SDL_DestroyRenderer(renderer); - window = SDL_CreateWindow("VBA-M", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - screenWidth, screenHeight, flags); + +#ifndef ENABLE_SDL3 + window = SDL_CreateWindow("VBA-M", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screenWidth, screenHeight, flags); + if (!openGL) { renderer = SDL_CreateRenderer(window, -1, 0); } +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) + if (openGL) + { + systemMessage(0, "Renderer: OpenGL (%s)", openGL == 2 ? "bilinear" : "no filter"); + } else { +#endif + SDL_GetRendererInfo(renderer, &render_info); + + systemMessage(0, "Renderer: SDL %d.%d (%s)", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, render_info.name); +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) + } +#endif + + SDL_RenderSetLogicalSize(renderer, screenWidth, screenHeight); + SDL_RenderGetLogicalSize(renderer, &render_width, &render_height); + + makes_sense = (SDL_bool)(screenWidth >= render_width && screenHeight >= render_height); + SDL_RenderSetIntegerScale(renderer, makes_sense); +#else + window = SDL_CreateWindow("VBA-M", screenWidth, screenHeight, flags); +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) + if (!openGL) { +#endif + renderer = SDL_CreateRenderer(window, NULL); +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) + } +#endif + +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) + if (openGL) + { + systemMessage(0, "Renderer: OpenGL (%s)", openGL == 2 ? "bilinear" : "nearest"); + } else { +#endif + systemMessage(0, "Renderer: SDL %d.%d (%s)", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_GetRendererName(renderer)); +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) + } +#endif + + SDL_GetCurrentRenderOutputSize(renderer, &window_width, &window_height); + SDL_GetRenderLogicalPresentation(renderer, &render_width, &render_height, &representation); + + makes_sense = (window_width >= render_width && window_height >= render_height); + + if (makes_sense == true) + { + SDL_SetRenderLogicalPresentation(renderer, screenWidth, screenHeight, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); + } else { + SDL_SetRenderLogicalPresentation(renderer, screenWidth, screenHeight, SDL_LOGICAL_PRESENTATION_DISABLED); + } + + SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_LINEAR); +#endif + if (window == NULL) { systemMessage(0, "Failed to set video mode"); SDL_Quit(); exit(-1); } - uint32_t rmask, gmask, bmask; + if (userColorDepth != 0) + { + systemColorDepth = userColorDepth; - if (openGL) { - rmask = 0xFF000000; - gmask = 0x00FF0000; - bmask = 0x0000FF00; + switch (systemColorDepth) + { + case 8: + srcPitch = sizeX * (systemColorDepth >> 3) + 2; + break; + + case 16: + srcPitch = sizeX * (systemColorDepth >> 3) + 4; + break; + + case 24: + srcPitch = sizeX * (systemColorDepth >> 3); + break; + + case 32: + srcPitch = sizeX * (systemColorDepth >> 3) + 4; + break; + + default: + fprintf(stderr, "Wrong color depth %d\n", systemColorDepth); + exit(-1); + break; + } + } else { +#ifdef CONFIG_8BIT + systemColorDepth = 8; + srcPitch = sizeX * (systemColorDepth >> 3) + 2; +#elif defined(CONFIG_16BIT) + systemColorDepth = 16; + srcPitch = sizeX * (systemColorDepth >> 3) + 4; +#elif defined(CONFIG_24BIT) + systemColorDepth = 24; + srcPitch = sizeX * (systemColorDepth >> 3); +#else /* defined(CONFIG_32BIT) */ + systemColorDepth = 32; + srcPitch = sizeX * (systemColorDepth >> 3) + 4; +#endif + } + + if (systemColorDepth == 8) + { + rmask = 0x000000E0; + gmask = 0x0000001C; + bmask = 0x00000003; + } else if (systemColorDepth == 16) { +#ifdef CONFIG_RGB565 + rmask = 0x0000F800; + gmask = 0x000007E0; + bmask = 0x0000001F; +#else + rmask = 0x00007C00; + gmask = 0x000003E0; + bmask = 0x0000001F; +#endif } else { rmask = 0x00FF0000; gmask = 0x0000FF00; @@ -904,27 +1127,41 @@ void sdlInitVideo() systemBlueShift = sdlCalculateShift(bmask); //printf("systemRedShift %d, systemGreenShift %d, systemBlueShift %d\n", - // systemRedShift, systemGreenShift, systemBlueShift); + // systemRedShift, systemGreenShift, systemBlueShift); // originally 3, 11, 19 -> 27, 19, 11 +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (openGL) { - // Align to BGRA instead of ABGR - systemRedShift += 8; - systemGreenShift += 8; - systemBlueShift += 8; + if (systemColorDepth == 32) + { + // Align to BGRA instead of ABGR + systemRedShift += 8; + systemGreenShift += 8; + systemBlueShift += 8; + } } +#endif - systemColorDepth = 32; - srcPitch = sizeX * 4 + 4; - +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (openGL) { glcontext = SDL_GL_CreateContext(window); sdlOpenGLInit(screenWidth, screenHeight); } +#endif sdlResizeVideo(); } +#ifdef ENABLE_SDL3 +#ifndef SDL_KMOD_META +#define SDL_KMOD_META SDL_KMOD_GUI +#endif + +#define MOD_KEYS (SDL_KMOD_CTRL | SDL_KMOD_SHIFT | SDL_KMOD_ALT | SDL_KMOD_META) +#define MOD_NOCTRL (SDL_KMOD_SHIFT | SDL_KMOD_ALT | SDL_KMOD_META) +#define MOD_NOALT (SDL_KMOD_CTRL | SDL_KMOD_SHIFT | SDL_KMOD_META) +#define MOD_NOSHIFT (SDL_KMOD_CTRL | SDL_KMOD_ALT | SDL_KMOD_META) +#else #ifndef KMOD_META #define KMOD_META KMOD_GUI #endif @@ -933,6 +1170,7 @@ void sdlInitVideo() #define MOD_NOCTRL (KMOD_SHIFT | KMOD_ALT | KMOD_META) #define MOD_NOALT (KMOD_CTRL | KMOD_SHIFT | KMOD_META) #define MOD_NOSHIFT (KMOD_CTRL | KMOD_ALT | KMOD_META) +#endif /* * 04.02.2008 (xKiv): factored out from sdlPollEvents @@ -949,7 +1187,7 @@ void change_rewind(int howmuch) rewindCounter = 0; { char rewindMsgBuffer[50]; - sprintf(rewindMsgBuffer, "Rewind to %1d [%d]", rewindPos + 1, rewindSerials[rewindPos]); + snprintf(rewindMsgBuffer, sizeof(rewindMsgBuffer), "Rewind to %1d [%d]", rewindPos + 1, rewindSerials[rewindPos]); rewindMsgBuffer[49] = 0; systemConsoleMessage(rewindMsgBuffer); } @@ -1054,56 +1292,113 @@ void sdlPollEvents() SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { - case SDL_QUIT: +#ifndef ENABLE_SDL3 + case SDL_QUIT: +#else + case SDL_EVENT_QUIT: +#endif emulating = 0; break; - case SDL_WINDOWEVENT: - switch (event.window.event) { - case SDL_WINDOWEVENT_FOCUS_GAINED: - if (pauseWhenInactive) - if (paused) { - if (emulating) { - paused = false; - soundResume(); - } - } - break; - case SDL_WINDOWEVENT_FOCUS_LOST: - if (pauseWhenInactive) { - wasPaused = true; +#ifdef ENABLE_SDL3 + case SDL_EVENT_WINDOW_FOCUS_GAINED: + if (pauseWhenInactive) + if (paused) { if (emulating) { - paused = true; - soundPause(); + paused = false; + soundResume(); } - - memset(delta, 255, delta_size); } - break; - case SDL_WINDOWEVENT_RESIZED: - if (openGL) - sdlOpenGLScaleWithAspect(event.window.data1, event.window.data2); - break; + break; + case SDL_EVENT_WINDOW_FOCUS_LOST: + if (pauseWhenInactive) { + wasPaused = true; + if (emulating) { + paused = true; + soundPause(); + } + + memset(delta, 255, delta_size); } break; - case SDL_MOUSEMOTION: - case SDL_MOUSEBUTTONUP: - case SDL_MOUSEBUTTONDOWN: + case SDL_EVENT_WINDOW_RESIZED: +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) + if (openGL) + sdlOpenGLScaleWithAspect(event.window.data1, event.window.data2); +#endif + break; + case SDL_EVENT_MOUSE_MOTION: + case SDL_EVENT_MOUSE_BUTTON_UP: + case SDL_EVENT_MOUSE_BUTTON_DOWN: +#else + case SDL_WINDOWEVENT: + switch (event.window.event) { + case SDL_WINDOWEVENT_FOCUS_GAINED: + if (pauseWhenInactive) + if (paused) { + if (emulating) { + paused = false; + soundResume(); + } + } + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + if (pauseWhenInactive) { + wasPaused = true; + if (emulating) { + paused = true; + soundPause(); + } + + memset(delta, 255, delta_size); + } + break; + case SDL_WINDOWEVENT_RESIZED: +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) + if (openGL) + sdlOpenGLScaleWithAspect(event.window.data1, event.window.data2); +#endif + break; + } + break; + case SDL_MOUSEMOTION: + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEBUTTONDOWN: +#endif if (fullScreen) { - SDL_ShowCursor(SDL_ENABLE); +#ifdef ENABLE_SDL3 + SDL_ShowCursor(); +#else + SDL_ShowCursor(true); +#endif mouseCounter = 120; } break; - case SDL_JOYHATMOTION: - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: - case SDL_JOYAXISMOTION: - case SDL_KEYDOWN: +#ifndef ENABLE_SDL3 + case SDL_JOYHATMOTION: + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + case SDL_JOYAXISMOTION: + case SDL_KEYDOWN: +#else + case SDL_EVENT_JOYSTICK_HAT_MOTION: + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + case SDL_EVENT_JOYSTICK_BUTTON_UP: + case SDL_EVENT_JOYSTICK_AXIS_MOTION: + case SDL_EVENT_KEY_DOWN: +#endif inputProcessSDLEvent(event); break; - case SDL_KEYUP: - switch (event.key.keysym.sym) { - case SDLK_r: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#ifndef ENABLE_SDL3 + case SDL_KEYUP: + switch (event.key.keysym.sym) { + case SDLK_r: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#else + case SDL_EVENT_KEY_UP: + switch (event.key.key) { + case SDLK_R: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) { +#endif if (emulating) { emulator.emuReset(); @@ -1111,31 +1406,61 @@ void sdlPollEvents() } } break; - case SDLK_b: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) +#ifndef ENABLE_SDL3 + case SDLK_b: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) +#else + case SDLK_B: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) +#endif change_rewind(-1); break; - case SDLK_v: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) +#ifndef ENABLE_SDL3 + case SDLK_v: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) +#else + case SDLK_V: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) +#endif change_rewind(+1); break; - case SDLK_h: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) +#ifndef ENABLE_SDL3 + case SDLK_h: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) +#else + case SDLK_H: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) +#endif change_rewind(0); break; - case SDLK_j: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) +#ifndef ENABLE_SDL3 + case SDLK_j: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) +#else + case SDLK_J: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) +#endif change_rewind((rewindTopPos - rewindPos) * ((rewindTopPos > rewindPos) ? +1 : -1)); break; - case SDLK_e: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#ifndef ENABLE_SDL3 + case SDLK_e: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#else + case SDLK_E: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) { +#endif coreOptions.cheatsEnabled = !coreOptions.cheatsEnabled; systemConsoleMessage(coreOptions.cheatsEnabled ? "Cheats on" : "Cheats off"); } break; - case SDLK_s: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#ifndef ENABLE_SDL3 + case SDLK_s: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#else + case SDLK_S: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) { +#endif if (sdlSoundToggledOff) { // was off // restore saved state soundSetEnable(sdlSoundToggledOff); @@ -1197,8 +1522,13 @@ void sdlPollEvents() } break; - case SDLK_p: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#ifndef ENABLE_SDL3 + case SDLK_p: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#else + case SDLK_P: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) { +#endif paused = !paused; if (paused) soundPause(); @@ -1212,21 +1542,35 @@ void sdlPollEvents() case SDLK_ESCAPE: emulating = 0; break; - case SDLK_f: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#ifndef ENABLE_SDL3 + case SDLK_f: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { + fullScreen = !fullScreen; + SDL_SetWindowFullscreen(window, fullScreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); +#else + case SDLK_F: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) { fullScreen = !fullScreen; - SDL_SetWindowFullscreen(window, fullScreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + SDL_SetWindowFullscreen(window, fullScreen ? true : false); +#endif +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (openGL) { if (fullScreen) sdlOpenGLScaleWithAspect(desktopWidth, desktopHeight); else sdlOpenGLScaleWithAspect(destWidth, destHeight); } +#endif //sdlInitVideo(); } break; - case SDLK_g: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#ifndef ENABLE_SDL3 + case SDLK_g: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#else + case SDLK_G: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) { +#endif filterFunction = 0; while (!filterFunction) { filter = (Filter)((filter + 1) % kInvalidFilter); @@ -1258,23 +1602,38 @@ void sdlPollEvents() case SDLK_F6: case SDLK_F7: case SDLK_F8: - if (!(event.key.keysym.mod & MOD_NOSHIFT) && (event.key.keysym.mod & KMOD_SHIFT)) { - sdlHandleSavestateKey(event.key.keysym.sym - SDLK_F1, 1); // with SHIFT - } else if (!(event.key.keysym.mod & MOD_KEYS)) { +#ifndef ENABLE_SDL3 + if (!(event.key.keysym.mod & MOD_NOSHIFT) && (event.key.keysym.mod & KMOD_SHIFT)) { + sdlHandleSavestateKey(event.key.keysym.sym - SDLK_F1, 1); // with SHIFT + } else if (!(event.key.keysym.mod & MOD_KEYS)) { sdlHandleSavestateKey(event.key.keysym.sym - SDLK_F1, 0); // without SHIFT +#else + if (!(event.key.mod & MOD_NOSHIFT) && (event.key.mod & SDL_KMOD_SHIFT)) { + sdlHandleSavestateKey(event.key.key - SDLK_F1, 1); // with SHIFT + } else if (!(event.key.mod & MOD_KEYS)) { + sdlHandleSavestateKey(event.key.key - SDLK_F1, 0); // without SHIFT +#endif } break; /* backups - only load */ case SDLK_F9: /* F9 is "load backup" - saved state from *just before* the last restore */ - if (!(event.key.keysym.mod & MOD_NOSHIFT)) /* must work with or without shift, but only without other modifiers*/ +#ifndef ENABLE_SDL3 + if (!(event.key.keysym.mod & MOD_NOSHIFT)) /* must work with or without shift, but only without other modifiers*/ +#else + if (!(event.key.mod & MOD_NOSHIFT)) /* must work with or without shift, but only without other modifiers*/ +#endif { sdlReadState(SLOT_POS_LOAD_BACKUP); } break; case SDLK_F10: /* F10 is "save backup" - what was in the last overwritten savestate before we overwrote it*/ - if (!(event.key.keysym.mod & MOD_NOSHIFT)) /* must work with or without shift, but only without other modifiers*/ +#ifndef ENABLE_SDL3 + if (!(event.key.keysym.mod & MOD_NOSHIFT)) /* must work with or without shift, but only without other modifiers*/ +#else + if (!(event.key.mod & MOD_NOSHIFT)) /* must work with or without shift, but only without other modifiers*/ +#endif { sdlReadState(SLOT_POS_SAVE_BACKUP); } @@ -1283,7 +1642,11 @@ void sdlPollEvents() case SDLK_2: case SDLK_3: case SDLK_4: - if (!(event.key.keysym.mod & MOD_NOALT) && (event.key.keysym.mod & KMOD_ALT)) { +#ifndef ENABLE_SDL3 + if (!(event.key.keysym.mod & MOD_NOALT) && (event.key.keysym.mod & KMOD_ALT)) { +#else + if (!(event.key.mod & MOD_NOALT) && (event.key.mod & SDL_KMOD_ALT)) { +#endif const char* disableMessages[4] = { "autofire A disabled", "autofire B disabled", "autofire R disabled", @@ -1294,6 +1657,24 @@ void sdlPollEvents() "autofire L" }; EKey k = KEY_BUTTON_A; +#ifdef ENABLE_SDL3 + if (event.key.key == SDLK_1) + k = KEY_BUTTON_A; + else if (event.key.key == SDLK_2) + k = KEY_BUTTON_B; + else if (event.key.key == SDLK_3) + k = KEY_BUTTON_R; + else if (event.key.key == SDLK_4) + k = KEY_BUTTON_L; + + if (inputToggleAutoFire(k)) { + systemScreenMessage(enableMessages[event.key.key - SDLK_1]); + } else { + systemScreenMessage(disableMessages[event.key.key - SDLK_1]); + } + } else if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) { + int mask = 0x0100 << (event.key.key - SDLK_1); +#else if (event.key.keysym.sym == SDLK_1) k = KEY_BUTTON_A; else if (event.key.keysym.sym == SDLK_2) @@ -1310,6 +1691,7 @@ void sdlPollEvents() } } else if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { int mask = 0x0100 << (event.key.keysym.sym - SDLK_1); +#endif coreOptions.layerSettings ^= mask; coreOptions.layerEnable = DISPCNT & coreOptions.layerSettings; CPUUpdateRenderBuffers(false); @@ -1319,14 +1701,24 @@ void sdlPollEvents() case SDLK_6: case SDLK_7: case SDLK_8: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#ifndef ENABLE_SDL3 + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { int mask = 0x0100 << (event.key.keysym.sym - SDLK_1); +#else + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) { + int mask = 0x0100 << (event.key.key - SDLK_1); +#endif coreOptions.layerSettings ^= mask; coreOptions.layerEnable = DISPCNT & coreOptions.layerSettings; } break; - case SDLK_n: - if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#ifndef ENABLE_SDL3 + case SDLK_n: + if (!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { +#else + case SDLK_N: + if (!(event.key.mod & MOD_NOCTRL) && (event.key.mod & SDL_KMOD_CTRL)) { +#endif if (paused) paused = false; pauseNextFrame = true; @@ -1447,44 +1839,44 @@ void usage(char* cmd) printf("\ \n\ Options:\n\ - -O, --opengl=MODE Set OpenGL texture filter\n\ - --no-opengl 0 - Disable OpenGL\n\ - --opengl-nearest 1 - No filtering\n\ - --opengl-bilinear 2 - Bilinear filtering\n\ - -F, --fullscreen Full screen\n\ + -O, --opengl=MODE Set OpenGL texture filter\n\ + --no-opengl 0 - Disable OpenGL\n\ + --opengl-nearest 1 - No filtering\n\ + --opengl-bilinear 2 - Bilinear filtering\n\ + -F, --fullscreen Full screen\n\ -G, --gdb=PROTOCOL GNU Remote Stub mode:\n\ - tcp - use TCP at port 55555\n\ + tcp - use TCP at port 55555\n\ tcp:PORT - use TCP at port PORT\n\ pipe - use pipe transport\n\ - -I, --ifb-filter=FILTER Select interframe blending filter:\n\ + -I, --ifb-filter=FILTER Select interframe blending filter:\n\ "); for (int i = 0; i < (int)kInvalidIFBFilter; i++) printf(" %d - %s\n", i, getIFBFilterName((IFBFilter)i)); printf("\ -N, --no-debug Don't parse debug information\n\ - -S, --flash-size=SIZE Set the Flash size\n\ - --flash-64k 0 - 64K Flash\n\ - --flash-128k 1 - 128K Flash\n\ - -T, --throttle=THROTTLE Set the desired throttle (5...1000)\n\ - -b, --bios=BIOS Use given bios file\n\ - -c, --config=FILE Read the given configuration file\n\ - -f, --filter=FILTER Select filter:\n\ + -S, --flash-size=SIZE Set the Flash size\n\ + --flash-64k 0 - 64K Flash\n\ + --flash-128k 1 - 128K Flash\n\ + -T, --throttle=THROTTLE Set the desired throttle (5...1000)\n\ + -b, --bios=BIOS Use given bios file\n\ + -c, --config=FILE Read the given configuration file\n\ + -f, --filter=FILTER Select filter:\n\ "); for (int i = 0; i < (int)kInvalidFilter; i++) printf(" %d - %s\n", i, getFilterName((Filter)i)); printf("\ -h, --help Print this help\n\ - -i, --patch=PATCH Apply given patch\n\ - -p, --profile=[HERTZ] Enable profiling\n\ - -s, --frameskip=FRAMESKIP Set frame skip (0...9)\n\ - -t, --save-type=TYPE Set the available save type\n\ - --save-auto 0 - Automatic (EEPROM, SRAM, FLASH)\n\ - --save-eeprom 1 - EEPROM\n\ - --save-sram 2 - SRAM\n\ - --save-flash 3 - FLASH\n\ - --save-sensor 4 - EEPROM+Sensor\n\ - --save-none 5 - NONE\n\ - -v, --verbose=VERBOSE Set verbose logging (trace.log)\n\ + -i, --patch=PATCH Apply given patch\n\ + -p, --profile=[HERTZ] Enable profiling\n\ + -s, --frameskip=FRAMESKIP Set frame skip (0...9)\n\ + -t, --save-type=TYPE Set the available save type\n\ + --save-auto 0 - Automatic (EEPROM, SRAM, FLASH)\n\ + --save-eeprom 1 - EEPROM\n\ + --save-sram 2 - SRAM\n\ + --save-flash 3 - FLASH\n\ + --save-sensor 4 - EEPROM+Sensor\n\ + --save-none 5 - NONE\n\ + -v, --verbose=VERBOSE Set verbose logging (trace.log)\n\ 1 - SWI\n\ 2 - Unaligned memory access\n\ 4 - Illegal memory write\n\ @@ -1497,20 +1889,21 @@ Options:\n\ 512 - AGBPrint messages\n\ \n\ Long options only:\n\ - --agb-print Enable AGBPrint support\n\ - --auto-frameskip Enable auto frameskipping\n\ + --agb-print Enable AGBPrint support\n\ + --auto-frameskip Enable auto frameskipping\n\ --no-agb-print Disable AGBPrint support\n\ - --no-auto-frameskip Disable auto frameskipping\n\ + --no-auto-frameskip Disable auto frameskipping\n\ + --color-depth Set color depth (8, 16, 24 or 32)\n\ --no-patch Do not automatically apply patch\n\ --no-pause-when-inactive Don't pause when inactive\n\ - --no-rtc Disable RTC support\n\ - --no-show-speed Don't show emulation speed\n\ - --no-throttle Disable throttle\n\ - --pause-when-inactive Pause when inactive\n\ - --rtc Enable RTC support\n\ - --show-speed-normal Show emulation speed\n\ - --show-speed-detailed Show detailed speed data\n\ - --cheat 'CHEAT' Add a cheat\n\ + --no-rtc Disable RTC support\n\ + --no-show-speed Don't show emulation speed\n\ + --no-throttle Disable throttle\n\ + --pause-when-inactive Pause when inactive\n\ + --rtc Enable RTC support\n\ + --show-speed-normal Show emulation speed\n\ + --show-speed-detailed Show detailed speed data\n\ + --cheat 'CHEAT' Add a cheat\n\ "); } @@ -1535,7 +1928,7 @@ void handleRewinds() resize /* actual size */ )) { char rewMsgBuf[100]; - sprintf(rewMsgBuf, "Remembered rewind %1d (of %1d), serial %d.", curSavePos + 1, rewindCount, rewindSerial); + snprintf(rewMsgBuf, sizeof(rewMsgBuf), "Remembered rewind %1d (of %1d), serial %d.", curSavePos + 1, rewindCount, rewindSerial); rewMsgBuf[99] = 0; systemConsoleMessage(rewMsgBuf); rewindSerials[curSavePos] = rewindSerial; @@ -1554,7 +1947,7 @@ void handleRewinds() void SetHomeConfigDir() { - sprintf(homeConfigDir, "%s%s", get_xdg_user_config_home().c_str(), DOT_DIR); + snprintf(homeConfigDir, sizeof(homeConfigDir), "%s%s", get_xdg_user_config_home().c_str(), DOT_DIR); struct stat s; if (stat(homeDataDir, &s) == -1 || !S_ISDIR(s.st_mode)) mkdir(homeDataDir, 0755); @@ -1562,7 +1955,7 @@ void SetHomeConfigDir() void SetHomeDataDir() { - sprintf(homeDataDir, "%s%s", get_xdg_user_data_home().c_str(), DOT_DIR); + snprintf(homeDataDir, sizeof(homeConfigDir), "%s%s", get_xdg_user_data_home().c_str(), DOT_DIR); struct stat s; if (stat(homeDataDir, &s) == -1 || !S_ISDIR(s.st_mode)) mkdir(homeDataDir, 0755); @@ -1695,25 +2088,25 @@ int main(int argc, char** argv) char* tmp; // no patch given yet - look for ROMBASENAME.ips tmp = (char*)malloc(strlen(filename) + 4 + 1); - sprintf(tmp, "%s.ips", filename); + snprintf(tmp, strlen(filename) + 4, "%s.ips", filename); patchNames[patchNum] = tmp; patchNum++; // no patch given yet - look for ROMBASENAME.ups tmp = (char*)malloc(strlen(filename) + 4 + 1); - sprintf(tmp, "%s.ups", filename); + snprintf(tmp, strlen(filename) + 4, "%s.ups", filename); patchNames[patchNum] = tmp; patchNum++; // no patch given yet - look for ROMBASENAME.bps tmp = (char*)malloc(strlen(filename) + 4 + 1); - sprintf(tmp, "%s.bps", filename); + snprintf(tmp, strlen(filename) + 4, "%s.bps", filename); patchNames[patchNum] = tmp; patchNum++; // no patch given yet - look for ROMBASENAME.ppf tmp = (char*)malloc(strlen(filename) + 4 + 1); - sprintf(tmp, "%s.ppf", filename); + snprintf(tmp, strlen(filename) + 4, "%s.ppf", filename); patchNames[patchNum] = tmp; patchNum++; } @@ -1802,20 +2195,38 @@ int main(int argc, char** argv) if (debugger) remoteInit(); - int flags = SDL_INIT_VIDEO | SDL_INIT_TIMER; + int flags = SDL_INIT_VIDEO; +#ifdef ENABLE_SDL3 + if (SDL_InitSubSystem(flags) == false) { +#else + if (SDL_InitSubSystem(flags) < 0) { +#endif + systemMessage(0, "Failed to init SDL subsystem: %s", SDL_GetError()); + exit(-1); + } + +#ifdef ENABLE_SDL3 + if (SDL_Init(flags) == false) { +#else if (SDL_Init(flags) < 0) { +#endif systemMessage(0, "Failed to init SDL: %s", SDL_GetError()); exit(-1); } +#ifdef ENABLE_SDL3 + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == false) { +#else if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { +#endif systemMessage(0, "Failed to init joystick support: %s", SDL_GetError()); } #if defined(VBAM_ENABLE_LIRC) StartLirc(); #endif + inputInitJoysticks(); if (cartridgeType == IMAGE_GBA) { @@ -1855,8 +2266,8 @@ int main(int argc, char** argv) if (systemColorDepth == 15) systemColorDepth = 16; - if (systemColorDepth != 16 && systemColorDepth != 24 && systemColorDepth != 32) { - fprintf(stderr, "Unsupported color depth '%d'.\nOnly 16, 24 and 32 bit color depths are supported\n", systemColorDepth); + if (systemColorDepth != 8 && systemColorDepth != 16 && systemColorDepth != 24 && systemColorDepth != 32) { + fprintf(stderr, "Unsupported color depth '%d'.\nOnly 8, 16, 24 and 32 bit color depths are supported\n", systemColorDepth); exit(-1); } @@ -1921,7 +2332,11 @@ int main(int argc, char** argv) if (mouseCounter) { mouseCounter--; if (mouseCounter == 0) - SDL_ShowCursor(SDL_DISABLE); +#ifdef ENABLE_SDL3 + SDL_HideCursor(); +#else + SDL_ShowCursor(false); +#endif } } @@ -1930,9 +2345,15 @@ int main(int argc, char** argv) remoteCleanUp(); soundShutdown(); +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (openGL) { +#ifdef ENABLE_SDL3 + SDL_GL_DestroyContext(glcontext); +#else SDL_GL_DeleteContext(glcontext); +#endif } +#endif if (gbRom != NULL || g_rom != NULL) { sdlWriteBattery(); @@ -1993,9 +2414,9 @@ void drawSpeed(uint8_t* screen, int pitch, int x, int y) { char buffer[50]; if (showSpeed == 1) - sprintf(buffer, "%d%%", systemSpeed); + snprintf(buffer, sizeof(buffer), "%d%%", systemSpeed); else - sprintf(buffer, "%3d%%(%d, %d fps)", systemSpeed, + snprintf(buffer, sizeof(buffer), "%3d%%(%d, %d fps)", systemSpeed, systemFrameSkip, showRenderedFrames); @@ -2009,41 +2430,74 @@ void systemDrawScreen() renderedFrames++; +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (openGL) screen = filterPix; else { +#endif screen = (uint8_t*)surface->pixels; SDL_LockSurface(surface); +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) } +#endif if (ifbFunction) ifbFunction(g_pix + srcPitch, srcPitch, sizeX, sizeY); - filterFunction(g_pix + srcPitch, srcPitch, delta, screen, - destPitch, sizeX, sizeY); + filterFunction(g_pix + srcPitch, srcPitch, delta, screen, destPitch, sizeX, sizeY); +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (openGL) { int bytes = (systemColorDepth >> 3); for (int i = 0; i < destWidth; i++) for (int j = 0; j < destHeight; j++) { - uint8_t k; - k = filterPix[i * bytes + j * destPitch + 3]; - filterPix[i * bytes + j * destPitch + 3] = filterPix[i * bytes + j * destPitch + 1]; - filterPix[i * bytes + j * destPitch + 1] = k; + uint8_t k = 0; + uint8_t l = 0; + + if (systemColorDepth == 24) + { + k = filterPix[i * bytes + j * destPitch + 2]; + filterPix[i * bytes + j * destPitch + 2] = filterPix[i * bytes + j * destPitch]; + filterPix[i * bytes + j * destPitch] = k; + } else if (systemColorDepth == 32) { + k = filterPix[i * bytes + j * destPitch + 3]; + l = filterPix[i * bytes + j * destPitch + 2]; + filterPix[i * bytes + j * destPitch + 3] = 0; + filterPix[i * bytes + j * destPitch + 2] = filterPix[i * bytes + j * destPitch + 1]; + filterPix[i * bytes + j * destPitch + 1] = l; + filterPix[i * bytes + j * destPitch] = k; + } else { + k = filterPix[i * bytes + j * destPitch + 3]; + filterPix[i * bytes + j * destPitch + 3] = filterPix[i * bytes + j * destPitch + 1]; + filterPix[i * bytes + j * destPitch + 1] = k; + } } } +#endif drawScreenMessage(screen, destPitch, 10, destHeight - 20, 3000); if (showSpeed && fullScreen) drawSpeed(screen, destPitch, 10, 20); +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) if (openGL) { glClear(GL_COLOR_BUFFER_BIT); glPixelStorei(GL_UNPACK_ROW_LENGTH, destWidth); - if (systemColorDepth == 16) + if (systemColorDepth == 8) + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, destWidth, destHeight, + GL_RGB, GL_UNSIGNED_BYTE_3_3_2, screen); + else if (systemColorDepth == 16) +#ifdef CONFIG_RGB565 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, destWidth, destHeight, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, screen); +#else + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, destWidth, destHeight, + GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, screen); +#endif + else if (systemColorDepth == 24) + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, destWidth, destHeight, + GL_RGB, GL_UNSIGNED_BYTE, screen); else glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, destWidth, destHeight, //GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, screen); @@ -2062,11 +2516,18 @@ void systemDrawScreen() glEnd(); SDL_GL_SwapWindow(window); } else { +#endif SDL_UnlockSurface(surface); SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch); +#ifdef ENABLE_SDL3 + SDL_RenderTexture(renderer, texture, NULL, NULL); +#else SDL_RenderCopy(renderer, texture, NULL, NULL); +#endif SDL_RenderPresent(renderer); +#if !defined(CONFIG_IDF_TARGET) && !defined(NO_OPENGL) } +#endif } void systemSendScreen() @@ -2088,9 +2549,9 @@ void systemShowSpeed(int speed) if (!fullScreen && showSpeed) { char buffer[80]; if (showSpeed == 1) - sprintf(buffer, "VBA-M - %d%%", systemSpeed); + snprintf(buffer, sizeof(buffer), "VBA-M - %d%%", systemSpeed); else - sprintf(buffer, "VBA-M - %d%%(%d, %d fps)", systemSpeed, + snprintf(buffer, sizeof(buffer), "VBA-M - %d%%(%d, %d fps)", systemSpeed, systemFrameSkip, showRenderedFrames); @@ -2160,20 +2621,20 @@ void systemScreenCapture(int a) if (captureFormat) { if (screenShotDir) - sprintf(buffer, "%s%c%s%02d.bmp", screenShotDir, kFileSep, gameFile, a); + snprintf(buffer, sizeof(buffer), "%s%c%s%02d.bmp", screenShotDir, kFileSep, gameFile, a); else if (access(gameDir, W_OK) == 0) - sprintf(buffer, "%s%c%s%02d.bmp", gameDir, kFileSep, gameFile, a); + snprintf(buffer, sizeof(buffer), "%s%c%s%02d.bmp", gameDir, kFileSep, gameFile, a); else - sprintf(buffer, "%s%c%s%02d.bmp", homeDataDir, kFileSep, gameFile, a); + snprintf(buffer, sizeof(buffer), "%s%c%s%02d.bmp", homeDataDir, kFileSep, gameFile, a); result = emulator.emuWriteBMP(buffer); } else { if (screenShotDir) - sprintf(buffer, "%s%c%s%02d.png", screenShotDir, kFileSep, gameFile, a); + snprintf(buffer, sizeof(buffer), "%s%c%s%02d.png", screenShotDir, kFileSep, gameFile, a); else if (access(gameDir, W_OK) == 0) - sprintf(buffer, "%s%c%s%02d.png", gameDir, kFileSep, gameFile, a); + snprintf(buffer, sizeof(buffer), "%s%c%s%02d.png", gameDir, kFileSep, gameFile, a); else - sprintf(buffer, "%s%c%s%02d.png", homeDataDir, kFileSep, gameFile, a); + snprintf(buffer, sizeof(buffer), "%s%c%s%02d.png", homeDataDir, kFileSep, gameFile, a); result = emulator.emuWritePNG(buffer); } diff --git a/src/sdl/audio_sdl.cpp b/src/sdl/audio_sdl.cpp index c0bac5f0..0cfa826b 100644 --- a/src/sdl/audio_sdl.cpp +++ b/src/sdl/audio_sdl.cpp @@ -20,7 +20,9 @@ #include #include +#ifndef ENABLE_SDL3 #include +#endif #include "core/gba/gbaGlobals.h" #include "core/gba/gbaSound.h" @@ -37,9 +39,23 @@ SoundSDL::SoundSDL(): initialized(false) {} +#ifndef ENABLE_SDL3 void SoundSDL::soundCallback(void* data, uint8_t* stream, int len) { reinterpret_cast(data)->read(reinterpret_cast(stream), len); } +#else +void SoundSDL::soundCallback(void* data, SDL_AudioStream *stream, int additional_length, int length) { + uint16_t streamdata[8192]; + (void)additional_length; + + while (length > 0) + { + reinterpret_cast(data)->read(reinterpret_cast(streamdata), length > sizeof(streamdata) ? sizeof(streamdata) : length); + SDL_PutAudioStreamData(stream, streamdata, length > sizeof(streamdata) ? sizeof(streamdata) : length); + length -= sizeof(streamdata); + } +} +#endif bool SoundSDL::should_wait() { return emulating && !coreOptions.speedup && current_rate && !gba_joybus_active; @@ -65,7 +81,11 @@ void SoundSDL::read(uint16_t* stream, int length) { if (!buffer_size()) { if (should_wait()) +#ifndef ENABLE_SDL3 SDL_SemWait(data_available); +#else + SDL_WaitSemaphore(data_available); +#endif else return; } @@ -76,7 +96,11 @@ void SoundSDL::read(uint16_t* stream, int length) { SDL_UnlockMutex(mutex); +#ifndef ENABLE_SDL3 SDL_SemPost(data_read); +#else + SDL_SignalSemaphore(data_read); +#endif } void SoundSDL::write(uint16_t * finalWave, int length) { @@ -85,8 +109,16 @@ void SoundSDL::write(uint16_t * finalWave, int length) { SDL_LockMutex(mutex); +#ifndef ENABLE_SDL3 if (SDL_GetAudioDeviceStatus(sound_device) != SDL_AUDIO_PLAYING) SDL_PauseAudioDevice(sound_device, 0); +#else + if (SDL_AudioDevicePaused(sound_device) == true) + { + SDL_ResumeAudioStreamDevice(sound_stream); + SDL_ResumeAudioDevice(sound_device); + } +#endif std::size_t samples = length / 4; std::size_t avail; @@ -99,10 +131,18 @@ void SoundSDL::write(uint16_t * finalWave, int length) { SDL_UnlockMutex(mutex); +#ifndef ENABLE_SDL3 SDL_SemPost(data_available); +#else + SDL_SignalSemaphore(data_available); +#endif if (should_wait()) - SDL_SemWait(data_read); +#ifndef ENABLE_SDL3 + SDL_SemWait(data_read); +#else + SDL_WaitSemaphore(data_read); +#endif else // Drop the remainder of the audio data return; @@ -125,15 +165,50 @@ bool SoundSDL::init(long sampleRate) { // for "no throttle" use regular rate, audio is just dropped audio.freq = current_rate ? static_cast(sampleRate * (current_rate / 100.0)) : sampleRate; +#ifndef ENABLE_SDL3 audio.format = AUDIO_S16SYS; +#else + audio.format = SDL_AUDIO_S16; +#endif + audio.channels = 2; + +#ifndef ENABLE_SDL3 audio.samples = 2048; audio.callback = soundCallback; audio.userdata = this; - if (!SDL_WasInit(SDL_INIT_AUDIO)) SDL_Init(SDL_INIT_AUDIO); + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) +#else + if (SDL_InitSubSystem(SDL_INIT_AUDIO) == false) +#endif + { + std::cerr << "Failed to init audio subsystem: " << SDL_GetError() << std::endl; + return false; + } +#ifndef ENABLE_SDL3 + if (SDL_WasInit(SDL_INIT_AUDIO) < 0) +#else + if (SDL_WasInit(SDL_INIT_AUDIO) == false) +#endif + { + SDL_Init(SDL_INIT_AUDIO); + } + +#ifndef ENABLE_SDL3 sound_device = SDL_OpenAudioDevice(NULL, 0, &audio, NULL, 0); +#else + sound_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio, soundCallback, this); + + if(sound_stream == NULL) + { + std::cerr << "Failed to open audio stream: " << SDL_GetError() << std::endl; + return false; + } + + sound_device = SDL_GetAudioStreamDevice(sound_stream); +#endif if(sound_device == 0) { std::cerr << "Failed to open audio: " << SDL_GetError() << std::endl; @@ -147,7 +222,10 @@ bool SoundSDL::init(long sampleRate) { data_read = SDL_CreateSemaphore(1); // turn off audio events because we are not processing them -#if SDL_VERSION_ATLEAST(2, 0, 4) +#if SDL_VERSION_ATLEAST(3, 2, 0) + SDL_SetEventEnabled(SDL_EVENT_AUDIO_DEVICE_ADDED, false); + SDL_SetEventEnabled(SDL_EVENT_AUDIO_DEVICE_REMOVED, false); +#elif SDL_VERSION_ATLEAST(2, 0, 4) SDL_EventState(SDL_AUDIODEVICEADDED, SDL_IGNORE); SDL_EventState(SDL_AUDIODEVICEREMOVED, SDL_IGNORE); #endif @@ -164,8 +242,13 @@ void SoundSDL::deinit() { SDL_LockMutex(mutex); int is_emulating = emulating; emulating = 0; +#ifndef ENABLE_SDL3 SDL_SemPost(data_available); SDL_SemPost(data_read); +#else + SDL_SignalSemaphore(data_available); + SDL_SignalSemaphore(data_read); +#endif SDL_UnlockMutex(mutex); SDL_Delay(100); diff --git a/src/sdl/audio_sdl.h b/src/sdl/audio_sdl.h index fbe0c58c..71ff48e1 100644 --- a/src/sdl/audio_sdl.h +++ b/src/sdl/audio_sdl.h @@ -18,7 +18,11 @@ #ifndef VBAM_SDL_AUDIO_SDL_H_ #define VBAM_SDL_AUDIO_SDL_H_ +#ifndef ENABLE_SDL3 #include +#else +#include +#endif #include "core/base/ringbuffer.h" #include "core/base/sound_driver.h" @@ -29,11 +33,16 @@ public: ~SoundSDL() override; private: - static void soundCallback(void* data, uint8_t* stream, int length); - void read(uint16_t* stream, int length); +#ifdef ENABLE_SDL3 + static void soundCallback(void* data, SDL_AudioStream *stream, int additional_length, int length); +#else + static void soundCallback(void* data, uint8_t* stream, int len); +#endif + bool should_wait(); std::size_t buffer_size(); void deinit(); + void read(uint16_t* stream, int length); // SoundDriver implementation. bool init(long sampleRate) override; @@ -47,9 +56,17 @@ private: SDL_AudioDeviceID sound_device = 0; +#ifdef ENABLE_SDL3 + SDL_AudioStream *sound_stream = NULL; + SDL_Mutex* mutex; + SDL_Semaphore* data_available; + SDL_Semaphore* data_read; +#else SDL_mutex* mutex; - SDL_sem* data_available; - SDL_sem* data_read; + SDL_semaphore* data_available; + SDL_semaphore* data_read; +#endif + SDL_AudioSpec audio_spec; unsigned short current_rate; diff --git a/src/sdl/debugger.cpp b/src/sdl/debugger.cpp index 011dcc4b..dd583681 100644 --- a/src/sdl/debugger.cpp +++ b/src/sdl/debugger.cpp @@ -30,6 +30,10 @@ #include "core/gba/gbaSound.h" #include "sdl/exprNode.h" +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#endif + extern bool debugger; extern int emulating; extern void sdlWriteState(int num); @@ -377,11 +381,11 @@ static const char* debuggerPrintType(Type* t) strcpy(buffer, debuggerPrintType(t->pointer)); else strcpy(buffer, "void"); - sprintf(buffer2, "%s *", buffer); + snprintf(buffer2, sizeof(buffer2), "%s *", buffer); return buffer2; } else if (t->type == TYPE_reference) { strcpy(buffer, debuggerPrintType(t->pointer)); - sprintf(buffer2, "%s &", buffer); + snprintf(buffer2, sizeof(buffer2), "%s &", buffer); return buffer2; } return t->name; @@ -1364,7 +1368,7 @@ static void debuggerDisassembleArm(FILE* f, uint32_t pc, int count) if (l > len) len = l; } - sprintf(format, "%%08x %%-%ds %%s\n", len); + snprintf(format, sizeof(format), "%%08x %%-%ds %%s\n", len); for (i = 0; i < count; i++) { uint32_t addr = pc; pc += disArm(pc, buffer, 4096, 2); @@ -1383,7 +1387,7 @@ static void debuggerDisassembleThumb(FILE* f, uint32_t pc, int count) if (l > len) len = l; } - sprintf(format, "%%08x %%-%ds %%s\n", len); + snprintf(format, sizeof(format), "%%08x %%-%ds %%s\n", len); for (i = 0; i < count; i++) { uint32_t addr = pc; @@ -1781,7 +1785,7 @@ static void debuggerRegisters(int, char**) ((!(reg[16].I & 0x40)) ? '.' : 'F'), (armState ? '.' : 'T'), armMode); - sprintf(buffer, "%08x", armState ? reg[15].I - 4 : reg[15].I - 2); + snprintf(buffer, sizeof(buffer), "%08x", armState ? reg[15].I - 4 : reg[15].I - 2); command[1] = buffer; debuggerDisassemble(3, command); } diff --git a/src/sdl/dictionary.c b/src/sdl/dictionary.c index 59a595b3..d6841cb0 100644 --- a/src/sdl/dictionary.c +++ b/src/sdl/dictionary.c @@ -19,6 +19,10 @@ #include #include +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#endif + /** Maximum value size for integers and doubles. */ #define MAXVALSZ 1024 @@ -372,12 +376,12 @@ int main(int argc, char *argv[]) /* Set values in dictionary */ printf("setting %d values...\n", NVALS); for (i = 0; i < NVALS; i++) { - sprintf(cval, "%04d", i); + snprintf(cval, sizeof(cval), "%04d", i); dictionary_set(d, cval, "salut"); } printf("getting %d values...\n", NVALS); for (i = 0; i < NVALS; i++) { - sprintf(cval, "%04d", i); + snprintf(cval, sizeof(cval), "%04d", i); val = dictionary_get(d, cval, DICT_INVALID_KEY); if (val == DICT_INVALID_KEY) { printf("cannot get value for key [%s]\n", cval); @@ -385,7 +389,7 @@ int main(int argc, char *argv[]) } printf("unsetting %d values...\n", NVALS); for (i = 0; i < NVALS; i++) { - sprintf(cval, "%04d", i); + snprintf(cval, sizeof(cval), "%04d", i); dictionary_unset(d, cval); } if (d->n != 0) { diff --git a/src/sdl/filters.cpp b/src/sdl/filters.cpp index 9762a373..d5716dcb 100644 --- a/src/sdl/filters.cpp +++ b/src/sdl/filters.cpp @@ -307,6 +307,9 @@ void sdlStretchx4(uint8_t* src, uint8_t* dest, int width) { } } +void (*sdlStretcher8[4])(uint8_t*, uint8_t*, int) = { + sdlStretchx1, sdlStretchx2, sdlStretchx3, sdlStretchx4}; + void (*sdlStretcher16[4])(uint8_t*, uint8_t*, int) = { sdlStretchx1, sdlStretchx2, sdlStretchx3, sdlStretchx4}; @@ -391,6 +394,9 @@ bool sdlStretchInit(int colorDepth, int sizeMultiplier, int srcWidth) { sdlMakeStretcher(srcWidth, sizeMultiplier); #else switch (colorDepth) { + case 8: + sdlStretcher = sdlStretcher8[sizeMultiplier]; + break; case 16: sdlStretcher = sdlStretcher16[sizeMultiplier]; break; @@ -493,33 +499,34 @@ void sdlStretch4x(uint8_t* srcPtr, struct FilterDesc { char name[30]; int enlargeFactor; + FilterFunc func8; FilterFunc func16; FilterFunc func24; FilterFunc func32; }; -const FilterDesc Filters[] = {{"Stretch 1x", 1, sdlStretch1x, sdlStretch1x, sdlStretch1x}, - {"Stretch 2x", 2, sdlStretch2x, sdlStretch2x, sdlStretch2x}, - {"2xSaI", 2, _2xSaI, 0, _2xSaI32}, - {"Super 2xSaI", 2, Super2xSaI, 0, Super2xSaI32}, - {"Super Eagle", 2, SuperEagle, 0, SuperEagle32}, - {"Pixelate", 2, Pixelate, 0, Pixelate32}, - {"AdvanceMAME Scale2x", 2, AdMame2x, 0, AdMame2x32}, - {"Bilinear", 2, Bilinear, 0, Bilinear32}, - {"Bilinear Plus", 2, BilinearPlus, 0, BilinearPlus32}, - {"Scanlines", 2, Scanlines, 0, Scanlines32}, - {"TV Mode", 2, ScanlinesTV, 0, ScanlinesTV32}, - {"lq2x", 2, lq2x, 0, lq2x32}, - {"hq2x", 2, hq2x, 0, hq2x32}, - {"xbrz2x", 2, 0, 0, xbrz2x32}, - {"Stretch 3x", 3, sdlStretch3x, sdlStretch3x, sdlStretch3x}, - {"hq3x", 3, hq3x16, 0, hq3x32_32}, - {"xbrz3x", 3, 0, 0, xbrz3x32}, - {"Stretch 4x", 4, sdlStretch4x, sdlStretch4x, sdlStretch4x}, - {"hq4x", 4, hq4x16, 0, hq4x32_32}, - {"xbrz4x", 4, 0, 0, xbrz4x32}, - {"xbrz5x", 5, 0, 0, xbrz5x32}, - {"xbrz6x", 6, 0, 0, xbrz6x32}}; +const FilterDesc Filters[] = {{"Stretch 1x", 1, sdlStretch1x, sdlStretch1x, sdlStretch1x, sdlStretch1x }, + {"Stretch 2x", 2, sdlStretch2x, sdlStretch2x, sdlStretch2x, sdlStretch2x}, + {"2xSaI", 2, 0, _2xSaI, 0, _2xSaI32}, + {"Super 2xSaI", 2, 0, Super2xSaI, 0, Super2xSaI32}, + {"Super Eagle", 2, 0, SuperEagle, 0, SuperEagle32}, + {"Pixelate", 2, 0, Pixelate, 0, Pixelate32}, + {"AdvanceMAME Scale2x", 2, 0, AdMame2x, 0, AdMame2x32}, + {"Bilinear", 2, 0, Bilinear, 0, Bilinear32}, + {"Bilinear Plus", 2, 0, BilinearPlus, 0, BilinearPlus32}, + {"Scanlines", 2, 0, Scanlines, 0, Scanlines32}, + {"TV Mode", 2, 0, ScanlinesTV, 0, ScanlinesTV32}, + {"lq2x", 2, 0, lq2x, 0, lq2x32}, + {"hq2x", 2, 0, hq2x, 0, hq2x32}, + {"xbrz2x", 2, 0, 0, 0, xbrz2x32}, + {"Stretch 3x", 3, sdlStretch3x, sdlStretch3x, sdlStretch3x, sdlStretch3x}, + {"hq3x", 3, 0, hq3x16, 0, hq3x32_32}, + {"xbrz3x", 3, 0, 0, 0, xbrz3x32}, + {"Stretch 4x", 4, sdlStretch4x, sdlStretch4x, sdlStretch4x, sdlStretch4x}, + {"hq4x", 4, 0, hq4x16, 0, hq4x32_32}, + {"xbrz4x", 4, 0, 0, 0, xbrz4x32}, + {"xbrz5x", 5, 0, 0, 0, xbrz5x32}, + {"xbrz6x", 6, 0, 0, 0, xbrz6x32}}; int getFilterEnlargeFactor(const int f) { return Filters[f].enlargeFactor; @@ -533,6 +540,9 @@ FilterFunc initFilter(const int f, const int colorDepth, const int srcWidth) { FilterFunc func; switch (colorDepth) { + case 8: + func = Filters[f].func8; + break; case 15: case 16: func = Filters[f].func16; @@ -585,26 +595,33 @@ FilterFunc initFilter(const int f, const int colorDepth, const int srcWidth) { struct IFBFilterDesc { char name[30]; + IFBFilterFunc func8; IFBFilterFunc func16; + IFBFilterFunc func24; IFBFilterFunc func32; }; -const IFBFilterDesc IFBFilters[] = {{"No interframe blending", 0, 0}, - {"Interframe motion blur", MotionBlurIB, MotionBlurIB32}, - {"Smart interframe blending", SmartIB, SmartIB32}}; +const IFBFilterDesc IFBFilters[] = {{"No interframe blending", 0, 0, 0}, + {"Interframe motion blur", MotionBlurIB8, MotionBlurIB, MotionBlurIB24, MotionBlurIB32}, + {"Smart interframe blending", SmartIB8, SmartIB, SmartIB24, SmartIB32}}; IFBFilterFunc initIFBFilter(const int f, const int colorDepth) { IFBFilterFunc func; switch (colorDepth) { + case 8: + func = IFBFilters[f].func8; + break; case 15: case 16: func = IFBFilters[f].func16; break; + case 24: + func = IFBFilters[f].func24; + break; case 32: func = IFBFilters[f].func32; break; - case 24: default: func = 0; break; diff --git a/src/sdl/iniparser.c b/src/sdl/iniparser.c index 8c0c6b1f..0de0b259 100644 --- a/src/sdl/iniparser.c +++ b/src/sdl/iniparser.c @@ -11,6 +11,10 @@ #include +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#endif + /*---------------------------- Defines -------------------------------------*/ #define ASCIILINESZ (1024) #define INI_INVALID_KEY ((char *)-1) @@ -269,7 +273,7 @@ void iniparser_dumpsection_ini(dictionary *d, char *s, FILE *f) seclen = (int)strlen(s); // fprintf(f, "\n[%s]\n", s); fprintf(f, "[%s]\n", s); - sprintf(keym, "%s:", s); + snprintf(keym, sizeof(keym), "%s:", s); for (j = 0; j < d->size; j++) { if (d->key[j] == NULL) continue; @@ -303,7 +307,7 @@ int iniparser_getsecnkeys(dictionary *d, char *s) return nkeys; seclen = (int)strlen(s); - sprintf(keym, "%s:", s); + snprintf(keym, sizeof(keym), "%s:", s); for (j = 0; j < d->size; j++) { if (d->key[j] == NULL) @@ -349,7 +353,7 @@ char **iniparser_getseckeys(dictionary *d, char *s) keys = (char **)malloc(nkeys * sizeof(char *)); seclen = (int)strlen(s); - sprintf(keym, "%s:", s); + snprintf(keym, sizeof(keym), "%s:", s); i = 0; @@ -710,9 +714,9 @@ dictionary *iniparser_load(const char *ininame) case LINE_VALUE: if (strlen(section)) - sprintf(tmp, "%s:%s", section, key); + snprintf(tmp, sizeof(tmp), "%s:%s", section, key); else - sprintf(tmp, "preferences:%s", key); + snprintf(tmp, sizeof(tmp), "preferences:%s", key); errs = dictionary_set(dict, tmp, val); break; diff --git a/src/sdl/inputSDL.cpp b/src/sdl/inputSDL.cpp index 068098a8..f42db894 100644 --- a/src/sdl/inputSDL.cpp +++ b/src/sdl/inputSDL.cpp @@ -19,6 +19,8 @@ #include "sdl/ConfigManager.h" +#include + #define SDLBUTTONS_NUM 14 static void sdlUpdateKey(uint32_t key, bool down); @@ -63,11 +65,23 @@ static uint32_t joypad[5][SDLBUTTONS_NUM] = { { SDLK_LEFT, SDLK_RIGHT, SDLK_UP, SDLK_DOWN, +#ifdef ENABLE_SDL3 + SDLK_Z, SDLK_X, +#else SDLK_z, SDLK_x, +#endif SDLK_RETURN, SDLK_BACKSPACE, +#ifdef ENABLE_SDL3 + SDLK_A, SDLK_S, +#else SDLK_a, SDLK_s, +#endif SDLK_SPACE, SDLK_F12, +#ifdef ENABLE_SDL3 + SDLK_Q, SDLK_W, +#else SDLK_q, SDLK_w, +#endif } }; @@ -106,18 +120,37 @@ static uint32_t sdlGetAxisCode(const SDL_Event& event) uint32_t inputGetEventCode(const SDL_Event& event) { switch (event.type) { +#ifdef ENABLE_SDL3 + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + return event.key.key; +#else case SDL_KEYDOWN: case SDL_KEYUP: return event.key.keysym.sym; +#endif break; +#ifdef ENABLE_SDL3 + case SDL_EVENT_JOYSTICK_HAT_MOTION: +#else case SDL_JOYHATMOTION: +#endif return sdlGetHatCode(event); break; +#ifdef ENABLE_SDL3 + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + case SDL_EVENT_JOYSTICK_BUTTON_UP: +#else case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: +#endif return sdlGetButtonCode(event); break; +#ifdef ENABLE_SDL3 + case SDL_EVENT_JOYSTICK_AXIS_MOTION: +#else case SDL_JOYAXISMOTION: +#endif return sdlGetAxisCode(event); break; default: @@ -348,18 +381,30 @@ static bool sdlCheckJoyKey(int key) // joystick button int button = what - 128; +#ifdef ENABLE_SDL3 + if (button >= SDL_GetNumJoystickButtons(sdlDevices[dev])) +#else if (button >= SDL_JoystickNumButtons(sdlDevices[dev])) +#endif return false; } else if (what < 0x20) { // joystick axis what >>= 1; +#ifdef ENABLE_SDL3 + if (what >= SDL_GetNumJoystickAxes(sdlDevices[dev])) +#else if (what >= SDL_JoystickNumAxes(sdlDevices[dev])) +#endif return false; } else if (what < 0x30) { // joystick hat what = (what & 15); what >>= 2; +#ifdef ENABLE_SDL3 + if (what >= SDL_GetNumJoystickHats(sdlDevices[dev])) +#else if (what >= SDL_JoystickNumHats(sdlDevices[dev])) +#endif return false; } @@ -369,17 +414,23 @@ static bool sdlCheckJoyKey(int key) void inputInitJoysticks() { +#ifdef ENABLE_SDL3 + SDL_JoystickID *joysticks = SDL_GetJoysticks(&sdlNumDevices); +#else + sdlNumDevices = SDL_NumJoysticks(); +#endif + bool usesJoy = false; + // The main joypad has to be entirely defined for (int i = 0; i < SDLBUTTONS_NUM; i++) { if (!joypad[PAD_MAIN][i]) joypad[PAD_MAIN][i] = joypad[PAD_DEFAULT][i]; } - sdlNumDevices = SDL_NumJoysticks(); - if (sdlNumDevices) + { sdlDevices = (SDL_Joystick**)calloc(1, sdlNumDevices * sizeof(SDL_Joystick**)); - bool usesJoy = false; + } for (int j = 0; j < 4; j++) { for (int i = 0; i < SDLBUTTONS_NUM; i++) { @@ -391,12 +442,17 @@ void inputInitJoysticks() if (sdlDevices) { if (dev < sdlNumDevices) { if (sdlDevices[dev] == NULL) { +#ifndef ENABLE_SDL3 sdlDevices[dev] = SDL_JoystickOpen(dev); +#else + sdlDevices[dev] = SDL_OpenJoystick(joysticks[dev]); +#endif } ok = sdlCheckJoyKey(joypad[j][i]); - } else + } else { ok = false; + } } if (!ok) @@ -416,7 +472,11 @@ void inputInitJoysticks() if (sdlDevices) { if (dev < sdlNumDevices) { if (sdlDevices[dev] == NULL) { +#ifndef ENABLE_SDL3 sdlDevices[dev] = SDL_JoystickOpen(dev); +#else + sdlDevices[dev] = SDL_OpenJoystick(joysticks[dev]); +#endif } ok = sdlCheckJoyKey(motion[i]); @@ -432,34 +492,68 @@ void inputInitJoysticks() } if (usesJoy) + { +#ifdef ENABLE_SDL3 + SDL_SetJoystickEventsEnabled(true); +#else SDL_JoystickEventState(SDL_ENABLE); +#endif + } } void inputProcessSDLEvent(const SDL_Event& event) { - // fprintf(stdout, "%x\n", inputGetEventCode(event)); + // fprintf(stdout, "%x\n", inputGetEventCode(event)); switch (event.type) { +#ifdef ENABLE_SDL3 + case SDL_EVENT_KEY_DOWN: + if (!event.key.mod) + sdlUpdateKey(event.key.key, true); +#else case SDL_KEYDOWN: if (!event.key.keysym.mod) sdlUpdateKey(event.key.keysym.sym, true); +#endif break; +#ifdef ENABLE_SDL3 + case SDL_EVENT_KEY_UP: + if (!event.key.mod) + sdlUpdateKey(event.key.key, false); +#else case SDL_KEYUP: if (!event.key.keysym.mod) sdlUpdateKey(event.key.keysym.sym, false); +#endif break; +#ifdef ENABLE_SDL3 + case SDL_EVENT_JOYSTICK_HAT_MOTION: +#else case SDL_JOYHATMOTION: +#endif sdlUpdateJoyHat(event.jhat.which, event.jhat.hat, event.jhat.value); break; - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: +#ifdef ENABLE_SDL3 + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + case SDL_EVENT_JOYSTICK_BUTTON_UP: sdlUpdateJoyButton(event.jbutton.which, event.jbutton.button, - event.jbutton.state == SDL_PRESSED); + event.jbutton.down == true); +#else + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + sdlUpdateJoyButton(event.jbutton.which, + event.jbutton.button, + event.jbutton.state == SDL_PRESSED); +#endif break; +#ifdef ENABLE_SDL3 + case SDL_EVENT_JOYSTICK_AXIS_MOTION: +#else case SDL_JOYAXISMOTION: +#endif sdlUpdateJoyAxis(event.jaxis.which, event.jaxis.axis, event.jaxis.value); diff --git a/src/sdl/inputSDL.h b/src/sdl/inputSDL.h index 0ca419dc..6519361a 100644 --- a/src/sdl/inputSDL.h +++ b/src/sdl/inputSDL.h @@ -20,7 +20,11 @@ #include -#include +#ifndef ENABLE_SDL3 +#include +#else +#include +#endif enum EKey { KEY_LEFT, diff --git a/src/wx/AAPLShaderTypes.h b/src/wx/AAPLShaderTypes.h new file mode 100644 index 00000000..c827da82 --- /dev/null +++ b/src/wx/AAPLShaderTypes.h @@ -0,0 +1,42 @@ +/* +See the LICENSE.txt file for this sample’s licensing information. + +Abstract: +Header containing types and enum constants shared between Metal shaders and C/ObjC source +*/ + +#ifndef AAPLShaderTypes_h +#define AAPLShaderTypes_h + +#include + +// Buffer index values shared between shader and C code to ensure Metal shader buffer inputs match +// Metal API buffer set calls +typedef enum AAPLVertexInputIndex +{ + AAPLVertexInputIndexVertices = 0, + AAPLVertexInputIndexViewportSize = 1, +} AAPLVertexInputIndex; + +// Texture index values shared between shader and C code to ensure Metal shader buffer inputs match +// Metal API texture set calls +typedef enum AAPLTextureIndex +{ + AAPLTextureIndexBaseColor = 0, +} AAPLTextureIndex; + +// This structure defines the layout of each vertex in the array of vertices set as an input to the +// Metal vertex shader. Since this header is shared between the .metal shader and C code, +// you can be sure that the layout of the vertex array in the code matches the layout that +// the vertex shader expects + +typedef struct +{ + // Positions in pixel space. A value of 100 indicates 100 pixels from the origin/center. + vector_float2 position; + + // 2D texture coordinate + vector_float2 textureCoordinate; +} AAPLVertex; + +#endif /* AAPLShaderTypes_h */ diff --git a/src/wx/AAPLShaders.metal b/src/wx/AAPLShaders.metal new file mode 100644 index 00000000..b100a56e --- /dev/null +++ b/src/wx/AAPLShaders.metal @@ -0,0 +1,76 @@ +/* +See the LICENSE.txt file for this sample’s licensing information. + +Abstract: +Metal shaders used for this sample +*/ + +#include +#include + +using namespace metal; + +// Include header shared between this Metal shader code and C code executing Metal API commands +#include "AAPLShaderTypes.h" + +struct RasterizerData +{ + // The [[position]] attribute qualifier of this member indicates this value is + // the clip space position of the vertex when this structure is returned from + // the vertex shader + float4 position [[position]]; + + // Since this member does not have a special attribute qualifier, the rasterizer + // will interpolate its value with values of other vertices making up the triangle + // and pass that interpolated value to the fragment shader for each fragment in + // that triangle. + float2 textureCoordinate; + +}; + +// Vertex Function +vertex RasterizerData +vertexShader(uint vertexID [[ vertex_id ]], + constant AAPLVertex *vertexArray [[ buffer(AAPLVertexInputIndexVertices) ]], + constant vector_uint2 *viewportSizePointer [[ buffer(AAPLVertexInputIndexViewportSize) ]]) +{ + + RasterizerData out; + + // Index into the array of positions to get the current vertex. + // Positions are specified in pixel dimensions (i.e. a value of 100 is 100 pixels from + // the origin) + float2 pixelSpacePosition = vertexArray[vertexID].position.xy; + + // Get the viewport size and cast to float. + float2 viewportSize = float2(*viewportSizePointer); + + // To convert from positions in pixel space to positions in clip-space, + // divide the pixel coordinates by half the size of the viewport. + // Z is set to 0.0 and w to 1.0 because this is 2D sample. + out.position = vector_float4(0.0, 0.0, 0.0, 1.0); + out.position.xy = pixelSpacePosition / viewportSize; + + // Pass the input textureCoordinate straight to the output RasterizerData. This value will be + // interpolated with the other textureCoordinate values in the vertices that make up the + // triangle. + out.textureCoordinate = vertexArray[vertexID].textureCoordinate; + + return out; +} + +// Fragment function +fragment float4 +samplingShader(RasterizerData in [[stage_in]], + texture2d colorTexture [[ texture(AAPLTextureIndexBaseColor) ]]) +{ + constexpr sampler textureSampler (mag_filter::linear, + min_filter::linear); + + // Sample the texture to obtain a color + const half4 colorSample = colorTexture.sample(textureSampler, in.textureCoordinate); + + // return the color of the texture + return float4(colorSample); +} + diff --git a/src/wx/CMakeLists.txt b/src/wx/CMakeLists.txt index 2a29385d..a76abba5 100644 --- a/src/wx/CMakeLists.txt +++ b/src/wx/CMakeLists.txt @@ -8,6 +8,8 @@ include(VbamFunctions) set(VBAM_WX_COMMON audio/audio.cpp audio/audio.h + audio/internal/sdl.cpp + audio/internal/sdl.h audio/internal/openal.cpp audio/internal/openal.h background-input.cpp @@ -156,6 +158,11 @@ if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg") find_package(nanosvg) endif() +if(APPLE) + find_library(METAL Metal) + find_library(METALKIT MetalKit) +endif() + set(ENABLE_OPENGL TRUE) find_package(wxWidgets COMPONENTS xrc xml html adv net core base gl ${wx_find_extra}) @@ -254,6 +261,11 @@ function(configure_wx_target target) _add_compile_definitions(${wxWidgets_DEFINITIONS_DEBUG}) endif() + # No Metal + if(NOT CMAKE_Metal_COMPILER) + _add_compile_definitions(NO_METAL) + endif() + # OpenAL. if(OPENAL_STATIC) _add_compile_definitions(AL_LIBTYPE_STATIC) @@ -294,8 +306,8 @@ function(configure_wx_target target) _add_compile_definitions(NO_D3D) endif() - # SDL2. - _add_link_libraries(${VBAM_SDL2_LIBS}) + # SDL. + _add_link_libraries(${VBAM_SDL_LIBS}) # OpenGL. if(ENABLE_OPENGL) @@ -303,6 +315,15 @@ function(configure_wx_target target) else() _add_compile_definitions(NO_OGL) endif() + + # Metal + if(APPLE) + if(CMAKE_Metal_COMPILER) + _add_link_libraries($) + _add_link_libraries($) + endif() + endif() + endfunction() # Sub-projects. @@ -320,7 +341,12 @@ add_executable( ) target_sources(visualboyadvance-m PRIVATE ${VBAM_WX_COMMON} ${VBAM_ICON_PATH}) -target_include_directories(visualboyadvance-m PRIVATE ${SDL2_INCLUDE_DIRS}) + +if(ENABLE_SDL3) + target_include_directories(visualboyadvance-m PRIVATE ${SDL3_INCLUDE_DIRS}) +else() + target_include_directories(visualboyadvance-m PRIVATE ${SDL2_INCLUDE_DIRS}) +endif() target_link_libraries( visualboyadvance-m @@ -1047,3 +1073,27 @@ install( if (UNIX) install(FILES ${CMAKE_SOURCE_DIR}/src/debian/visualboyadvance-m.6 DESTINATION ${CMAKE_INSTALL_MANDIR}/man6) endif() + +if(APPLE) + if(CMAKE_Metal_COMPILER) + set(VBAM_SHADER default.metallib) + + set(ShaderBase_HEADERS + AAPLShaderTypes.h + ) + + set(ShaderBase_SOURCES + AAPLShaders.metal + ) + + add_metal_shader_library(default + STANDARD macos-metal1.1 + ${ShaderBase_SOURCES} + ${ShaderBase_HEADERS} + ) + + target_embed_metal_shader_libraries(visualboyadvance-m + default + ) + endif() +endif() diff --git a/src/wx/audio/audio.cpp b/src/wx/audio/audio.cpp index eb08b2bb..9141df09 100644 --- a/src/wx/audio/audio.cpp +++ b/src/wx/audio/audio.cpp @@ -1,6 +1,7 @@ #include "wx/audio/audio.h" #include "core/base/check.h" +#include "wx/audio/internal/sdl.h" #include "wx/audio/internal/openal.h" #if defined(__WXMSW__) @@ -22,6 +23,9 @@ std::vector EnumerateAudioDevices(const config::AudioApi& audio_api case config::AudioApi::kOpenAL: return audio::internal::GetOpenALDevices(); + case config::AudioApi::kSDL: + return audio::internal::GetSDLDevices(); + #if defined(__WXMSW__) case config::AudioApi::kDirectSound: return audio::internal::GetDirectSoundDevices(); @@ -49,6 +53,9 @@ std::unique_ptr CreateSoundDriver(const config::AudioApi& api) { case config::AudioApi::kOpenAL: return audio::internal::CreateOpenALDriver(); + case config::AudioApi::kSDL: + return audio::internal::CreateSDLDriver(); + #if defined(__WXMSW__) case config::AudioApi::kDirectSound: return audio::internal::CreateDirectSoundDriver(); diff --git a/src/wx/audio/internal/openal.cpp b/src/wx/audio/internal/openal.cpp index 59e5f420..6c381bc1 100644 --- a/src/wx/audio/internal/openal.cpp +++ b/src/wx/audio/internal/openal.cpp @@ -396,4 +396,4 @@ std::unique_ptr CreateOpenALDriver() { } } // namespace internal -} // namespace audio \ No newline at end of file +} // namespace audio diff --git a/src/wx/audio/internal/sdl.cpp b/src/wx/audio/internal/sdl.cpp new file mode 100644 index 00000000..605f1b11 --- /dev/null +++ b/src/wx/audio/internal/sdl.cpp @@ -0,0 +1,381 @@ +#include "wx/audio/internal/sdl.h" + +// === LOGALL writes very detailed informations to vba-trace.log === +// #define LOGALL + +// on win32 and mac, pointer typedefs only happen with AL_NO_PROTOTYPES +// on mac, ALC_NO_PROTOTYPES as well + +// #define AL_NO_PROTOTYPES 1 + +// 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 + +#ifdef ENABLE_SDL3 +#include +#else +#include +#endif + +#include +#include +#include +#include + +#include "core/base/sound_driver.h" +#include "core/base/check.h" +#include "core/gba/gbaGlobals.h" +#include "core/gba/gbaSound.h" +#include "wx/config/option-proxy.h" + +#ifndef LOGALL +// replace logging functions with comments +#ifdef winlog +#undef winlog +#endif +// https://stackoverflow.com/a/1306690/262458 +#define winlog(x, ...) \ + do { \ + } while (0) +#define debugState() // +#endif + +extern int emulating; + +namespace audio { +namespace internal { + +namespace { + +class SDLAudio : public SoundDriver { +public: + SDLAudio(); + ~SDLAudio() override; + + bool init(long sampleRate) override; // initialize the sound buffer queue + void deinit(); + 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: + SDL_AudioDeviceID sound_device = 0; + SDL_AudioSpec audio; + +#ifdef ENABLE_SDL3 + SDL_AudioStream *sound_stream = NULL; + SDL_Mutex* mutex; +#else + SDL_mutex* mutex; +#endif + +#ifdef ENABLE_SDL3 + SDL_AudioDeviceID *sdl_devices; + int sdl_devices_count = 0; +#else + unsigned short current_rate; +#endif + + bool initialized = false; +}; + +SDLAudio::SDLAudio(): +sound_device(0), +#ifndef ENABLE_SDL3 +current_rate(static_cast(coreOptions.throttle)), +#endif +initialized(false) +{} + +void SDLAudio::deinit() { + if (!initialized) + return; + + initialized = false; + + SDL_LockMutex(mutex); + int is_emulating = emulating; + emulating = 0; + SDL_UnlockMutex(mutex); + + SDL_DestroyMutex(mutex); + mutex = nullptr; + + SDL_CloseAudioDevice(sound_device); + + emulating = is_emulating; +} + +SDLAudio::~SDLAudio() { + deinit(); + + SDL_QuitSubSystem(SDL_INIT_AUDIO); +} + +bool SDLAudio::init(long sampleRate) { +#ifdef ENABLE_SDL3 + int current_device = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK; + sdl_devices = SDL_GetAudioPlaybackDevices(&sdl_devices_count); + const char *devs = NULL; +#endif + + winlog("SDLAudio::init\n"); + if (initialized) deinit(); + + SDL_memset(&audio, 0, sizeof(audio)); + +#ifdef ENABLE_SDL3 + // for "no throttle" use regular rate, audio is just dropped + audio.freq = sampleRate; + + audio.format = SDL_AUDIO_S16; +#else + // for "no throttle" use regular rate, audio is just dropped + audio.freq = current_rate ? static_cast(sampleRate * ((float)current_rate / 100.0)) : sampleRate; + + audio.format = AUDIO_S16SYS; +#endif + + audio.channels = 2; + +#ifdef ENABLE_SDL3 + if (SDL_InitSubSystem(SDL_INIT_AUDIO) == false) { +#else + audio.samples = 2048; + audio.callback = NULL; + audio.userdata = NULL; + + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { +#endif + return false; + } + +#ifdef ENABLE_SDL3 + if (SDL_WasInit(SDL_INIT_AUDIO) == false) { +#else + if (SDL_WasInit(SDL_INIT_AUDIO) < 0) { +#endif + SDL_Init(SDL_INIT_AUDIO); + } + +#ifdef ENABLE_SDL3 +#ifdef ONLY_DEFAULT_AUDIO_DEVICE + sound_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio, NULL, NULL); +#else + for (int i = 0; i < sdl_devices_count; i++) { + devs = SDL_GetAudioDeviceName(sdl_devices[i]); + const wxString device_name(devs, wxConvLibc); + + if (device_name == OPTION(kSoundAudioDevice)) + { + current_device = i; + break; + } + } + + if (OPTION(kSoundAudioDevice) == _("Default device")) { + sound_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio, NULL, NULL); + } else { + sound_stream = SDL_OpenAudioDeviceStream(sdl_devices[current_device], &audio, NULL, NULL); + } + + if(sound_stream == NULL) { + return false; + } + + sound_device = SDL_GetAudioStreamDevice(sound_stream); +#endif +#else +#ifdef ONLY_DEFAULT_AUDIO_DEVICE + sound_device = SDL_OpenAudioDevice(NULL, 0, &audio, NULL, 0); +#else + const wxString device_name = OPTION(kSoundAudioDevice); + + if (device_name == _("Default device")) { + sound_device = SDL_OpenAudioDevice(NULL, 0, &audio, NULL, 0); + } else { + sound_device = SDL_OpenAudioDevice(device_name.mb_str(), 0, &audio, NULL, 0); + } +#endif +#endif + + if(sound_device == 0) { + return false; + } + + mutex = SDL_CreateMutex(); + + // turn off audio events because we are not processing them +#ifdef ENABLE_SDL3 + SDL_SetEventEnabled(SDL_EVENT_AUDIO_DEVICE_ADDED, false); + SDL_SetEventEnabled(SDL_EVENT_AUDIO_DEVICE_REMOVED, false); +#elif SDL_VERSION_ATLEAST(2, 0, 4) + SDL_EventState(SDL_AUDIODEVICEADDED, SDL_IGNORE); + SDL_EventState(SDL_AUDIODEVICEREMOVED, SDL_IGNORE); +#endif + + return initialized = true; +} + +void SDLAudio::setThrottle(unsigned short throttle_) { + if (!initialized) + return; + + if (throttle_ == 0) + throttle_ = 450; + +#ifdef ENABLE_SDL3 + SDL_SetAudioStreamFrequencyRatio(sound_stream, (float)throttle_ / 100.0f); +#else + current_rate = throttle_; + + reset(); +#endif +} + +void SDLAudio::resume() { + if (!initialized) + return; + + winlog("SDLAudio::resume\n"); + +#ifdef ENABLE_SDL3 + if (SDL_AudioDevicePaused(sound_device) == true) { + SDL_ResumeAudioStreamDevice(sound_stream); + SDL_ResumeAudioDevice(sound_device); + } +#else + if (SDL_GetAudioDeviceStatus(sound_device) != SDL_AUDIO_PLAYING) { + SDL_PauseAudioDevice(sound_device, 0); + } +#endif +} + +void SDLAudio::pause() { + if (!initialized) + return; + + winlog("SDLAudio::pause\n"); + +#ifdef ENABLE_SDL3 + if (SDL_AudioDevicePaused(sound_device) == true) { + SDL_PauseAudioStreamDevice(sound_stream); + SDL_PauseAudioDevice(sound_device); + } +#else + if (SDL_GetAudioDeviceStatus(sound_device) != SDL_AUDIO_PLAYING) { + SDL_PauseAudioDevice(sound_device, 1); + } +#endif +} + +void SDLAudio::reset() { + if (!initialized) + return; + + winlog("SDLAudio::reset\n"); + + init(soundGetSampleRate()); +} + +void SDLAudio::write(uint16_t* finalWave, int length) { + int res = 0; + + if (!initialized) + return; + + SDL_LockMutex(mutex); + +#ifdef ENABLE_SDL3 + if (SDL_AudioDevicePaused(sound_device) == true) { + SDL_ResumeAudioStreamDevice(sound_stream); + SDL_ResumeAudioDevice(sound_device); + } +#else + if (SDL_GetAudioDeviceStatus(sound_device) != SDL_AUDIO_PLAYING) { + SDL_PauseAudioDevice(sound_device, 0); + } +#endif + +#ifdef ENABLE_SDL3 + res = (int)SDL_PutAudioStreamData(sound_stream, finalWave, length) == true; + + while (res && SDL_GetAudioStreamQueued(sound_stream) > (2048 * audio.channels * sizeof(uint16_t))) { + SDL_Delay(1); + } +#else + res = SDL_QueueAudio(sound_device, finalWave, length) == 0; + + while (res && SDL_GetQueuedAudioSize(sound_device) > (audio.samples * audio.channels * sizeof(uint16_t))) { + SDL_Delay(1); + } +#endif + + winlog("SDL audio queue result: %d\n", res); + + SDL_UnlockMutex(mutex); +} + +} // namespace + +std::vector GetSDLDevices() { + std::vector devices; + +#ifdef ONLY_DEFAULT_AUDIO_DEVICE + devices.push_back({_("Default device"), wxEmptyString}); +#else +#ifdef ENABLE_SDL3 + const char *devs = NULL; + SDL_AudioDeviceID *sdl_devices = NULL; + int sdl_devices_count = 0; + + SDL_InitSubSystem(SDL_INIT_AUDIO); + + sdl_devices = SDL_GetAudioPlaybackDevices(&sdl_devices_count); + + devices.push_back({_("Default device"), _("Default device")}); + + for (int i = 0; i < sdl_devices_count; i++) + { + devs = SDL_GetAudioDeviceName(sdl_devices[i]); + + if (devs != NULL) + { + const wxString device_name(devs, wxConvLibc); + devices.push_back({device_name, device_name}); + } + } +#else + const char *devs = NULL; + int sdl_devices_count = 0; + + SDL_InitSubSystem(SDL_INIT_AUDIO); + + sdl_devices_count = SDL_GetNumAudioDevices(0); + + devices.push_back({_("Default device"), _("Default device")}); + + for (int i = 0; i < sdl_devices_count; i++) + { + devs = SDL_GetAudioDeviceName(i, 0); + const wxString device_name(devs, wxConvLibc); + devices.push_back({device_name, device_name}); + } +#endif +#endif + + return devices; +} + +std::unique_ptr CreateSDLDriver() { + winlog("newSDL\n"); + return std::make_unique(); +} + +} // namespace internal +} // namespace audio diff --git a/src/wx/audio/internal/sdl.h b/src/wx/audio/internal/sdl.h new file mode 100644 index 00000000..59a81e88 --- /dev/null +++ b/src/wx/audio/internal/sdl.h @@ -0,0 +1,18 @@ +#ifndef WX_AUDIO_INTERNAL_SDL_H_ +#define WX_AUDIO_INTERNAL_SDL_H_ + +#include "wx/audio/audio.h" + +namespace audio { +namespace internal { + +// Returns the set of OpenAL devices. +std::vector GetSDLDevices(); + +// Creates an OpenAL sound driver. +std::unique_ptr CreateSDLDriver(); + +} // namespace internal +} // namespace audio + +#endif // WX_AUDIO_INTERNAL_SDL_H_ diff --git a/src/wx/cmdevents.cpp b/src/wx/cmdevents.cpp index 907f485d..839194b6 100644 --- a/src/wx/cmdevents.cpp +++ b/src/wx/cmdevents.cpp @@ -2134,6 +2134,9 @@ EVT_HANDLER_MASK(DisplayConfigure, "Display options...", CMDEN_NREC_ANY) return; } + const uint32_t bitdepth = OPTION(kBitDepth); + systemColorDepth = (int)((bitdepth + 1) << 3); + const int frame_skip = OPTION(kPrefFrameSkip); if (frame_skip != -1) { systemFrameSkip = frame_skip; @@ -2381,6 +2384,11 @@ EVT_HANDLER(NoStatusMsg, "Disable on-screen status messages") GetMenuOptionConfig("NoStatusMsg", config::OptionID::kPrefDisableStatus); } +EVT_HANDLER(BitDepth, "Bit depth") +{ + GetMenuOptionConfig("BitDepth", config::OptionID::kBitDepth); +} + EVT_HANDLER(FrameSkipAuto, "Auto Skip frames.") { GetMenuOptionConfig("FrameSkipAuto", config::OptionID::kPrefAutoFrameSkip); diff --git a/src/wx/config/internal/option-internal.cpp b/src/wx/config/internal/option-internal.cpp index 24774beb..10054bc0 100644 --- a/src/wx/config/internal/option-internal.cpp +++ b/src/wx/config/internal/option-internal.cpp @@ -72,10 +72,14 @@ static const std::array kInterframeStrings = { static const std::array kRenderMethodStrings = { "simple", "opengl", + "sdl_video", #if defined(__WXMSW__) && !defined(NO_D3D) "direct3d", #elif defined(__WXMAC__) "quartz2d", +#ifndef NO_METAL + "metal", +#endif #endif }; @@ -84,6 +88,7 @@ static const std::array kRenderMethodStrings = { // error since kNbAudioApis is automatically updated. static const std::array kAudioApiStrings = { "openal", + "sdl_audio", #if defined(__WXMSW__) "directsound", #endif @@ -137,10 +142,16 @@ std::array& Option::All() { Interframe interframe = Interframe::kNone; bool keep_on_top = false; int32_t max_threads = 0; + +#if defined(__WXMAC__) && !defined(NO_METAL) + RenderMethod render_method = RenderMethod::kMetal; +#else #if defined(NO_OGL) - RenderMethod render_method = RenderMethod::kSimple; + //RenderMethod render_method = RenderMethod::kSimple; + RenderMethod render_method = RenderMethod::kSDL; #else RenderMethod render_method = RenderMethod::kOpenGL; +#endif #endif double video_scale = 3; bool retain_aspect = true; @@ -228,6 +239,8 @@ std::array& Option::All() { bool dsound_hw_accel = false; bool upmix = false; int32_t volume = 100; + uint32_t bitdepth = 3; + wxString sdlrenderer = wxString("default"); }; static OwnedOptions g_owned_opts; @@ -243,11 +256,13 @@ std::array& Option::All() { Option(OptionID::kDispFilter, &g_owned_opts.filter), Option(OptionID::kDispFilterPlugin, &g_owned_opts.filter_plugin), Option(OptionID::kDispIFB, &g_owned_opts.interframe), + Option(OptionID::kBitDepth, &g_owned_opts.bitdepth, 0, 3), Option(OptionID::kDispKeepOnTop, &g_owned_opts.keep_on_top), Option(OptionID::kDispMaxThreads, &g_owned_opts.max_threads, 0, 256), Option(OptionID::kDispRenderMethod, &g_owned_opts.render_method), Option(OptionID::kDispScale, &g_owned_opts.video_scale, 1, 6), Option(OptionID::kDispStretch, &g_owned_opts.retain_aspect), + Option(OptionID::kSDLRenderer, &g_owned_opts.sdlrenderer), /// GB Option(OptionID::kGBBiosFile, &g_owned_opts.gb_bios), @@ -383,6 +398,7 @@ const std::array kAllOptionsData = { OptionData{"Display/Filter", "", _("Full-screen filter to apply")}, OptionData{"Display/FilterPlugin", "", _("Filter plugin library")}, OptionData{"Display/IFB", "", _("Interframe blending function")}, + OptionData{"Display/BitDepth", "BitDepth", _("Bit depth")}, OptionData{"Display/KeepOnTop", "KeepOnTop", _("Keep window on top")}, OptionData{"Display/MaxThreads", "Multithread", _("Maximum number of threads to run filters in")}, @@ -390,6 +406,7 @@ const std::array kAllOptionsData = { _("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/SDLRenderer", "", _("SDL renderer")}, /// GB OptionData{"GB/BiosFile", "", _("BIOS file to use for Game Boy, if enabled")}, diff --git a/src/wx/config/option-id.h b/src/wx/config/option-id.h index 630a8171..95360627 100644 --- a/src/wx/config/option-id.h +++ b/src/wx/config/option-id.h @@ -11,11 +11,13 @@ enum class OptionID { kDispFilter, kDispFilterPlugin, kDispIFB, + kBitDepth, kDispKeepOnTop, kDispMaxThreads, kDispRenderMethod, kDispScale, kDispStretch, + kSDLRenderer, /// GB kGBBiosFile, diff --git a/src/wx/config/option-proxy.h b/src/wx/config/option-proxy.h index 769dbf65..74e3b706 100644 --- a/src/wx/config/option-proxy.h +++ b/src/wx/config/option-proxy.h @@ -15,11 +15,13 @@ static constexpr std::array kOptionsTypes = { /*kDispFilter*/ Option::Type::kFilter, /*kDispFilterPlugin*/ Option::Type::kString, /*kDispIFB*/ Option::Type::kInterframe, + /*kBitDepth*/ Option::Type::kUnsigned, /*kDispKeepOnTop*/ Option::Type::kBool, /*kDispMaxThreads*/ Option::Type::kInt, /*kDispRenderMethod*/ Option::Type::kRenderMethod, /*kDispScale*/ Option::Type::kDouble, /*kDispStretch*/ Option::Type::kBool, + /*kSDLRenderer*/ Option::Type::kString, /// GB /*kGBBiosFile*/ Option::Type::kString, diff --git a/src/wx/config/option-test.cpp b/src/wx/config/option-test.cpp index 706397d5..6dbfaf3e 100644 --- a/src/wx/config/option-test.cpp +++ b/src/wx/config/option-test.cpp @@ -268,6 +268,9 @@ TEST(OptionTest, Enum) { EXPECT_TRUE(option->SetRenderMethod(config::RenderMethod::kSimple)); EXPECT_EQ(option->GetRenderMethod(), config::RenderMethod::kSimple); + EXPECT_TRUE(option->SetRenderMethod(config::RenderMethod::kSDL)); + EXPECT_EQ(option->GetRenderMethod(), config::RenderMethod::kSDL); + EXPECT_TRUE(option->SetEnumString("opengl")); EXPECT_EQ(option->GetRenderMethod(), config::RenderMethod::kOpenGL); } diff --git a/src/wx/config/option.h b/src/wx/config/option.h index d6a3ca09..3930c356 100644 --- a/src/wx/config/option.h +++ b/src/wx/config/option.h @@ -66,10 +66,14 @@ static constexpr size_t kNbInterframes = static_cast(Interframe::kLast); enum class RenderMethod { kSimple = 0, kOpenGL, + kSDL, #if defined(__WXMSW__) && !defined(NO_D3D) kDirect3d, #elif defined(__WXMAC__) kQuartz2d, +#ifndef NO_METAL + kMetal, +#endif #endif // Do not add anything under here. @@ -80,6 +84,7 @@ static constexpr size_t kNbRenderMethods = static_cast(RenderMethod::kLa // Values for kAudioApi. enum class AudioApi { kOpenAL, + kSDL, #if defined(__WXMSW__) kDirectSound, #endif // __WXMSW__ diff --git a/src/wx/dialogs/display-config.cpp b/src/wx/dialogs/display-config.cpp index 75cdf558..57673b35 100644 --- a/src/wx/dialogs/display-config.cpp +++ b/src/wx/dialogs/display-config.cpp @@ -22,10 +22,58 @@ #include "wx/widgets/render-plugin.h" #include "wx/wxvbam.h" +#ifdef ENABLE_SDL3 +#include +#else +#include +#endif + namespace dialogs { namespace { +class SDLDevicesValidator : public widgets::OptionValidator { +public: + SDLDevicesValidator() : widgets::OptionValidator(config::OptionID::kSDLRenderer) {} + ~SDLDevicesValidator() override = default; + +private: + // OptionValidator implementation. + wxObject* Clone() const override { return new SDLDevicesValidator(); } + + bool IsWindowValueValid() override { return true; } + + bool WriteToWindow() override { + wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice); + VBAM_CHECK(choice); + + const wxString& device_id = option()->GetString(); + for (size_t i = 0; i < choice->GetCount(); i++) { + const wxString& choide_id = + dynamic_cast(choice->GetClientObject(i))->GetData(); + if (device_id == choide_id) { + choice->SetSelection(i); + return true; + } + } + + choice->SetSelection(0); + return true; + } + + bool WriteToOption() override { + const wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice); + VBAM_CHECK(choice); + const int selection = choice->GetSelection(); + if (selection == wxNOT_FOUND) { + return option()->SetString(wxEmptyString); + } + + return option()->SetString( + dynamic_cast(choice->GetClientObject(selection))->GetData()); + } +}; + // Custom validator for the kDispScale option. We rely on the existing // wxFloatingPointValidator validator for this. class ScaleValidator : public wxFloatingPointValidator, @@ -234,6 +282,10 @@ DisplayConfig::DisplayConfig(wxWindow* parent) std::bind(&DisplayConfig::OnInterframeChanged, this, std::placeholders::_1)) { + GetValidatedChild("BitDepth") + ->SetValidator( + widgets::OptionChoiceValidator(config::OptionID::kBitDepth)); + // Speed GetValidatedChild("FrameSkip") ->SetValidator( @@ -253,14 +305,24 @@ DisplayConfig::DisplayConfig(wxWindow* parent) ->SetValidator(wxGenericValidator(&gopts.max_scale)); // Basic - GetValidatedChild("OutputSimple") - ->SetValidator(RenderValidator(config::RenderMethod::kSimple)); + wxWindow *render_method = GetValidatedChild("OutputSimple"); + render_method->SetValidator(RenderValidator(config::RenderMethod::kSimple)); + + render_method = GetValidatedChild("OutputSDL"); + render_method->SetValidator(RenderValidator(config::RenderMethod::kSDL)); #if defined(__WXMAC__) - GetValidatedChild("OutputQuartz2D") - ->SetValidator(RenderValidator(config::RenderMethod::kQuartz2d)); + render_method = GetValidatedChild("OutputQuartz2D"); + render_method->SetValidator(RenderValidator(config::RenderMethod::kQuartz2d)); +#ifndef NO_METAL + render_method = GetValidatedChild("OutputMetal"); + render_method->SetValidator(RenderValidator(config::RenderMethod::kMetal)); +#else + GetValidatedChild("OutputMetal")->Hide(); +#endif #else GetValidatedChild("OutputQuartz2D")->Hide(); + GetValidatedChild("OutputMetal")->Hide(); #endif #ifdef NO_OGL @@ -270,22 +332,25 @@ DisplayConfig::DisplayConfig(wxWindow* parent) if (IsWayland()) { GetValidatedChild("OutputOpenGL")->Hide(); } else { - GetValidatedChild("OutputOpenGL") - ->SetValidator(RenderValidator(config::RenderMethod::kOpenGL)); + render_method = GetValidatedChild("OutputOpenGL"); + render_method->SetValidator(RenderValidator(config::RenderMethod::kOpenGL)); } #else - GetValidatedChild("OutputOpenGL") - ->SetValidator(RenderValidator(config::RenderMethod::kOpenGL)); + render_method = GetValidatedChild("OutputOpenGL"); + render_method->SetValidator(RenderValidator(config::RenderMethod::kOpenGL)); #endif // NO_OGL #if defined(__WXMSW__) && !defined(NO_D3D) // Enable the Direct3D option on Windows. - GetValidatedChild("OutputDirect3D") - ->SetValidator(RenderValidator(config::RenderMethod::kDirect3d)); + render_method = GetValidatedChild("OutputDirect3D"); + render_method->SetValidator(RenderValidator(config::RenderMethod::kDirect3d)); #else GetValidatedChild("OutputDirect3D")->Hide(); #endif + sdlrenderer_selector_ = GetValidatedChild("SDLRenderer"); + sdlrenderer_selector_->SetValidator(SDLDevicesValidator()); + filter_selector_ = GetValidatedChild("Filter"); filter_selector_->SetValidator(FilterValidator()); filter_selector_->Bind(wxEVT_CHOICE, &DisplayConfig::UpdatePlugin, this, @@ -305,12 +370,16 @@ DisplayConfig::DisplayConfig(wxWindow* parent) } void DisplayConfig::OnDialogShowEvent(wxShowEvent& event) { + wxCommandEvent dummy_event; + if (event.IsShown()) { PopulatePluginOptions(); } else { StopPluginHandler(); } + FillRendererList(dummy_event); + // Let the event propagate. event.Skip(); } @@ -415,6 +484,43 @@ void DisplayConfig::OnInterframeChanged(config::Option* option) { interframe_selector_->GetString(static_cast(interframe)))); } +void DisplayConfig::FillRendererList(wxCommandEvent& event) { +#ifndef ENABLE_SDL3 + SDL_RendererInfo render_info; +#endif + int num_drivers = 0; + + SDL_InitSubSystem(SDL_INIT_VIDEO); + SDL_Init(SDL_INIT_VIDEO); + + num_drivers = SDL_GetNumRenderDrivers(); + + sdlrenderer_selector_->Clear(); + sdlrenderer_selector_->Append("default", new wxStringClientData("default")); + sdlrenderer_selector_->SetSelection(0); + + for (int i = 0; i < num_drivers; i++) { +#ifdef ENABLE_SDL3 + sdlrenderer_selector_->Append(SDL_GetRenderDriver(i), new wxStringClientData(SDL_GetRenderDriver(i))); + + if (OPTION(kSDLRenderer) == wxString(SDL_GetRenderDriver(i))) { + sdlrenderer_selector_->SetSelection(i+1); + } +#else + SDL_GetRenderDriverInfo(i, &render_info); + + sdlrenderer_selector_->Append(render_info.name, new wxStringClientData(render_info.name)); + + if (OPTION(kSDLRenderer) == wxString(render_info.name)) { + sdlrenderer_selector_->SetSelection(i+1); + } +#endif + } + + // Let the event propagate. + event.Skip(); +} + void DisplayConfig::HidePluginOptions() { plugin_label_->Hide(); plugin_selector_->Hide(); diff --git a/src/wx/dialogs/display-config.h b/src/wx/dialogs/display-config.h index 82bbfee1..8aee5248 100644 --- a/src/wx/dialogs/display-config.h +++ b/src/wx/dialogs/display-config.h @@ -45,6 +45,9 @@ private: // Displays the new interframe name on the screen. void OnInterframeChanged(config::Option* option); + // Renderer changed + void FillRendererList(wxCommandEvent& event); + // Hides/Shows the plugin-related filter options. void HidePluginOptions(); void ShowPluginOptions(); @@ -53,6 +56,7 @@ private: wxChoice* plugin_selector_; wxChoice* filter_selector_; wxChoice* interframe_selector_; + wxChoice* sdlrenderer_selector_; const config::OptionsObserver filter_observer_; const config::OptionsObserver interframe_observer_; }; diff --git a/src/wx/dialogs/sound-config.cpp b/src/wx/dialogs/sound-config.cpp index 40c7b12a..d07e5982 100644 --- a/src/wx/dialogs/sound-config.cpp +++ b/src/wx/dialogs/sound-config.cpp @@ -158,6 +158,12 @@ SoundConfig::SoundConfig(wxWindow* parent) : BaseDialog(parent, "SoundConfig") { std::bind(&SoundConfig::OnAudioApiChanged, this, std::placeholders::_1, config::AudioApi::kOpenAL)); + audio_api_button = GetValidatedChild("SDL"); + audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kSDL)); + audio_api_button->Bind(wxEVT_RADIOBUTTON, + std::bind(&SoundConfig::OnAudioApiChanged, this, std::placeholders::_1, + config::AudioApi::kSDL)); + audio_api_button = GetValidatedChild("DirectSound"); #if defined(__WXMSW__) audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kDirectSound)); @@ -296,4 +302,4 @@ void SoundConfig::OnAudioApiChanged(wxCommandEvent& event, config::AudioApi audi event.Skip(); } -} // namespace dialogs \ No newline at end of file +} // namespace dialogs diff --git a/src/wx/drawing.h b/src/wx/drawing.h index 42a54a04..046bc2fb 100644 --- a/src/wx/drawing.h +++ b/src/wx/drawing.h @@ -49,6 +49,26 @@ protected: }; #endif +class SDLDrawingPanel : public DrawingPanel { +public: + SDLDrawingPanel(wxWindow* parent, int _width, int _height); + virtual ~SDLDrawingPanel(); + +protected: + void DrawArea(); + void DrawArea(uint8_t** data); + void DrawArea(wxWindowDC&) override; + void PaintEv(wxPaintEvent& ev) override; + void EraseBackground(wxEraseEvent& ev) override; + void OnSize(wxSizeEvent& ev) override; + void DrawingPanelInit() override; + +private: + SDL_Window *sdlwindow; + SDL_Texture *texture; + SDL_Renderer *renderer; +}; + #if defined(__WXMSW__) && !defined(NO_D3D) class DXDrawingPanel : public DrawingPanel { public: @@ -60,6 +80,81 @@ protected: #endif #if defined(__WXMAC__) +#ifndef NO_METAL +#ifdef __OBJC__ +#import +#import +#import +#endif + +#include + +typedef enum AAPLVertexInputIndex +{ + AAPLVertexInputIndexVertices = 0, + AAPLVertexInputIndexViewportSize = 1, +} AAPLVertexInputIndex; + +// Texture index values shared between shader and C code to ensure Metal shader buffer inputs match +// Metal API texture set calls +typedef enum AAPLTextureIndex +{ + AAPLTextureIndexBaseColor = 0, +} AAPLTextureIndex; + +// This structure defines the layout of each vertex in the array of vertices set as an input to the +// Metal vertex shader. Since this header is shared between the .metal shader and C code, +// you can be sure that the layout of the vertex array in the code matches the layout that +// the vertex shader expects + +typedef struct +{ + // Positions in pixel space. A value of 100 indicates 100 pixels from the origin/center. + vector_float2 position; + + // 2D texture coordinate + vector_float2 textureCoordinate; +} AAPLVertex; + +bool is_macosx_1012_or_newer(); + +class MetalDrawingPanel : public DrawingPanel { +public: + MetalDrawingPanel(wxWindow* parent, int _width, int _height); + virtual ~MetalDrawingPanel(); + +protected: + void DrawArea(); + void DrawArea(uint8_t** data); + void DrawArea(wxWindowDC&) override; + void PaintEv(wxPaintEvent& ev) override; + void EraseBackground(wxEraseEvent& ev) override; + void OnSize(wxSizeEvent& ev) override; + void DrawingPanelInit() override; + +#ifdef __OBJC__ +private: + void CreateMetalView(); + id loadTextureUsingData(void *data); + + NSView *view; + MTKView *metalView; + NSRect metalFrame; + MTLRenderPassDescriptor *renderPassDescriptor; + id renderEncoder; + id commandBuffer; + id _device; + id _commandQueue; + id _pipelineState; + id _texture; + id _vertices; + NSUInteger _numVertices; + vector_uint2 _viewportSize; + vector_uint2 _contentSize; +#endif +}; +#endif + class Quartz2DDrawingPanel : public BasicDrawingPanel { public: Quartz2DDrawingPanel(wxWindow* parent, int _width, int _height); diff --git a/src/wx/gfxviewers.cpp b/src/wx/gfxviewers.cpp index 3b1bce7f..698fdc66 100644 --- a/src/wx/gfxviewers.cpp +++ b/src/wx/gfxviewers.cpp @@ -11,12 +11,37 @@ #include "wx/viewsupt.h" #include "wx/wxvbam.h" +#if __STDC_WANT_SECURE_LIB__ +#define snprintf sprintf_s +#endif + namespace { void utilReadScreenPixels(uint8_t* dest, int w, int h) { uint8_t* b = dest; int sizeX = w; int sizeY = h; switch (systemColorDepth) { + case 8: { + uint8_t* p = (uint8_t*)(g_pix + (w + 2)); // skip first black line + for (int y = 0; y < sizeY; y++) { + for (int x = 0; x < sizeX; x++) { + uint8_t v = *p++; + + // White color fix + if (v == 0xff) { + *b++ = 0xff; + *b++ = 0xff; + *b++ = 0xff; + } else { + *b++ = (((v >> 5) & 0x7) << 5); + *b++ = (((v >> 2) & 0x7) << 5); + *b++ = ((v & 0x3) << 6); + } + } + p++; // skip black pixel for filters + p++; // skip black pixel for filters + } + } break; case 16: { uint16_t* p = (uint16_t*)(g_pix + (w + 2) * 2); // skip first black line for (int y = 0; y < sizeY; y++) { @@ -928,8 +953,9 @@ public: for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { - uint32_t color = g_vram[0x10000 + (((c + (y >> 3) * inc) * 32 + (y & 7) * 8 + (x >> 3) * 64 + (x & 7)) & 0x7FFF)]; + uint32_t color = g_vram[0x10000 + (((c + (((y >> 3) * inc) << 5) + ((y & 7) << 4) + ((x >> 3) << 6) + (x & 7))) & 0x7FFF)]; color = pal[color]; + *bmp++ = (color & 0x1f) << 3; *bmp++ = ((color >> 5) & 0x1f) << 3; *bmp++ = ((color >> 10) & 0x1f) << 3; @@ -950,7 +976,7 @@ public: for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { - uint32_t color = g_vram[0x10000 + (((c + (y >> 3) * inc) * 32 + (y & 7) * 4 + (x >> 3) * 32 + ((x & 7) >> 1)) & 0x7FFF)]; + uint32_t color = g_vram[0x10000 + ((((((c + (((y >> 3) * inc) << 5)) + ((y & 7) << 2)) + ((x >> 3) << 5)) + ((x & 7) >> 1))) & 0x7FFF)]; if (x & 1) color >>= 4; @@ -958,6 +984,7 @@ public: color &= 0x0F; color = pal[palette + color]; + *bmp++ = (color & 0x1f) << 3; *bmp++ = ((color >> 5) & 0x1f) << 3; *bmp++ = ((color >> 10) & 0x1f) << 3; @@ -1246,7 +1273,7 @@ void savepal(wxWindow* parent, const uint8_t* data, int ncols, const wxString ty for (int i = 0; i < ncols; i++, data += 3) { char buf[14]; - int l = sprintf(buf, "%d %d %d\r\n", data[0], data[1], data[2]); + int l = snprintf(buf, sizeof(buf), "%d %d %d\r\n", data[0], data[1], data[2]); f.Write(buf, l); } diff --git a/src/wx/macsupport.mm b/src/wx/macsupport.mm index 103df1a1..de12cb58 100644 --- a/src/wx/macsupport.mm +++ b/src/wx/macsupport.mm @@ -5,8 +5,410 @@ #include #include "wx/drawing.h" +#include "wx/config/option-id.h" +#include "wx/config/option-proxy.h" +#include "wx/config/option.h" #include "wx/wxvbam.h" +#ifndef NO_METAL +bool is_macosx_1012_or_newer() +{ + // Mac OS X 10.12 version check + if (NSAppKitVersionNumber >= 1504) { + return true; + } + + return false; +} + +MetalDrawingPanel::~MetalDrawingPanel() +{ + if (did_init) + { + renderPassDescriptor = nil; + commandBuffer = nil; + renderEncoder = nil; + + if (metalView != nil) + [metalView removeFromSuperview]; + + if (_device != nil) + [_device release]; + + if (_commandQueue != nil) + [_commandQueue release]; + + if (_pipelineState != nil) + [_pipelineState release]; + + if (_texture != nil) + [_texture release]; + + if (_vertices != nil) + [_vertices release]; + + did_init = false; + } +} + +void MetalDrawingPanel::EraseBackground(wxEraseEvent& ev) +{ + (void)ev; // unused params + // do nothing, do not allow propagation +} + +void MetalDrawingPanel::CreateMetalView() +{ + view = (NSView *)wxGetApp().frame->GetPanel()->GetHandle(); + view.layerContentsPlacement = NSViewLayerContentsPlacementCenter; + view.layer.backgroundColor = [NSColor colorWithCalibratedRed:0.0f + green:0.0f + blue:0.0f + alpha:0.0f].CGColor; + + metalView = [[MTKView alloc] init]; + + metalView.layer.backgroundColor = [NSColor colorWithCalibratedRed:0.0f + green:0.0f + blue:0.0f + alpha:0.0f].CGColor; + metalView.device = MTLCreateSystemDefaultDevice(); + metalView.colorPixelFormat = MTLPixelFormatRGBA8Unorm; + + metalView.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize; + metalView.layerContentsPlacement = NSViewLayerContentsPlacementCenter; + metalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + metalView.layer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable; + metalView.layer.needsDisplayOnBoundsChange = YES; + ((CAMetalLayer *)metalView.layer).device = metalView.device; + + _device = metalView.device; + + const AAPLVertex quadVertices[] = + { + // Pixel positions, Texture coordinates + { { (float)(width * scale), (float)-(height * scale) }, { 1.f, 1.f } }, + { { (float)-(width * scale), (float)-(height * scale) }, { 0.f, 1.f } }, + { { (float)-(width * scale), (float)(height * scale) }, { 0.f, 0.f } }, + + { { (float)(width * scale), (float)-(height * scale) }, { 1.f, 1.f } }, + { { (float)-(width * scale), (float)(height * scale) }, { 0.f, 0.f } }, + { { (float)(width * scale), (float)(height * scale) }, { 1.f, 0.f } }, + }; + + // Create a vertex buffer, and initialize it with the quadVertices array + _vertices = [_device newBufferWithBytes:quadVertices + length:sizeof(quadVertices) + options:MTLResourceStorageModeShared]; + + // Calculate the number of vertices by dividing the byte length by the size of each vertex + _numVertices = sizeof(quadVertices) / sizeof(AAPLVertex); + + /// Create the render pipeline. + + // Load the shaders from the default library + id defaultLibrary = [_device newDefaultLibrary]; + id vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; + id fragmentFunction = [defaultLibrary newFunctionWithName:@"samplingShader"]; + + // Set up a descriptor for creating a pipeline state object + MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; + pipelineStateDescriptor.label = @"Texturing Pipeline"; + pipelineStateDescriptor.vertexFunction = vertexFunction; + pipelineStateDescriptor.fragmentFunction = fragmentFunction; + pipelineStateDescriptor.colorAttachments[0].pixelFormat = metalView.colorPixelFormat; + + NSError *error = NULL; + _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor + error:&error]; + + _commandQueue = [_device newCommandQueue]; + + if (OPTION(kDispStretch) == false) { + metalView.frame = view.frame; + metalFrame = view.frame; + } else { + float scaleX = view.frame.size.width / (width * scale); + float scaleY = view.frame.size.height / (height * scale); + float scaleF = 0; + if (scaleX < scaleY) { + scaleF = scaleX; + } else { + scaleF = scaleY; + } + + if (scaleF == 0) { + scaleF = 1; + } + + metalFrame.size.width = (width * scale) * scaleF; + metalFrame.size.height = (height * scale) * scaleF; + metalFrame.origin.x = (view.frame.size.width - metalFrame.size.width) / 2; + metalFrame.origin.y = (view.frame.size.height - metalFrame.size.height) / 2; + metalView.frame = metalFrame; + } + + metalView.wantsLayer = YES; + metalView.layer.contentsScale = 1.0; + metalView.autoResizeDrawable = NO; + metalView.drawableSize = metalFrame.size; + ((CAMetalLayer *)metalView.layer).drawableSize = metalFrame.size; + + _contentSize.x = metalFrame.size.width; + _contentSize.y = metalFrame.size.height; + _viewportSize.x = width * scale; + _viewportSize.y = height * scale; + + [view addSubview:metalView]; +} + +void MetalDrawingPanel::DrawingPanelInit() +{ + CreateMetalView(); + + did_init = true; +} + +id MetalDrawingPanel::loadTextureUsingData(void *data) +{ + MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc] init]; + + // Indicate that each pixel has a blue, green, red, and alpha channel, where each channel is + // an 8-bit unsigned normalized value (i.e. 0 maps to 0.0 and 255 maps to 1.0) + textureDescriptor.pixelFormat = metalView.colorPixelFormat; + textureDescriptor.usage = MTLTextureUsageRenderTarget; + + // Set the pixel dimensions of the texture + textureDescriptor.width = width * scale; + textureDescriptor.height = height * scale; + + // Create the texture from the device by using the descriptor + id texture = [_device newTextureWithDescriptor:textureDescriptor]; + + // Calculate the number of bytes per row in the image. + NSUInteger bytesPerRow = 0; + bytesPerRow = std::ceil((width * scale * 4) + 4); + + MTLRegion region = { + { 0, 0, 0 }, // MTLOrigin + {(NSUInteger)(width * scale), (NSUInteger)(height * scale), 1} // MTLSize + }; + + // Copy the bytes from the data object into the texture + [texture replaceRegion:region + mipmapLevel:0 + withBytes:data + bytesPerRow:bytesPerRow]; + + return texture; +} + +void MetalDrawingPanel::OnSize(wxSizeEvent& ev) +{ + if (OPTION(kDispStretch) == false) { + metalView.frame = view.frame; + metalFrame = view.frame; + } else { + float scaleX = view.frame.size.width / (width * scale); + float scaleY = view.frame.size.height / (height * scale); + float scaleF = 0; + if (scaleX < scaleY) { + scaleF = scaleX; + } else { + scaleF = scaleY; + } + + if (scaleF == 0) { + scaleF = 1; + } + + metalFrame.size.width = (width * scale) * scaleF; + metalFrame.size.height = (height * scale) * scaleF; + metalFrame.origin.x = (view.frame.size.width - metalFrame.size.width) / 2; + metalFrame.origin.y = (view.frame.size.height - metalFrame.size.height) / 2; + metalView.frame = metalFrame; + } + + metalView.drawableSize = metalFrame.size; + ((CAMetalLayer *)metalView.layer).drawableSize = metalFrame.size; + + _contentSize.x = metalFrame.size.width; + _contentSize.y = metalFrame.size.height; + _viewportSize.x = width * scale; + _viewportSize.y = height * scale; + + if (todraw) { + DrawArea(); + } + + ev.Skip(); +} + +void MetalDrawingPanel::DrawArea(wxWindowDC& dc) +{ + (void)dc; + DrawArea(); +} + +void MetalDrawingPanel::DrawArea() +{ + uint32_t srcPitch = 0; + + if (!did_init) + DrawingPanelInit(); + + if (systemColorDepth == 8) { + srcPitch = std::ceil(width * scale) + 2; + } else if (systemColorDepth == 16) { + srcPitch = std::ceil(width * scale * 2) + 4; + } else if (systemColorDepth == 24) { + srcPitch = std::ceil(width * scale * 3); + } else { + srcPitch = std::ceil(width * scale * 4) + 4; + } + + if (systemColorDepth == 8) { + int pos = 0; + int src_pos = 0; + uint8_t *src = todraw + srcPitch; + uint32_t *dst = (uint32_t *)calloc(4, std::ceil((width * scale) * (height * scale) + 1)); + + for (int y = 0; y < (height * scale); y++) { + for (int x = 0; x < (width * scale); x++) { +#if wxBYTE_ORDER == wxLITTLE_ENDIAN + if (src[src_pos] == 0xff) { + dst[pos] = 0x00ffffff; + } else { + dst[pos] = (src[src_pos] & 0xe0) + ((src[src_pos] & 0x1c) << 11) + ((src[src_pos] & 0x3) << 22); + } +#else + if (src[src_pos] == 0xff) { + dst[pos] = 0xffffff00; + } else { + dst[pos] = ((src[src_pos] & 0xe0) << 24) + ((src[src_pos] & 0x1c) << 19) + ((src[src_pos] & 0x3) << 14); + } +#endif + pos++; + src_pos++; + } + pos++; + src_pos += 2; + } + + _texture = loadTextureUsingData(dst); + + if (dst != NULL) { + free(dst); + } + } else if (systemColorDepth == 16) { + int pos = 0; + int src_pos = 0; + uint8_t *src = todraw + srcPitch; + uint32_t *dst = (uint32_t *)calloc(4, std::ceil((width * scale) * (height * scale) + 1)); + uint16_t *src16 = (uint16_t *)src; + + for (int y = 0; y < (height * scale); y++) { + for (int x = 0; x < (width * scale); x++) { +#if wxBYTE_ORDER == wxLITTLE_ENDIAN + if (src16[src_pos] == 0x7fff) { + dst[pos] = 0x00ffffff; + } else { + dst[pos] = ((src16[src_pos] & 0x7c00) >> 7) + ((src16[src_pos] & 0x03e0) << 6) + ((src16[src_pos] & 0x1f) << 19); + } +#else + if (src16[src_pos] == 0x7fff) { + dst[pos] = 0xffffff00; + } else { + dst[pos] = ((src16[src_pos] & 0x7c00) << 17) + ((src16[src_pos] & 0x03e0) << 14) + ((src16[src_pos] & 0x1f) << 11); + } +#endif + pos++; + src_pos++; + } + pos++; + src_pos += 2; + } + + _texture = loadTextureUsingData(dst); + + if (dst != NULL) { + free(dst); + } + } else if (systemColorDepth == 24) { + int pos = 0; + int src_pos = 0; + uint8_t *src = todraw + srcPitch; + uint8_t *dst = (uint8_t *)calloc(4, std::ceil((width * scale) * (height * scale) + 1)); + + for (int y = 0; y < (height * scale); y++) { + for (int x = 0; x < (width * scale); x++) { + dst[pos] = src[src_pos]; + dst[pos+1] = src[src_pos+1]; + dst[pos+2] = src[src_pos+2]; + dst[pos+3] = 0; + + pos += 4; + src_pos += 3; + } + + pos += 4; + } + + _texture = loadTextureUsingData(dst); + + if (dst != NULL) { + free(dst); + } + } else { + _texture = loadTextureUsingData(todraw + srcPitch); + } + + // Create a new command buffer for each render pass to the current drawable + commandBuffer = [_commandQueue commandBuffer]; + commandBuffer.label = @"MyCommand"; + + // Obtain a renderPassDescriptor + renderPassDescriptor = [metalView currentRenderPassDescriptor]; + + if(renderPassDescriptor != nil) + { + renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + renderEncoder.label = @"MyRenderEncoder"; + + [renderEncoder setViewport:(MTLViewport){0.0, 0.0, (double)(_contentSize.x), (double)(_contentSize.y), 0.0, 1.0 }]; + + [renderEncoder setRenderPipelineState:_pipelineState]; + + [renderEncoder setVertexBuffer:_vertices + offset:0 + atIndex:AAPLVertexInputIndexVertices]; + + [renderEncoder setVertexBytes:&_viewportSize + length:sizeof(_viewportSize) + atIndex:AAPLVertexInputIndexViewportSize]; + + // Set the texture object. The AAPLTextureIndexBaseColor enum value corresponds + /// to the 'colorMap' argument in the 'samplingShader' function because its + // texture attribute qualifier also uses AAPLTextureIndexBaseColor for its index. + [renderEncoder setFragmentTexture:_texture + atIndex:AAPLTextureIndexBaseColor]; + + // Draw the triangles. + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle + vertexStart:0 + vertexCount:_numVertices]; + + [renderEncoder endEncoding]; + + // Schedule a present once the framebuffer is complete using the next drawable + [commandBuffer presentDrawable:metalView.currentDrawable]; + } + + // Finalize rendering here & push the command buffer to the GPU + [commandBuffer commit]; +} +#endif + Quartz2DDrawingPanel::Quartz2DDrawingPanel(wxWindow* parent, int _width, int _height) : BasicDrawingPanel(parent, _width, _height) { @@ -14,8 +416,7 @@ Quartz2DDrawingPanel::Quartz2DDrawingPanel(wxWindow* parent, int _width, int _he void Quartz2DDrawingPanel::DrawImage(wxWindowDC& dc, wxImage* im) { - NSView* view = (NSView*)(GetWindow()->GetHandle()); - + NSView *view = (NSView *)GetWindow()->GetHandle(); size_t w = std::ceil(width * scale); size_t h = std::ceil(height * scale); size_t size = w * h * 3; @@ -23,18 +424,19 @@ void Quartz2DDrawingPanel::DrawImage(wxWindowDC& dc, wxImage* im) CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, im->GetData(), size, NULL); CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGImageRef image = nil; - CGImageRef image = CGImageCreate( - w, h, 8, 24, w * 3, color_space, - kCGBitmapByteOrderDefault, - provider, NULL, true, kCGRenderingIntentDefault - ); + image = CGImageCreate(w, h, 8, 24, w * 3, color_space, kCGBitmapByteOrderDefault, + provider, NULL, true, kCGRenderingIntentDefault); // draw the image - +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 + CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; +#else [view lockFocus]; CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; +#endif CGContextSaveGState(context); @@ -59,7 +461,11 @@ void Quartz2DDrawingPanel::DrawImage(wxWindowDC& dc, wxImage* im) dc.DrawRectangle(w-2, h-2, w, h); } + [view setNeedsDisplay:YES]; + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 [view unlockFocus]; +#endif // and release everything diff --git a/src/wx/panel.cpp b/src/wx/panel.cpp index e0f2e3e9..e53eaf6a 100644 --- a/src/wx/panel.cpp +++ b/src/wx/panel.cpp @@ -1,3 +1,7 @@ +#if defined(__APPLE__) && defined(__MACH__) +#define GL_SILENCE_DEPRECATION 1 +#endif + #include "wx/wxvbam.h" #include @@ -8,6 +12,7 @@ #include #define Status int #include + #include #include // For Wayland EGL. #ifdef HAVE_EGL @@ -18,6 +23,12 @@ #endif #endif +#ifdef ENABLE_SDL3 +#include +#else +#include +#endif + #include #include @@ -27,6 +38,7 @@ #include "components/filters_interframe/interframe.h" #include "core/base/check.h" #include "core/base/file_util.h" +#include "core/base/image_util.h" #include "core/base/patch.h" #include "core/base/system.h" #include "core/base/version.h" @@ -56,6 +68,10 @@ #include #endif +#ifdef _MSC_VER +#define strdup _strdup +#endif + namespace { double GetFilterScale() { @@ -119,7 +135,9 @@ long GetSampleRate() { return 44100; } +#define out_8 (systemColorDepth == 8) #define out_16 (systemColorDepth == 16) +#define out_24 (systemColorDepth == 24) } // namespace @@ -145,7 +163,8 @@ GameArea::GameArea() mouse_active_time(0), render_observer_({config::OptionID::kDispBilinear, config::OptionID::kDispFilter, config::OptionID::kDispRenderMethod, config::OptionID::kDispIFB, - config::OptionID::kDispStretch, config::OptionID::kPrefVsync}, + config::OptionID::kDispStretch, config::OptionID::kPrefVsync, + config::OptionID::kSDLRenderer, config::OptionID::kBitDepth}, std::bind(&GameArea::ResetPanel, this)), scale_observer_(config::OptionID::kDispScale, std::bind(&GameArea::AdjustSize, this, true)), gb_border_observer_(config::OptionID::kPrefBorderOn, @@ -169,7 +188,8 @@ GameArea::GameArea() SetSizer(new wxBoxSizer(wxVERTICAL)); // all renderers prefer 32-bit // well, "simple" prefers 24-bit, but that's not available for filters - systemColorDepth = 32; + systemColorDepth = (OPTION(kBitDepth) + 1) << 3; + hq2x_init(32); Init_2xSaI(32); } @@ -1162,21 +1182,51 @@ void GameArea::OnIdle(wxIdleEvent& event) if (!panel) { switch (OPTION(kDispRenderMethod)) { case config::RenderMethod::kSimple: + no_border = false; + panel = new BasicDrawingPanel(this, basic_width, basic_height); break; + case config::RenderMethod::kSDL: + no_border = false; + + panel = new SDLDrawingPanel(this, basic_width, basic_height); + break; #ifdef __WXMAC__ +#ifndef NO_METAL + case config::RenderMethod::kMetal: + no_border = false; + + if (is_macosx_1012_or_newer()) { + panel = + new MetalDrawingPanel(this, basic_width, basic_height); + } else { + panel = new GLDrawingPanel(this, basic_width, basic_height); + log("Metal is unavailable, defaulting to OpenGL"); + } + break; +#endif case config::RenderMethod::kQuartz2d: + no_border = false; + panel = new Quartz2DDrawingPanel(this, basic_width, basic_height); break; #endif #ifndef NO_OGL case config::RenderMethod::kOpenGL: + if (out_8) { + no_border = true; + } else { + no_border = false; + } + panel = new GLDrawingPanel(this, basic_width, basic_height); break; #endif #if defined(__WXMSW__) && !defined(NO_D3D) case config::RenderMethod::kDirect3d: + no_border = false; + panel = new DXDrawingPanel(this, basic_width, basic_height); break; #endif @@ -1422,8 +1472,14 @@ DrawingPanelBase::DrawingPanelBase(int _width, int _height) rpi_->Flags &= ~RPI_555_SUPP; // FIXME: should this be 32 or 24? No docs or sample source systemColorDepth = 32; - } else + } else if (rpi_->Flags & RPI_555_SUPP) { + rpi_->Flags &= ~RPI_888_SUPP; systemColorDepth = 16; + } else { + rpi_->Flags &= ~RPI_888_SUPP; + rpi_->Flags &= ~RPI_555_SUPP; + systemColorDepth = 8; + } if (!rpi_->Output) { // note that in actual kega fusion plugins, rpi_->Output is @@ -1442,11 +1498,24 @@ DrawingPanelBase::DrawingPanelBase(int _width, int _height) if (OPTION(kDispFilter) != config::Filter::kPlugin) { scale *= GetFilterScale(); - systemColorDepth = 32; + + systemColorDepth = (OPTION(kBitDepth) + 1) << 3; } // Intialize color tables - if (systemColorDepth == 32) { + if (systemColorDepth == 24) { +#if wxBYTE_ORDER == wxLITTLE_ENDIAN + systemRedShift = 3; + systemGreenShift = 11; + systemBlueShift = 19; + RGB_LOW_BITS_MASK = 0x00010101; +#else + systemRedShift = 27; + systemGreenShift = 19; + systemBlueShift = 11; + RGB_LOW_BITS_MASK = 0x01010100; +#endif + } else if (systemColorDepth == 32) { #if wxBYTE_ORDER == wxLITTLE_ENDIAN systemRedShift = 3; systemGreenShift = 11; @@ -1546,25 +1615,35 @@ public: ExitCode Entry() override { // This is the band this thread will process // threadno == -1 means just do a dummy round on the border line + const int height_real = height_; const int procy = height_ * threadno_ / nthreads_; height_ = height_ * (threadno_ + 1) / nthreads_ - procy; const int inbpp = systemColorDepth >> 3; - const int inrb = systemColorDepth == 16 ? 2 - : systemColorDepth == 24 ? 0 - : 1; + const int inrb = out_8 ? 2 : out_16 ? 2 + : out_24 ? 0 : 1; const int instride = (width_ + inrb) * inbpp; - const int outbpp = out_16 ? 2 : systemColorDepth == 24 ? 3 : 4; - const int outrb = systemColorDepth == 24 ? 0 : 4; + const int instride32 = width_ * 4; + const int outbpp = systemColorDepth >> 3; + const int outrb = out_8 ? 2 : out_24 ? 0 : 4; const int outstride = std::ceil(width_ * outbpp * scale_) + outrb; + const int outstride32 = std::ceil(width_ * 4 * scale_); + uint8_t *dest = NULL; + int pos = 0; + uint32_t *src2_ = NULL; + uint32_t *dst2_ = NULL; delta_ += instride * procy; // FIXME: fugly hack if (OPTION(kDispRenderMethod) == config::RenderMethod::kOpenGL) { dst_ += (int)std::ceil(outstride * (procy + 1) * scale_); + } else if (OPTION(kDispRenderMethod) == config::RenderMethod::kSDL) { + dst_ += (int)std::ceil(outstride * (procy + 1) * scale_); } else { dst_ += (int)std::ceil(outstride * (procy + (1 / scale_)) * scale_); } + dest = dst_; + while (nthreads_ == 1 || sig_.Wait() == wxCOND_NO_ERROR) { if (!src_ /* && nthreads > 1 */) { lock_.Unlock(); @@ -1590,7 +1669,118 @@ public: // naturally, any of these with accumulation buffers like those // of the IFB filters will screw up royally as well - ApplyFilter(instride, outstride); + if (systemColorDepth == 32) { + ApplyFilter(instride, outstride); + } else { + src2_ = (uint32_t *)calloc(4, std::ceil(width_ * height_real)); + dst2_ = (uint32_t *)calloc(4, std::ceil((width_ * scale_) * (height_real * scale_))); + + if (out_8) { + int src_pos = 0; + for (int y = 0; y < height_real; y++) { + for (int x = 0; x < width_; x++) { +#if wxBYTE_ORDER == wxLITTLE_ENDIAN + src2_[pos] = (src_[src_pos] & 0xe0) + ((src_[src_pos] & 0x1c) << 11) + ((src_[src_pos] & 0x3) << 22); +#else + src2_[pos] = ((src_[src_pos] & 0xe0) << 24) + ((src_[src_pos] & 0x1c) << 19) + ((src_[src_pos] & 0x3) << 14); +#endif + pos++; + src_pos++; + } + src_pos += 2; + } + } else if (out_16) { + uint16_t *src16_ = (uint16_t *)src_; + int src_pos = 0; + for (int y = 0; y < height_real; y++) { + for (int x = 0; x < width_; x++) { +#if wxBYTE_ORDER == wxLITTLE_ENDIAN + src2_[pos] = ((src16_[src_pos] & 0x7c00) >> 7) + ((src16_[src_pos] & 0x03e0) << 6) + ((src16_[src_pos] & 0x1f) << 19); +#else + src2_[pos] = ((src16_[src_pos] & 0x7c00) << 17) + ((src16_[src_pos] & 0x03e0) << 14) + ((src16_[src_pos] & 0x1f) << 11); +#endif + pos++; + src_pos++; + } + src_pos += 2; + } + } else if (out_24) { + int src_pos = 0; + uint8_t *src8_ = (uint8_t *)src2_; + for (int y = 0; y < height_real; y++) { + for (int x = 0; x < width_; x++) { + src8_[pos] = src_[src_pos]; + src8_[pos+1] = src_[src_pos+1]; + src8_[pos+2] = src_[src_pos+2]; + src8_[pos+3] = 0; + + pos += 4; + src_pos += 3; + } + } + } + + src_ = (uint8_t *)src2_; + dst_ = (uint8_t *)dst2_; + + ApplyFilter(instride32, outstride32); + + dst_ = (uint8_t *)dst2_; + + if (out_8) { + int dst_pos = 0; + pos = 0; + for (int y = 0; y < (height_real * scale_); y++) { + for (int x = 0; x < (width_ * scale_); x++) { +#if wxBYTE_ORDER == wxLITTLE_ENDIAN + dest[pos] = (uint8_t)(((dst_[dst_pos] >> 5) << 5) + ((dst_[dst_pos+1] >> 5) << 2) + ((dst_[dst_pos+2] >> 6) & 0x3)); +#else + dest[pos] = (uint8_t)(((dst_[dst_pos+3] >> 5) << 5) + ((dst_[dst_pos+2] >> 5) << 2) + ((dst_[dst_pos+1] >> 6) & 0x3)); +#endif + pos++; + dst_pos += 4; + } + pos += 2; + } + } else if (out_16) { + uint16_t *dest16_ = (uint16_t *)dest; + int dst_pos = 0; + pos = 0; + for (int y = 0; y < (height_real * scale_); y++) { + for (int x = 0; x < (width_ * scale_); x++) { +#if wxBYTE_ORDER == wxLITTLE_ENDIAN + dest16_[pos] = (uint16_t)(((dst_[dst_pos] >> 3) << 10) + ((dst_[dst_pos+1] >> 3) << 5) + (dst_[dst_pos+2] >> 3)); +#else + dest16_[pos] = (uint16_t)(((dst_[dst_pos+3] >> 3) << 10) + ((dst_[dst_pos+2] >> 3) << 5) + (dst_[dst_pos+1] >> 3)); +#endif + pos++; + dst_pos += 4; + } + pos += 2; + } + } else if (out_24) { + int dst_pos = 0; + pos = 0; + for (int y = 0; y < (height_real * scale_); y++) { + for (int x = 0; x < (width_ * scale_); x++) { + dest[pos] = dst_[dst_pos]; + dest[pos+1] = dst_[dst_pos+1]; + dest[pos+2] = dst_[dst_pos+2]; + pos += 3; + dst_pos += 4; + } + } + } + + if (src2_ != NULL) { + free(src2_); + } + + if (dst2_ != NULL) { + free(dst2_); + } + } + if (nthreads_ == 1) { return 0; } @@ -1612,18 +1802,28 @@ private: break; case config::Interframe::kSmart: - if (systemColorDepth == 16) + if (out_8) { + SmartIB8(src_, instride, width_, procy, height_); + } else if (out_16) { SmartIB(src_, instride, width_, procy, height_); - else + } else if (out_24) { + SmartIB24(src_, instride, width_, procy, height_); + } else { SmartIB32(src_, instride, width_, procy, height_); + } break; case config::Interframe::kMotionBlur: // FIXME: if(renderer == d3d/gl && filter == NONE) break; - if (systemColorDepth == 16) + if (out_8) { + MotionBlurIB8(src_, instride, width_, procy, height_); + } else if (out_16) { MotionBlurIB(src_, instride, width_, procy, height_); - else + } else if (out_24) { + MotionBlurIB24(src_, instride, width_, procy, height_); + } else { MotionBlurIB32(src_, instride, width_, procy, height_); + } break; case config::Interframe::kLast: @@ -1781,8 +1981,8 @@ void DrawingPanelBase::DrawArea(uint8_t** data) // double-buffer buffer: // if filtering, this is filter output, retained for redraws // if not filtering, we still retain current image for redraws - int outbpp = out_16 ? 2 : systemColorDepth == 24 ? 3 : 4; - int outrb = systemColorDepth == 24 ? 0 : 4; + int outbpp = systemColorDepth >> 3; + int outrb = out_8 ? 2 : out_24 ? 0 : 4; int outstride = std::ceil(width * outbpp * scale) + outrb; if (!pixbuf2) { @@ -2064,28 +2264,496 @@ DrawingPanelBase::~DrawingPanelBase() disableKeyboardBackgroundInput(); } -BasicDrawingPanel::BasicDrawingPanel(wxWindow* parent, int _width, int _height) +SDLDrawingPanel::SDLDrawingPanel(wxWindow* parent, int _width, int _height) : DrawingPanel(parent, _width, _height) +{ + memset(delta, 0xff, sizeof(delta)); + + // wxImage is 24-bit RGB, so 24-bit is preferred. Filters require + // 16 or 32, though + if (OPTION(kDispFilter) == config::Filter::kNone && + OPTION(kDispIFB) == config::Interframe::kNone) { + // changing from 32 to 24 does not require regenerating color tables + systemColorDepth = (OPTION(kBitDepth) + 1) << 3; + } + + DrawingPanelInit(); +} + +SDLDrawingPanel::~SDLDrawingPanel() +{ + if (did_init) + { + SDL_DestroyWindow(sdlwindow); + SDL_DestroyTexture(texture); + SDL_DestroyRenderer(renderer); + + SDL_QuitSubSystem(SDL_INIT_VIDEO); + + did_init = false; + } +} + +void SDLDrawingPanel::EraseBackground(wxEraseEvent& ev) +{ + (void)ev; // unused params + // do nothing, do not allow propagation +} + +void SDLDrawingPanel::PaintEv(wxPaintEvent& ev) +{ + // FIXME: implement + if (!did_init) { + DrawingPanelInit(); + } + + (void)ev; // unused params + + if (!todraw) { + // since this is set for custom background, not drawing anything + // will cause garbage to be displayed, so draw a black area + draw_black_background(GetWindow()); + return; + } + + if (todraw) { + DrawArea(); + } +} + +void SDLDrawingPanel::DrawingPanelInit() +{ + wxString renderer_name = OPTION(kSDLRenderer); + +#ifdef ENABLE_SDL3 + SDL_PropertiesID props = SDL_CreateProperties(); +#endif +#ifdef __WXGTK__ + GtkWidget *widget = wxGetApp().frame->GetPanel()->GetHandle(); + gtk_widget_realize(widget); + XID xid = 0; + struct wl_surface *wayland_surface = NULL; + struct wl_display *wayland_display = NULL; + +#ifdef ENABLE_SDL3 + if (GDK_IS_WAYLAND_WINDOW(gtk_widget_get_window(widget))) { + wayland_display = gdk_wayland_display_get_wl_display(gtk_widget_get_display(widget)); + wayland_surface = gdk_wayland_window_get_wl_surface(gtk_widget_get_window(widget)); + + if (SDL_SetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, wayland_display) == false) { + systemScreenMessage(_("Failed to set wayland display")); + return; + } + } else { +#endif + xid = GDK_WINDOW_XID(gtk_widget_get_window(widget)); +#ifdef ENABLE_SDL3 + } +#endif + +#ifdef ENABLE_SDL3 + if (GDK_IS_WAYLAND_WINDOW(gtk_widget_get_window(widget))) { + SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "wayland"); + } else { + SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "x11"); + } +#else + SDL_SetHint(SDL_HINT_VIDEODRIVER, "x11"); +#endif +#endif + + DrawingPanel::DrawingPanelInit(); + +#ifdef ENABLE_SDL3 + if (SDL_InitSubSystem(SDL_INIT_VIDEO) == false) { +#else + if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { +#endif + systemScreenMessage(_("Failed to initialize SDL video subsystem")); + return; + } + +#ifdef ENABLE_SDL3 + if (SDL_WasInit(SDL_INIT_VIDEO) == false) { + if (SDL_Init(SDL_INIT_VIDEO) == false) { +#else + if (SDL_WasInit(SDL_INIT_VIDEO) < 0) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { +#endif + systemScreenMessage(_("Failed to initialize SDL video")); + return; + } + } + +#ifdef ENABLE_SDL3 +#ifdef __WXGTK__ + if (GDK_IS_WAYLAND_WINDOW(gtk_widget_get_window(widget))) { + if (SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, wayland_surface) == false) { + systemScreenMessage(_("Failed to set wayland surface")); + return; + } + } else { + if (SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER, xid) == false) +#elif defined(__WXMAC__) + if (SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_COCOA_VIEW_POINTER, wxGetApp().frame->GetPanel()->GetHandle()) == false) +#else + if (SDL_SetPointerProperty(props, "sdl2-compat.external_window", GetWindow()->GetHandle()) == false) +#endif + { + systemScreenMessage(_("Failed to set parent window")); + return; + } +#ifdef __WXGTK__ + } +#endif + + sdlwindow = SDL_CreateWindowWithProperties(props); + + if (sdlwindow == NULL) { + systemScreenMessage(_("Failed to create SDL window")); + return; + } + + SDL_DestroyProperties(props); + + if (OPTION(kSDLRenderer) == wxString("default")) { + renderer = SDL_CreateRenderer(sdlwindow, NULL); + log("SDL renderer: default"); + } else { + wxString renderer_name = OPTION(kSDLRenderer); + renderer = SDL_CreateRenderer(sdlwindow, renderer_name.mb_str()); + log("SDL renderer: %s", (const char *)renderer_name.mb_str()); + + if (renderer == NULL) { + log("ERROR: Renderer creating failed, using default renderer"); + renderer = SDL_CreateRenderer(sdlwindow, NULL); + } + } + + if (renderer == NULL) { + systemScreenMessage(_("Failed to create SDL renderer")); + return; + } +#else +#ifdef __WXGTK__ + sdlwindow = SDL_CreateWindowFrom((void *)xid); +#elif defined(__WXMAC__) + sdlwindow = SDL_CreateWindowFrom(wxGetApp().frame->GetPanel()->GetHandle()); +#else + sdlwindow = SDL_CreateWindowFrom(GetWindow()->GetHandle()); +#endif + + if (sdlwindow == NULL) { + systemScreenMessage(_("Failed to create SDL window")); + return; + } + + if (OPTION(kSDLRenderer) == wxString("default")) { + renderer = SDL_CreateRenderer(sdlwindow, -1, 0); + log("SDL renderer: default"); + } else { + for (int i = 0; i < SDL_GetNumRenderDrivers(); i++) { + wxString renderer_name = OPTION(kSDLRenderer); + SDL_RendererInfo render_info; + + SDL_GetRenderDriverInfo(i, &render_info); + + if (!strcmp(renderer_name.mb_str(), render_info.name)) { + if (!strcmp(render_info.name, "software")) { + renderer = SDL_CreateRenderer(sdlwindow, i, SDL_RENDERER_SOFTWARE); + } else { + renderer = SDL_CreateRenderer(sdlwindow, i, SDL_RENDERER_ACCELERATED); + } + + log("SDL renderer: %s", render_info.name); + } + } + + if (renderer == NULL) { + log("ERROR: Renderer creating failed, using default renderer"); + renderer = SDL_CreateRenderer(sdlwindow, -1, 0); + } + } + + if (renderer == NULL) { + systemScreenMessage(_("Failed to create SDL renderer")); + return; + } +#endif + + if (out_8) { +#ifdef ENABLE_SDL3 + texture = SDL_CreateTexture(renderer, SDL_GetPixelFormatForMasks(8, 0xE0, 0x1C, 0x03, 0x00), SDL_TEXTUREACCESS_STREAMING, (width * scale), (height * scale)); +#else + texture = SDL_CreateTexture(renderer, SDL_MasksToPixelFormatEnum(8, 0xE0, 0x1C, 0x03, 0x00), SDL_TEXTUREACCESS_STREAMING, (width * scale), (height * scale)); +#endif + } else if (out_16) { +#ifdef ENABLE_SDL3 + texture = SDL_CreateTexture(renderer, SDL_GetPixelFormatForMasks(16, 0x7C00, 0x03E0, 0x001F, 0x0000), SDL_TEXTUREACCESS_STREAMING, (width * scale), (height * scale)); +#else + texture = SDL_CreateTexture(renderer, SDL_MasksToPixelFormatEnum(16, 0x7C00, 0x03E0, 0x001F, 0x0000), SDL_TEXTUREACCESS_STREAMING, (width * scale), (height * scale)); +#endif + } else if (out_24) { +#ifdef ENABLE_SDL3 + texture = SDL_CreateTexture(renderer, SDL_GetPixelFormatForMasks(24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000), SDL_TEXTUREACCESS_STREAMING, (width * scale), (height * scale)); +#else + texture = SDL_CreateTexture(renderer, SDL_MasksToPixelFormatEnum(24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000), SDL_TEXTUREACCESS_STREAMING, (width * scale), (height * scale)); +#endif + } else { +#ifdef ENABLE_SDL3 + texture = SDL_CreateTexture(renderer, SDL_GetPixelFormatForMasks(32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000), SDL_TEXTUREACCESS_STREAMING, (width * scale), (height * scale)); +#else + texture = SDL_CreateTexture(renderer, SDL_MasksToPixelFormatEnum(32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000), SDL_TEXTUREACCESS_STREAMING, (width * scale), (height * scale)); +#endif + } + + did_init = true; +} + +void SDLDrawingPanel::OnSize(wxSizeEvent& ev) +{ + if (todraw) { + DrawArea(); + } + + ev.Skip(); +} + +void SDLDrawingPanel::DrawArea(wxWindowDC& dc) +{ + (void)dc; + DrawArea(); +} + +void SDLDrawingPanel::DrawArea() +{ + uint32_t srcPitch = 0; + + if (!did_init) + DrawingPanelInit(); + + if (out_8) { + srcPitch = std::ceil(width * scale) + 2; + } else if (out_16) { + srcPitch = std::ceil(width * scale * 2) + 4; + } else if (out_24) { + srcPitch = std::ceil(width * scale * 3); + } else { + srcPitch = std::ceil(width * scale * 4) + 4; + } + + SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00); + SDL_RenderClear(renderer); + SDL_UpdateTexture(texture, NULL, todraw + srcPitch, srcPitch); + +#ifdef ENABLE_SDL3 + SDL_RenderTexture(renderer, texture, NULL, NULL); +#else + SDL_RenderCopy(renderer, texture, NULL, NULL); +#endif + + SDL_RenderPresent(renderer); +} + +void SDLDrawingPanel::DrawArea(uint8_t** data) +{ + // double-buffer buffer: + // if filtering, this is filter output, retained for redraws + // if not filtering, we still retain current image for redraws + int outbpp = systemColorDepth >> 3; + int outrb = out_8 ? 2 : out_24 ? 0 : 4; + int outstride = std::ceil(width * outbpp * scale) + outrb; + + // FIXME: filters race condition? + const int max_threads = 1; + + if (!pixbuf2) { + int allocstride = outstride, alloch = height; + + // gb may write borders, so allocate enough for them + if (width == GameArea::GBWidth && height == GameArea::GBHeight) { + allocstride = std::ceil(GameArea::SGBWidth * outbpp * scale) + outrb; + alloch = GameArea::SGBHeight; + } + + pixbuf2 = (uint8_t*)calloc(allocstride, std::ceil((alloch + 2) * scale)); + } + + if (OPTION(kDispFilter) == config::Filter::kNone) { + todraw = *data; + // *data is assigned below, after old buf has been processed + pixbuf1 = pixbuf2; + pixbuf2 = todraw; + } else + todraw = pixbuf2; + + // First, apply filters, if applicable, in parallel, if enabled + // FIXME: && (gopts.ifb != FF_MOTION_BLUR || !renderer_can_motion_blur) + if (OPTION(kDispFilter) != config::Filter::kNone || + OPTION(kDispIFB) != config::Interframe::kNone) { + if (nthreads != max_threads) { + if (nthreads) { + if (nthreads > 1) + for (int i = 0; i < nthreads; i++) { + threads[i].lock_.Lock(); + threads[i].src_ = NULL; + threads[i].sig_.Signal(); + threads[i].lock_.Unlock(); + threads[i].Wait(); + } + + delete[] threads; + } + + nthreads = max_threads; + threads = new FilterThread[nthreads]; + // first time around, no threading in order to avoid + // static initializer conflicts + threads[0].threadno_ = 0; + threads[0].nthreads_ = 1; + threads[0].width_ = width; + threads[0].height_ = height; + threads[0].scale_ = scale; + threads[0].src_ = *data; + threads[0].dst_ = todraw; + threads[0].delta_ = delta; + threads[0].rpi_ = rpi_; + threads[0].Entry(); + + // go ahead and start the threads up, though + if (nthreads > 1) { + for (int i = 0; i < nthreads; i++) { + threads[i].threadno_ = i; + threads[i].nthreads_ = nthreads; + threads[i].width_ = width; + threads[i].height_ = height; + threads[i].scale_ = scale; + threads[i].dst_ = todraw; + threads[i].delta_ = delta; + threads[i].rpi_ = rpi_; + threads[i].done_ = &filt_done; + threads[i].lock_.Lock(); + threads[i].Create(); + threads[i].Run(); + } + } + } else if (nthreads == 1) { + threads[0].threadno_ = 0; + threads[0].nthreads_ = 1; + threads[0].width_ = width; + threads[0].height_ = height; + threads[0].scale_ = scale; + threads[0].src_ = *data; + threads[0].dst_ = todraw; + threads[0].delta_ = delta; + threads[0].rpi_ = rpi_; + threads[0].Entry(); + } else { + for (int i = 0; i < nthreads; i++) { + threads[i].lock_.Lock(); + threads[i].src_ = *data; + threads[i].sig_.Signal(); + threads[i].lock_.Unlock(); + } + + for (int i = 0; i < nthreads; i++) + filt_done.Wait(); + } + } + + // swap buffers now that src has been processed + if (OPTION(kDispFilter) == config::Filter::kNone) { + *data = pixbuf1; + } + + // draw OSD text old-style (directly into output buffer), if needed + // new style flickers too much, so we'll stick to this for now + if (wxGetApp().frame->IsFullScreen() || !OPTION(kPrefDisableStatus)) { + GameArea* panel = wxGetApp().frame->GetPanel(); + + if (panel->osdstat.size()) + drawText(todraw + outstride * (systemColorDepth != 24), outstride, + 10, 20, UTF8(panel->osdstat), OPTION(kPrefShowSpeedTransparent)); + + if (!panel->osdtext.empty()) { + if (systemGetClock() - panel->osdtime < OSD_TIME) { + wxString message = panel->osdtext; + int linelen = std::ceil(width * scale - 20) / 8; + int nlines = (message.size() + linelen - 1) / linelen; + int cury = height - 14 - nlines * 10; + char* buf = strdup(UTF8(message)); + char* ptr = buf; + + while (nlines > 1) { + char lchar = ptr[linelen]; + ptr[linelen] = 0; + drawText(todraw + outstride * (systemColorDepth != 24), + outstride, 10, cury, ptr, + OPTION(kPrefShowSpeedTransparent)); + cury += 10; + nlines--; + ptr += linelen; + *ptr = lchar; + } + + drawText(todraw + outstride * (systemColorDepth != 24), + outstride, 10, cury, ptr, + OPTION(kPrefShowSpeedTransparent)); + + free(buf); + buf = NULL; + } else + panel->osdtext.clear(); + } + } + + // Draw the current frame + DrawArea(); +} + +BasicDrawingPanel::BasicDrawingPanel(wxWindow* parent, int _width, int _height) + : DrawingPanel(parent, _width, _height) { // wxImage is 24-bit RGB, so 24-bit is preferred. Filters require // 16 or 32, though if (OPTION(kDispFilter) == config::Filter::kNone && OPTION(kDispIFB) == config::Interframe::kNone) { // changing from 32 to 24 does not require regenerating color tables - systemColorDepth = 32; - } - if (!did_init) { - DrawingPanelInit(); + systemColorDepth = (OPTION(kBitDepth) + 1) << 3; } + + DrawingPanelInit(); } - + void BasicDrawingPanel::DrawArea(wxWindowDC& dc) { wxImage* im; - - if (systemColorDepth == 24) { + + if (out_24) { // never scaled, no borders, no transformations needed im = new wxImage(width, height, todraw, true); + } else if (out_8) { + // scaled by filters, top/right borders, transform to 24-bit + im = new wxImage(std::ceil(width * scale), std::ceil(height * scale), false); + uint8_t* src = (uint8_t*)todraw + (int)std::ceil((width + 2) * scale); // skip top border + uint8_t* dst = im->GetData(); + + for (int y = 0; y < std::ceil(height * scale); y++) { + for (int x = 0; x < std::ceil(width * scale); x++, src++) { + // White color fix + if (*src == 0xff) { + *dst++ = 0xff; + *dst++ = 0xff; + *dst++ = 0xff; + } else { + *dst++ = (((*src >> 5) & 0x7) << 5); + *dst++ = (((*src >> 2) & 0x7) << 5); + *dst++ = ((*src & 0x3) << 6); + } + } + + src += 2; + } } else if (out_16) { // scaled by filters, top/right borders, transform to 24-bit im = new wxImage(std::ceil(width * scale), std::ceil(height * scale), false); @@ -2098,7 +2766,7 @@ void BasicDrawingPanel::DrawArea(wxWindowDC& dc) *dst++ = ((*src >> systemGreenShift) & 0x1f) << 3; *dst++ = ((*src >> systemBlueShift) & 0x1f) << 3; } - + src += 2; // skip rhs border } } else if (OPTION(kDispFilter) != config::Filter::kNone) { @@ -2113,7 +2781,7 @@ void BasicDrawingPanel::DrawArea(wxWindowDC& dc) *dst++ = *src >> (systemGreenShift - 3); *dst++ = *src >> (systemBlueShift - 3); } - + ++src; // skip rhs border } } else { // 32 bit @@ -2128,16 +2796,16 @@ void BasicDrawingPanel::DrawArea(wxWindowDC& dc) *dst++ = *src >> (systemGreenShift - 3); *dst++ = *src >> (systemBlueShift - 3); } - + ++src; // skip rhs border } } - + DrawImage(dc, im); - + delete im; } - + void BasicDrawingPanel::DrawImage(wxWindowDC& dc, wxImage* im) { double sx, sy; @@ -2149,7 +2817,7 @@ void BasicDrawingPanel::DrawImage(wxWindowDC& dc, wxImage* im) wxBitmap bm(*im); dc.DrawBitmap(bm, 0, 0); } - + #ifndef NO_OGL // following 3 for vsync #ifdef __WXMAC__ @@ -2165,7 +2833,7 @@ void BasicDrawingPanel::DrawImage(wxWindowDC& dc, wxImage* im) #include #include #endif - + // This is supposed to be the default, but DOUBLEBUFFER doesn't seem to be // turned on by default for wxGTK. static int glopts[] = { @@ -2178,16 +2846,16 @@ bool GLDrawingPanel::SetContext() // Check if the current context is valid if (!ctx #if wxCHECK_VERSION(3, 1, 0) - || !ctx->IsOK() + || !ctx->IsOK() #endif - ) + ) { // Delete the old context if (ctx) { delete ctx; ctx = nullptr; } - + // Create a new context ctx = new wxGLContext(this); DrawingPanelInit(); @@ -2197,16 +2865,16 @@ bool GLDrawingPanel::SetContext() return wxGLContext::SetCurrent(*this); #endif } - + GLDrawingPanel::GLDrawingPanel(wxWindow* parent, int _width, int _height) - : DrawingPanelBase(_width, _height) - , wxglc(parent, wxID_ANY, glopts, wxPoint(0, 0), parent->GetClientSize(), - wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) + : DrawingPanelBase(_width, _height) + , wxglc(parent, wxID_ANY, glopts, wxPoint(0, 0), parent->GetClientSize(), + wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) { widgets::RequestHighResolutionOpenGlSurfaceForWindow(this); SetContext(); } - + GLDrawingPanel::~GLDrawingPanel() { // this should be automatically deleted w/ context @@ -2217,22 +2885,22 @@ GLDrawingPanel::~GLDrawingPanel() glDeleteLists(vlist, 1); glDeleteTextures(1, &texid); } - + #ifndef wxGL_IMPLICIT_CONTEXT delete ctx; #endif } - + void GLDrawingPanel::DrawingPanelInit() { SetContext(); DrawingPanelBase::DrawingPanelInit(); - + AdjustViewport(); - + const bool bilinear = OPTION(kDispBilinear); - + // taken from GTK front end almost verbatim glDisable(GL_CULL_FACE); glEnable(GL_TEXTURE_2D); @@ -2260,27 +2928,30 @@ void GLDrawingPanel::DrawingPanelInit() bilinear ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, bilinear ? GL_LINEAR : GL_NEAREST); - + #define int_fmt out_16 ? GL_RGB5 : GL_RGB -#define tex_fmt out_16 ? GL_BGRA : GL_RGBA, \ - out_16 ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_BYTE +#define tex_fmt out_8 ? GL_RGB : \ + out_16 ? GL_BGRA : \ + out_24 ? GL_RGB : GL_RGBA, \ + out_8 ? GL_UNSIGNED_BYTE_3_3_2 : \ + out_16 ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_BYTE #if 0 - texsize = width > height ? width : height; - texsize = std::ceil(texsize * scale); - // texsize = 1 << ffs(texsize); - texsize = texsize | (texsize >> 1); - texsize = texsize | (texsize >> 2); - texsize = texsize | (texsize >> 4); - texsize = texsize | (texsize >> 8); - texsize = (texsize >> 1) + 1; - glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, texsize, texsize, 0, tex_fmt, NULL); + texsize = width > height ? width : height; + texsize = std::ceil(texsize * scale); + // texsize = 1 << ffs(texsize); + texsize = texsize | (texsize >> 1); + texsize = texsize | (texsize >> 2); + texsize = texsize | (texsize >> 4); + texsize = texsize | (texsize >> 8); + texsize = (texsize >> 1) + 1; + glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, texsize, texsize, 0, tex_fmt, NULL); #else // but really, most cards support non-p2 and rect // if not, use cairo or wx renderer glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, std::ceil(width * scale), std::ceil(height * scale), 0, tex_fmt, NULL); #endif glClearColor(0.0, 0.0, 0.0, 1.0); -// non-portable vsync code + // non-portable vsync code #if defined(__WXGTK__) if (IsWayland()) { #ifdef HAVE_EGL @@ -2288,7 +2959,7 @@ void GLDrawingPanel::DrawingPanelInit() wxLogDebug(_("Enabling EGL VSync.")); else wxLogDebug(_("Disabling EGL VSync.")); - + eglSwapInterval(0, OPTION(kPrefVsync)); #endif } @@ -2297,29 +2968,29 @@ void GLDrawingPanel::DrawingPanelInit() wxLogDebug(_("Enabling GLX VSync.")); else wxLogDebug(_("Disabling GLX VSync.")); - + static PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = NULL; static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = NULL; static PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA = NULL; auto display = GetX11Display(); auto default_screen = DefaultScreen(display); - + char* glxQuery = (char*)glXQueryExtensionsString(display, default_screen); - + if (strstr(glxQuery, "GLX_EXT_swap_control") != NULL) { glXSwapIntervalEXT = reinterpret_cast(glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT")); if (glXSwapIntervalEXT) glXSwapIntervalEXT(glXGetCurrentDisplay(), - glXGetCurrentDrawable(), OPTION(kPrefVsync)); + glXGetCurrentDrawable(), OPTION(kPrefVsync)); else systemScreenMessage(_("Failed to set glXSwapIntervalEXT")); } if (strstr(glxQuery, "GLX_SGI_swap_control") != NULL) { glXSwapIntervalSGI = reinterpret_cast(glXGetProcAddress((const GLubyte*)("glXSwapIntervalSGI"))); - + if (glXSwapIntervalSGI) glXSwapIntervalSGI(OPTION(kPrefVsync)); else @@ -2328,7 +2999,7 @@ void GLDrawingPanel::DrawingPanelInit() if (strstr(glxQuery, "GLX_MESA_swap_control") != NULL) { glXSwapIntervalMESA = reinterpret_cast(glXGetProcAddress((const GLubyte*)("glXSwapIntervalMESA"))); - + if (glXSwapIntervalMESA) glXSwapIntervalMESA(OPTION(kPrefVsync)); else @@ -2344,7 +3015,7 @@ void GLDrawingPanel::DrawingPanelInit() else if (strstr(wglGetExtensionsStringEXT(), "WGL_EXT_swap_control") == 0) { systemScreenMessage(_("No support for WGL_EXT_swap_control")); } - + typedef BOOL (__stdcall *PFNWGLSWAPINTERVALEXTPROC)(BOOL); static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); @@ -2360,126 +3031,146 @@ void GLDrawingPanel::DrawingPanelInit() systemScreenMessage(_("No VSYNC available on this platform")); #endif } - + void GLDrawingPanel::OnSize(wxSizeEvent& ev) { AdjustViewport(); - + // Temporary hack to backport 800d6ed69b from wxWidgets until 3.2.2 is released. if (IsWayland()) MoveWaylandSubsurface(this); ev.Skip(); } - + void GLDrawingPanel::AdjustViewport() { SetContext(); - + int x, y; widgets::GetRealPixelClientSize(this, &x, &y); glViewport(0, 0, x, y); } - + void GLDrawingPanel::RefreshGL() { SetContext(); - + // Rebind any textures or other OpenGL resources here - + glBindTexture(GL_TEXTURE_2D, texid); } - + void GLDrawingPanel::DrawArea(wxWindowDC& dc) { + uint8_t* src = NULL; + uint8_t* dst = NULL; + (void)dc; // unused params SetContext(); RefreshGL(); - + if (!did_init) DrawingPanelInit(); - + if (todraw) { - int rowlen = std::ceil(width * scale) + (out_16 ? 2 : 1); + int rowlen = std::ceil(width * scale) + (out_8 ? 0 : out_16 ? 2 : out_24 ? 0 : 1); glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlen); #if wxBYTE_ORDER == wxBIG_ENDIAN - - // FIXME: is this necessary? + + // FIXME: is this necessary? if (out_16) glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); - + #endif - glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, std::ceil(width * scale), (int)std::ceil(height * scale), - 0, tex_fmt, todraw + (int)std::ceil(rowlen * (out_16 ? 2 : 4) * scale)); + if (out_8) { + src = (uint8_t*)todraw + (int)std::ceil((width + 2) * ((systemColorDepth >> 3) * scale)); // skip top border + dst = (uint8_t*)todraw; + + for (int y = 0; y < std::ceil(height * scale); y++) { + for (int x = 0; x < std::ceil(width * scale); x++) { + *dst++ = *src++; + } + + src += 2; + } + + glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, (int)std::ceil(width * scale), (int)std::ceil(height * scale), + 0, tex_fmt, todraw); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, (int)std::ceil(width * scale), (int)std::ceil(height * scale), + 0, tex_fmt, todraw + (int)std::ceil(rowlen * ((systemColorDepth >> 3) * scale))); + } + glCallList(vlist); } else glClear(GL_COLOR_BUFFER_BIT); - + SwapBuffers(); } - + #endif // GL support - + #if defined(__WXMSW__) && !defined(NO_D3D) #define DIRECT3D_VERSION 0x0900 #include // main include file -//#include // required for font rendering + //#include // required for font rendering #include // contains debug functions - + DXDrawingPanel::DXDrawingPanel(wxWindow* parent, int _width, int _height) - : DrawingPanel(parent, _width, _height) + : DrawingPanel(parent, _width, _height) { // FIXME: implement } - + void DXDrawingPanel::DrawArea(wxWindowDC& dc) { // FIXME: implement if (!did_init) { - DrawingPanelInit(); + DrawingPanelInit(); } - + if (todraw) { } } #endif - + #ifndef NO_FFMPEG static const wxString media_err(recording::MediaRet ret) { switch (ret) { - case recording::MRET_OK: - return wxT(""); + case recording::MRET_OK: + return wxT(""); - case recording::MRET_ERR_NOMEM: - return _("Memory allocation error"); + case recording::MRET_ERR_NOMEM: + return _("Memory allocation error"); + + case recording::MRET_ERR_NOCODEC: + return _("Error initializing codec"); + + case recording::MRET_ERR_FERR: + return _("Error writing to output file"); + + case recording::MRET_ERR_FMTGUESS: + return _("Can't guess output format from file name"); - case recording::MRET_ERR_NOCODEC: - return _("Error initializing codec"); - - case recording::MRET_ERR_FERR: - return _("Error writing to output file"); - - case recording::MRET_ERR_FMTGUESS: - return _("Can't guess output format from file name"); - - default: - // case MRET_ERR_RECORDING: - // case MRET_ERR_BUFSIZE: - return _("Programming error; aborting!"); + default: + // case MRET_ERR_RECORDING: + // case MRET_ERR_BUFSIZE: + return _("Programming error; aborting!"); } } - + void GameArea::StartVidRecording(const wxString& fname) { recording::MediaRet ret; - + vid_rec.SetSampleRate(soundGetSampleRate()); if ((ret = vid_rec.Record(UTF8(fname), basic_width, basic_height, - systemColorDepth)) + systemColorDepth)) != recording::MRET_OK) wxLogError(_("Unable to begin recording to %s (%s)"), fname.mb_str(), - media_err(ret)); + media_err(ret)); else { MainFrame* mf = wxGetApp().frame; mf->cmd_enable &= ~(CMDEN_NVREC | CMDEN_NREC_ANY); @@ -2487,28 +3178,28 @@ void GameArea::StartVidRecording(const wxString& fname) mf->enable_menus(); } } - + void GameArea::StopVidRecording() { vid_rec.Stop(); MainFrame* mf = wxGetApp().frame; mf->cmd_enable &= ~CMDEN_VREC; mf->cmd_enable |= CMDEN_NVREC; - + if (!(mf->cmd_enable & (CMDEN_VREC | CMDEN_SREC))) mf->cmd_enable |= CMDEN_NREC_ANY; - + mf->enable_menus(); } - + void GameArea::StartSoundRecording(const wxString& fname) { recording::MediaRet ret; - + snd_rec.SetSampleRate(soundGetSampleRate()); if ((ret = snd_rec.Record(UTF8(fname))) != recording::MRET_OK) wxLogError(_("Unable to begin recording to %s (%s)"), fname.mb_str(), - media_err(ret)); + media_err(ret)); else { MainFrame* mf = wxGetApp().frame; mf->cmd_enable &= ~(CMDEN_NSREC | CMDEN_NREC_ANY); @@ -2516,99 +3207,99 @@ void GameArea::StartSoundRecording(const wxString& fname) mf->enable_menus(); } } - + void GameArea::StopSoundRecording() { snd_rec.Stop(); MainFrame* mf = wxGetApp().frame; mf->cmd_enable &= ~CMDEN_SREC; mf->cmd_enable |= CMDEN_NSREC; - + if (!(mf->cmd_enable & (CMDEN_VREC | CMDEN_SREC))) mf->cmd_enable |= CMDEN_NREC_ANY; - + mf->enable_menus(); } - + void GameArea::AddFrame(const uint16_t* data, int length) { recording::MediaRet ret; - + if ((ret = vid_rec.AddFrame(data, length)) != recording::MRET_OK) { wxLogError(_("Error in audio / video recording (%s); aborting"), - media_err(ret)); + media_err(ret)); vid_rec.Stop(); } - + if ((ret = snd_rec.AddFrame(data, length)) != recording::MRET_OK) { wxLogError(_("Error in audio recording (%s); aborting"), media_err(ret)); snd_rec.Stop(); } } - + void GameArea::AddFrame(const uint8_t* data) { recording::MediaRet ret; - + if ((ret = vid_rec.AddFrame(data)) != recording::MRET_OK) { wxLogError(_("Error in video recording (%s); aborting"), media_err(ret)); vid_rec.Stop(); } } #endif - + void GameArea::MouseEvent(wxMouseEvent& ev) { mouse_active_time = systemGetClock(); - + wxPoint cur_pos = wxGetMousePosition(); - + // Ignore small movements. if (!ev.Moving() || (std::abs(cur_pos.x - mouse_last_pos.x) >= 11 && std::abs(cur_pos.y - mouse_last_pos.y) >= 11)) { ShowPointer(); ShowMenuBar(); } - + mouse_last_pos = cur_pos; - + ev.Skip(); } - + void GameArea::ShowPointer() { if (!pointer_blanked || fullscreen) return; - + pointer_blanked = false; - + SetCursor(wxNullCursor); - + if (panel) panel->GetWindow()->SetCursor(wxNullCursor); } - + void GameArea::HidePointer() { if (pointer_blanked || !main_frame) return; - + // FIXME: make time configurable if ((fullscreen || (systemGetClock() - mouse_active_time) > 3000) && !(main_frame->MenusOpened() || main_frame->DialogOpened())) { pointer_blanked = true; SetCursor(wxCursor(wxCURSOR_BLANK)); - + // wxGTK requires that subwindows get the cursor as well if (panel) panel->GetWindow()->SetCursor(wxCursor(wxCURSOR_BLANK)); } } - -// We do not hide the menubar on mac, on mac it is not part of the main frame -// and the user can adjust hiding behavior herself. + + // We do not hide the menubar on mac, on mac it is not part of the main frame + // and the user can adjust hiding behavior herself. void GameArea::HideMenuBar() { #ifndef __WXMAC__ if (!main_frame || menu_bar_hidden || !gopts.hide_menu_bar) return; - + if (((systemGetClock() - mouse_active_time) > 3000) && !main_frame->MenusOpened()) { #ifdef __WXMSW__ current_hmenu = static_cast(main_frame->GetMenuBar()->GetHMenu()); @@ -2621,12 +3312,12 @@ void GameArea::HideMenuBar() } #endif } - + void GameArea::ShowMenuBar() { #ifndef __WXMAC__ if (!main_frame || !menu_bar_hidden) return; - + #ifdef __WXMSW__ if (current_hmenu != nullptr) { ::SetMenu(main_frame->GetHandle(), current_hmenu); @@ -2639,7 +3330,7 @@ void GameArea::ShowMenuBar() menu_bar_hidden = false; #endif } - + void GameArea::OnGBBorderChanged(config::Option* option) { if (game_type() == IMAGE_GB && gbSgbMode) { if (option->GetBool()) { @@ -2650,7 +3341,7 @@ void GameArea::OnGBBorderChanged(config::Option* option) { } } } - + void GameArea::UpdateLcdFilter() { if (loaded == IMAGE_GBA) gbafilter_update_colors(OPTION(kGBALCDFilter)); @@ -2659,7 +3350,7 @@ void GameArea::UpdateLcdFilter() { else gbafilter_update_colors(false); } - + void GameArea::SuspendScreenSaver() { #ifdef HAVE_XSS if (xscreensaver_suspended || !gopts.suspend_screensaver) @@ -2672,10 +3363,10 @@ void GameArea::SuspendScreenSaver() { } #endif // HAVE_XSS } - + void GameArea::UnsuspendScreenSaver() { #ifdef HAVE_XSS - // unsuspend screensaver + // unsuspend screensaver if (xscreensaver_suspended) { auto display = GetX11Display(); XScreenSaverSuspend(display, false); @@ -2683,28 +3374,226 @@ void GameArea::UnsuspendScreenSaver() { } #endif // HAVE_XSS } - + void GameArea::OnAudioRateChanged() { if (loaded == IMAGE_UNKNOWN) { return; } - + switch (game_type()) { case IMAGE_UNKNOWN: break; - + case IMAGE_GB: gbSoundSetSampleRate(GetSampleRate()); break; - + case IMAGE_GBA: soundSetSampleRate(GetSampleRate()); break; } } - + void GameArea::OnVolumeChanged(config::Option* option) { const int volume = option->GetInt(); soundSetVolume((float)volume / 100.0); systemScreenMessage(wxString::Format(_("Volume: %d %%"), volume)); } + +#ifdef __WXMAC__ +#ifndef NO_METAL +MetalDrawingPanel::MetalDrawingPanel(wxWindow* parent, int _width, int _height) + : DrawingPanel(parent, _width, _height) +{ + memset(delta, 0xff, sizeof(delta)); + + // wxImage is 24-bit RGB, so 24-bit is preferred. Filters require + // 16 or 32, though + if (OPTION(kDispFilter) == config::Filter::kNone && + OPTION(kDispIFB) == config::Interframe::kNone) { + // changing from 32 to 24 does not require regenerating color tables + systemColorDepth = (OPTION(kBitDepth) + 1) << 3; + } + + DrawingPanelInit(); +} + +void MetalDrawingPanel::DrawArea(uint8_t** data) +{ + // double-buffer buffer: + // if filtering, this is filter output, retained for redraws + // if not filtering, we still retain current image for redraws + int outbpp = systemColorDepth >> 3; + int outrb = out_8 ? 2 : out_24 ? 0 : 4; + int outstride = std::ceil(width * outbpp * scale) + outrb; + + // FIXME: filters race condition? + const int max_threads = 1; + + if (!pixbuf2) { + int allocstride = outstride, alloch = height; + + // gb may write borders, so allocate enough for them + if (width == GameArea::GBWidth && height == GameArea::GBHeight) { + allocstride = std::ceil(GameArea::SGBWidth * outbpp * scale) + outrb; + alloch = GameArea::SGBHeight; + } + + pixbuf2 = (uint8_t*)calloc(allocstride, std::ceil((alloch + 2) * scale)); + } + + if (OPTION(kDispFilter) == config::Filter::kNone) { + todraw = *data; + // *data is assigned below, after old buf has been processed + pixbuf1 = pixbuf2; + pixbuf2 = todraw; + } else + todraw = pixbuf2; + + // First, apply filters, if applicable, in parallel, if enabled + // FIXME: && (gopts.ifb != FF_MOTION_BLUR || !renderer_can_motion_blur) + if (OPTION(kDispFilter) != config::Filter::kNone || + OPTION(kDispIFB) != config::Interframe::kNone) { + if (nthreads != max_threads) { + if (nthreads) { + if (nthreads > 1) + for (int i = 0; i < nthreads; i++) { + threads[i].lock_.Lock(); + threads[i].src_ = NULL; + threads[i].sig_.Signal(); + threads[i].lock_.Unlock(); + threads[i].Wait(); + } + + delete[] threads; + } + + nthreads = max_threads; + threads = new FilterThread[nthreads]; + // first time around, no threading in order to avoid + // static initializer conflicts + threads[0].threadno_ = 0; + threads[0].nthreads_ = 1; + threads[0].width_ = width; + threads[0].height_ = height; + threads[0].scale_ = scale; + threads[0].src_ = *data; + threads[0].dst_ = todraw; + threads[0].delta_ = delta; + threads[0].rpi_ = rpi_; + threads[0].Entry(); + + // go ahead and start the threads up, though + if (nthreads > 1) { + for (int i = 0; i < nthreads; i++) { + threads[i].threadno_ = i; + threads[i].nthreads_ = nthreads; + threads[i].width_ = width; + threads[i].height_ = height; + threads[i].scale_ = scale; + threads[i].dst_ = todraw; + threads[i].delta_ = delta; + threads[i].rpi_ = rpi_; + threads[i].done_ = &filt_done; + threads[i].lock_.Lock(); + threads[i].Create(); + threads[i].Run(); + } + } + } else if (nthreads == 1) { + threads[0].threadno_ = 0; + threads[0].nthreads_ = 1; + threads[0].width_ = width; + threads[0].height_ = height; + threads[0].scale_ = scale; + threads[0].src_ = *data; + threads[0].dst_ = todraw; + threads[0].delta_ = delta; + threads[0].rpi_ = rpi_; + threads[0].Entry(); + } else { + for (int i = 0; i < nthreads; i++) { + threads[i].lock_.Lock(); + threads[i].src_ = *data; + threads[i].sig_.Signal(); + threads[i].lock_.Unlock(); + } + + for (int i = 0; i < nthreads; i++) + filt_done.Wait(); + } + } + + // swap buffers now that src has been processed + if (OPTION(kDispFilter) == config::Filter::kNone) { + *data = pixbuf1; + } + + // draw OSD text old-style (directly into output buffer), if needed + // new style flickers too much, so we'll stick to this for now + if (wxGetApp().frame->IsFullScreen() || !OPTION(kPrefDisableStatus)) { + GameArea* panel = wxGetApp().frame->GetPanel(); + + if (panel->osdstat.size()) + drawText(todraw + outstride * (systemColorDepth != 24), outstride, + 10, 20, UTF8(panel->osdstat), OPTION(kPrefShowSpeedTransparent)); + + if (!panel->osdtext.empty()) { + if (systemGetClock() - panel->osdtime < OSD_TIME) { + wxString message = panel->osdtext; + int linelen = std::ceil(width * scale - 20) / 8; + int nlines = (message.size() + linelen - 1) / linelen; + int cury = height - 14 - nlines * 10; + char* buf = strdup(UTF8(message)); + char* ptr = buf; + + while (nlines > 1) { + char lchar = ptr[linelen]; + ptr[linelen] = 0; + drawText(todraw + outstride * (systemColorDepth != 24), + outstride, 10, cury, ptr, + OPTION(kPrefShowSpeedTransparent)); + cury += 10; + nlines--; + ptr += linelen; + *ptr = lchar; + } + + drawText(todraw + outstride * (systemColorDepth != 24), + outstride, 10, cury, ptr, + OPTION(kPrefShowSpeedTransparent)); + + free(buf); + buf = NULL; + } else + panel->osdtext.clear(); + } + } + + // Draw the current frame + DrawArea(); +} + +void MetalDrawingPanel::PaintEv(wxPaintEvent& ev) +{ + // FIXME: implement + if (!did_init) { + DrawingPanelInit(); + } + + (void)ev; // unused params + + if (!todraw) { + // since this is set for custom background, not drawing anything + // will cause garbage to be displayed, so draw a black area + draw_black_background(GetWindow()); + return; + } + + if (todraw) { + DrawArea(); + } +} +#endif +#endif + diff --git a/src/wx/sys.cpp b/src/wx/sys.cpp index c1993047..23ae664a 100644 --- a/src/wx/sys.cpp +++ b/src/wx/sys.cpp @@ -4,7 +4,12 @@ #include #include #include + +#ifdef ENABLE_SDL3 +#include +#else #include +#endif #include "core/base/image_util.h" #include "core/gb/gbGlobals.h" @@ -24,6 +29,7 @@ int systemRedShift; int systemGreenShift; int systemBlueShift; int systemColorDepth; +uint8_t systemColorMap8[0x10000]; uint16_t systemColorMap16[0x10000]; uint32_t systemColorMap32[0x10000]; #define gs555(x) (x | (x << 5) | (x << 10)) diff --git a/src/wx/viewers.cpp b/src/wx/viewers.cpp index e1365694..af46e9b8 100644 --- a/src/wx/viewers.cpp +++ b/src/wx/viewers.cpp @@ -331,7 +331,7 @@ public: for (int i = 0; i < dis->nlines; i++) { dis->addrs.push_back(addr); - addr += gbDis(buf, addr); + addr += gbDis(buf, sizeof(buf), addr); dis->strings.push_back(wxString(buf, wxConvLibc)); } diff --git a/src/wx/widgets/sdl-poller.cpp b/src/wx/widgets/sdl-poller.cpp index 03efc566..3ba23437 100644 --- a/src/wx/widgets/sdl-poller.cpp +++ b/src/wx/widgets/sdl-poller.cpp @@ -5,7 +5,11 @@ #include #include +#ifdef ENABLE_SDL3 +#include +#else #include +#endif #include "core/base/check.h" #include "wx/config/option-id.h" @@ -107,7 +111,11 @@ private: SDL_JoystickID joystick_id_; // The SDL GameController instance. +#ifndef ENABLE_SDL3 SDL_GameController* game_controller_ = nullptr; +#else + SDL_Gamepad* game_controller_ = nullptr; +#endif // The SDL Joystick instance. SDL_Joystick* sdl_joystick_ = nullptr; @@ -126,6 +134,7 @@ private: }; JoyState::JoyState(bool enable_game_controller, int sdl_index) : wx_joystick_(sdl_index) { +#ifndef ENABLE_SDL3 if (enable_game_controller && SDL_IsGameController(sdl_index)) { game_controller_ = SDL_GameControllerOpen(sdl_index); if (game_controller_) @@ -133,11 +142,30 @@ JoyState::JoyState(bool enable_game_controller, int sdl_index) : wx_joystick_(sd } else { sdl_joystick_ = SDL_JoystickOpen(sdl_index); } +#else + int nrgamepads = 0; + int nrjoysticks = 0; + SDL_JoystickID *gamepads = SDL_GetGamepads(&nrgamepads); + SDL_JoystickID *joysticks = SDL_GetJoysticks(&nrjoysticks); + if (enable_game_controller && SDL_IsGamepad(gamepads[sdl_index])) { + game_controller_ = SDL_OpenGamepad(gamepads[sdl_index]); + if (game_controller_) + { + sdl_joystick_ = SDL_GetGamepadJoystick(game_controller_); + } + } else { + sdl_joystick_ = SDL_OpenJoystick(joysticks[sdl_index]); + } +#endif if (!sdl_joystick_) return; +#ifndef ENABLE_SDL3 joystick_id_ = SDL_JoystickInstanceID(sdl_joystick_); +#else + joystick_id_ = SDL_GetJoystickID(sdl_joystick_); +#endif } JoyState::~JoyState() { @@ -145,11 +173,19 @@ JoyState::~JoyState() { if (!sdl_joystick_) return; +#ifndef ENABLE_SDL3 if (game_controller_) { SDL_GameControllerClose(game_controller_); } else { SDL_JoystickClose(sdl_joystick_); } +#else + if (game_controller_) { + SDL_CloseGamepad(game_controller_); + } else { + SDL_CloseJoystick(sdl_joystick_); + } +#endif } JoyState::JoyState(JoyState&& other) : wx_joystick_(other.wx_joystick_) { @@ -280,7 +316,20 @@ std::vector JoyState::ProcessHatEvent(const uint8_t index, void JoyState::SetRumble(bool activate_rumble) { rumbling_ = activate_rumble; -#if SDL_VERSION_ATLEAST(2, 0, 9) +#ifdef ENABLE_SDL3 + if (game_controller_ == NULL) + return; + + if (rumbling_) { + SDL_RumbleGamepad(game_controller_, 0xFFFF, 0xFFFF, 300); + if (!IsRunning()) { + Start(150); + } + } else { + SDL_RumbleGamepad(game_controller_, 0, 0, 0); + Stop(); + } +#elif SDL_VERSION_ATLEAST(2, 0, 9) if (!game_controller_) return; @@ -309,16 +358,30 @@ SdlPoller::SdlPoller(EventHandlerProvider* const handler_provider) VBAM_CHECK(handler_provider); wxTimer::Start(50); +#ifndef ENABLE_SDL3 SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS); SDL_GameControllerEventState(SDL_ENABLE); SDL_JoystickEventState(SDL_ENABLE); +#else + SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_EVENTS); + SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_EVENTS); + SDL_SetGamepadEventsEnabled(true); + SDL_SetJoystickEventsEnabled(true); +#endif } SdlPoller::~SdlPoller() { wxTimer::Stop(); +#ifndef ENABLE_SDL3 SDL_QuitSubSystem(SDL_INIT_EVENTS); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); +#else + SDL_QuitSubSystem(SDL_INIT_EVENTS); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + SDL_QuitSubSystem(SDL_INIT_GAMEPAD); +#endif + SDL_Quit(); } @@ -348,6 +411,7 @@ void SdlPoller::Notify() { std::vector event_data; JoyState* joy_state = nullptr; switch (sdl_event.type) { +#ifndef ENABLE_SDL3 case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONUP: joy_state = FindJoyState(sdl_event.cbutton.which); @@ -355,41 +419,86 @@ void SdlPoller::Notify() { event_data = joy_state->ProcessButtonEvent(sdl_event.cbutton.button, sdl_event.cbutton.state); } +#else + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + case SDL_EVENT_GAMEPAD_BUTTON_UP: + joy_state = FindJoyState(sdl_event.gbutton.which); + if (joy_state) { + event_data = joy_state->ProcessButtonEvent(sdl_event.gbutton.button, + sdl_event.gbutton.down); + } +#endif break; +#ifndef ENABLE_SDL3 case SDL_CONTROLLERAXISMOTION: joy_state = FindJoyState(sdl_event.caxis.which); if (joy_state) { event_data = joy_state->ProcessAxisEvent( sdl_event.caxis.axis, AxisValueToStatus(sdl_event.caxis.value)); } +#else + case SDL_EVENT_GAMEPAD_AXIS_MOTION: + joy_state = FindJoyState(sdl_event.gaxis.which); + if (joy_state) { + event_data = joy_state->ProcessAxisEvent( + sdl_event.gaxis.axis, AxisValueToStatus(sdl_event.gaxis.value)); + } +#endif break; +#ifndef ENABLE_SDL3 case SDL_CONTROLLERDEVICEADDED: case SDL_CONTROLLERDEVICEREMOVED: +#else + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: +#endif // Do nothing. This will be handled with JOYDEVICEADDED and // JOYDEVICEREMOVED events. break; // Joystick events for non-GameControllers. +#ifndef ENABLE_SDL3 case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: +#else + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + case SDL_EVENT_JOYSTICK_BUTTON_UP: +#endif joy_state = FindJoyState(sdl_event.jbutton.which); if (joy_state && !joy_state->is_game_controller()) { +#ifndef ENABLE_SDL3 + event_data = joy_state->ProcessButtonEvent(sdl_event.cbutton.button, + sdl_event.cbutton.state); +#else event_data = joy_state->ProcessButtonEvent(sdl_event.jbutton.button, - sdl_event.jbutton.state); + sdl_event.jbutton.down); +#endif } break; +#ifndef ENABLE_SDL3 case SDL_JOYAXISMOTION: +#else + case SDL_EVENT_JOYSTICK_AXIS_MOTION: +#endif joy_state = FindJoyState(sdl_event.jaxis.which); if (joy_state && !joy_state->is_game_controller()) { event_data = joy_state->ProcessAxisEvent( +#ifndef ENABLE_SDL3 + sdl_event.caxis.axis, AxisValueToStatus(sdl_event.caxis.value)); +#else sdl_event.jaxis.axis, AxisValueToStatus(sdl_event.jaxis.value)); +#endif } break; +#ifndef ENABLE_SDL3 case SDL_JOYHATMOTION: +#else + case SDL_EVENT_JOYSTICK_HAT_MOTION: +#endif joy_state = FindJoyState(sdl_event.jhat.which); if (joy_state && !joy_state->is_game_controller()) { event_data = @@ -397,12 +506,20 @@ void SdlPoller::Notify() { } break; +#ifndef ENABLE_SDL3 case SDL_JOYDEVICEADDED: +#else + case SDL_EVENT_JOYSTICK_ADDED: +#endif // Always remap all controllers. RemapControllers(); break; +#ifndef ENABLE_SDL3 case SDL_JOYDEVICEREMOVED: +#else + case SDL_EVENT_JOYSTICK_REMOVED: +#endif joystick_states_.erase(sdl_event.jdevice.which); break; } @@ -426,11 +543,21 @@ JoyState* SdlPoller::FindJoyState(const SDL_JoystickID& joy_id) { } void SdlPoller::RemapControllers() { +#ifdef ENABLE_SDL3 + int total_joysticks = 0; + + SDL_GetJoysticks(&total_joysticks); +#endif + // Clear the current joystick states. joystick_states_.clear(); // Reconnect all controllers. +#ifndef ENABLE_SDL3 for (int i = 0; i < SDL_NumJoysticks(); ++i) { +#else + for (int i = 0; i < total_joysticks; ++i) { +#endif JoyState joy_state(enable_game_controller_, i); if (joy_state.IsValid()) { joystick_states_.insert({joy_state.joystick_id(), std::move(joy_state)}); diff --git a/src/wx/widgets/sdl-poller.h b/src/wx/widgets/sdl-poller.h index 831dbd16..b4bc2c03 100644 --- a/src/wx/widgets/sdl-poller.h +++ b/src/wx/widgets/sdl-poller.h @@ -5,7 +5,11 @@ #include +#ifndef ENABLE_SDL3 #include +#else +#include +#endif #include "wx/config/option-observer.h" #include "wx/widgets/event-handler-provider.h" diff --git a/src/wx/wxvbam.cpp b/src/wx/wxvbam.cpp index 3256db0b..2223f020 100644 --- a/src/wx/wxvbam.cpp +++ b/src/wx/wxvbam.cpp @@ -141,8 +141,32 @@ int main(int argc, char** argv) { wxString xdg_session_type = wxGetenv("XDG_SESSION_TYPE"); wxString wayland_display = wxGetenv("WAYLAND_DISPLAY"); - if (xdg_session_type == "wayland" || wayland_display.Contains("wayland")) + if (xdg_session_type == "wayland" || wayland_display.Contains("wayland")) { gdk_set_allowed_backends("x11,*"); + + if (wxGetenv("GDK_BACKEND") == NULL) { + wxSetEnv("GDK_BACKEND", "x11"); + } + } +#else +#ifdef __WXGTK__ + wxString xdg_session_type = wxGetenv("XDG_SESSION_TYPE"); + wxString wayland_display = wxGetenv("WAYLAND_DISPLAY"); + + if (xdg_session_type == "wayland" || wayland_display.Contains("wayland")) { + if (wxGetenv("GDK_BACKEND") == NULL) { +#ifdef ENABLE_SDL3 + wxSetEnv("GDK_BACKEND", "wayland"); +#else + wxSetEnv("GDK_BACKEND", "x11"); +#endif + } + } else { + if (wxGetenv("GDK_BACKEND") == NULL) { + wxSetEnv("GDK_BACKEND", "x11"); + } + } +#endif #endif // This will be freed on wxEntry exit. diff --git a/src/wx/xrc/DisplayConfig.xrc b/src/wx/xrc/DisplayConfig.xrc index 575248b5..2125836c 100644 --- a/src/wx/xrc/DisplayConfig.xrc +++ b/src/wx/xrc/DisplayConfig.xrc @@ -43,6 +43,13 @@ wxALL 5 + + + + + wxALL + 5 + @@ -50,6 +57,13 @@ wxALL 5 + + + + + wxALL + 5 + @@ -57,6 +71,8 @@ wxALL 5 + + wxVERTICAL @@ -141,6 +157,19 @@ wxALL|wxEXPAND 5 + + + + + wxRIGHT|wxALL|wxALIGN_CENTRE_VERTICAL + 5 + + + + + wxALL|wxEXPAND + 5 + 2 1 @@ -151,6 +180,35 @@ + + + + wxVERTICAL + + wxALL|wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL + 5 + + + + + + + + 8 + 16 + 24 + 32 + + + wxALL|wxEXPAND + 5 + + 2 + 1 + + + + diff --git a/src/wx/xrc/SoundConfig.xrc b/src/wx/xrc/SoundConfig.xrc index 6100bf71..7a62054d 100644 --- a/src/wx/xrc/SoundConfig.xrc +++ b/src/wx/xrc/SoundConfig.xrc @@ -107,6 +107,13 @@ wxALL|wxEXPAND 5 + + + + + wxALL|wxEXPAND + 5 + diff --git a/third_party/include/stb/stb_image_write.h b/third_party/include/stb/stb_image_write.h index cffd473c..afe675b2 100644 --- a/third_party/include/stb/stb_image_write.h +++ b/third_party/include/stb/stb_image_write.h @@ -735,7 +735,7 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f #ifdef __STDC_WANT_SECURE_LIB__ len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else - len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + len = snprintf(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #endif s->func(s->context, buffer, len);