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