diff --git a/.gitignore b/.gitignore index 999fdd67311..fedbc461f46 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,10 @@ CMakeCache.txt CMakeFiles CMakeSettings.json +CMakeUserPresets.json +compile_commands.json +/.cache +/.vscode cmake_install.cmake hle-bios.bin version.c diff --git a/CHANGES b/CHANGES index d850f2002ed..13d7b0f4c3b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,16 +1,18 @@ 0.11.0: (Future) Features: + - New option to lock the maximum frame size - Scripting: New `input` API for getting raw keyboard/mouse/controller state - Scripting: New `storage` API for saving data for a script, e.g. settings - Scripting: Debugger integration to allow for breakpoints and watchpoints - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 - Debugger: Add range watchpoints Emulation fixes: + - GB Audio: Fix audio envelope timing resetting too often (fixes mgba.io/i/3164) - GB I/O: Fix STAT writing IRQ trigger conditions (fixes mgba.io/i/2501) - GB Serialize: Add missing Pocket Cam state to savestates - GB Video: Implement DMG-style sprite ordering - GBA: Unhandled bkpt should be treated as an undefined exception - - GBA GPIO: Fix tilt scale and orientation (fixes mgba.io/i/2703) + - GBA GPIO: Fix gyro read-out start (fixes mgba.io/i/3141) - GBA I/O: Fix HALTCNT access behavior (fixes mgba.io/i/2309) - GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes mgba.io/i/3110) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) @@ -18,6 +20,7 @@ Other fixes: - Core: Fix inconsistencies with setting game-specific overrides (fixes mgba.io/i/2963) - Debugger: Fix writing to specific segment in command-line debugger - GB: Fix uninitialized save data when loading undersized temporary saves + - GBA Memory: Let raw access read high MMIO addresses - Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560) - Updater: Fix updating appimage across filesystems Misc: @@ -25,6 +28,7 @@ Misc: - GB: Prevent incompatible BIOSes from being used on differing models - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs + - GBA Audio: Remove broken XQ audio pending rewrite - mGUI: Enable auto-softpatching (closes mgba.io/i/2899) - mGUI: Persist fast forwarding after closing menu (fixes mgba.io/i/2414) - Qt: Handle multiple save game files for disparate games separately (fixes mgba.io/i/2887) diff --git a/CMakeLists.txt b/CMakeLists.txt index a1e730e0430..2d69b000ba2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,18 +39,18 @@ if(NOT MSVC) # TODO: Remove this once mScript KV pairs support const correctness set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=incompatible-pointer-types") endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Woverloaded-virtual") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Woverloaded-virtual -Werror=reorder") else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-") endif() if(NOT LIBMGBA_ONLY) - set(USE_DEBUGGERS ON CACHE BOOL "Whether or not to enable the debugging infrastructure") + set(ENABLE_DEBUGGERS ON CACHE BOOL "Whether or not to enable the debugging infrastructure") if (NOT WIN32) set(USE_EDITLINE ON CACHE BOOL "Whether or not to enable the CLI-mode debugger") endif() - set(USE_GDB_STUB ON CACHE BOOL "Whether or not to enable the GDB stub ARM debugger") + set(ENABLE_GDB_STUB ON CACHE BOOL "Whether or not to enable the GDB stub ARM debugger") set(USE_FFMPEG ON CACHE BOOL "Whether or not to enable FFmpeg support") set(USE_ZLIB ON CACHE BOOL "Whether or not to enable zlib support") set(USE_MINIZIP ON CACHE BOOL "Whether or not to enable external minizip support") @@ -68,9 +68,6 @@ if(NOT LIBMGBA_ONLY) set(BUILD_QT ON CACHE BOOL "Build Qt frontend") set(BUILD_SDL ON CACHE BOOL "Build SDL frontend") set(BUILD_LIBRETRO OFF CACHE BOOL "Build libretro core") - if(APPLE) - set(BUILD_OPENEMU OFF CACHE BOOL "Build OpenEmu core") - endif() set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool") set(BUILD_TEST OFF CACHE BOOL "Build testing harness") set(BUILD_SUITE OFF CACHE BOOL "Build test suite") @@ -80,7 +77,7 @@ if(NOT LIBMGBA_ONLY) set(BUILD_PYTHON OFF CACHE BOOL "Build Python bindings") set(BUILD_STATIC OFF CACHE BOOL "Build a static library") set(BUILD_SHARED ON CACHE BOOL "Build a shared library") - set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)") + set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro core)") set(BUILD_GL ON CACHE BOOL "Build with OpenGL") set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2") set(BUILD_GLES3 ON CACHE BOOL "Build with OpenGL|ES 3") @@ -144,11 +141,6 @@ if (BUILD_LIBRETRO) mark_as_advanced(LIBRETRO_LIBDIR) endif() -if (BUILD_OPENEMU) - set(OE_LIBDIR "${LIBDIR}" CACHE PATH "Installed library directory (OpenEmu)") - mark_as_advanced(OE_LIBDIR) -endif() - if (DISTBUILD) set(EXTRA_LICENSES "" CACHE FILEPATH "Extra licenses to include in distribution packaages") mark_as_advanced(EXTRA_LICENSES) @@ -217,13 +209,15 @@ endif() # Platform support if(WIN32) set(WIN32_VERSION "${LIB_VERSION_MAJOR},${LIB_VERSION_MINOR},${LIB_VERSION_PATCH}") - add_definitions(-D_WIN32_WINNT=0x0600) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) if(MSVC) add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) add_definitions(-D_UNICODE -DUNICODE) else() add_definitions(-D_GNU_SOURCE) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + add_compile_definitions(_FILE_OFFSET_BITS=64) + endif() endif() list(APPEND OS_LIB ws2_32 shlwapi) list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/vfs-w32.c) @@ -299,7 +293,7 @@ endif() if(DEFINED 3DS OR DEFINED PSP2 OR DEFINED WII OR DEFINED SWITCH) set(IS_EMBEDDED ON) - set(USE_DEBUGGERS OFF) + set(ENABLE_DEBUGGERS OFF) set(USE_SQLITE3 OFF) set(USE_DISCORD_RPC OFF) set(USE_LIBZIP OFF CACHE BOOL "") @@ -311,12 +305,12 @@ if(DEFINED SWITCH) endif() if(NOT M_CORE_GBA) - set(USE_GDB_STUB OFF) + set(ENABLE_GDB_STUB OFF) endif() -if(NOT USE_DEBUGGERS) +if(NOT ENABLE_DEBUGGERS) set(USE_EDITLINE OFF) - set(USE_GDB_STUB OFF) + set(ENABLE_GDB_STUB OFF) endif() if(WII) @@ -343,18 +337,14 @@ find_function(popcount32) find_function(futimens) find_function(futimes) +find_function(localtime_r) find_function(realpath) if(ANDROID AND ANDROID_NDK_MAJOR GREATER 13) - find_function(localtime_r) - set(HAVE_STRTOF_L ON) -elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - find_function(localtime_r) + list(APPEND FUNCTION_DEFINES HAVE_STRTOF_L) +elseif(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") # The strtof_l on Linux not actually exposed nor actually strtof_l - set(HAVE_STRTOF_L OFF) -else() - find_function(localtime_r) find_function(strtof_l) endif() @@ -491,7 +481,7 @@ if(NOT BUILD_GLES2 AND NOT BUILD_GLES3 AND NOT LIBMGBA_ONLY) endif() if(DISABLE_DEPS) - set(USE_GDB_STUB OFF) + set(ENABLE_GDB_STUB OFF) set(USE_DISCORD_RPC OFF) set(USE_JSON_C OFF) set(USE_SQLITE3 OFF) @@ -543,8 +533,8 @@ else() set(DEBUGGER_LIB "") endif() -if(USE_GDB_STUB) - list(APPEND FEATURES GDB_STUB) +if(ENABLE_GDB_STUB) + list(APPEND ENABLES GDB_STUB) endif() source_group("Debugger" FILES ${DEBUGGER_SRC}) @@ -588,8 +578,6 @@ if(USE_FFMPEG) endif() endif() -list(APPEND THIRD_PARTY_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/blip_buf/blip_buf.c") - if(WANT_ZLIB AND NOT USE_ZLIB) set(SKIP_INSTALL_ALL ON) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/zlib zlib EXCLUDE_FROM_ALL) @@ -853,10 +841,10 @@ if(M_CORE_GBA) list(APPEND TEST_SRC ${ARM_TEST_SRC} ${GBA_TEST_SRC}) endif() -if(USE_DEBUGGERS) +if(ENABLE_DEBUGGERS) list(APPEND FEATURE_SRC ${DEBUGGER_SRC}) list(APPEND TEST_SRC ${DEBUGGER_TEST_SRC}) - list(APPEND FEATURES DEBUGGERS) + list(APPEND ENABLES DEBUGGERS) endif() if(ENABLE_SCRIPTING) @@ -1005,22 +993,6 @@ if(BUILD_LIBRETRO) endif() endif() -if(BUILD_OPENEMU) - find_library(FOUNDATION Foundation) - find_library(OPENEMUBASE OpenEmuBase) - file(GLOB OE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/openemu/*.m) - add_library(${BINARY_NAME}-openemu MODULE ${CORE_SRC} ${OS_SRC}) - set_target_properties(${BINARY_NAME}-openemu PROPERTIES - MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/openemu/Info.plist.in - BUNDLE TRUE - BUNDLE_EXTENSION oecoreplugin - OUTPUT_NAME ${PROJECT_NAME} - COMPILE_OPTIONS "-fobjc-arc" - COMPILE_DEFINITIONS "DISABLE_THREADING;MGBA_STANDALONE;${OS_DEFINES};${FUNCTION_DEFINES};MINIMAL_CORE=1") - target_link_libraries(${BINARY_NAME}-openemu ${OS_LIB} ${FOUNDATION} ${OPENEMUBASE}) - install(TARGETS ${BINARY_NAME}-openemu LIBRARY DESTINATION ${OE_LIBDIR} COMPONENT ${BINARY_NAME}.oecoreplugin NAMELINK_SKIP) -endif() - if(BUILD_QT AND (WIN32 OR APPLE OR CMAKE_SYSTEM_NAME STREQUAL "Linux")) set(BUILD_UPDATER ON) endif() @@ -1087,7 +1059,7 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/mgba-util DESTINATION ${CM install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/mgba/flags.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mgba COMPONENT ${BINARY_NAME}-dev) # Packaging -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/licenses/blip_buf.txt DESTINATION ${CMAKE_INSTALL_DOCDIR}/licenses COMPONENT ${BINARY_NAME}) +install(FILES DESTINATION ${CMAKE_INSTALL_DOCDIR}/licenses COMPONENT ${BINARY_NAME}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/licenses/inih.txt DESTINATION ${CMAKE_INSTALL_DOCDIR}/licenses COMPONENT ${BINARY_NAME}) if(USE_DISCORD_RPC) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/licenses/discord-rpc.txt DESTINATION ${CMAKE_INSTALL_DOCDIR}/licenses COMPONENT ${BINARY_NAME}) @@ -1314,11 +1286,11 @@ if(NOT QUIET AND NOT LIBMGBA_ONLY) message(STATUS " Game Boy Advance: ${M_CORE_GBA}") message(STATUS " Game Boy: ${M_CORE_GB}") message(STATUS "Features:") - message(STATUS " Debuggers: ${USE_DEBUGGERS}") + message(STATUS " Debuggers: ${ENABLE_DEBUGGERS}") if(NOT WIN32) message(STATUS " CLI debugger: ${USE_EDITLINE}") endif() - message(STATUS " GDB stub: ${USE_GDB_STUB}") + message(STATUS " GDB stub: ${ENABLE_GDB_STUB}") message(STATUS " GIF/Video recording: ${USE_FFMPEG}") message(STATUS " Screenshot/advanced savestate support: ${USE_PNG}") message(STATUS " ZIP support: ${SUMMARY_ZIP}") @@ -1349,9 +1321,6 @@ if(NOT QUIET AND NOT LIBMGBA_ONLY) message(STATUS " ROM tester: ${BUILD_ROM_TEST}") message(STATUS "Cores:") message(STATUS " Libretro core: ${BUILD_LIBRETRO}") - if(APPLE) - message(STATUS " OpenEmu core: ${BUILD_OPENEMU}") - endif() message(STATUS "Libraries:") message(STATUS " Static: ${BUILD_STATIC}") message(STATUS " Shared: ${BUILD_SHARED}") diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000000..584b1987dae --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,21 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "clang", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "gcc", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 78b69f255ed..d937fa0c9b7 100644 --- a/README.md +++ b/README.md @@ -260,7 +260,6 @@ mGBA is Copyright © 2013 – 2023 Jeffrey Pfau. It is distributed under the [Mo mGBA contains the following third-party libraries: - [inih](https://github.com/benhoyt/inih), which is copyright © 2009 – 2020 Ben Hoyt and used under a BSD 3-clause license. -- [blip-buf](https://code.google.com/archive/p/blip-buf), which is copyright © 2003 – 2009 Shay Green and used under a Lesser GNU Public License. - [LZMA SDK](http://www.7-zip.org/sdk.html), which is public domain. - [MurmurHash3](https://github.com/aappleby/smhasher) implementation by Austin Appleby, which is public domain. - [getopt for MSVC](https://github.com/skandhurkat/Getopt-for-Visual-Studio/), which is public domain. diff --git a/README_DE.md b/README_DE.md index 9b653c5d241..9a4066a0f02 100644 --- a/README_DE.md +++ b/README_DE.md @@ -247,7 +247,6 @@ Copyright für mGBA © 2013 – 2021 Jeffrey Pfau. mGBA wird unter der [Mozilla mGBA beinhaltet die folgenden Bibliotheken von Drittanbietern: - [inih](https://github.com/benhoyt/inih), Copyright © 2009 - 2020 Ben Hoyt, verwendet unter einer BSD 3-clause-Lizenz. -- [blip-buf](https://code.google.com/archive/b/blip-buf), Copyright © 2003 - 2009 Shay Green, verwendet unter einer Lesser GNU Public License. - [LZMA SDK](http://www.7-zip.org/sdk.html), Public Domain. - [MurmurHash3](https://github.com/aappleby/smhasher), Implementierung von Austin Appleby, Public Domain. - [getopt fot MSVC](https://github.com/skandhurkat/Getopt-for-Visual-Studio/), Public Domain. diff --git a/README_ES.md b/README_ES.md index f174e114c88..1a6d0b79784 100644 --- a/README_ES.md +++ b/README_ES.md @@ -247,7 +247,6 @@ mGBA es Copyright © 2013 – 2021 Jeffrey Pfau. Es distribuído bajo la [licenc mGBA contiene las siguientes bibliotecas de terceros: - [inih](https://github.com/benhoyt/inih), que es copyright © 2009 - 2020 Ben Hoyt y se utiliza bajo licencia de la cláusula 3 de BSD. -- [blip-buf](https://code.google.com/archive/p/blip-buf), que es copyright © 2003 - 2009 Shay Green y se usa bajo LGPL. - [LZMA SDK](http://www.7-zip.org/sdk.html), la cual está en el dominio público. - [MurmurHash3](https://github.com/aappleby/smhasher), implementación por Austin Appleby, la cual está en el dominio público. - [getopt for MSVC](https://github.com/skandhurkat/Getopt-for-Visual-Studio/), la cual está en el dominio público. diff --git a/README_ZH_CN.md b/README_ZH_CN.md index 757f1d0c6dc..ab725694c6e 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -245,7 +245,6 @@ mGBA 版权 © 2013 – 2020 Jeffrey Pfau。基于 [Mozilla 公共许可证版 mGBA 包含以下第三方库: - [inih](https://github.com/benhoyt/inih):版权 © 2009 – 2020 Ben Hoyt,基于 BSD 3-clause 许可证使用。 -- [blip-buf](https://code.google.com/archive/p/blip-buf):版权 © 2003 – 2009 Shay Green,基于 Lesser GNU 公共许可证使用。 - [LZMA SDK](http://www.7-zip.org/sdk.html):属公有领域使用。 - [MurmurHash3](https://github.com/aappleby/smhasher):由 Austin Appleby 实施,属公有领域使用。 - [getopt for MSVC](https://github.com/skandhurkat/Getopt-for-Visual-Studio/):属公有领域使用。 diff --git a/cinema/gb/samesuite/apu/channel_1/nrx2_speed_change/xbaseline_0000.png b/cinema/gb/samesuite/apu/channel_1/nrx2_speed_change/xbaseline_0000.png index 0032b919f1a..e541e5c0ba5 100644 Binary files a/cinema/gb/samesuite/apu/channel_1/nrx2_speed_change/xbaseline_0000.png and b/cinema/gb/samesuite/apu/channel_1/nrx2_speed_change/xbaseline_0000.png differ diff --git a/cinema/gb/samesuite/apu/channel_2/nrx2_speed_change/xbaseline_0000.png b/cinema/gb/samesuite/apu/channel_2/nrx2_speed_change/xbaseline_0000.png index 2804b6f3a5f..5173a31fdeb 100644 Binary files a/cinema/gb/samesuite/apu/channel_2/nrx2_speed_change/xbaseline_0000.png and b/cinema/gb/samesuite/apu/channel_2/nrx2_speed_change/xbaseline_0000.png differ diff --git a/include/mgba-util/audio-buffer.h b/include/mgba-util/audio-buffer.h new file mode 100644 index 00000000000..b6d7608223a --- /dev/null +++ b/include/mgba-util/audio-buffer.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2013-2024 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef M_AUDIO_BUFFER_H +#define M_AUDIO_BUFFER_H + +#include + +CXX_GUARD_START + +#include + +struct mAudioBuffer { + struct mCircleBuffer data; + unsigned channels; +}; + +void mAudioBufferInit(struct mAudioBuffer* buffer, size_t capacity, unsigned channels); +void mAudioBufferDeinit(struct mAudioBuffer* buffer); + +size_t mAudioBufferAvailable(const struct mAudioBuffer* buffer); +size_t mAudioBufferCapacity(const struct mAudioBuffer* buffer); + +void mAudioBufferClear(struct mAudioBuffer* buffer); +int16_t mAudioBufferPeek(const struct mAudioBuffer* buffer, unsigned channel, size_t offset); +size_t mAudioBufferDump(const struct mAudioBuffer* buffer, int16_t* samples, size_t count, size_t offset); +size_t mAudioBufferRead(struct mAudioBuffer* buffer, int16_t* samples, size_t count); +size_t mAudioBufferWrite(struct mAudioBuffer* buffer, const int16_t* samples, size_t count); + +CXX_GUARD_END + +#endif diff --git a/include/mgba-util/audio-resampler.h b/include/mgba-util/audio-resampler.h new file mode 100644 index 00000000000..e1ec3d84c60 --- /dev/null +++ b/include/mgba-util/audio-resampler.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2013-2024 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef M_AUDIO_RESAMPLER_H +#define M_AUDIO_RESAMPLER_H + +#include + +CXX_GUARD_START + +#include + +struct mAudioBuffer; +struct mAudioResampler { + struct mAudioBuffer* source; + struct mAudioBuffer* destination; + double sourceRate; + double destRate; + double timestamp; + double lowWaterMark; + double highWaterMark; + enum mInterpolatorType interpType; + union { + struct mInterpolator interp; + struct mInterpolatorSinc sinc; + struct mInterpolatorCosine cosine; + }; + bool consume; +}; + +void mAudioResamplerInit(struct mAudioResampler*, enum mInterpolatorType); +void mAudioResamplerDeinit(struct mAudioResampler*); +void mAudioResamplerSetSource(struct mAudioResampler*, struct mAudioBuffer* source, double rate, bool consume); +void mAudioResamplerSetDestination(struct mAudioResampler*, struct mAudioBuffer* destination, double rate); +size_t mAudioResamplerProcess(struct mAudioResampler*); + +CXX_GUARD_END + +#endif + diff --git a/include/mgba-util/circle-buffer.h b/include/mgba-util/circle-buffer.h index dee1f1f2adb..c93040c6a41 100644 --- a/include/mgba-util/circle-buffer.h +++ b/include/mgba-util/circle-buffer.h @@ -10,7 +10,7 @@ CXX_GUARD_START -struct CircleBuffer { +struct mCircleBuffer { void* data; size_t capacity; size_t size; @@ -18,20 +18,21 @@ struct CircleBuffer { void* writePtr; }; -void CircleBufferInit(struct CircleBuffer* buffer, unsigned capacity); -void CircleBufferDeinit(struct CircleBuffer* buffer); -size_t CircleBufferSize(const struct CircleBuffer* buffer); -size_t CircleBufferCapacity(const struct CircleBuffer* buffer); -void CircleBufferClear(struct CircleBuffer* buffer); -int CircleBufferWrite8(struct CircleBuffer* buffer, int8_t value); -int CircleBufferWrite16(struct CircleBuffer* buffer, int16_t value); -int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value); -size_t CircleBufferWrite(struct CircleBuffer* buffer, const void* input, size_t length); -int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value); -int CircleBufferRead16(struct CircleBuffer* buffer, int16_t* value); -int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value); -size_t CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length); -size_t CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length); +void mCircleBufferInit(struct mCircleBuffer* buffer, unsigned capacity); +void mCircleBufferDeinit(struct mCircleBuffer* buffer); +size_t mCircleBufferSize(const struct mCircleBuffer* buffer); +size_t mCircleBufferCapacity(const struct mCircleBuffer* buffer); +void mCircleBufferClear(struct mCircleBuffer* buffer); +int mCircleBufferWrite8(struct mCircleBuffer* buffer, int8_t value); +int mCircleBufferWrite16(struct mCircleBuffer* buffer, int16_t value); +int mCircleBufferWrite32(struct mCircleBuffer* buffer, int32_t value); +size_t mCircleBufferWrite(struct mCircleBuffer* buffer, const void* input, size_t length); +size_t mCircleBufferWriteTruncate(struct mCircleBuffer* buffer, const void* input, size_t length); +int mCircleBufferRead8(struct mCircleBuffer* buffer, int8_t* value); +int mCircleBufferRead16(struct mCircleBuffer* buffer, int16_t* value); +int mCircleBufferRead32(struct mCircleBuffer* buffer, int32_t* value); +size_t mCircleBufferRead(struct mCircleBuffer* buffer, void* output, size_t length); +size_t mCircleBufferDump(const struct mCircleBuffer* buffer, void* output, size_t length, size_t offset); CXX_GUARD_END diff --git a/include/mgba-util/common.h b/include/mgba-util/common.h index 9625d3d909b..5ef829da870 100644 --- a/include/mgba-util/common.h +++ b/include/mgba-util/common.h @@ -14,12 +14,21 @@ #define CXX_GUARD_END #endif -#ifdef __MINGW32__ -#define __USE_MINGW_ANSI_STDIO 1 -#endif - CXX_GUARD_START +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +// Require Windows 7 or newer +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 +#elif _WIN32_WINNT < 0x0601 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 +#endif +// WinSock2 gets very angry if it's included too late +#include +#endif + #include #include #include @@ -35,11 +44,6 @@ CXX_GUARD_START #include #include -#ifdef _WIN32 -// WinSock2 gets very angry if it is included too late -#include -#endif - #if defined(_MSC_VER) || defined(__cplusplus) #define restrict __restrict #endif diff --git a/include/mgba-util/image.h b/include/mgba-util/image.h index 31514687d9c..b86b2135a4b 100644 --- a/include/mgba-util/image.h +++ b/include/mgba-util/image.h @@ -138,6 +138,7 @@ void mPainterInit(struct mPainter*, struct mImage* backing); void mPainterDrawRectangle(struct mPainter*, int x, int y, int width, int height); void mPainterDrawLine(struct mPainter*, int x1, int y1, int x2, int y2); void mPainterDrawCircle(struct mPainter*, int x, int y, int diameter); +void mPainterDrawMask(struct mPainter*, const struct mImage* mask, int x, int y); uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to); uint32_t mImageColorConvert(uint32_t color, const struct mImage* from, enum mColorFormat to); diff --git a/include/mgba-util/interpolator.h b/include/mgba-util/interpolator.h new file mode 100644 index 00000000000..b7a59acd5eb --- /dev/null +++ b/include/mgba-util/interpolator.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2013-2024 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef M_INTERPOLATOR_H +#define M_INTERPOLATOR_H + +#include + +CXX_GUARD_START + +enum mInterpolatorType { + mINTERPOLATOR_SINC, + mINTERPOLATOR_COSINE, +}; + +struct mInterpolationData { + int16_t (*at)(int index, const void* context); + void* context; +}; + +struct mInterpolator { + int16_t (*interpolate)(const struct mInterpolator* interp, const struct mInterpolationData* data, double time, double sampleStep); +}; + +struct mInterpolatorSinc { + struct mInterpolator d; + + unsigned resolution; + unsigned width; + double* sincLut; + double* windowLut; +}; + +struct mInterpolatorCosine { + struct mInterpolator d; + + unsigned resolution; + double* lut; +}; + +void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution, unsigned width); +void mInterpolatorSincDeinit(struct mInterpolatorSinc* interp); + +void mInterpolatorCosineInit(struct mInterpolatorCosine* interp, unsigned resolution); +void mInterpolatorCosineDeinit(struct mInterpolatorCosine* interp); + +CXX_GUARD_END + +#endif diff --git a/include/mgba-util/platform/psp2/threading.h b/include/mgba-util/platform/psp2/threading.h index 4c084804cd3..2edcc480645 100644 --- a/include/mgba-util/platform/psp2/threading.h +++ b/include/mgba-util/platform/psp2/threading.h @@ -9,7 +9,7 @@ #include typedef SceUID Thread; -typedef SceUID Mutex; +typedef SceKernelLwMutexWork Mutex; typedef struct { Mutex mutex; SceUID semaphore; @@ -20,28 +20,23 @@ typedef THREAD_ENTRY (*ThreadEntry)(void*); #define THREAD_EXIT(RES) return RES static inline int MutexInit(Mutex* mutex) { - Mutex id = sceKernelCreateMutex("mutex", 0, 0, 0); - if (id < 0) { - return id; - } - *mutex = id; - return 0; + return sceKernelCreateLwMutex(mutex, "mutex", 0, 0, 0); } static inline int MutexDeinit(Mutex* mutex) { - return sceKernelDeleteMutex(*mutex); + return sceKernelDeleteLwMutex(mutex); } static inline int MutexLock(Mutex* mutex) { - return sceKernelLockMutex(*mutex, 1, 0); + return sceKernelLockLwMutex(mutex, 1, 0); } static inline int MutexTryLock(Mutex* mutex) { - return sceKernelTryLockMutex(*mutex, 1); + return sceKernelTryLockLwMutex(mutex, 1); } static inline int MutexUnlock(Mutex* mutex) { - return sceKernelUnlockMutex(*mutex, 1); + return sceKernelUnlockLwMutex(mutex, 1); } static inline int ConditionInit(Condition* cond) { diff --git a/include/mgba-util/platform/windows/threading.h b/include/mgba-util/platform/windows/threading.h index 3f5de5a773d..7b1589075d2 100644 --- a/include/mgba-util/platform/windows/threading.h +++ b/include/mgba-util/platform/windows/threading.h @@ -8,8 +8,6 @@ #include -#define _WIN32_WINNT 0x0600 -#include #define THREAD_ENTRY DWORD WINAPI typedef THREAD_ENTRY ThreadEntry(LPVOID); #define THREAD_EXIT(RES) return RES diff --git a/include/mgba-util/vfs.h b/include/mgba-util/vfs.h index eee68b99e31..b9c4ca6b238 100644 --- a/include/mgba-util/vfs.h +++ b/include/mgba-util/vfs.h @@ -73,8 +73,8 @@ struct VFile* VFileFromMemory(void* mem, size_t size); struct VFile* VFileFromConstMemory(const void* mem, size_t size); struct VFile* VFileMemChunk(const void* mem, size_t size); -struct CircleBuffer; -struct VFile* VFileFIFO(struct CircleBuffer* backing); +struct mCircleBuffer; +struct VFile* VFileFIFO(struct mCircleBuffer* backing); struct VDir* VDirOpen(const char* path); struct VDir* VDirOpenArchive(const char* path); diff --git a/include/mgba/core/blip_buf.h b/include/mgba/core/blip_buf.h deleted file mode 100644 index 22b0a3ec8cd..00000000000 --- a/include/mgba/core/blip_buf.h +++ /dev/null @@ -1,72 +0,0 @@ -/** \file -Sample buffer that resamples from input clock rate to output sample rate */ - -/* blip_buf 1.1.0 */ -#ifndef BLIP_BUF_H -#define BLIP_BUF_H - -#ifdef __cplusplus - extern "C" { -#endif - -/** First parameter of most functions is blip_t*, or const blip_t* if nothing -is changed. */ -typedef struct blip_t blip_t; - -/** Creates new buffer that can hold at most sample_count samples. Sets rates -so that there are blip_max_ratio clocks per sample. Returns pointer to new -buffer, or NULL if insufficient memory. */ -blip_t* blip_new( int sample_count ); - -/** Sets approximate input clock rate and output sample rate. For every -clock_rate input clocks, approximately sample_rate samples are generated. */ -void blip_set_rates( blip_t*, double clock_rate, double sample_rate ); - -enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate, -clock_rate must not be greater than sample_rate*blip_max_ratio. */ -blip_max_ratio = 0x100000 }; - -/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */ -void blip_clear( blip_t* ); - -/** Adds positive/negative delta into buffer at specified clock time. */ -void blip_add_delta( blip_t*, unsigned int clock_time, int delta ); - -/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */ -void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta ); - -/** Length of time frame, in clocks, needed to make sample_count additional -samples available. */ -int blip_clocks_needed( const blip_t*, int sample_count ); - -enum { /** Maximum number of samples that can be generated from one time frame. */ -blip_max_frame = 4000 }; - -/** Makes input clocks before clock_duration available for reading as output -samples. Also begins new time frame at clock_duration, so that clock time 0 in -the new time frame specifies the same clock as clock_duration in the old time -frame specified. Deltas can have been added slightly past clock_duration (up to -however many clocks there are in two output samples). */ -void blip_end_frame( blip_t*, unsigned int clock_duration ); - -/** Number of buffered samples available for reading. */ -int blip_samples_avail( const blip_t* ); - -/** Reads and removes at most 'count' samples and writes them to 'out'. If -'stereo' is true, writes output to every other element of 'out', allowing easy -interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed -samples. Returns number of samples actually read. */ -int blip_read_samples( blip_t*, short out [], int count, int stereo ); - -/** Frees buffer. No effect if NULL is passed. */ -void blip_delete( blip_t* ); - - -/* Deprecated */ -typedef blip_t blip_buffer_t; - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index 052e367957f..a4bf5826cc0 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -18,7 +18,7 @@ CXX_GUARD_START #include #endif #include -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS #include #endif @@ -32,6 +32,7 @@ enum mCoreChecksumType { mCHECKSUM_CRC32, }; +struct mAudioBuffer; struct mCoreConfig; struct mCoreSync; struct mDebuggerSymbols; @@ -78,7 +79,8 @@ struct mCore { void (*getPixels)(struct mCore*, const void** buffer, size_t* stride); void (*putPixels)(struct mCore*, const void* buffer, size_t stride); - struct blip_t* (*getAudioChannel)(struct mCore*, int ch); + unsigned (*audioSampleRate)(const struct mCore*); + struct mAudioBuffer* (*getAudioBuffer)(struct mCore*); void (*setAudioBufferSize)(struct mCore*, size_t samples); size_t (*getAudioBufferSize)(struct mCore*); @@ -146,7 +148,7 @@ struct mCore { bool (*readRegister)(const struct mCore*, const char* name, void* out); bool (*writeRegister)(struct mCore*, const char* name, const void* in); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS bool (*supportsDebuggerType)(struct mCore*, enum mDebuggerType); struct mDebuggerPlatform* (*debuggerPlatform)(struct mCore*); struct CLIDebuggerSystem* (*cliDebuggerSystem)(struct mCore*); @@ -216,10 +218,12 @@ void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size); void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask); const struct mCoreMemoryBlock* mCoreGetMemoryBlockInfo(struct mCore* core, uint32_t address); +double mCoreCalculateFramerateRatio(const struct mCore* core, double desiredFrameRate); + #ifdef USE_ELF struct ELF; bool mCoreLoadELF(struct mCore* core, struct ELF* elf); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void mCoreLoadELFSymbols(struct mDebuggerSymbols* symbols, struct ELF*); #endif #endif diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index f1af5e50d5e..b07f300f3f5 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -16,7 +16,7 @@ CXX_GUARD_START struct mCore; struct mStateExtdataItem; -struct blip_t; +struct mAudioBuffer; enum mCoreFeature { mCORE_FEATURE_OPENGL = 1, @@ -41,7 +41,7 @@ struct mAVStream { void (*audioRateChanged)(struct mAVStream*, unsigned rate); void (*postVideoFrame)(struct mAVStream*, const color_t* buffer, size_t stride); void (*postAudioFrame)(struct mAVStream*, int16_t left, int16_t right); - void (*postAudioBuffer)(struct mAVStream*, struct blip_t* left, struct blip_t* right); + void (*postAudioBuffer)(struct mAVStream*, struct mAudioBuffer*); }; struct mStereoSample { diff --git a/include/mgba/core/rewind.h b/include/mgba/core/rewind.h index 9d259555746..2f33886cad3 100644 --- a/include/mgba/core/rewind.h +++ b/include/mgba/core/rewind.h @@ -40,7 +40,7 @@ void mCoreRewindContextDeinit(struct mCoreRewindContext*); struct mCore; void mCoreRewindAppend(struct mCoreRewindContext*, struct mCore*); -bool mCoreRewindRestore(struct mCoreRewindContext*, struct mCore*); +bool mCoreRewindRestore(struct mCoreRewindContext*, struct mCore*, unsigned count); CXX_GUARD_END diff --git a/include/mgba/core/scripting.h b/include/mgba/core/scripting.h index 64587488302..a8fdbc425e1 100644 --- a/include/mgba/core/scripting.h +++ b/include/mgba/core/scripting.h @@ -10,7 +10,7 @@ CXX_GUARD_START -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS #include #endif #include @@ -35,7 +35,7 @@ struct mScriptEngine { void (*run)(struct mScriptEngine*); bool (*lookupSymbol)(struct mScriptEngine*, const char* name, int32_t* out); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void (*debuggerEntered)(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); #endif }; @@ -63,7 +63,7 @@ void mScriptBridgeDestroy(struct mScriptBridge*); void mScriptBridgeInstallEngine(struct mScriptBridge*, struct mScriptEngine*); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void mScriptBridgeSetDebugger(struct mScriptBridge*, struct mDebugger*); struct mDebugger* mScriptBridgeGetDebugger(struct mScriptBridge*); void mScriptBridgeDebuggerEntered(struct mScriptBridge*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); diff --git a/include/mgba/core/sync.h b/include/mgba/core/sync.h index a44994b07be..df4e777bb0e 100644 --- a/include/mgba/core/sync.h +++ b/include/mgba/core/sync.h @@ -22,6 +22,7 @@ struct mCoreSync { bool audioWait; Condition audioRequiredCond; Mutex audioBufferMutex; + size_t audioHighWater; float fpsTarget; }; @@ -32,8 +33,8 @@ bool mCoreSyncWaitFrameStart(struct mCoreSync* sync); void mCoreSyncWaitFrameEnd(struct mCoreSync* sync); void mCoreSyncSetVideoSync(struct mCoreSync* sync, bool wait); -struct blip_t; -bool mCoreSyncProduceAudio(struct mCoreSync* sync, const struct blip_t*, size_t samples); +struct mAudioBuffer; +bool mCoreSyncProduceAudio(struct mCoreSync* sync, const struct mAudioBuffer*); void mCoreSyncLockAudio(struct mCoreSync* sync); void mCoreSyncUnlockAudio(struct mCoreSync* sync); void mCoreSyncConsumeAudio(struct mCoreSync* sync); diff --git a/include/mgba/core/thread.h b/include/mgba/core/thread.h index d3d8a9e9277..9f2c229bec2 100644 --- a/include/mgba/core/thread.h +++ b/include/mgba/core/thread.h @@ -87,7 +87,8 @@ struct mCoreThreadInternal { int requested; Mutex stateMutex; - Condition stateCond; + Condition stateOnThreadCond; + Condition stateOffThreadCond; int interruptDepth; bool frameWasOn; diff --git a/include/mgba/feature/proxy-backend.h b/include/mgba/feature/proxy-backend.h index 8c0b9e42713..ffd38eb47c3 100644 --- a/include/mgba/feature/proxy-backend.h +++ b/include/mgba/feature/proxy-backend.h @@ -38,6 +38,8 @@ union mVideoBackendCommandData { struct { unsigned width; unsigned height; + unsigned maxW; + unsigned maxH; } u; const void* image; }; diff --git a/include/mgba/feature/video-backend.h b/include/mgba/feature/video-backend.h index adf6f839523..f01e656c9fb 100644 --- a/include/mgba/feature/video-backend.h +++ b/include/mgba/feature/video-backend.h @@ -48,7 +48,7 @@ struct VideoBackend { void (*layerDimensions)(const struct VideoBackend*, enum VideoLayer, struct mRectangle*); void (*swap)(struct VideoBackend*); void (*clear)(struct VideoBackend*); - void (*contextResized)(struct VideoBackend*, unsigned w, unsigned h); + void (*contextResized)(struct VideoBackend*, unsigned w, unsigned h, unsigned maxW, unsigned maxH); void (*setImageSize)(struct VideoBackend*, enum VideoLayer, int w, int h); void (*imageSize)(struct VideoBackend*, enum VideoLayer, int* w, int* h); void (*setImage)(struct VideoBackend*, enum VideoLayer, const void* frame); diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index ecd99e2f994..de26ef73168 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -58,6 +58,13 @@ enum GBVideoLayer { GB_LAYER_OBJ }; +enum GBColorLookup { + GB_COLORS_NONE = 0, + GB_COLORS_CGB = 1, + GB_COLORS_SGB = 2, + GB_COLORS_SGB_CGB_FALLBACK = GB_COLORS_CGB | GB_COLORS_SGB +}; + struct GBSIODriver { struct GBSIO* p; @@ -67,6 +74,20 @@ struct GBSIODriver { uint8_t (*writeSC)(struct GBSIODriver* driver, uint8_t value); }; +struct GBCartridgeOverride { + int headerCrc32; + enum GBModel model; + enum GBMemoryBankControllerType mbc; + + uint32_t gbColors[12]; +}; + +struct GBColorPreset { + const char* name; + uint32_t colors[12]; +}; + +struct Configuration; struct VFile; bool GBIsROM(struct VFile* vf); @@ -78,6 +99,12 @@ const char* GBModelToName(enum GBModel); int GBValidModels(const uint8_t* bank0); +bool GBOverrideFind(const struct Configuration*, struct GBCartridgeOverride* override); +bool GBOverrideColorFind(struct GBCartridgeOverride* override, enum GBColorLookup); +void GBOverrideSave(struct Configuration*, const struct GBCartridgeOverride* override); + +size_t GBColorPresetList(const struct GBColorPreset** presets); + CXX_GUARD_END #endif diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index bf10c59ba31..c64d0039119 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -13,6 +13,8 @@ CXX_GUARD_START #include #include +#define GBA_IDLE_LOOP_NONE 0xFFFFFFFF + enum { GBA_VIDEO_HORIZONTAL_PIXELS = 240, GBA_VIDEO_VERTICAL_PIXELS = 160, @@ -45,7 +47,31 @@ enum GBAVideoLayer { GBA_LAYER_OBJWIN, }; -struct GBA; +enum GBASavedataType { + GBA_SAVEDATA_AUTODETECT = -1, + GBA_SAVEDATA_FORCE_NONE = 0, + GBA_SAVEDATA_SRAM = 1, + GBA_SAVEDATA_FLASH512 = 2, + GBA_SAVEDATA_FLASH1M = 3, + GBA_SAVEDATA_EEPROM = 4, + GBA_SAVEDATA_EEPROM512 = 5, + GBA_SAVEDATA_SRAM512 = 6, +}; + +enum GBAHardwareDevice { + HW_NO_OVERRIDE = 0x8000, + HW_NONE = 0, + HW_RTC = 1, + HW_RUMBLE = 2, + HW_LIGHT_SENSOR = 4, + HW_GYRO = 8, + HW_TILT = 16, + HW_GB_PLAYER = 32, + HW_GB_PLAYER_DETECTION = 64, + HW_EREADER = 128 +}; + +struct Configuration; struct GBAAudio; struct GBASIO; struct GBAVideoRenderer; @@ -58,9 +84,13 @@ enum { mPERIPH_GBA_BATTLECHIP_GATE, }; -bool GBAIsROM(struct VFile* vf); -bool GBAIsMB(struct VFile* vf); -bool GBAIsBIOS(struct VFile* vf); +struct GBACartridgeOverride { + char id[4]; + enum GBASavedataType savetype; + int hardware; + uint32_t idleLoop; + bool vbaBugCompat; +}; struct GBALuminanceSource { void (*sample)(struct GBALuminanceSource*); @@ -68,6 +98,13 @@ struct GBALuminanceSource { uint8_t (*readLuminance)(struct GBALuminanceSource*); }; +bool GBAIsROM(struct VFile* vf); +bool GBAIsMB(struct VFile* vf); +bool GBAIsBIOS(struct VFile* vf); + +bool GBAOverrideFind(const struct Configuration*, struct GBACartridgeOverride* override); +void GBAOverrideSave(struct Configuration*, const struct GBACartridgeOverride* override); + struct GBASIODriver { struct GBASIO* p; @@ -98,6 +135,7 @@ struct GBASIOBattlechipGate { void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate*); +struct GBA; void GBACartEReaderQueueCard(struct GBA* gba, const void* data, size_t size); struct EReaderScan; diff --git a/include/mgba/internal/arm/decoder.h b/include/mgba/internal/arm/decoder.h index c0efb22e86c..f6e19769884 100644 --- a/include/mgba/internal/arm/decoder.h +++ b/include/mgba/internal/arm/decoder.h @@ -221,7 +221,7 @@ bool ARMDecodeThumbCombine(struct ARMInstructionInfo* info1, struct ARMInstructi struct ARMInstructionInfo* out); uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS struct mDebuggerSymbols; int ARMDisassemble(const struct ARMInstructionInfo* info, struct ARMCore* core, const struct mDebuggerSymbols* symbols, uint32_t pc, char* buffer, int blen); #endif diff --git a/include/mgba/internal/gb/audio.h b/include/mgba/internal/gb/audio.h index 5b8993ac5a3..8b9b775c3c4 100644 --- a/include/mgba/internal/gb/audio.h +++ b/include/mgba/internal/gb/audio.h @@ -12,6 +12,7 @@ CXX_GUARD_START #include #include +#include #define GB_MAX_SAMPLES 32 @@ -166,14 +167,9 @@ struct GBAudio { struct GBAudioWaveChannel ch3; struct GBAudioNoiseChannel ch4; - struct blip_t* left; - struct blip_t* right; - int16_t lastLeft; - int16_t lastRight; + struct mAudioBuffer buffer; int32_t capLeft; int32_t capRight; - int clock; - int32_t clockRate; uint8_t volumeRight; uint8_t volumeLeft; diff --git a/include/mgba/internal/gb/overrides.h b/include/mgba/internal/gb/overrides.h index 86cac0f9d67..9d16ce65c04 100644 --- a/include/mgba/internal/gb/overrides.h +++ b/include/mgba/internal/gb/overrides.h @@ -12,33 +12,6 @@ CXX_GUARD_START #include -enum GBColorLookup { - GB_COLORS_NONE = 0, - GB_COLORS_CGB = 1, - GB_COLORS_SGB = 2, - GB_COLORS_SGB_CGB_FALLBACK = GB_COLORS_CGB | GB_COLORS_SGB -}; - -struct GBCartridgeOverride { - int headerCrc32; - enum GBModel model; - enum GBMemoryBankControllerType mbc; - - uint32_t gbColors[12]; -}; - -struct GBColorPreset { - const char* name; - uint32_t colors[12]; -}; - -struct Configuration; -bool GBOverrideFind(const struct Configuration*, struct GBCartridgeOverride* override); -bool GBOverrideColorFind(struct GBCartridgeOverride* override, enum GBColorLookup); -void GBOverrideSave(struct Configuration*, const struct GBCartridgeOverride* override); - -size_t GBColorPresetList(const struct GBColorPreset** presets); - struct GB; void GBOverrideApply(struct GB*, const struct GBCartridgeOverride*); void GBOverrideApplyDefaults(struct GB*); diff --git a/include/mgba/internal/gba/audio.h b/include/mgba/internal/gba/audio.h index 8b212dd6a23..5b5db3a621b 100644 --- a/include/mgba/internal/gba/audio.h +++ b/include/mgba/internal/gba/audio.h @@ -56,7 +56,6 @@ DECL_BITFIELD(GBARegisterSOUNDBIAS, uint16_t); DECL_BITS(GBARegisterSOUNDBIAS, Bias, 0, 10); DECL_BITS(GBARegisterSOUNDBIAS, Resolution, 14, 2); -struct GBAAudioMixer; struct GBAAudio { struct GBA* p; @@ -64,10 +63,6 @@ struct GBAAudio { struct GBAAudioFIFO chA; struct GBAAudioFIFO chB; - int16_t lastLeft; - int16_t lastRight; - int clock; - uint8_t volume; bool volumeChA; bool volumeChB; @@ -82,8 +77,6 @@ struct GBAAudio { size_t samples; GBARegisterSOUNDBIAS soundbias; - struct GBAAudioMixer* mixer; - bool externalMixing; int32_t sampleInterval; int32_t lastSample; @@ -253,32 +246,12 @@ struct GBAMP2kTrack { struct GBAMP2kMusicPlayerTrack track; struct GBAMP2kSoundChannel* channel; uint8_t lastCommand; - struct CircleBuffer buffer; + struct mCircleBuffer buffer; uint32_t samplePlaying; float currentOffset; bool waiting; }; -struct GBAAudioMixer { - struct mCPUComponent d; - struct GBAAudio* p; - - uint32_t contextAddress; - - bool (*engage)(struct GBAAudioMixer* mixer, uint32_t address); - void (*vblank)(struct GBAAudioMixer* mixer); - void (*step)(struct GBAAudioMixer* mixer); - - struct GBAMP2kContext context; - struct GBAMP2kMusicPlayerInfo player; - struct GBAMP2kTrack activeTracks[MP2K_MAX_SOUND_CHANNELS]; - - double tempo; - double frame; - - struct mStereoSample last; -}; - void GBAAudioInit(struct GBAAudio* audio, size_t samples); void GBAAudioReset(struct GBAAudio* audio); void GBAAudioDeinit(struct GBAAudio* audio); @@ -313,8 +286,6 @@ struct GBASerializedState; void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state); void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state); -float GBAAudioCalculateRatio(float inputSampleRate, float desiredFPS, float desiredSampleRatio); - CXX_GUARD_END #endif diff --git a/include/mgba/internal/gba/cart/gpio.h b/include/mgba/internal/gba/cart/gpio.h index 86ddc941f73..77785e6122c 100644 --- a/include/mgba/internal/gba/cart/gpio.h +++ b/include/mgba/internal/gba/cart/gpio.h @@ -18,19 +18,6 @@ mLOG_DECLARE_CATEGORY(GBA_HW); #define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL) -enum GBAHardwareDevice { - HW_NO_OVERRIDE = 0x8000, - HW_NONE = 0, - HW_RTC = 1, - HW_RUMBLE = 2, - HW_LIGHT_SENSOR = 4, - HW_GYRO = 8, - HW_TILT = 16, - HW_GB_PLAYER = 32, - HW_GB_PLAYER_DETECTION = 64, - HW_EREADER = 128 -}; - enum GPIORegister { GPIO_REG_DATA = 0xC4, GPIO_REG_DIRECTION = 0xC6, diff --git a/include/mgba/internal/gba/extra/audio-mixer.h b/include/mgba/internal/gba/extra/audio-mixer.h deleted file mode 100644 index 369a4aaa3a0..00000000000 --- a/include/mgba/internal/gba/extra/audio-mixer.h +++ /dev/null @@ -1,19 +0,0 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef GBA_AUDIO_MIXER_H -#define GBA_AUDIO_MIXER_H - -#include - -CXX_GUARD_START - -#include - -void GBAAudioMixerCreate(struct GBAAudioMixer* mixer); - -CXX_GUARD_END - -#endif diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index 020dd2ab5db..e66ac47f337 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -160,7 +160,7 @@ struct ELF; bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target); #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS struct mDebugger; void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger); void GBADetachDebugger(struct GBA* gba); diff --git a/include/mgba/internal/gba/memory.h b/include/mgba/internal/gba/memory.h index f9e963d7672..c88e1d092c2 100644 --- a/include/mgba/internal/gba/memory.h +++ b/include/mgba/internal/gba/memory.h @@ -138,8 +138,6 @@ struct GBAMemory { struct GBAPrintContext agbPrintCtxBackup; uint32_t agbPrintFuncBackup; uint16_t* agbPrintBufferBackup; - - bool mirroring; }; struct GBA; diff --git a/include/mgba/internal/gba/overrides.h b/include/mgba/internal/gba/overrides.h index c835a62a13a..4bec8bf15d1 100644 --- a/include/mgba/internal/gba/overrides.h +++ b/include/mgba/internal/gba/overrides.h @@ -10,22 +10,7 @@ CXX_GUARD_START -#include - -#define IDLE_LOOP_NONE 0xFFFFFFFF - -struct GBACartridgeOverride { - char id[4]; - enum SavedataType savetype; - int hardware; - uint32_t idleLoop; - bool mirroring; - bool vbaBugCompat; -}; - -struct Configuration; -bool GBAOverrideFind(const struct Configuration*, struct GBACartridgeOverride* override); -void GBAOverrideSave(struct Configuration*, const struct GBACartridgeOverride* override); +#include struct GBA; void GBAOverrideApply(struct GBA*, const struct GBACartridgeOverride*); diff --git a/include/mgba/internal/gba/savedata.h b/include/mgba/internal/gba/savedata.h index 102c2304138..274241a766e 100644 --- a/include/mgba/internal/gba/savedata.h +++ b/include/mgba/internal/gba/savedata.h @@ -12,22 +12,12 @@ CXX_GUARD_START #include #include +#include mLOG_DECLARE_CATEGORY(GBA_SAVE); struct VFile; -enum SavedataType { - SAVEDATA_AUTODETECT = -1, - SAVEDATA_FORCE_NONE = 0, - SAVEDATA_SRAM = 1, - SAVEDATA_FLASH512 = 2, - SAVEDATA_FLASH1M = 3, - SAVEDATA_EEPROM = 4, - SAVEDATA_EEPROM512 = 5, - SAVEDATA_SRAM512 = 6, -}; - enum SavedataCommand { EEPROM_COMMAND_NULL = 0, EEPROM_COMMAND_PENDING = 1, @@ -68,7 +58,7 @@ enum { }; struct GBASavedata { - enum SavedataType type; + enum GBASavedataType type; uint8_t* data; enum SavedataCommand command; struct VFile* vf; @@ -108,7 +98,7 @@ void GBASavedataUnmask(struct GBASavedata* savedata); size_t GBASavedataSize(const struct GBASavedata* savedata); bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out); bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in); -void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type); +void GBASavedataForceType(struct GBASavedata* savedata, enum GBASavedataType type); void GBASavedataInitFlash(struct GBASavedata* savedata); void GBASavedataInitEEPROM(struct GBASavedata* savedata); diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index 4acf7591ac6..70eed0af58b 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -254,7 +254,7 @@ CXX_GUARD_START return false; \ } -#define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, NRET, RETURN, NPARAMS, DEFAULTS, ...) \ +#define _mSCRIPT_DECLARE_STRUCT_METHOD_HEAD(TYPE, NAME) \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx); \ static const struct mScriptFunction _mSTStructBindingFunction_ ## TYPE ## _ ## NAME = { \ .call = &_mSTStructBinding_ ## TYPE ## _ ## NAME \ @@ -269,6 +269,9 @@ CXX_GUARD_START .alloc = _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME, \ .details = { \ .function = { \ + +#define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, NRET, RETURN, NPARAMS, DEFAULTS, ...) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_HEAD(TYPE, NAME) \ .parameters = { \ .count = _mSUCC_ ## NPARAMS, \ .entries = { mSCRIPT_TYPE_MS_ ## S(TYPE), _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ @@ -283,6 +286,23 @@ CXX_GUARD_START } \ }; +#define _mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD(TYPE, NAME, S, NRET, RETURN) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_HEAD(TYPE, NAME) \ + .parameters = { \ + .count = 1, \ + .entries = { mSCRIPT_TYPE_MS_ ## S(TYPE) }, \ + .names = { "this" }, \ + .defaults = NULL, \ + .variable = true, \ + }, \ + .returnType = { \ + .count = NRET, \ + .entries = { RETURN } \ + }, \ + }, \ + } \ + }; + #define _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, RETURN, NAME, CONST, NPARAMS, ...) \ typedef RETURN (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mCOMMA_ ## NPARAMS(CONST struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__))) @@ -302,6 +322,20 @@ CXX_GUARD_START return true; \ } \ +#define _mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD_BINDING(TYPE, NAME, T) \ + static const struct mScriptFunctionOverload _mSTStructBindingOverloads_ ## TYPE ## _ ## NAME[mSCRIPT_OVERLOADS_MAX]; \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + const struct mScriptFunctionOverload* overload = mScriptFunctionFindOverload(_mSTStructBindingOverloads_ ## TYPE ## _ ## NAME, &frame->arguments); \ + if (!overload) { \ + return false; \ + } \ + if (!mScriptCoerceFrame(&overload->type->details.function.parameters, &frame->arguments, &frame->arguments)) { \ + return false; \ + } \ + return overload->function->call(frame, overload->function->context); \ + } + #define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ @@ -346,6 +380,22 @@ CXX_GUARD_START _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, 0, NPARAMS, _mIDENT(_mSTStructBindingDefaults_ ## TYPE ## _ ## NAME, __VA_ARGS__) \ _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) +#define mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD(TYPE, RETURN, NAME) \ + _mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN) \ + _mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD_BINDING(TYPE, NAME, S) + +#define mSCRIPT_DECLARE_STRUCT_OVERLOADED_VOID_METHOD(TYPE, NAME) \ + _mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD(TYPE, NAME, S, 0, 0) \ + _mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD_BINDING(TYPE, NAME, S) + +#define mSCRIPT_DECLARE_STRUCT_OVERLOADED_C_METHOD(TYPE, RETURN, NAME) \ + _mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN) \ + _mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD_BINDING(TYPE, NAME, CS) + +#define mSCRIPT_DECLARE_STRUCT_OVERLOADED_VOID_C_METHOD(TYPE, NAME) \ + _mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD(TYPE, NAME, CS, 0, 0) \ + _mSCRIPT_DECLARE_STRUCT_OVERLOADED_METHOD_BINDING(TYPE, NAME, CS) + #define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) @@ -417,27 +467,39 @@ CXX_GUARD_START #define mSCRIPT_DEFINE_DEFAULTS_END } -#define _mSCRIPT_DEFINE_STRUCT_BINDING(INIT_TYPE, TYPE, EXPORTED_NAME, NAME) { \ +#define mSCRIPT_DEFINE_STRUCT_METHOD_OVERLOADS(STRUCT, METHOD) \ + static const struct mScriptFunctionOverload _mSTStructBindingOverloads_ ## STRUCT ## _ ## METHOD[mSCRIPT_OVERLOADS_MAX] = { \ + +#define mSCRIPT_DEFINE_STRUCT_METHOD_OVERLOAD(TYPE, FUNCTION) { \ + .type = &_mSTStructBindingType_ ## TYPE ## _ ## FUNCTION, \ + .function = &_mSTStructBindingFunction_ ## TYPE ## _ ## FUNCTION \ +}, + +#define mSCRIPT_DEFINE_OVERLOADS_END { NULL, NULL } } + +#define _mSCRIPT_DEFINE_STRUCT_BINDING(INIT_TYPE, TYPE, EXPORTED_NAME, NAME, OVERLOADS) { \ .type = mSCRIPT_CLASS_INIT_ ## INIT_TYPE, \ .info = { \ .member = { \ .name = #EXPORTED_NAME, \ - .type = &_mSTStructBindingType_ ## TYPE ## _ ## NAME \ + .type = &_mSTStructBindingType_ ## TYPE ## _ ## NAME, \ + .overloads = OVERLOADS, \ } \ }, \ }, #define mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, EXPORTED_NAME, NAME) \ - _mSCRIPT_DEFINE_STRUCT_BINDING(INSTANCE_MEMBER, TYPE, EXPORTED_NAME, NAME) + _mSCRIPT_DEFINE_STRUCT_BINDING(INSTANCE_MEMBER, TYPE, EXPORTED_NAME, NAME, NULL) #define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME) - -#define mSCRIPT_DEFINE_STRUCT_INIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, _init) -#define mSCRIPT_DEFINE_STRUCT_INIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, NAME) -#define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit) -#define mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, NAME) -#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(GET, TYPE, _get, _get) -#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE, SETTER) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, SETTER, SETTER) +#define mSCRIPT_DEFINE_STRUCT_OVERLOADED_METHOD(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(INSTANCE_MEMBER, TYPE, NAME, NAME, _mSTStructBindingOverloads_ ## TYPE ## _ ## NAME) + +#define mSCRIPT_DEFINE_STRUCT_INIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, _init, NULL) +#define mSCRIPT_DEFINE_STRUCT_INIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, NAME, NULL) +#define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit, NULL) +#define mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, NAME, NULL) +#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(GET, TYPE, _get, _get, NULL) +#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE, SETTER) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, SETTER, SETTER, NULL) #define mSCRIPT_DEFINE_DOC_STRUCT_METHOD(SCOPE, TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(doc_ ## TYPE, NAME, NAME) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 1475f1a6ec6..5b07abe3bc2 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -16,6 +16,7 @@ CXX_GUARD_START #define mSCRIPT_VALUE_UNREF -1 #define mSCRIPT_PARAMS_MAX 8 +#define mSCRIPT_OVERLOADS_MAX 8 #define mSCRIPT_VALUE_DOC_FUNCTION(NAME) (&_mScriptDoc_ ## NAME) @@ -259,6 +260,7 @@ struct mScriptClassMember { const char* name; const char* docstring; const struct mScriptType* type; + const struct mScriptFunctionOverload* overloads; size_t offset; bool readonly; }; @@ -275,6 +277,7 @@ struct mScriptClassInitDetails { const struct mScriptType* parent; struct mScriptClassMember member; struct mScriptClassCastMember castMember; + const struct mScriptFunctionOverload* overload; } info; }; @@ -332,6 +335,11 @@ struct mScriptFunction { void* context; }; +struct mScriptFunctionOverload { + const struct mScriptType* type; + const struct mScriptFunction* function; +}; + struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type); void mScriptValueRef(struct mScriptValue* val); void mScriptValueDeref(struct mScriptValue* val); @@ -385,7 +393,8 @@ bool mScriptPopBool(struct mScriptList* list, bool* out); bool mScriptPopPointer(struct mScriptList* list, void** out); bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output); -bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame); +bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, const struct mScriptList* input, struct mScriptList* output); +const struct mScriptFunctionOverload* mScriptFunctionFindOverload(const struct mScriptFunctionOverload* overloads, struct mScriptList* frame); CXX_GUARD_END diff --git a/libretro-build/Makefile.common b/libretro-build/Makefile.common index 258fc4de268..5c891952518 100644 --- a/libretro-build/Makefile.common +++ b/libretro-build/Makefile.common @@ -114,9 +114,9 @@ SOURCES_C := $(CORE_DIR)/src/arm/arm.c \ $(CORE_DIR)/src/platform/libretro/libretro.c \ $(CORE_DIR)/src/sm83/isa-sm83.c \ $(CORE_DIR)/src/sm83/sm83.c \ - $(CORE_DIR)/src/third-party/blip_buf/blip_buf.c \ $(CORE_DIR)/src/third-party/inih/ini.c \ $(CORE_DIR)/src/util/circle-buffer.c \ + $(CORE_DIR)/src/util/audio-buffer.c \ $(CORE_DIR)/src/util/configuration.c \ $(CORE_DIR)/src/util/formatting.c \ $(CORE_DIR)/src/util/gbk-table.c \ diff --git a/res/licenses/blip_buf.txt b/res/licenses/blip_buf.txt deleted file mode 100644 index 5faba9d48ce..00000000000 --- a/res/licenses/blip_buf.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/res/scripts/color-mask.lua b/res/scripts/color-mask.lua new file mode 100644 index 00000000000..25a51f8052b --- /dev/null +++ b/res/scripts/color-mask.lua @@ -0,0 +1,34 @@ +local state = {} +state.wheel = image.load(script.dir .. "/wheel.png") +state.overlay = canvas:newLayer(state.wheel.width, state.wheel.height) +state.painter = image.newPainter(state.overlay.image) +state.phase = 0 +state.speed = 0.01 +state.painter:setFill(true) +state.painter:setStrokeWidth(0) + +function state.update() + local r = math.fmod(state.phase * 3, math.pi * 2) + local g = math.fmod(state.phase * 5, math.pi * 2) + local b = math.fmod(state.phase * 7, math.pi * 2) + local color = 0xFF000000 + color = color | math.floor((math.sin(r) + 1) * 127.5) << 16 + color = color | math.floor((math.sin(g) + 1) * 127.5) << 8 + color = color | math.floor((math.sin(b) + 1) * 127.5) + + -- Clear image + state.painter:setBlend(false) + state.painter:setFillColor(0) + state.painter:drawRectangle(0, 0, state.wheel.width, state.wheel.height) + -- Draw mask + state.painter:setBlend(true) + state.painter:setFillColor(color | 0xFF000000) + state.painter:drawMask(state.wheel, 0, 0) + + state.overlay:update() + state.phase = math.fmod(state.phase + state.speed, math.pi * 2 * 3 * 5 * 7) +end + +callbacks:add("frame", state.update) + + diff --git a/res/scripts/logo-bg.png b/res/scripts/logo-bg.png new file mode 100644 index 00000000000..908e410d972 Binary files /dev/null and b/res/scripts/logo-bg.png differ diff --git a/res/scripts/logo-bounce.lua b/res/scripts/logo-bounce.lua index c8192638c22..aae32ce40c5 100644 --- a/res/scripts/logo-bounce.lua +++ b/res/scripts/logo-bounce.lua @@ -1,44 +1,61 @@ math.randomseed(os.time()) local state = {} -state.logo = image.load(script.dir .. "/logo.png") -state.overlay = canvas:newLayer(state.logo.width, state.logo.height) -state.overlay.image:drawImageOpaque(state.logo, 0, 0) -state.x = math.random() * (canvas:screenWidth() - state.logo.width) -state.y = math.random() * (canvas:screenHeight() - state.logo.height) +state.logo_fg = image.load(script.dir .. "/logo-fg.png") +state.logo_bg = image.load(script.dir .. "/logo-bg.png") +state.overlay = canvas:newLayer(state.logo_fg.width, state.logo_fg.height) +state.x = math.random() * (canvas:screenWidth() - state.logo_fg.width) +state.y = math.random() * (canvas:screenHeight() - state.logo_fg.height) +state.overlay:setPosition(math.floor(state.x), math.floor(state.y)) state.direction = math.floor(math.random() * 3) state.speed = 0.5 -state.overlay:setPosition(math.floor(state.x), math.floor(state.y)) -state.overlay:update() +function state.recolor() + local r = math.floor(math.random() * 255) + local g = math.floor(math.random() * 255) + local b = math.floor(math.random() * 255) + local color = 0xFF000000 | (r << 16) | (g << 8) | b + state.overlay.image:drawImageOpaque(state.logo_bg, 0, 0) + local painter = image.newPainter(state.overlay.image) + painter:setFill(true) + painter:setFillColor(color) + painter:setBlend(true) + painter:drawMask(state.logo_fg, 0, 0) + state.overlay:update() +end function state.update() if state.direction & 1 == 1 then state.x = state.x + 1 - if state.x > canvas:screenWidth() - state.logo.width then - state.x = (canvas:screenWidth() - state.logo.width) * 2 - state.x + if state.x > canvas:screenWidth() - state.logo_fg.width then + state.x = (canvas:screenWidth() - state.logo_fg.width) * 2 - state.x state.direction = state.direction ~ 1 + state.recolor() end else state.x = state.x - 1 if state.x < 0 then state.x = -state.x state.direction = state.direction ~ 1 + state.recolor() end end if state.direction & 2 == 2 then state.y = state.y + 1 - if state.y > canvas:screenHeight() - state.logo.height then - state.y = (canvas:screenHeight() - state.logo.height) * 2 - state.y + if state.y > canvas:screenHeight() - state.logo_fg.height then + state.y = (canvas:screenHeight() - state.logo_fg.height) * 2 - state.y state.direction = state.direction ~ 2 + state.recolor() end else state.y = state.y - 1 if state.y < 0 then state.y = -state.y state.direction = state.direction ~ 2 + state.recolor() end end state.overlay:setPosition(math.floor(state.x), math.floor(state.y)) end +state.recolor() callbacks:add("frame", state.update) diff --git a/res/scripts/logo-fg.png b/res/scripts/logo-fg.png new file mode 100644 index 00000000000..c401fb0112f Binary files /dev/null and b/res/scripts/logo-fg.png differ diff --git a/res/scripts/wheel.png b/res/scripts/wheel.png new file mode 100644 index 00000000000..5e7bcca12b9 Binary files /dev/null and b/res/scripts/wheel.png differ diff --git a/src/arm/decoder.c b/src/arm/decoder.c index 01622cc9df1..21ea0e872c3 100644 --- a/src/arm/decoder.c +++ b/src/arm/decoder.c @@ -9,7 +9,7 @@ #include #include -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS #define ADVANCE(AMOUNT) \ if (AMOUNT >= blen) { \ buffer[blen - 1] = '\0'; \ diff --git a/src/core/core.c b/src/core/core.c index a7de239430f..bcc72115a77 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -442,6 +442,12 @@ const struct mCoreMemoryBlock* mCoreGetMemoryBlockInfo(struct mCore* core, uint3 return NULL; } +double mCoreCalculateFramerateRatio(const struct mCore* core, double desiredFrameRate) { + uint32_t clockRate = core->frequency(core); + uint32_t frameCycles = core->frameCycles(core); + return clockRate / (desiredFrameRate * frameCycles); +} + #ifdef USE_ELF bool mCoreLoadELF(struct mCore* core, struct ELF* elf) { struct ELFProgramHeaders ph; @@ -467,7 +473,7 @@ bool mCoreLoadELF(struct mCore* core, struct ELF* elf) { return true; } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void mCoreLoadELFSymbols(struct mDebuggerSymbols* symbols, struct ELF* elf) { size_t symIndex = ELFFindSection(elf, ".symtab"); size_t names = ELFFindSection(elf, ".strtab"); diff --git a/src/core/flags.h.in b/src/core/flags.h.in index 3bd142a44ac..ebfd67ed93a 100644 --- a/src/core/flags.h.in +++ b/src/core/flags.h.in @@ -49,14 +49,22 @@ // ENABLE flags +#ifndef ENABLE_DEBUGGERS +#cmakedefine ENABLE_DEBUGGERS +#endif + +#ifndef ENABLE_GDB_STUB +#cmakedefine ENABLE_GDB_STUB +#endif + #ifndef ENABLE_SCRIPTING #cmakedefine ENABLE_SCRIPTING #endif // USE flags -#ifndef USE_DEBUGGERS -#cmakedefine USE_DEBUGGERS +#ifndef USE_DISCORD_RPC +#cmakedefine USE_DISCORD_RPC #endif #ifndef USE_EDITLINE @@ -75,10 +83,6 @@ #cmakedefine USE_FFMPEG #endif -#ifndef USE_GDB_STUB -#cmakedefine USE_GDB_STUB -#endif - #ifndef USE_JSON_C #cmakedefine USE_JSON_C #endif @@ -133,6 +137,10 @@ #cmakedefine HAVE_CRC32 #endif +#ifndef HAVE_LOCALE +#cmakedefine HAVE_LOCALE +#endif + #ifndef HAVE_LOCALTIME_R #cmakedefine HAVE_LOCALTIME_R #endif @@ -149,6 +157,10 @@ #cmakedefine HAVE_PTHREAD_SETNAME_NP #endif +#ifndef HAVE_SNPRINTF_L +#cmakedefine HAVE_SNPRINTF_L +#endif + #ifndef HAVE_STRDUP #cmakedefine HAVE_STRDUP #endif diff --git a/src/core/rewind.c b/src/core/rewind.c index 21120ec2154..b8ba752dd7f 100644 --- a/src/core/rewind.c +++ b/src/core/rewind.c @@ -115,7 +115,7 @@ void _rewindDiff(struct mCoreRewindContext* context) { context->currentState->unmap(context->currentState, next, size); } -bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) { +bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core, unsigned count) { #ifndef DISABLE_THREADING if (context->onThread) { MutexLock(&context->mutex); @@ -129,30 +129,34 @@ bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) #endif return false; } - --context->size; - mCoreLoadStateNamed(core, context->previousState, SAVESTATE_SAVEDATA | SAVESTATE_RTC); - if (context->current == 0) { - context->current = mCoreRewindPatchesSize(&context->patchMemory); - } - --context->current; + for (; count && context->size; --count, --context->size) { + if (context->current == 0) { + context->current = mCoreRewindPatchesSize(&context->patchMemory); + } + --context->current; - if (context->size) { - struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current); - size_t size2 = context->previousState->size(context->previousState); - size_t size = context->currentState->size(context->currentState); - if (size2 < size) { - size = size2; + if (context->size > 1) { + struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current); + size_t size2 = context->previousState->size(context->previousState); + size_t size = context->currentState->size(context->currentState); + if (size2 < size) { + size = size2; + } + void* current = context->currentState->map(context->currentState, size, MAP_READ); + void* previous = context->previousState->map(context->previousState, size, MAP_WRITE); + patch->d.applyPatch(&patch->d, previous, size, current, size); + context->currentState->unmap(context->currentState, current, size); + context->previousState->unmap(context->previousState, previous, size); } - void* current = context->currentState->map(context->currentState, size, MAP_READ); - void* previous = context->previousState->map(context->previousState, size, MAP_WRITE); - patch->d.applyPatch(&patch->d, previous, size, current, size); - context->currentState->unmap(context->currentState, current, size); - context->previousState->unmap(context->previousState, previous, size); + struct VFile* nextState = context->previousState; + context->previousState = context->currentState; + context->currentState = nextState; } - struct VFile* nextState = context->previousState; - context->previousState = context->currentState; - context->currentState = nextState; + + mCoreLoadStateNamed(core, context->currentState, SAVESTATE_SAVEDATA | SAVESTATE_RTC); + + #ifndef DISABLE_THREADING if (context->onThread) { MutexUnlock(&context->mutex); diff --git a/src/core/scripting.c b/src/core/scripting.c index 5b1f39a63f7..acf324e1ff4 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -64,7 +64,7 @@ static void _seRun(const char* key, void* value, void* user) { se->run(se); } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS struct mScriptDebuggerEntry { enum mDebuggerEntryReason reason; struct mDebuggerEntryInfo* info; @@ -98,7 +98,7 @@ void mScriptBridgeInstallEngine(struct mScriptBridge* sb, struct mScriptEngine* HashTableInsert(&sb->engines, name, se); } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void mScriptBridgeSetDebugger(struct mScriptBridge* sb, struct mDebugger* debugger) { if (sb->debugger == debugger) { return; @@ -159,7 +159,7 @@ struct mScriptMemoryDomain { struct mCoreMemoryBlock block; }; -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS struct mScriptBreakpointName { uint32_t address; uint32_t maxAddress; @@ -182,6 +182,7 @@ struct mScriptDebugger { struct Table cbidMap; struct Table bpidMap; int64_t nextBreakpoint; + bool reentered; }; #endif @@ -189,7 +190,7 @@ struct mScriptCoreAdapter { struct mCore* core; struct mScriptContext* context; struct mScriptValue memory; -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS struct mScriptDebugger debugger; #endif struct mRumble rumble; @@ -701,7 +702,7 @@ static void _rebuildMemoryMap(struct mScriptContext* context, struct mScriptCore } } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS static void _freeBreakpoint(void* bp) { struct mScriptBreakpoint* point = bp; HashTableDeinit(&point->callbacks); @@ -779,6 +780,7 @@ static void _scriptDebuggerInit(struct mDebuggerModule* debugger) { struct mScriptDebugger* scriptDebugger = (struct mScriptDebugger*) debugger; debugger->isPaused = false; debugger->needsCallback = false; + scriptDebugger->reentered = false; HashTableInit(&scriptDebugger->breakpoints, 0, _freeBreakpoint); HashTableInit(&scriptDebugger->cbidMap, 0, NULL); @@ -812,6 +814,11 @@ static void _scriptDebuggerEntered(struct mDebuggerModule* debugger, enum mDebug default: return; } + + if (scriptDebugger->reentered) { + return; + } + _runCallbacks(scriptDebugger, point); debugger->isPaused = false; } @@ -929,7 +936,7 @@ static bool _mScriptCoreAdapterClearBreakpoint(struct mScriptCoreAdapter* adapte static void _mScriptCoreAdapterDeinit(struct mScriptCoreAdapter* adapter) { _clearMemoryMap(adapter->context, adapter, false); adapter->memory.type->free(&adapter->memory); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (adapter->core->debugger) { mDebuggerDetachModule(adapter->core->debugger, &adapter->debugger.d); } @@ -976,18 +983,105 @@ static void _mScriptCoreAdapterSetLuminanceCb(struct mScriptCoreAdapter* adapter adapter->luminanceCb = callback; } +static uint32_t _mScriptCoreAdapterRead8(struct mScriptCoreAdapter* adapter, uint32_t address) { +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = true; +#endif + uint32_t value = adapter->core->busRead8(adapter->core, address); +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = false; +#endif + return value; +} + +static uint32_t _mScriptCoreAdapterRead16(struct mScriptCoreAdapter* adapter, uint32_t address) { +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = true; +#endif + uint32_t value = adapter->core->busRead16(adapter->core, address); +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = false; +#endif + return value; +} + +static uint32_t _mScriptCoreAdapterRead32(struct mScriptCoreAdapter* adapter, uint32_t address) { +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = true; +#endif + uint32_t value = adapter->core->busRead32(adapter->core, address); +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = false; +#endif + return value; +} + +static struct mScriptValue* _mScriptCoreAdapterReadRange(struct mScriptCoreAdapter* adapter, uint32_t address, uint32_t length) { +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = true; +#endif + struct mScriptValue* value = mScriptStringCreateEmpty(length); + char* buffer = value->value.string->buffer; + uint32_t i; + for (i = 0; i < length; ++i, ++address) { + buffer[i] = adapter->core->busRead8(adapter->core, address); + } +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = false; +#endif + return value; +} + +static void _mScriptCoreAdapterWrite8(struct mScriptCoreAdapter* adapter, uint32_t address, uint8_t value) { +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = true; +#endif + adapter->core->busWrite8(adapter->core, address, value); +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = false; +#endif +} + +static void _mScriptCoreAdapterWrite16(struct mScriptCoreAdapter* adapter, uint32_t address, uint16_t value) { +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = true; +#endif + adapter->core->busWrite16(adapter->core, address, value); +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = false; +#endif +} + +static void _mScriptCoreAdapterWrite32(struct mScriptCoreAdapter* adapter, uint32_t address, uint32_t value) { +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = true; +#endif + adapter->core->busWrite32(adapter->core, address, value); +#ifdef ENABLE_DEBUGGERS + adapter->debugger.reentered = false; +#endif +} + mSCRIPT_DECLARE_STRUCT(mScriptCoreAdapter); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, W(mCore), _get, _mScriptCoreAdapterGet, 1, CHARP, name); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, _deinit, _mScriptCoreAdapterDeinit, 0); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, reset, _mScriptCoreAdapterReset, 0); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, WTABLE, setRotationCallbacks, _mScriptCoreAdapterSetRotationCbTable, 1, WTABLE, cbTable); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, setSolarSensorCallback, _mScriptCoreAdapterSetLuminanceCb, 1, WRAPPER, callback); -#ifdef USE_DEBUGGERS + +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, U32, read8, _mScriptCoreAdapterRead8, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, U32, read16, _mScriptCoreAdapterRead16, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, U32, read32, _mScriptCoreAdapterRead32, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, WSTR, readRange, _mScriptCoreAdapterReadRange, 2, U32, address, U32, length); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, write8, _mScriptCoreAdapterWrite8, 2, U32, address, U8, value); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, write16, _mScriptCoreAdapterWrite16, 2, U32, address, U16, value); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, write32, _mScriptCoreAdapterWrite32, 2, U32, address, U32, value); + +#ifdef ENABLE_DEBUGGERS mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptCoreAdapter, S64, setBreakpoint, _mScriptCoreAdapterSetBreakpoint, 3, WRAPPER, callback, U32, address, S32, segment); mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptCoreAdapter, S64, setWatchpoint, _mScriptCoreAdapterSetWatchpoint, 4, WRAPPER, callback, U32, address, S32, type, S32, segment); mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptCoreAdapter, S64, setRangeWatchpoint, _mScriptCoreAdapterSetRangeWatchpoint, 5, WRAPPER, callback, U32, minAddress, U32, maxAddress, S32, type, S32, segment); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, BOOL, clearBreakpoint, _mScriptCoreAdapterClearBreakpoint, 1, S64, cbid); -#endif mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptCoreAdapter, setBreakpoint) mSCRIPT_NO_DEFAULT, @@ -1009,6 +1103,7 @@ mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptCoreAdapter, setRangeWatchpoint) mSCRIPT_NO_DEFAULT, mSCRIPT_S32(-1) mSCRIPT_DEFINE_DEFAULTS_END; +#endif mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter) mSCRIPT_DEFINE_CLASS_DOCSTRING( @@ -1040,7 +1135,14 @@ mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter) "Note that the full range of values is not used by games, and the exact range depends on the calibration done by the game itself." ) mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, setSolarSensorCallback) -#ifdef USE_DEBUGGERS + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, read8) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, read16) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, read32) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, readRange) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, write8) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, write16) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, write32) +#ifdef ENABLE_DEBUGGERS mSCRIPT_DEFINE_DOCSTRING("Set a breakpoint at a given address") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, setBreakpoint) mSCRIPT_DEFINE_DOCSTRING("Clear a breakpoint or watchpoint for a given id returned by a previous call") diff --git a/src/core/sync.c b/src/core/sync.c index b2a65493bc0..e46e7dcda00 100644 --- a/src/core/sync.c +++ b/src/core/sync.c @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include -#include +#include static void _changeVideoSync(struct mCoreSync* sync, bool wait) { // Make sure the video thread can process events while the GBA thread is paused @@ -79,17 +79,17 @@ void mCoreSyncSetVideoSync(struct mCoreSync* sync, bool wait) { _changeVideoSync(sync, wait); } -bool mCoreSyncProduceAudio(struct mCoreSync* sync, const struct blip_t* buf, size_t samples) { +bool mCoreSyncProduceAudio(struct mCoreSync* sync, const struct mAudioBuffer* buf) { if (!sync) { return true; } - size_t produced = blip_samples_avail(buf); + size_t produced = mAudioBufferAvailable(buf); size_t producedNew = produced; - while (sync->audioWait && producedNew >= samples) { + while (sync->audioWait && sync->audioHighWater && producedNew >= sync->audioHighWater) { ConditionWait(&sync->audioRequiredCond, &sync->audioBufferMutex); produced = producedNew; - producedNew = blip_samples_avail(buf); + producedNew = mAudioBufferAvailable(buf); } MutexUnlock(&sync->audioBufferMutex); return producedNew != produced; diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index c2593bf14a1..e66f2a722fd 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -327,7 +327,7 @@ M_TEST_DEFINE(screenshot) { TEARDOWN_CORE; } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void _setupBp(struct mCore* core) { switch (core->platform(core)) { #ifdef M_CORE_GBA @@ -547,6 +547,40 @@ M_TEST_DEFINE(basicWatchpoint) { mDebuggerDeinit(&debugger); } +M_TEST_DEFINE(watchpointReentrant) { + SETUP_LUA; + mScriptContextAttachStdlib(&context); + CREATE_CORE; + struct mDebugger debugger; + core->reset(core); + mScriptContextAttachCore(&context, core); + + mDebuggerInit(&debugger); + mDebuggerAttach(&debugger, core); + + TEST_PROGRAM( + "hit = 0\n" + "function bkpt()\n" + " hit = hit + 1\n" + "end" + ); + struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE); + lua->setGlobal(lua, "base", &base); + TEST_PROGRAM("assert(0 < emu:setWatchpoint(bkpt, base, C.WATCHPOINT_TYPE.READ))"); + + TEST_PROGRAM("hit = 0"); + core->busRead8(core, RAM_BASE); + TEST_PROGRAM("assert(hit == 1)"); + TEST_PROGRAM("emu:read8(base)"); + TEST_PROGRAM("assert(hit == 1)"); + core->busRead8(core, RAM_BASE); + TEST_PROGRAM("assert(hit == 2)"); + + mScriptContextDeinit(&context); + TEARDOWN_CORE; + mDebuggerDeinit(&debugger); +} + M_TEST_DEFINE(removeBreakpoint) { SETUP_LUA; mScriptContextAttachStdlib(&context); @@ -727,7 +761,7 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore, cmocka_unit_test(memoryWrite), cmocka_unit_test(logging), cmocka_unit_test(screenshot), -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS #ifdef M_CORE_GBA cmocka_unit_test(basicBreakpointGBA), #endif @@ -736,6 +770,7 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore, #endif cmocka_unit_test(multipleBreakpoint), cmocka_unit_test(basicWatchpoint), + cmocka_unit_test(watchpointReentrant), cmocka_unit_test(removeBreakpoint), cmocka_unit_test(overlappingBreakpoint), cmocka_unit_test(overlappingWatchpoint), diff --git a/src/core/thread.c b/src/core/thread.c index 903d4b8446a..0be5f81e21f 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -5,7 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include -#include #include #ifdef ENABLE_SCRIPTING #include @@ -42,18 +41,14 @@ static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) { static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args); -static void _changeState(struct mCoreThreadInternal* threadContext, enum mCoreThreadState newState, bool broadcast) { - MutexLock(&threadContext->stateMutex); +static void _changeState(struct mCoreThreadInternal* threadContext, enum mCoreThreadState newState) { threadContext->state = newState; - if (broadcast) { - ConditionWake(&threadContext->stateCond); - } - MutexUnlock(&threadContext->stateMutex); + ConditionWake(&threadContext->stateOffThreadCond); } static void _waitOnInterrupt(struct mCoreThreadInternal* threadContext) { while (threadContext->state == mTHREAD_INTERRUPTED || threadContext->state == mTHREAD_INTERRUPTING) { - ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); + ConditionWait(&threadContext->stateOnThreadCond, &threadContext->stateMutex); } } @@ -107,14 +102,14 @@ static void _wait(struct mCoreThreadInternal* threadContext) { MutexUnlock(&threadContext->sync.audioBufferMutex); } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (threadContext->core && threadContext->core->debugger) { mDebuggerInterrupt(threadContext->core->debugger); } #endif MutexLock(&threadContext->stateMutex); - ConditionWake(&threadContext->stateCond); + ConditionWake(&threadContext->stateOnThreadCond); } static void _waitOnRequest(struct mCoreThreadInternal* threadContext, enum mCoreThreadRequest request) { @@ -144,7 +139,7 @@ static void _sendRequest(struct mCoreThreadInternal* threadContext, enum mCoreTh static void _cancelRequest(struct mCoreThreadInternal* threadContext, enum mCoreThreadRequest request) { threadContext->requested &= ~request; _pokeRequest(threadContext); - ConditionWake(&threadContext->stateCond); + ConditionWake(&threadContext->stateOnThreadCond); } void _frameStarted(void* context) { @@ -153,7 +148,7 @@ void _frameStarted(void* context) { return; } if (thread->core->opts.rewindEnable && thread->core->opts.rewindBufferCapacity > 0) { - if (!thread->impl->rewinding || !mCoreRewindRestore(&thread->impl->rewind, thread->core)) { + if (!thread->impl->rewinding || !mCoreRewindRestore(&thread->impl->rewind, thread->core, 1)) { if (thread->impl->rewind.rewindFrameCounter == 0) { mCoreRewindAppend(&thread->impl->rewind, thread->core); thread->impl->rewind.rewindFrameCounter = thread->core->opts.rewindBufferInterval; @@ -178,7 +173,9 @@ void _crashed(void* context) { if (!thread) { return; } - _changeState(thread->impl, mTHREAD_CRASHED, true); + MutexLock(&thread->impl->stateMutex); + _changeState(thread->impl, mTHREAD_CRASHED); + MutexUnlock(&thread->impl->stateMutex); } void _coreSleep(void* context) { @@ -196,7 +193,9 @@ void _coreShutdown(void* context) { if (!thread) { return; } - _changeState(thread->impl, mTHREAD_EXITING, true); + MutexLock(&thread->impl->stateMutex); + _changeState(thread->impl, mTHREAD_EXITING); + MutexUnlock(&thread->impl->stateMutex); } #ifdef ENABLE_SCRIPTING @@ -218,17 +217,17 @@ ADD_CALLBACK(savedataUpdated) ADD_CALLBACK(alarm) #undef ADD_CALLBACK -#define CALLBACK(NAME) _script_ ## NAME +#define SCRIPT(NAME) _script_ ## NAME static void _mCoreThreadAddCallbacks(struct mCoreThread* threadContext) { struct mCoreCallbacks callbacks = { - .videoFrameEnded = CALLBACK(frame), - .coreCrashed = CALLBACK(crashed), - .sleep = CALLBACK(sleep), - .shutdown = CALLBACK(stop), - .keysRead = CALLBACK(keysRead), - .savedataUpdated = CALLBACK(savedataUpdated), - .alarm = CALLBACK(alarm), + .videoFrameEnded = SCRIPT(frame), + .coreCrashed = SCRIPT(crashed), + .sleep = SCRIPT(sleep), + .shutdown = SCRIPT(stop), + .keysRead = SCRIPT(keysRead), + .savedataUpdated = SCRIPT(savedataUpdated), + .alarm = SCRIPT(alarm), .context = threadContext }; threadContext->core->addCoreCallbacks(threadContext->core, &callbacks); @@ -303,7 +302,9 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { core->reset(core); threadContext->impl->core = core; - _changeState(threadContext->impl, mTHREAD_RUNNING, true); + MutexLock(&threadContext->impl->stateMutex); + _changeState(threadContext->impl, mTHREAD_RUNNING); + MutexUnlock(&threadContext->impl->stateMutex); if (threadContext->resetCallback) { threadContext->resetCallback(threadContext); @@ -326,44 +327,47 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { bool wasPaused = false; int pendingRequests = 0; + MutexLock(&impl->stateMutex); while (impl->state < mTHREAD_EXITING) { -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS struct mDebugger* debugger = core->debugger; if (debugger) { + MutexUnlock(&impl->stateMutex); mDebuggerRun(debugger); + MutexLock(&impl->stateMutex); if (debugger->state == DEBUGGER_SHUTDOWN) { - _changeState(impl, mTHREAD_EXITING, false); + impl->state = mTHREAD_EXITING; } } else #endif { while (impl->state == mTHREAD_RUNNING) { + MutexUnlock(&impl->stateMutex); core->runLoop(core); + MutexLock(&impl->stateMutex); } } - MutexLock(&impl->stateMutex); while (impl->state >= mTHREAD_MIN_WAITING && impl->state < mTHREAD_EXITING) { if (impl->state == mTHREAD_INTERRUPTING) { - impl->state = mTHREAD_INTERRUPTED; - ConditionWake(&impl->stateCond); + _changeState(impl, mTHREAD_INTERRUPTED); } while (impl->state >= mTHREAD_MIN_WAITING && impl->state <= mTHREAD_MAX_WAITING) { -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (debugger && debugger->state != DEBUGGER_SHUTDOWN) { mDebuggerUpdate(debugger); - ConditionWaitTimed(&impl->stateCond, &impl->stateMutex, 10); + ConditionWaitTimed(&impl->stateOnThreadCond, &impl->stateMutex, 10); } else #endif { - ConditionWait(&impl->stateCond, &impl->stateMutex); + ConditionWait(&impl->stateOnThreadCond, &impl->stateMutex); } if (impl->sync.audioWait) { MutexUnlock(&impl->stateMutex); mCoreSyncLockAudio(&impl->sync); - mCoreSyncProduceAudio(&impl->sync, core->getAudioChannel(core, 0), core->getAudioBufferSize(core)); + mCoreSyncProduceAudio(&impl->sync, core->getAudioBuffer(core)); MutexLock(&impl->stateMutex); } } @@ -386,14 +390,13 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { if (impl->state == mTHREAD_REQUEST) { if (pendingRequests) { if (pendingRequests & mTHREAD_REQ_PAUSE) { - impl->state = mTHREAD_PAUSED; + _changeState(impl, mTHREAD_PAUSED); } if (pendingRequests & mTHREAD_REQ_WAIT) { - impl->state = mTHREAD_PAUSED; + _changeState(impl, mTHREAD_PAUSED); } } else { - impl->state = mTHREAD_RUNNING; - ConditionWake(&threadContext->impl->stateCond); + _changeState(impl, mTHREAD_RUNNING); } } MutexUnlock(&impl->stateMutex); @@ -427,11 +430,14 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { threadContext->run(threadContext); } } + MutexLock(&impl->stateMutex); } - while (impl->state < mTHREAD_SHUTDOWN) { - _changeState(impl, mTHREAD_SHUTDOWN, false); + if (impl->state < mTHREAD_SHUTDOWN) { + impl->state = mTHREAD_SHUTDOWN; } + ConditionWake(&threadContext->impl->stateOffThreadCond); + MutexUnlock(&impl->stateMutex); if (core->opts.rewindEnable) { mCoreRewindContextDeinit(&impl->rewind); @@ -469,7 +475,8 @@ bool mCoreThreadStart(struct mCoreThread* threadContext) { } MutexInit(&threadContext->impl->stateMutex); - ConditionInit(&threadContext->impl->stateCond); + ConditionInit(&threadContext->impl->stateOnThreadCond); + ConditionInit(&threadContext->impl->stateOffThreadCond); MutexInit(&threadContext->impl->sync.videoFrameMutex); ConditionInit(&threadContext->impl->sync.videoFrameAvailableCond); @@ -490,11 +497,12 @@ bool mCoreThreadStart(struct mCoreThread* threadContext) { threadContext->impl->sync.audioWait = threadContext->core->opts.audioSync; threadContext->impl->sync.videoFrameWait = threadContext->core->opts.videoSync; threadContext->impl->sync.fpsTarget = threadContext->core->opts.fpsTarget; + threadContext->impl->sync.audioHighWater = 512; MutexLock(&threadContext->impl->stateMutex); ThreadCreate(&threadContext->impl->thread, _mCoreThreadRun, threadContext); while (threadContext->impl->state < mTHREAD_RUNNING) { - ConditionWait(&threadContext->impl->stateCond, &threadContext->impl->stateMutex); + ConditionWait(&threadContext->impl->stateOffThreadCond, &threadContext->impl->stateMutex); } MutexUnlock(&threadContext->impl->stateMutex); @@ -536,7 +544,7 @@ bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) { void mCoreThreadMarkCrashed(struct mCoreThread* threadContext) { MutexLock(&threadContext->impl->stateMutex); - threadContext->impl->state = mTHREAD_CRASHED; + _changeState(threadContext->impl, mTHREAD_CRASHED); MutexUnlock(&threadContext->impl->stateMutex); } @@ -544,7 +552,7 @@ void mCoreThreadClearCrashed(struct mCoreThread* threadContext) { MutexLock(&threadContext->impl->stateMutex); if (threadContext->impl->state == mTHREAD_CRASHED) { threadContext->impl->state = mTHREAD_REQUEST; - ConditionWake(&threadContext->impl->stateCond); + ConditionWake(&threadContext->impl->stateOnThreadCond); } MutexUnlock(&threadContext->impl->stateMutex); } @@ -553,7 +561,7 @@ void mCoreThreadEnd(struct mCoreThread* threadContext) { MutexLock(&threadContext->impl->stateMutex); _waitOnInterrupt(threadContext->impl); threadContext->impl->state = mTHREAD_EXITING; - ConditionWake(&threadContext->impl->stateCond); + ConditionWake(&threadContext->impl->stateOnThreadCond); MutexUnlock(&threadContext->impl->stateMutex); MutexLock(&threadContext->impl->sync.audioBufferMutex); threadContext->impl->sync.audioWait = 0; @@ -582,7 +590,8 @@ void mCoreThreadJoin(struct mCoreThread* threadContext) { ThreadJoin(&threadContext->impl->thread); MutexDeinit(&threadContext->impl->stateMutex); - ConditionDeinit(&threadContext->impl->stateCond); + ConditionDeinit(&threadContext->impl->stateOnThreadCond); + ConditionDeinit(&threadContext->impl->stateOffThreadCond); MutexDeinit(&threadContext->impl->sync.videoFrameMutex); ConditionWake(&threadContext->impl->sync.videoFrameAvailableCond); @@ -634,7 +643,6 @@ void mCoreThreadInterruptFromThread(struct mCoreThread* threadContext) { return; } threadContext->impl->state = mTHREAD_INTERRUPTING; - ConditionWake(&threadContext->impl->stateCond); MutexUnlock(&threadContext->impl->stateMutex); } @@ -650,7 +658,7 @@ void mCoreThreadContinue(struct mCoreThread* threadContext) { } else { threadContext->impl->state = mTHREAD_RUNNING; } - ConditionWake(&threadContext->impl->stateCond); + ConditionWake(&threadContext->impl->stateOnThreadCond); } MutexUnlock(&threadContext->impl->stateMutex); } @@ -710,7 +718,7 @@ void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) threadContext->impl->rewinding = rewinding; if (rewinding && threadContext->impl->state == mTHREAD_CRASHED) { threadContext->impl->state = mTHREAD_REQUEST; - ConditionWake(&threadContext->impl->stateCond); + ConditionWake(&threadContext->impl->stateOnThreadCond); } MutexUnlock(&threadContext->impl->stateMutex); } diff --git a/src/debugger/CMakeLists.txt b/src/debugger/CMakeLists.txt index ce2ade635e8..976f88c931d 100644 --- a/src/debugger/CMakeLists.txt +++ b/src/debugger/CMakeLists.txt @@ -15,7 +15,7 @@ if(USE_EDITLINE) list(APPEND SOURCE_FILES cli-el-backend.c) endif() -if(USE_GDB_STUB) +if(ENABLE_GDB_STUB) list(APPEND SOURCE_FILES gdb-stub.c) endif() diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 9c9696f9f65..95b2037dcd4 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -615,7 +615,7 @@ static struct ParseTree* _parseTree(const char** string) { struct ParseTree* tree = NULL; if (!error) { tree = parseTreeCreate(); - parseLexedExpression(tree, &lv); + error = !parseLexedExpression(tree, &lv); } lexFree(&lv); LexVectorClear(&lv); @@ -1420,7 +1420,7 @@ static void _loadSymbols(struct CLIDebugger* debugger, struct CLIDebugVector* dv #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS mCoreLoadELFSymbols(symbolTable, elf); #endif ELFClose(elf); diff --git a/src/debugger/debugger.c b/src/debugger/debugger.c index 7de4772d721..a03f106d928 100644 --- a/src/debugger/debugger.c +++ b/src/debugger/debugger.c @@ -10,7 +10,7 @@ #include #include -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB #include #endif @@ -37,7 +37,7 @@ struct mDebuggerModule* mDebuggerCreateModule(enum mDebuggerType type, struct mC union DebugUnion { struct mDebuggerModule d; struct CLIDebugger cli; -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB struct GDBStub gdb; #endif }; @@ -52,7 +52,7 @@ struct mDebuggerModule* mDebuggerCreateModule(enum mDebuggerType type, struct mC CLIDebuggerAttachSystem(&debugger->cli, sys); break; case DEBUGGER_GDB: -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB GDBStubCreate(&debugger->gdb); struct Address localHost = { .version = IPV4, diff --git a/src/feature/commandline.c b/src/feature/commandline.c index 7e720886d3a..cdf788b1990 100644 --- a/src/feature/commandline.c +++ b/src/feature/commandline.c @@ -12,7 +12,7 @@ #include #include -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB #include #endif #ifdef USE_EDITLINE @@ -40,7 +40,7 @@ static const struct option _options[] = { #ifdef USE_EDITLINE { "debug", no_argument, 0, 'd' }, #endif -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB { "gdb", no_argument, 0, 'g' }, #endif { "help", no_argument, 0, 'h' }, @@ -85,7 +85,7 @@ bool mArgumentsParse(struct mArguments* args, int argc, char* const* argv, struc #ifdef USE_EDITLINE "d" #endif -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB "g" #endif ; @@ -151,7 +151,7 @@ bool mArgumentsParse(struct mArguments* args, int argc, char* const* argv, struc args->debugCli = true; break; #endif -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB case 'g': args->debugAtStart = true; args->debugGdb = true; @@ -231,7 +231,7 @@ bool mArgumentsApplyDebugger(const struct mArguments* args, struct mCore* core, } #endif -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB if (args->debugGdb) { struct mDebuggerModule* module = mDebuggerCreateModule(DEBUGGER_GDB, core); if (module) { @@ -355,7 +355,7 @@ void usage(const char* arg0, const char* prologue, const char* epilogue, const s #ifdef USE_EDITLINE " -d, --debug Use command-line debugger\n" #endif -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB " -g, --gdb Start GDB session (default port 2345)\n" #endif " -l, --log-level N Log level mask\n" diff --git a/src/feature/gui/gui-config.c b/src/feature/gui/gui-config.c index 4a6c7987196..e110ef76857 100644 --- a/src/feature/gui/gui-config.c +++ b/src/feature/gui/gui-config.c @@ -12,7 +12,7 @@ #include #ifdef M_CORE_GB #include -#include +#include #endif #include #include diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index 8c25a79fbd7..b3ce21ad970 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -220,7 +220,7 @@ void mGUIInit(struct mGUIRunner* runner, const char* port) { runner->fps = 0; runner->lastFpsCheck = 0; runner->totalDelta = 0; - CircleBufferInit(&runner->fpsBuffer, FPS_BUFFER_SIZE * sizeof(uint32_t)); + mCircleBufferInit(&runner->fpsBuffer, FPS_BUFFER_SIZE * sizeof(uint32_t)); mInputMapInit(&runner->params.keyMap, &_mGUIKeyInfo); mCoreConfigInit(&runner->config, runner->port); @@ -284,7 +284,7 @@ void mGUIDeinit(struct mGUIRunner* runner) { if (runner->teardown) { runner->teardown(runner); } - CircleBufferDeinit(&runner->fpsBuffer); + mCircleBufferDeinit(&runner->fpsBuffer); mInputMapDeinit(&runner->params.keyMap); mCoreConfigDeinit(&runner->config); if (logger.vf) { @@ -502,7 +502,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { runner->fps = 0; bool fastForward = false; while (running) { - CircleBufferClear(&runner->fpsBuffer); + mCircleBufferClear(&runner->fpsBuffer); runner->totalDelta = 0; struct timeval tv; gettimeofday(&tv, 0); @@ -610,17 +610,17 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { uint64_t delta = t - runner->lastFpsCheck; runner->lastFpsCheck = t; if (delta > 0x7FFFFFFFLL) { - CircleBufferClear(&runner->fpsBuffer); + mCircleBufferClear(&runner->fpsBuffer); runner->fps = 0; } - if (CircleBufferSize(&runner->fpsBuffer) == CircleBufferCapacity(&runner->fpsBuffer)) { + if (mCircleBufferSize(&runner->fpsBuffer) == mCircleBufferCapacity(&runner->fpsBuffer)) { int32_t last; - CircleBufferRead32(&runner->fpsBuffer, &last); + mCircleBufferRead32(&runner->fpsBuffer, &last); runner->totalDelta -= last; } - CircleBufferWrite32(&runner->fpsBuffer, delta); + mCircleBufferWrite32(&runner->fpsBuffer, delta); runner->totalDelta += delta; - runner->fps = (CircleBufferSize(&runner->fpsBuffer) * FPS_GRANULARITY * 1000000.0f) / (runner->totalDelta * sizeof(uint32_t)); + runner->fps = (mCircleBufferSize(&runner->fpsBuffer) * FPS_GRANULARITY * 1000000.0f) / (runner->totalDelta * sizeof(uint32_t)); } } if (frame % (AUTOSAVE_GRANULARITY * (fastForwarding ? 2 : 1)) == 0) { diff --git a/src/feature/gui/gui-runner.h b/src/feature/gui/gui-runner.h index 0dc412838bd..334ef57d966 100644 --- a/src/feature/gui/gui-runner.h +++ b/src/feature/gui/gui-runner.h @@ -78,7 +78,7 @@ struct mGUIRunner { float fps; int64_t lastFpsCheck; int32_t totalDelta; - struct CircleBuffer fpsBuffer; + struct mCircleBuffer fpsBuffer; void (*setup)(struct mGUIRunner*); void (*teardown)(struct mGUIRunner*); diff --git a/src/feature/proxy-backend.c b/src/feature/proxy-backend.c index fb6e555edbf..54003422722 100644 --- a/src/feature/proxy-backend.c +++ b/src/feature/proxy-backend.c @@ -61,7 +61,7 @@ static void _mVideoProxyBackendClear(struct VideoBackend* v) { mVideoProxyBackendSubmit(proxy, &cmd, NULL); } -static void _mVideoProxyBackendContextResized(struct VideoBackend* v, unsigned w, unsigned h) { +static void _mVideoProxyBackendContextResized(struct VideoBackend* v, unsigned w, unsigned h, unsigned maxW, unsigned maxH) { struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v; struct mVideoBackendCommand cmd = { .cmd = mVB_CMD_CONTEXT_RESIZED, @@ -69,6 +69,8 @@ static void _mVideoProxyBackendContextResized(struct VideoBackend* v, unsigned w .u = { .width = w, .height = h, + .maxW = maxW, + .maxH = maxH, } } }; @@ -139,8 +141,8 @@ void mVideoProxyBackendInit(struct mVideoProxyBackend* proxy, struct VideoBacken proxy->d.drawFrame = _mVideoProxyBackendDrawFrame; proxy->backend = backend; - RingFIFOInit(&proxy->in, 0x400); - RingFIFOInit(&proxy->out, 0x400); + RingFIFOInit(&proxy->in, sizeof(union mVideoBackendCommandData) * 0x80); + RingFIFOInit(&proxy->out, sizeof(union mVideoBackendCommandData) * 0x80); MutexInit(&proxy->inLock); MutexInit(&proxy->outLock); ConditionInit(&proxy->inWait); @@ -209,7 +211,7 @@ bool mVideoProxyBackendRun(struct mVideoProxyBackend* proxy, bool block) { proxy->backend->clear(proxy->backend); break; case mVB_CMD_CONTEXT_RESIZED: - proxy->backend->contextResized(proxy->backend, cmd.data.u.width, cmd.data.u.height); + proxy->backend->contextResized(proxy->backend, cmd.data.u.width, cmd.data.u.height, cmd.data.u.maxW, cmd.data.u.maxH); break; case mVB_CMD_SET_IMAGE_SIZE: proxy->backend->setImageSize(proxy->backend, cmd.layer, cmd.data.s.width, cmd.data.s.height); @@ -229,7 +231,7 @@ bool mVideoProxyBackendRun(struct mVideoProxyBackend* proxy, bool block) { } ok = true; } - } while (block); + } while (block && !ok); return ok; } diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index fb2065b425b..978700864c5 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -87,8 +87,8 @@ struct mVideoLogChannel { enum mVideoLoggerInjectionPoint injectionPoint; uint32_t ignorePackets; - struct CircleBuffer injectedBuffer; - struct CircleBuffer buffer; + struct mCircleBuffer injectedBuffer; + struct mCircleBuffer buffer; }; struct mVideoLogContext { @@ -662,8 +662,8 @@ bool mVideoLogContextLoad(struct mVideoLogContext* context, struct VFile* vf) { size_t i; for (i = 0; i < context->nChannels; ++i) { - CircleBufferInit(&context->channels[i].injectedBuffer, BUFFER_BASE_SIZE); - CircleBufferInit(&context->channels[i].buffer, BUFFER_BASE_SIZE); + mCircleBufferInit(&context->channels[i].injectedBuffer, BUFFER_BASE_SIZE); + mCircleBufferInit(&context->channels[i].buffer, BUFFER_BASE_SIZE); context->channels[i].bufferRemaining = 0; context->channels[i].currentPointer = pointer; context->channels[i].p = context; @@ -676,8 +676,8 @@ bool mVideoLogContextLoad(struct mVideoLogContext* context, struct VFile* vf) { #ifdef USE_ZLIB static void _flushBufferCompressed(struct mVideoLogContext* context) { - struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer; - if (!CircleBufferSize(buffer)) { + struct mCircleBuffer* buffer = &context->channels[context->activeChannel].buffer; + if (!mCircleBufferSize(buffer)) { return; } struct VFile* vfm = VFileMemChunk(NULL, 0); @@ -707,20 +707,20 @@ static void _flushBuffer(struct mVideoLogContext* context) { } #endif - struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer; - if (!CircleBufferSize(buffer)) { + struct mCircleBuffer* buffer = &context->channels[context->activeChannel].buffer; + if (!mCircleBufferSize(buffer)) { return; } struct mVLBlockHeader header = { 0 }; STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType); - STORE_32LE(CircleBufferSize(buffer), 0, &header.length); + STORE_32LE(mCircleBufferSize(buffer), 0, &header.length); STORE_32LE(context->activeChannel, 0, &header.channelId); context->backing->write(context->backing, &header, sizeof(header)); uint8_t writeBuffer[0x800]; - while (CircleBufferSize(buffer)) { - size_t read = CircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer)); + while (mCircleBufferSize(buffer)) { + size_t read = mCircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer)); context->backing->write(context->backing, writeBuffer, read); } } @@ -743,8 +743,8 @@ void mVideoLogContextDestroy(struct mCore* core, struct mVideoLogContext* contex size_t i; for (i = 0; i < context->nChannels; ++i) { - CircleBufferDeinit(&context->channels[i].injectedBuffer); - CircleBufferDeinit(&context->channels[i].buffer); + mCircleBufferDeinit(&context->channels[i].injectedBuffer); + mCircleBufferDeinit(&context->channels[i].buffer); #ifdef USE_ZLIB if (context->channels[i].inflating) { inflateEnd(&context->channels[i].inflateStream); @@ -778,8 +778,8 @@ void mVideoLogContextRewind(struct mVideoLogContext* context, struct mCore* core size_t i; for (i = 0; i < context->nChannels; ++i) { - CircleBufferClear(&context->channels[i].injectedBuffer); - CircleBufferClear(&context->channels[i].buffer); + mCircleBufferClear(&context->channels[i].injectedBuffer); + mCircleBufferClear(&context->channels[i].buffer); context->channels[i].bufferRemaining = 0; context->channels[i].currentPointer = pointer; #ifdef USE_ZLIB @@ -805,8 +805,8 @@ int mVideoLoggerAddChannel(struct mVideoLogContext* context) { int chid = context->nChannels; ++context->nChannels; context->channels[chid].p = context; - CircleBufferInit(&context->channels[chid].injectedBuffer, BUFFER_BASE_SIZE); - CircleBufferInit(&context->channels[chid].buffer, BUFFER_BASE_SIZE); + mCircleBufferInit(&context->channels[chid].injectedBuffer, BUFFER_BASE_SIZE); + mCircleBufferInit(&context->channels[chid].buffer, BUFFER_BASE_SIZE); context->channels[chid].injecting = false; context->channels[chid].injectionPoint = LOGGER_INJECTION_IMMEDIATE; context->channels[chid].ignorePackets = 0; @@ -898,7 +898,7 @@ static size_t _readBufferCompressed(struct VFile* vf, struct mVideoLogChannel* c } } - thisWrite = CircleBufferWrite(&channel->buffer, zbuffer, thisWrite - channel->inflateStream.avail_out); + thisWrite = mCircleBufferWrite(&channel->buffer, zbuffer, thisWrite - channel->inflateStream.avail_out); length -= thisWrite; read += thisWrite; @@ -921,7 +921,7 @@ static void _readBuffer(struct VFile* vf, struct mVideoLogChannel* channel, size if (thisRead <= 0) { return; } - size_t thisWrite = CircleBufferWrite(&channel->buffer, buffer, thisRead); + size_t thisWrite = mCircleBufferWrite(&channel->buffer, buffer, thisRead); length -= thisWrite; channel->bufferRemaining -= thisWrite; channel->currentPointer += thisWrite; @@ -986,16 +986,16 @@ static ssize_t mVideoLoggerReadChannel(struct mVideoLogChannel* channel, void* d if (channelId >= mVL_MAX_CHANNELS) { return 0; } - struct CircleBuffer* buffer = &channel->buffer; + struct mCircleBuffer* buffer = &channel->buffer; if (channel->injecting) { buffer = &channel->injectedBuffer; } - if (CircleBufferSize(buffer) >= length) { - return CircleBufferRead(buffer, data, length); + if (mCircleBufferSize(buffer) >= length) { + return mCircleBufferRead(buffer, data, length); } ssize_t size = 0; - if (CircleBufferSize(buffer)) { - size = CircleBufferRead(buffer, data, CircleBufferSize(buffer)); + if (mCircleBufferSize(buffer)) { + size = mCircleBufferRead(buffer, data, mCircleBufferSize(buffer)); if (size <= 0) { return size; } @@ -1005,7 +1005,7 @@ static ssize_t mVideoLoggerReadChannel(struct mVideoLogChannel* channel, void* d if (channel->injecting || !_fillBuffer(context, channelId, BUFFER_BASE_SIZE)) { return size; } - size += CircleBufferRead(buffer, data, length); + size += mCircleBufferRead(buffer, data, length); return size; } @@ -1019,20 +1019,20 @@ static ssize_t mVideoLoggerWriteChannel(struct mVideoLogChannel* channel, const _flushBuffer(context); context->activeChannel = channelId; } - struct CircleBuffer* buffer = &channel->buffer; + struct mCircleBuffer* buffer = &channel->buffer; if (channel->injecting) { buffer = &channel->injectedBuffer; } - if (CircleBufferCapacity(buffer) - CircleBufferSize(buffer) < length) { + if (mCircleBufferCapacity(buffer) - mCircleBufferSize(buffer) < length) { _flushBuffer(context); - if (CircleBufferCapacity(buffer) < length) { - CircleBufferDeinit(buffer); - CircleBufferInit(buffer, toPow2(length << 1)); + if (mCircleBufferCapacity(buffer) < length) { + mCircleBufferDeinit(buffer); + mCircleBufferInit(buffer, toPow2(length << 1)); } } - ssize_t read = CircleBufferWrite(buffer, data, length); - if (CircleBufferCapacity(buffer) == CircleBufferSize(buffer)) { + ssize_t read = mCircleBufferWrite(buffer, data, length); + if (mCircleBufferCapacity(buffer) == mCircleBufferSize(buffer)) { _flushBuffer(context); } return read; diff --git a/src/gb/audio.c b/src/gb/audio.c index 996fce66d14..4230fb88c21 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -5,7 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include -#include #include #include #include @@ -15,15 +14,10 @@ #include #endif -#ifdef __3DS__ -#define blip_add_delta blip_add_delta_fast -#endif - +#define AUDIO_BUFFER_SAMPLES 0x4000 #define FRAME_CYCLES (DMG_SM83_FREQUENCY >> 9) const uint32_t DMG_SM83_FREQUENCY = 0x400000; -static const int CLOCKS_PER_BLIP_FRAME = 0x1000; -static const unsigned BLIP_BUFFER_SIZE = 0x4000; static const int SAMPLE_INTERVAL = 32; static const int FILTER = 65368; const int GB_AUDIO_VOLUME_MAX = 0x100; @@ -33,10 +27,10 @@ static void _writeDuty(struct GBAudioEnvelope* envelope, uint8_t value); static bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudioStyle style); static void _resetSweep(struct GBAudioSweep* sweep); -static bool _resetEnvelope(struct GBAudioEnvelope* sweep); +static bool _resetEnvelope(struct GBAudioEnvelope* sweep, enum GBAudioStyle style); static void _updateEnvelope(struct GBAudioEnvelope* envelope); -static void _updateEnvelopeDead(struct GBAudioEnvelope* envelope); +static void _updateEnvelopeDead(struct GBAudioEnvelope* envelope, enum GBAudioStyle style); static bool _updateSweep(struct GBAudioSquareChannel* sweep, bool initial); static void _updateSquareSample(struct GBAudioSquareChannel* ch); @@ -57,12 +51,7 @@ static const int _squareChannelDuty[4][8] = { void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAudioStyle style) { audio->samples = samples; - audio->left = blip_new(BLIP_BUFFER_SIZE); - audio->right = blip_new(BLIP_BUFFER_SIZE); - audio->clockRate = DMG_SM83_FREQUENCY; - // Guess too large; we hang producing extra samples if we guess too low - blip_set_rates(audio->left, DMG_SM83_FREQUENCY, 96000); - blip_set_rates(audio->right, DMG_SM83_FREQUENCY, 96000); + mAudioBufferInit(&audio->buffer, AUDIO_BUFFER_SAMPLES, 2); audio->forceDisableCh[0] = false; audio->forceDisableCh[1] = false; audio->forceDisableCh[2] = false; @@ -86,8 +75,7 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu } void GBAudioDeinit(struct GBAudio* audio) { - blip_delete(audio->left); - blip_delete(audio->right); + mAudioBufferDeinit(&audio->buffer); } void GBAudioReset(struct GBAudio* audio) { @@ -123,11 +111,9 @@ void GBAudioReset(struct GBAudio* audio) { audio->sampleInterval = SAMPLE_INTERVAL * GB_MAX_SAMPLES; audio->lastSample = 0; audio->sampleIndex = 0; - audio->lastLeft = 0; - audio->lastRight = 0; audio->capLeft = 0; audio->capRight = 0; - audio->clock = 0; + mAudioBufferClear(&audio->buffer); audio->playingCh1 = false; audio->playingCh2 = false; audio->playingCh3 = false; @@ -140,14 +126,8 @@ void GBAudioReset(struct GBAudio* audio) { } void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) { - if (samples > BLIP_BUFFER_SIZE / 2) { - samples = BLIP_BUFFER_SIZE / 2; - } mCoreSyncLockAudio(audio->p->sync); audio->samples = samples; - blip_clear(audio->left); - blip_clear(audio->right); - audio->clock = 0; mCoreSyncConsumeAudio(audio->p->sync); } @@ -192,7 +172,7 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) { } } if (GBAudioRegisterControlIsRestart(value << 8)) { - audio->playingCh1 = _resetEnvelope(&audio->ch1.envelope); + audio->playingCh1 = _resetEnvelope(&audio->ch1.envelope, audio->style); audio->ch1.sweep.realFrequency = audio->ch1.control.frequency; _resetSweep(&audio->ch1.sweep); if (audio->playingCh1 && audio->ch1.sweep.shift) { @@ -243,7 +223,7 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) { } } if (GBAudioRegisterControlIsRestart(value << 8)) { - audio->playingCh2 = _resetEnvelope(&audio->ch2.envelope); + audio->playingCh2 = _resetEnvelope(&audio->ch2.envelope, audio->style); if (!audio->ch2.control.length) { audio->ch2.control.length = 64; @@ -383,7 +363,7 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) { } } if (GBAudioRegisterNoiseControlIsRestart(value)) { - audio->playingCh4 = _resetEnvelope(&audio->ch4.envelope); + audio->playingCh4 = _resetEnvelope(&audio->ch4.envelope, audio->style); audio->ch4.lfsr = 0; if (!audio->ch4.length) { @@ -845,41 +825,33 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { unsigned produced; int i; for (i = 0; i < GB_MAX_SAMPLES; ++i) { - int16_t sampleLeft = audio->currentSamples[i].left; - int16_t sampleRight = audio->currentSamples[i].right; - if ((size_t) blip_samples_avail(audio->left) < audio->samples) { - blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft); - blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight); - audio->lastLeft = sampleLeft; - audio->lastRight = sampleRight; - audio->clock += SAMPLE_INTERVAL; - if (audio->clock >= CLOCKS_PER_BLIP_FRAME) { - blip_end_frame(audio->left, CLOCKS_PER_BLIP_FRAME); - blip_end_frame(audio->right, CLOCKS_PER_BLIP_FRAME); - audio->clock -= CLOCKS_PER_BLIP_FRAME; - } - } + int16_t sample[2] = { + audio->currentSamples[i].left, + audio->currentSamples[i].right + }; + mAudioBufferWrite(&audio->buffer, sample, 1); if (audio->p->stream && audio->p->stream->postAudioFrame) { - audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight); + audio->p->stream->postAudioFrame(audio->p->stream, sample[0], sample[1]); } } - produced = blip_samples_avail(audio->left); + produced = mAudioBufferAvailable(&audio->buffer); bool wait = produced >= audio->samples; - if (!mCoreSyncProduceAudio(audio->p->sync, audio->left, audio->samples)) { + if (!mCoreSyncProduceAudio(audio->p->sync, &audio->buffer)) { // Interrupted audio->p->earlyExit = true; } if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) { - audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right); + audio->p->stream->postAudioBuffer(audio->p->stream, &audio->buffer); } mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval * audio->timingFactor - cyclesLate); } -bool _resetEnvelope(struct GBAudioEnvelope* envelope) { +bool _resetEnvelope(struct GBAudioEnvelope* envelope, enum GBAudioStyle style) { envelope->currentVolume = envelope->initialVolume; - _updateEnvelopeDead(envelope); + envelope->nextStep = envelope->stepTime; + _updateEnvelopeDead(envelope, style); return envelope->initialVolume || envelope->direction; } @@ -932,7 +904,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudi } envelope->currentVolume &= 0xF; } - _updateEnvelopeDead(envelope); + _updateEnvelopeDead(envelope, style); return envelope->initialVolume || envelope->direction; } @@ -968,16 +940,20 @@ static void _updateEnvelope(struct GBAudioEnvelope* envelope) { } } -static void _updateEnvelopeDead(struct GBAudioEnvelope* envelope) { +static void _updateEnvelopeDead(struct GBAudioEnvelope* envelope, enum GBAudioStyle style) { if (!envelope->stepTime) { envelope->dead = envelope->currentVolume ? 1 : 2; } else if (!envelope->direction && !envelope->currentVolume) { envelope->dead = 2; } else if (envelope->direction && envelope->currentVolume == 0xF) { envelope->dead = 1; - } else { + } else if (envelope->dead) { + // TODO: Figure out if this happens on DMG/CGB or just AGB + // TODO: Figure out the exact circumstances that lead to reloading the step + if (style == GB_AUDIO_GBA) { + envelope->nextStep = envelope->stepTime; + } envelope->dead = 0; - envelope->nextStep = envelope->stepTime; } } diff --git a/src/gb/core.c b/src/gb/core.c index 9154221d6d2..7023b29f738 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -164,7 +164,7 @@ static void _GBCoreDeinit(struct mCore* core) { #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 mDirectorySetDeinit(&core->dirs); #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (core->symbolTable) { mDebuggerSymbolTableDestroy(core->symbolTable); } @@ -428,16 +428,9 @@ static void _GBCorePutPixels(struct mCore* core, const void* buffer, size_t stri gbcore->renderer.d.putPixels(&gbcore->renderer.d, stride, buffer); } -static struct blip_t* _GBCoreGetAudioChannel(struct mCore* core, int ch) { +static struct mAudioBuffer* _GBCoreGetAudioBuffer(struct mCore* core) { struct GB* gb = core->board; - switch (ch) { - case 0: - return gb->audio.left; - case 1: - return gb->audio.right; - default: - return NULL; - } + return &gb->audio.buffer; } static void _GBCoreSetAudioBufferSize(struct mCore* core, size_t samples) { @@ -445,6 +438,11 @@ static void _GBCoreSetAudioBufferSize(struct mCore* core, size_t samples) { GBAudioResizeBuffer(&gb->audio, samples); } +static unsigned _GBCoreAudioSampleRate(const struct mCore* core) { + UNUSED(core); + return 131072; +} + static size_t _GBCoreGetAudioBufferSize(struct mCore* core) { struct GB* gb = core->board; return gb->audio.samples; @@ -1060,7 +1058,7 @@ static bool _GBCoreWriteRegister(struct mCore* core, const char* name, const voi return false; } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) { UNUSED(core); switch (type) { @@ -1302,7 +1300,8 @@ struct mCore* GBCoreCreate(void) { core->setVideoGLTex = _GBCoreSetVideoGLTex; core->getPixels = _GBCoreGetPixels; core->putPixels = _GBCorePutPixels; - core->getAudioChannel = _GBCoreGetAudioChannel; + core->audioSampleRate = _GBCoreAudioSampleRate; + core->getAudioBuffer = _GBCoreGetAudioBuffer; core->setAudioBufferSize = _GBCoreSetAudioBufferSize; core->getAudioBufferSize = _GBCoreGetAudioBufferSize; core->setAVStream = _GBCoreSetAVStream; @@ -1352,7 +1351,7 @@ struct mCore* GBCoreCreate(void) { core->listRegisters = _GBCoreListRegisters; core->readRegister = _GBCoreReadRegister; core->writeRegister = _GBCoreWriteRegister; -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS core->supportsDebuggerType = _GBCoreSupportsDebuggerType; core->debuggerPlatform = _GBCoreDebuggerPlatform; core->cliDebuggerSystem = _GBCoreCliDebuggerSystem; diff --git a/src/gb/gb.c b/src/gb/gb.c index 9583125244b..c0e966c27ca 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -106,7 +106,7 @@ static void GBDeinit(struct mCPUComponent* component) { bool GBLoadGBX(struct GBXMetadata* metadata, struct VFile* vf) { uint8_t footer[16]; - if (vf->seek(vf, -sizeof(footer), SEEK_END) < 0) { + if (vf->seek(vf, -(off_t) sizeof(footer), SEEK_END) < 0) { return false; } if (vf->read(vf, footer, sizeof(footer)) < (ssize_t) sizeof(footer)) { @@ -940,7 +940,7 @@ void GBProcessEvents(struct SM83Core* cpu) { nextEvent = cycles; do { -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS gb->timing.globalCycles += nextEvent; #endif nextEvent = mTimingTick(&gb->timing, nextEvent); @@ -1057,7 +1057,7 @@ void GBStop(struct SM83Core* cpu) { void GBIllegal(struct SM83Core* cpu) { struct GB* gb = (struct GB*) cpu->master; mLOG(GB, GAME_ERROR, "Hit illegal opcode at address %04X:%02X", cpu->pc, cpu->bus); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) { struct mDebuggerEntryInfo info = { .address = cpu->pc, @@ -1100,7 +1100,7 @@ bool GBIsROM(struct VFile* vf) { } uint8_t footer[16]; - vf->seek(vf, -sizeof(footer), SEEK_END); + vf->seek(vf, -(off_t) sizeof(footer), SEEK_END); if (vf->read(vf, footer, sizeof(footer)) < (ssize_t) sizeof(footer)) { return false; } diff --git a/src/gba/CMakeLists.txt b/src/gba/CMakeLists.txt index 4bf354f3a37..933ba3facce 100644 --- a/src/gba/CMakeLists.txt +++ b/src/gba/CMakeLists.txt @@ -40,7 +40,6 @@ set(SIO_FILES sio/lockstep.c) set(EXTRA_FILES - extra/audio-mixer.c extra/battlechip.c extra/proxy.c) diff --git a/src/gba/audio.c b/src/gba/audio.c index fb0e1b62c63..8ccc7f9d800 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -16,17 +15,12 @@ #define MP2K_LOCK_MAX 8 -#ifdef __3DS__ -#define blip_add_delta blip_add_delta_fast -#endif - mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio", "gba.audio"); const unsigned GBA_AUDIO_SAMPLES = 2048; const int GBA_AUDIO_VOLUME_MAX = 0x100; static const int SAMPLE_INTERVAL = GBA_ARM7TDMI_FREQUENCY / 0x4000; -static const int CLOCKS_PER_FRAME = 0x800; static int _applyBias(struct GBAAudio* audio, int sample); static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate); @@ -41,20 +35,14 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) { #ifdef __BIG_ENDIAN__ ++nr52; #endif - GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA); + GBAudioInit(&audio->psg, samples, nr52, GB_AUDIO_GBA); audio->psg.timing = &audio->p->timing; - audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY; audio->psg.frameEvent.context = audio; audio->samples = samples; - // Guess too large; we hang producing extra samples if we guess too low - blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000); - blip_set_rates(audio->psg.right, GBA_ARM7TDMI_FREQUENCY, 96000); - audio->externalMixing = false; audio->forceDisableChA = false; audio->forceDisableChB = false; audio->masterVolume = GBA_AUDIO_VOLUME_MAX; - audio->mixer = NULL; } void GBAAudioReset(struct GBAAudio* audio) { @@ -95,10 +83,6 @@ void GBAAudioReset(struct GBAAudio* audio) { audio->enable = false; audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / 0x8000; audio->psg.sampleInterval = audio->sampleInterval; - - blip_clear(audio->psg.left); - blip_clear(audio->psg.right); - audio->clock = 0; } void GBAAudioDeinit(struct GBAAudio* audio) { @@ -106,14 +90,9 @@ void GBAAudioDeinit(struct GBAAudio* audio) { } void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) { - if (samples > 0x2000) { - samples = 0x2000; - } mCoreSyncLockAudio(audio->p->sync); audio->samples = samples; - blip_clear(audio->psg.left); - blip_clear(audio->psg.right); - audio->clock = 0; + audio->psg.samples = samples; mCoreSyncConsumeAudio(audio->p->sync); } @@ -131,27 +110,6 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* mLOG(GBA_AUDIO, GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest); return; } - if (audio->mixer) { - uint32_t source = info->source; - uint32_t offsets[] = { 0x350, 0x980 }; - size_t i; - for (i = 0; i < sizeof(offsets) / sizeof(*offsets); ++i) { - if (source < GBA_BASE_EWRAM + offsets[i]) { - continue; - } - if (source >= GBA_BASE_IO + offsets[i]) { - continue; - } - uint32_t value = GBALoad32(audio->p->cpu, source - offsets[i], NULL); - if (value - MP2K_MAGIC <= MP2K_LOCK_MAX) { - audio->mixer->engage(audio->mixer, source - offsets[i]); - break; - } - } - if (i == sizeof(offsets) / sizeof(*offsets)) { - audio->externalMixing = false; - } - } } void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) { @@ -392,28 +350,23 @@ void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp) { sampleLeft >>= psgShift; sampleRight >>= psgShift; - if (audio->mixer) { - audio->mixer->step(audio->mixer); - } - if (!audio->externalMixing) { - if (!audio->forceDisableChA) { - if (audio->chALeft) { - sampleLeft += (audio->chA.samples[sample] << 2) >> !audio->volumeChA; - } - - if (audio->chARight) { - sampleRight += (audio->chA.samples[sample] << 2) >> !audio->volumeChA; - } + if (!audio->forceDisableChA) { + if (audio->chALeft) { + sampleLeft += (audio->chA.samples[sample] << 2) >> !audio->volumeChA; } - if (!audio->forceDisableChB) { - if (audio->chBLeft) { - sampleLeft += (audio->chB.samples[sample] << 2) >> !audio->volumeChB; - } + if (audio->chARight) { + sampleRight += (audio->chA.samples[sample] << 2) >> !audio->volumeChA; + } + } + + if (!audio->forceDisableChB) { + if (audio->chBLeft) { + sampleLeft += (audio->chB.samples[sample] << 2) >> !audio->volumeChB; + } - if (audio->chBRight) { - sampleRight += (audio->chB.samples[sample] << 2) >> !audio->volumeChB; - } + if (audio->chBRight) { + sampleRight += (audio->chB.samples[sample] << 2) >> !audio->volumeChB; } } @@ -442,34 +395,24 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { unsigned produced; int i; for (i = 0; i < samples; ++i) { - int16_t sampleLeft = audio->currentSamples[i].left; - int16_t sampleRight = audio->currentSamples[i].right; - if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) { - blip_add_delta(audio->psg.left, audio->clock, sampleLeft - audio->lastLeft); - blip_add_delta(audio->psg.right, audio->clock, sampleRight - audio->lastRight); - audio->lastLeft = sampleLeft; - audio->lastRight = sampleRight; - audio->clock += audio->sampleInterval; - if (audio->clock >= CLOCKS_PER_FRAME) { - blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME); - blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME); - audio->clock -= CLOCKS_PER_FRAME; - } - } - + int16_t sample[2] = { + audio->currentSamples[i].left, + audio->currentSamples[i].right + }; + mAudioBufferWrite(&audio->psg.buffer, sample, 1); if (audio->p->stream && audio->p->stream->postAudioFrame) { - audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight); + audio->p->stream->postAudioFrame(audio->p->stream, sample[0], sample[1]); } } - produced = blip_samples_avail(audio->psg.left); + produced = mAudioBufferAvailable(&audio->psg.buffer); bool wait = produced >= audio->samples; - if (!mCoreSyncProduceAudio(audio->p->sync, audio->psg.left, audio->samples)) { + if (!mCoreSyncProduceAudio(audio->p->sync, &audio->psg.buffer)) { // Interrupted audio->p->earlyExit = true; } if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) { - audio->p->stream->postAudioBuffer(audio->p->stream, audio->psg.left, audio->psg.right); + audio->p->stream->postAudioBuffer(audio->p->stream, &audio->psg.buffer); } mTimingSchedule(timing, &audio->sampleEvent, SAMPLE_INTERVAL - cyclesLate); @@ -587,7 +530,3 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState } mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when); } - -float GBAAudioCalculateRatio(float inputSampleRate, float desiredFPS, float desiredSampleRate) { - return desiredSampleRate * GBA_ARM7TDMI_FREQUENCY / (VIDEO_TOTAL_LENGTH * desiredFPS * inputSampleRate); -} diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index 53c14dafda5..811dc1e8a1c 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -320,18 +320,20 @@ void _gyroReadPins(struct GBACartridgeHardware* hw) { return; } + // Write bit on falling edge + bool doOutput = hw->gyroEdge && !(hw->pinState & 2); if (hw->pinState & 1) { if (gyro->sample) { gyro->sample(gyro); } int32_t sample = gyro->readGyroZ(gyro); - // Normalize to ~12 bits, focused on 0x6C0 - hw->gyroSample = (sample >> 21) + 0x6C0; // Crop off an extra bit so that we can't go negative + // Normalize to ~12 bits, focused on 0x700 + hw->gyroSample = (sample >> 21) + 0x700; // Crop off an extra bit so that we can't go negative + doOutput = true; } - if (hw->gyroEdge && !(hw->pinState & 2)) { - // Write bit on falling edge + if (doOutput) { unsigned bit = hw->gyroSample >> 15; hw->gyroSample <<= 1; _outputPins(hw, bit << 2); diff --git a/src/gba/core.c b/src/gba/core.c index 46ad70f088c..4a437f36d54 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #ifndef DISABLE_THREADING #include @@ -191,7 +190,6 @@ static const struct mCoreRegisterInfo _GBARegisters[] = { struct mVideoLogContext; -#define CPU_COMPONENT_AUDIO_MIXER CPU_COMPONENT_MISC_1 #define LOGO_CRC32 0xD0BEB55E struct GBACore { @@ -216,7 +214,6 @@ struct GBACore { bool hasOverride; struct mDebuggerPlatform* debuggerPlatform; struct mCheatDevice* cheatDevice; - struct GBAAudioMixer* audioMixer; struct mCoreMemoryBlock memoryBlocks[12]; size_t nMemoryBlocks; int memoryBlockType; @@ -269,7 +266,6 @@ static bool _GBACoreInit(struct mCore* core) { #ifndef MINIMAL_CORE gbacore->logContext = NULL; #endif - gbacore->audioMixer = NULL; GBACreate(gba); // TODO: Restore cheats @@ -313,7 +309,7 @@ static void _GBACoreDeinit(struct mCore* core) { #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 mDirectorySetDeinit(&core->dirs); #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (core->symbolTable) { mDebuggerSymbolTableDestroy(core->symbolTable); } @@ -324,7 +320,6 @@ static void _GBACoreDeinit(struct mCore* core) { if (gbacore->cheatDevice) { mCheatDeviceDestroy(gbacore->cheatDevice); } - free(gbacore->audioMixer); mCoreConfigFreeOpts(&core->opts); free(core); } @@ -374,7 +369,7 @@ static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* con } else if (strcasecmp(idleOptimization, "remove") == 0) { gba->idleOptimization = IDLE_LOOP_REMOVE; } else if (strcasecmp(idleOptimization, "detect") == 0) { - if (gba->idleLoop == IDLE_LOOP_NONE) { + if (gba->idleLoop == GBA_IDLE_LOOP_NONE) { gba->idleOptimization = IDLE_LOOP_DETECT; } else { gba->idleOptimization = IDLE_LOOP_REMOVE; @@ -387,7 +382,6 @@ static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* con mCoreConfigCopyValue(&core->config, config, "allowOpposingDirections"); mCoreConfigCopyValue(&core->config, config, "gba.bios"); mCoreConfigCopyValue(&core->config, config, "gba.forceGbp"); - mCoreConfigCopyValue(&core->config, config, "gba.audioHle"); mCoreConfigCopyValue(&core->config, config, "vbaBugCompat"); #ifndef DISABLE_THREADING @@ -559,16 +553,14 @@ static void _GBACorePutPixels(struct mCore* core, const void* buffer, size_t str gba->video.renderer->putPixels(gba->video.renderer, stride, buffer); } -static struct blip_t* _GBACoreGetAudioChannel(struct mCore* core, int ch) { +static unsigned _GBACoreAudioSampleRate(const struct mCore* core) { + UNUSED(core); + return 65536; +} + +static struct mAudioBuffer* _GBACoreGetAudioBuffer(struct mCore* core) { struct GBA* gba = core->board; - switch (ch) { - case 0: - return gba->audio.psg.left; - case 1: - return gba->audio.psg.right; - default: - return NULL; - } + return &gba->audio.psg.buffer; } static void _GBACoreSetAudioBufferSize(struct mCore* core, size_t samples) { @@ -729,16 +721,6 @@ static void _GBACoreReset(struct mCore* core) { } } -#ifndef MINIMAL_CORE - int useAudioMixer; - if (!gbacore->audioMixer && mCoreConfigGetIntValue(&core->config, "gba.audioHle", &useAudioMixer) && useAudioMixer) { - gbacore->audioMixer = malloc(sizeof(*gbacore->audioMixer)); - GBAAudioMixerCreate(gbacore->audioMixer); - ((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_AUDIO_MIXER] = &gbacore->audioMixer->d; - ARMHotplugAttach(core->cpu, CPU_COMPONENT_AUDIO_MIXER); - } -#endif - bool forceGbp = false; bool vbaBugCompat = true; mCoreConfigGetBoolValue(&core->config, "gba.forceGbp", &forceGbp); @@ -1004,27 +986,27 @@ size_t _GBACoreListMemoryBlocks(const struct mCore* core, const struct mCoreMemo if (gbacore->memoryBlockType != gba->memory.savedata.type) { switch (gba->memory.savedata.type) { - case SAVEDATA_SRAM: + case GBA_SAVEDATA_SRAM: memcpy(gbacore->memoryBlocks, _GBAMemoryBlocksSRAM, sizeof(_GBAMemoryBlocksSRAM)); gbacore->nMemoryBlocks = sizeof(_GBAMemoryBlocksSRAM) / sizeof(*_GBAMemoryBlocksSRAM); break; - case SAVEDATA_SRAM512: + case GBA_SAVEDATA_SRAM512: memcpy(gbacore->memoryBlocks, _GBAMemoryBlocksSRAM512, sizeof(_GBAMemoryBlocksSRAM512)); gbacore->nMemoryBlocks = sizeof(_GBAMemoryBlocksSRAM512) / sizeof(*_GBAMemoryBlocksSRAM512); break; - case SAVEDATA_FLASH512: + case GBA_SAVEDATA_FLASH512: memcpy(gbacore->memoryBlocks, _GBAMemoryBlocksFlash512, sizeof(_GBAMemoryBlocksFlash512)); gbacore->nMemoryBlocks = sizeof(_GBAMemoryBlocksFlash512) / sizeof(*_GBAMemoryBlocksFlash512); break; - case SAVEDATA_FLASH1M: + case GBA_SAVEDATA_FLASH1M: memcpy(gbacore->memoryBlocks, _GBAMemoryBlocksFlash1M, sizeof(_GBAMemoryBlocksFlash1M)); gbacore->nMemoryBlocks = sizeof(_GBAMemoryBlocksFlash1M) / sizeof(*_GBAMemoryBlocksFlash1M); break; - case SAVEDATA_EEPROM: + case GBA_SAVEDATA_EEPROM: memcpy(gbacore->memoryBlocks, _GBAMemoryBlocksEEPROM, sizeof(_GBAMemoryBlocksEEPROM)); gbacore->nMemoryBlocks = sizeof(_GBAMemoryBlocksEEPROM) / sizeof(*_GBAMemoryBlocksEEPROM); break; - case SAVEDATA_EEPROM512: + case GBA_SAVEDATA_EEPROM512: memcpy(gbacore->memoryBlocks, _GBAMemoryBlocksEEPROM512, sizeof(_GBAMemoryBlocksEEPROM512)); gbacore->nMemoryBlocks = sizeof(_GBAMemoryBlocksEEPROM512) / sizeof(*_GBAMemoryBlocksEEPROM512); break; @@ -1076,7 +1058,7 @@ void* _GBACoreGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) { *sizeOut = gba->memory.romSize; return gba->memory.rom; case GBA_REGION_SRAM: - if (gba->memory.savedata.type == SAVEDATA_FLASH1M) { + if (gba->memory.savedata.type == GBA_SAVEDATA_FLASH1M) { *sizeOut = GBA_SIZE_FLASH1M; return gba->memory.savedata.currentBank; } @@ -1218,7 +1200,7 @@ static bool _GBACoreWriteRegister(struct mCore* core, const char* name, const vo return true; } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) { UNUSED(core); switch (type) { @@ -1280,7 +1262,7 @@ static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS mCoreLoadELFSymbols(core->symbolTable, elf); #endif ELFClose(elf); @@ -1515,7 +1497,8 @@ struct mCore* GBACoreCreate(void) { core->setVideoGLTex = _GBACoreSetVideoGLTex; core->getPixels = _GBACoreGetPixels; core->putPixels = _GBACorePutPixels; - core->getAudioChannel = _GBACoreGetAudioChannel; + core->audioSampleRate = _GBACoreAudioSampleRate; + core->getAudioBuffer = _GBACoreGetAudioBuffer; core->setAudioBufferSize = _GBACoreSetAudioBufferSize; core->getAudioBufferSize = _GBACoreGetAudioBufferSize; core->addCoreCallbacks = _GBACoreAddCoreCallbacks; @@ -1565,7 +1548,7 @@ struct mCore* GBACoreCreate(void) { core->listRegisters = _GBACoreListRegisters; core->readRegister = _GBACoreReadRegister; core->writeRegister = _GBACoreWriteRegister; -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS core->supportsDebuggerType = _GBACoreSupportsDebuggerType; core->debuggerPlatform = _GBACoreDebuggerPlatform; core->cliDebuggerSystem = _GBACoreCliDebuggerSystem; diff --git a/src/gba/dma.c b/src/gba/dma.c index 11daee2a11b..ad7e9bc6560 100644 --- a/src/gba/dma.c +++ b/src/gba/dma.c @@ -278,7 +278,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { } cpu->memory.store32(cpu, dest, memory->dmaTransferRegister, 0); } else { - if (sourceRegion == GBA_REGION_ROM2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) { + if (sourceRegion == GBA_REGION_ROM2_EX && (memory->savedata.type == GBA_SAVEDATA_EEPROM || memory->savedata.type == GBA_SAVEDATA_EEPROM512)) { memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata); memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; } else if (source) { @@ -286,11 +286,11 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; } if (destRegion == GBA_REGION_ROM2_EX) { - if (memory->savedata.type == SAVEDATA_AUTODETECT) { + if (memory->savedata.type == GBA_SAVEDATA_AUTODETECT) { mLOG(GBA_MEM, INFO, "Detected EEPROM savegame"); GBASavedataInitEEPROM(&memory->savedata); } - if (memory->savedata.type == SAVEDATA_EEPROM512 || memory->savedata.type == SAVEDATA_EEPROM) { + if (memory->savedata.type == GBA_SAVEDATA_EEPROM512 || memory->savedata.type == GBA_SAVEDATA_EEPROM) { GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, info->nextCount); } } else { diff --git a/src/gba/extra/audio-mixer.c b/src/gba/extra/audio-mixer.c deleted file mode 100644 index 2a14f1ce661..00000000000 --- a/src/gba/extra/audio-mixer.c +++ /dev/null @@ -1,306 +0,0 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include - -#include -#include -#include - -#define OVERSAMPLE 2 - -static void _mp2kInit(void* cpu, struct mCPUComponent* component); -static void _mp2kDeinit(struct mCPUComponent* component); - -static bool _mp2kEngage(struct GBAAudioMixer* mixer, uint32_t address); -static void _mp2kVblank(struct GBAAudioMixer* mixer); -static void _mp2kStep(struct GBAAudioMixer* mixer); - -void GBAAudioMixerCreate(struct GBAAudioMixer* mixer) { - mixer->d.init = _mp2kInit; - mixer->d.deinit = _mp2kDeinit; - mixer->engage = _mp2kEngage; - mixer->vblank = _mp2kVblank; - mixer->step = _mp2kStep; -} - -void _mp2kInit(void* cpu, struct mCPUComponent* component) { - struct ARMCore* arm = cpu; - struct GBA* gba = (struct GBA*) arm->master; - struct GBAAudioMixer* mixer = (struct GBAAudioMixer*) component; - gba->audio.mixer = mixer; - mixer->p = &gba->audio; - mixer->contextAddress = 0; - mixer->tempo = 120.0 / 75.0; - mixer->frame = 0; - mixer->last.left = 0; - mixer->last.right = 0; - memset(&mixer->context, 0, sizeof(mixer->context)); - memset(&mixer->activeTracks, 0, sizeof(mixer->activeTracks)); - - size_t i; - for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) { - mixer->activeTracks[i].channel = &mixer->context.chans[i]; - CircleBufferInit(&mixer->activeTracks[i].buffer, 0x10000); - } -} - -void _mp2kDeinit(struct mCPUComponent* component) { - struct GBAAudioMixer* mixer = (struct GBAAudioMixer*) component; - size_t i; - for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) { - CircleBufferDeinit(&mixer->activeTracks[i].buffer); - } -} - -static void _loadInstrument(struct ARMCore* cpu, struct GBAMP2kInstrument* instrument, uint32_t base) { - struct ARMMemory* memory = &cpu->memory; - instrument->type = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, type), 0); - instrument->key = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, key), 0); - instrument->length = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, length), 0); - instrument->ps.pan = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, ps.pan), 0); - if (instrument->type == 0x40 || instrument->type == 0x80) { - instrument->data.subTable = memory->load32(cpu, base + offsetof(struct GBAMP2kInstrument, data.subTable), 0); - instrument->extInfo.map = memory->load32(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.map), 0); - } else { - instrument->data.waveData = memory->load32(cpu, base + offsetof(struct GBAMP2kInstrument, data.waveData), 0); - instrument->extInfo.adsr.attack = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.attack), 0); - instrument->extInfo.adsr.decay = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.decay), 0); - instrument->extInfo.adsr.sustain = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.sustain), 0); - instrument->extInfo.adsr.release = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.release), 0); - } -} - -static void _lookupInstrument(struct ARMCore* cpu, struct GBAMP2kInstrument* instrument, uint8_t key) { - struct ARMMemory* memory = &cpu->memory; - if (instrument->type == 0x40) { - uint32_t subInstrumentBase = instrument->data.subTable; - uint32_t keyTable = instrument->extInfo.map; - uint8_t id = memory->load8(cpu, keyTable + key, 0); - subInstrumentBase += 12 * id; - _loadInstrument(cpu, instrument, subInstrumentBase); - } - if (instrument->type == 0x80) { - uint32_t subInstrumentBase = instrument->data.subTable; - subInstrumentBase += 12 * key; - _loadInstrument(cpu, instrument, subInstrumentBase); - } -} - -static void _stepSample(struct GBAAudioMixer* mixer, struct GBAMP2kTrack* track) { - struct ARMCore* cpu = mixer->p->p->cpu; - struct ARMMemory* memory = &cpu->memory; - uint32_t headerAddress; - struct GBAMP2kInstrument instrument = track->track.instrument; - - uint8_t note = track->track.key; - _lookupInstrument(cpu, &instrument, note); - double freq; - - switch (instrument.type) { - case 0x00: - case 0x08: - case 0x40: - case 0x80: - freq = GBA_ARM7TDMI_FREQUENCY / (double) track->channel->freq; - break; - default: - // We don't care about PSG channels - return; - } - headerAddress = instrument.data.waveData; - if (headerAddress < 0x20) { - mLOG(GBA_AUDIO, ERROR, "Audio track has invalid instrument"); - return; - } - uint32_t loopOffset = memory->load32(cpu, headerAddress + 0x8, 0); - uint32_t endOffset = memory->load32(cpu, headerAddress + 0xC, 0); - uint32_t sampleBase = headerAddress + 0x10; - uint32_t sampleI = track->samplePlaying; - double sampleOffset = track->currentOffset; - double updates = VIDEO_TOTAL_LENGTH / (mixer->tempo * mixer->p->sampleInterval / OVERSAMPLE); - int nSample; - for (nSample = 0; nSample < updates; ++nSample) { - int8_t sample = memory->load8(cpu, sampleBase + sampleI, 0); - - struct mStereoSample stereo = { - (sample * track->channel->leftVolume * track->channel->envelopeV) >> 9, - (sample * track->channel->rightVolume * track->channel->envelopeV) >> 9 - }; - - CircleBufferWrite16(&track->buffer, stereo.left); - CircleBufferWrite16(&track->buffer, stereo.right); - - sampleOffset += mixer->p->sampleInterval / OVERSAMPLE; - while (sampleOffset > freq) { - sampleOffset -= freq; - ++sampleI; - if (sampleI >= endOffset) { - sampleI = loopOffset; - } - } - } - - track->samplePlaying = sampleI; - track->currentOffset = sampleOffset; -} - -static void _mp2kReload(struct GBAAudioMixer* mixer) { - struct ARMCore* cpu = mixer->p->p->cpu; - struct ARMMemory* memory = &cpu->memory; - mixer->context.magic = memory->load32(cpu, mixer->contextAddress + offsetof(struct GBAMP2kContext, magic), 0); - int i; - for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) { - struct GBAMP2kSoundChannel* ch = &mixer->context.chans[i]; - struct GBAMP2kTrack* track = &mixer->activeTracks[i]; - track->waiting = false; - uint32_t base = mixer->contextAddress + offsetof(struct GBAMP2kContext, chans[i]); - - ch->status = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, status), 0); - ch->type = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, type), 0); - ch->rightVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, rightVolume), 0); - ch->leftVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, leftVolume), 0); - ch->adsr.attack = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.attack), 0); - ch->adsr.decay = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.decay), 0); - ch->adsr.sustain = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.sustain), 0); - ch->adsr.release = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.release), 0); - ch->ky = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, ky), 0); - ch->envelopeV = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, envelopeV), 0); - ch->envelopeRight = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, envelopeRight), 0); - ch->envelopeLeft = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, envelopeLeft), 0); - ch->echoVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, echoVolume), 0); - ch->echoLength = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, echoLength), 0); - ch->d1 = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d1), 0); - ch->d2 = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d2), 0); - ch->gt = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, gt), 0); - ch->midiKey = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, midiKey), 0); - ch->ve = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, ve), 0); - ch->pr = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, pr), 0); - ch->rp = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, rp), 0); - ch->d3[0] = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d3[0]), 0); - ch->d3[1] = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d3[1]), 0); - ch->d3[2] = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d3[2]), 0); - ch->ct = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, ct), 0); - ch->fw = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, fw), 0); - ch->freq = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, freq), 0); - ch->waveData = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, waveData), 0); - ch->cp = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, cp), 0); - ch->track = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, track), 0); - ch->pp = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, pp), 0); - ch->np = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, np), 0); - ch->d4 = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, d4), 0); - ch->xpi = memory->load16(cpu, base + offsetof(struct GBAMP2kSoundChannel, xpi), 0); - ch->xpc = memory->load16(cpu, base + offsetof(struct GBAMP2kSoundChannel, xpc), 0); - - base = ch->track; - if (base) { - track->track.flags = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, flags), 0); - track->track.wait = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, wait), 0); - track->track.patternLevel = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternLevel), 0); - track->track.repN = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, repN), 0); - track->track.gateTime = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, gateTime), 0); - track->track.key = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, key), 0); - track->track.velocity = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, velocity), 0); - track->track.runningStatus = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, runningStatus), 0); - track->track.keyM = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, keyM), 0); - track->track.pitM = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, pitM), 0); - track->track.keyShift = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, keyShift), 0); - track->track.keyShiftX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, keyShiftX), 0); - track->track.tune = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, tune), 0); - track->track.pitX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, pitX), 0); - track->track.bend = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, bend), 0); - track->track.bendRange = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, bendRange), 0); - track->track.volMR = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, volMR), 0); - track->track.volML = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, volML), 0); - track->track.vol = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, vol), 0); - track->track.volX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, volX), 0); - track->track.pan = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, pan), 0); - track->track.panX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, panX), 0); - track->track.modM = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, modM), 0); - track->track.mod = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, mod), 0); - track->track.modT = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, modT), 0); - track->track.lfoSpeed = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoSpeed), 0); - track->track.lfoSpeedC = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoSpeedC), 0); - track->track.lfoDelay = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoDelay), 0); - track->track.lfoDelayC = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoDelayC), 0); - track->track.priority = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, priority), 0); - track->track.echoVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, echoVolume), 0); - track->track.echoLength = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, echoLength), 0); - track->track.chan = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, chan), 0); - _loadInstrument(cpu, &track->track.instrument, base + offsetof(struct GBAMP2kMusicPlayerTrack, instrument)); - track->track.cmdPtr = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, cmdPtr), 0); - track->track.patternStack[0] = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternStack[0]), 0); - track->track.patternStack[1] = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternStack[1]), 0); - track->track.patternStack[2] = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternStack[2]), 0); - } else { - memset(&track->track, 0, sizeof(track->track)); - } - if (track->track.runningStatus == 0xCD) { - // XCMD isn't supported - mixer->p->externalMixing = false; - } - } -} - -bool _mp2kEngage(struct GBAAudioMixer* mixer, uint32_t address) { - if (address != mixer->contextAddress) { - mixer->contextAddress = address; - mixer->p->externalMixing = true; - _mp2kReload(mixer); - } - return true; -} - -void _mp2kStep(struct GBAAudioMixer* mixer) { - mixer->frame += mixer->p->sampleInterval; - - while (mixer->frame >= VIDEO_TOTAL_LENGTH / mixer->tempo) { - int i; - for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) { - struct GBAMP2kTrack* track = &mixer->activeTracks[i]; - if (track->channel->status > 0) { - _stepSample(mixer, track); - } else { - track->currentOffset = 0; - track->samplePlaying = 0; - CircleBufferClear(&track->buffer); - } - } - mixer->frame -= VIDEO_TOTAL_LENGTH / mixer->tempo; - } - - uint32_t interval = mixer->p->sampleInterval / OVERSAMPLE; - int i; - for (i = 0; i < OVERSAMPLE; ++i) { - struct mStereoSample sample = {0}; - size_t track; - for (track = 0; track < MP2K_MAX_SOUND_CHANNELS; ++track) { - if (!mixer->activeTracks[track].channel->status) { - continue; - } - int16_t value; - CircleBufferRead16(&mixer->activeTracks[track].buffer, &value); - sample.left += value; - CircleBufferRead16(&mixer->activeTracks[track].buffer, &value); - sample.right += value; - } - sample.left = (sample.left * mixer->p->masterVolume) >> 8; - sample.right = (sample.right * mixer->p->masterVolume) >> 8; - if (mixer->p->externalMixing) { - blip_add_delta(mixer->p->psg.left, mixer->p->clock + i * interval, sample.left - mixer->last.left); - blip_add_delta(mixer->p->psg.right, mixer->p->clock + i * interval, sample.right - mixer->last.right); - } - mixer->last = sample; - } -} - -void _mp2kVblank(struct GBAAudioMixer* mixer) { - if (!mixer->contextAddress) { - return; - } - mLOG(GBA_AUDIO, DEBUG, "Frame"); - mixer->p->externalMixing = true; - _mp2kReload(mixer); -} diff --git a/src/gba/gba.c b/src/gba/gba.c index 7586ae66c06..fb0a84ae7e5 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -49,7 +49,7 @@ static void GBATestIRQNoDelay(struct ARMCore* cpu); static void _triggerIRQ(struct mTiming*, void* user, uint32_t cyclesLate); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS static bool _setSoftwareBreakpoint(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t* opcode); static void _clearSoftwareBreakpoint(struct ARMDebugger*, const struct ARMDebugBreakpoint*); #endif @@ -111,7 +111,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { gba->biosChecksum = GBAChecksum(gba->memory.bios, GBA_SIZE_BIOS); gba->idleOptimization = IDLE_LOOP_REMOVE; - gba->idleLoop = IDLE_LOOP_NONE; + gba->idleLoop = GBA_IDLE_LOOP_NONE; gba->vbaBugCompat = false; gba->hardCrash = true; @@ -165,7 +165,7 @@ void GBAUnloadROM(struct GBA* gba) { gba->memory.savedata.realVf->close(gba->memory.savedata.realVf); gba->memory.savedata.realVf = 0; } - gba->idleLoop = IDLE_LOOP_NONE; + gba->idleLoop = GBA_IDLE_LOOP_NONE; } void GBADestroy(struct GBA* gba) { @@ -305,7 +305,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) { do { int32_t cycles = cpu->cycles; cpu->cycles = 0; -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS gba->timing.globalCycles += cycles < nextEvent ? nextEvent : cycles; #endif #ifndef NDEBUG @@ -338,7 +338,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) { } } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) { gba->debugger = (struct ARMDebugger*) debugger->platform; gba->debugger->setSoftwareBreakpoint = _setSoftwareBreakpoint; @@ -469,10 +469,10 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { } bool GBALoadSave(struct GBA* gba, struct VFile* sav) { - enum SavedataType type = gba->memory.savedata.type; + enum GBASavedataType type = gba->memory.savedata.type; GBASavedataDeinit(&gba->memory.savedata); GBASavedataInit(&gba->memory.savedata, sav); - if (type != SAVEDATA_AUTODETECT) { + if (type != GBA_SAVEDATA_AUTODETECT) { GBASavedataForceType(&gba->memory.savedata, type); } return sav; @@ -850,7 +850,7 @@ void GBAGetGameTitle(const struct GBA* gba, char* out) { void GBAHitStub(struct ARMCore* cpu, uint32_t opcode) { struct GBA* gba = (struct GBA*) cpu->master; UNUSED(gba); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (gba->debugger) { struct mDebuggerEntryInfo info = { .address = _ARMPCAddress(cpu), @@ -873,7 +873,7 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) { // TODO: More sensible category? mLOG(GBA, WARN, "Illegal opcode: %08x", opcode); } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (gba->debugger) { struct mDebuggerEntryInfo info = { .address = _ARMPCAddress(cpu), @@ -888,7 +888,7 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) { void GBABreakpoint(struct ARMCore* cpu, int immediate) { struct GBA* gba = (struct GBA*) cpu->master; switch (immediate) { -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS case CPU_COMPONENT_DEBUGGER: if (gba->debugger) { struct mDebuggerEntryInfo info = { @@ -928,10 +928,6 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) { void GBAFrameStarted(struct GBA* gba) { GBATestKeypadIRQ(gba); - if (gba->audio.mixer) { - gba->audio.mixer->vblank(gba->audio.mixer); - } - size_t c; for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c); @@ -1053,7 +1049,7 @@ void GBAClearBreakpoint(struct GBA* gba, uint32_t address, enum ExecutionMode mo } } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS static bool _setSoftwareBreakpoint(struct ARMDebugger* debugger, uint32_t address, enum ExecutionMode mode, uint32_t* opcode) { GBASetBreakpoint((struct GBA*) debugger->cpu->master, &debugger->d.p->d, address, mode, opcode); return true; diff --git a/src/gba/memory.c b/src/gba/memory.c index 9418a2706bd..4c72ca80e8d 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -595,7 +595,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { break; case GBA_REGION_ROM2_EX: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; - if (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512) { + if (memory->savedata.type == GBA_SAVEDATA_EEPROM || memory->savedata.type == GBA_SAVEDATA_EEPROM512) { value = GBASavedataReadEEPROM(&memory->savedata); } else if ((address & 0x0DFC0000) >= 0x0DF80000 && memory->hw.devices & HW_EREADER) { value = GBACartEReaderRead(&memory->ereader, address); @@ -702,7 +702,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { case GBA_REGION_SRAM: case GBA_REGION_SRAM_MIRROR: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; - if (memory->savedata.type == SAVEDATA_AUTODETECT) { + if (memory->savedata.type == GBA_SAVEDATA_AUTODETECT) { mLOG(GBA_MEM, INFO, "Detected SRAM savegame"); GBASavedataInitSRAM(&memory->savedata); } @@ -711,13 +711,13 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } if (memory->hw.devices & HW_EREADER && (address & 0xE00FF80) >= 0xE00FF80) { value = GBACartEReaderReadFlash(&memory->ereader, address); - } else if (memory->savedata.type == SAVEDATA_SRAM) { + } else if (memory->savedata.type == GBA_SAVEDATA_SRAM) { value = memory->savedata.data[address & (GBA_SIZE_SRAM - 1)]; - } else if (memory->savedata.type == SAVEDATA_FLASH512 || memory->savedata.type == SAVEDATA_FLASH1M) { + } else if (memory->savedata.type == GBA_SAVEDATA_FLASH512 || memory->savedata.type == GBA_SAVEDATA_FLASH1M) { value = GBASavedataReadFlash(&memory->savedata, address); } else if (memory->hw.devices & HW_TILT) { value = GBAHardwareTiltRead(&memory->hw, address & OFFSET_MASK); - } else if (memory->savedata.type == SAVEDATA_SRAM512) { + } else if (memory->savedata.type == GBA_SAVEDATA_SRAM512) { value = memory->savedata.data[address & (GBA_SIZE_SRAM512 - 1)]; } else { mLOG(GBA_MEM, GAME_ERROR, "Reading from non-existent SRAM: 0x%08X", address); @@ -975,11 +975,11 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle if ((address & 0x0DFC0000) >= 0x0DF80000 && memory->hw.devices & HW_EREADER) { GBACartEReaderWrite(&memory->ereader, address, value); break; - } else if (memory->savedata.type == SAVEDATA_AUTODETECT) { + } else if (memory->savedata.type == GBA_SAVEDATA_AUTODETECT) { mLOG(GBA_MEM, INFO, "Detected EEPROM savegame"); GBASavedataInitEEPROM(&memory->savedata); } - if (memory->savedata.type == SAVEDATA_EEPROM512 || memory->savedata.type == SAVEDATA_EEPROM) { + if (memory->savedata.type == GBA_SAVEDATA_EEPROM512 || memory->savedata.type == GBA_SAVEDATA_EEPROM) { GBASavedataWriteEEPROM(&memory->savedata, value, 1); break; } @@ -1050,7 +1050,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo break; case GBA_REGION_SRAM: case GBA_REGION_SRAM_MIRROR: - if (memory->savedata.type == SAVEDATA_AUTODETECT) { + if (memory->savedata.type == GBA_SAVEDATA_AUTODETECT) { if (address == SAVEDATA_FLASH_BASE) { mLOG(GBA_MEM, INFO, "Detected Flash savegame"); GBASavedataInitFlash(&memory->savedata); @@ -1061,9 +1061,9 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo } if (memory->hw.devices & HW_EREADER && (address & 0xE00FF80) >= 0xE00FF80) { GBACartEReaderWriteFlash(&memory->ereader, address, value); - } else if (memory->savedata.type == SAVEDATA_FLASH512 || memory->savedata.type == SAVEDATA_FLASH1M) { + } else if (memory->savedata.type == GBA_SAVEDATA_FLASH512 || memory->savedata.type == GBA_SAVEDATA_FLASH1M) { GBASavedataWriteFlash(&memory->savedata, address, value); - } else if (memory->savedata.type == SAVEDATA_SRAM) { + } else if (memory->savedata.type == GBA_SAVEDATA_SRAM) { if (memory->vfame.cartType) { GBAVFameSramWrite(&memory->vfame, address, value, memory->savedata.data); } else { @@ -1072,7 +1072,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo memory->savedata.dirty |= mSAVEDATA_DIRT_NEW; } else if (memory->hw.devices & HW_TILT) { GBAHardwareTiltWrite(&memory->hw, address & OFFSET_MASK, value); - } else if (memory->savedata.type == SAVEDATA_SRAM512) { + } else if (memory->savedata.type == GBA_SAVEDATA_SRAM512) { memory->savedata.data[address & (GBA_SIZE_SRAM512 - 1)] = value; memory->savedata.dirty |= mSAVEDATA_DIRT_NEW; } else { @@ -1118,10 +1118,8 @@ uint32_t GBAView32(struct ARMCore* cpu, uint32_t address) { value = GBALoad32(cpu, address, 0); break; case GBA_REGION_IO: - if ((address & OFFSET_MASK) < GBA_REG_MAX) { - value = gba->memory.io[(address & OFFSET_MASK) >> 1]; - value |= gba->memory.io[((address & OFFSET_MASK) >> 1) + 1] << 16; - } + value = GBAView16(cpu, address); + value |= GBAView16(cpu, address + 2) << 16; break; case GBA_REGION_SRAM: value = GBALoad8(cpu, address, 0); @@ -1159,7 +1157,10 @@ uint16_t GBAView16(struct ARMCore* cpu, uint32_t address) { value = GBALoad16(cpu, address, 0); break; case GBA_REGION_IO: - if ((address & OFFSET_MASK) < GBA_REG_MAX) { + if ((address & OFFSET_MASK) < GBA_REG_MAX || (address & OFFSET_MASK) == GBA_REG_POSTFLG) { + value = gba->memory.io[(address & OFFSET_MASK) >> 1]; + } else if ((address & OFFSET_MASK) == GBA_REG_EXWAITCNT_LO || (address & OFFSET_MASK) == GBA_REG_EXWAITCNT_HI) { + address += GBA_REG_INTERNAL_EXWAITCNT_LO - GBA_REG_EXWAITCNT_LO; value = gba->memory.io[(address & OFFSET_MASK) >> 1]; } break; @@ -1263,7 +1264,7 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o break; case GBA_REGION_SRAM: case GBA_REGION_SRAM_MIRROR: - if (memory->savedata.type == SAVEDATA_SRAM) { + if (memory->savedata.type == GBA_SAVEDATA_SRAM) { LOAD_32(oldValue, address & (GBA_SIZE_SRAM - 4), memory->savedata.data); STORE_32(value, address & (GBA_SIZE_SRAM - 4), memory->savedata.data); } else { @@ -1333,7 +1334,7 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o break; case GBA_REGION_SRAM: case GBA_REGION_SRAM_MIRROR: - if (memory->savedata.type == SAVEDATA_SRAM) { + if (memory->savedata.type == GBA_SAVEDATA_SRAM) { LOAD_16(oldValue, address & (GBA_SIZE_SRAM - 2), memory->savedata.data); STORE_16(value, address & (GBA_SIZE_SRAM - 2), memory->savedata.data); } else { @@ -1391,7 +1392,7 @@ void GBAPatch8(struct ARMCore* cpu, uint32_t address, int8_t value, int8_t* old) break; case GBA_REGION_SRAM: case GBA_REGION_SRAM_MIRROR: - if (memory->savedata.type == SAVEDATA_SRAM) { + if (memory->savedata.type == GBA_SAVEDATA_SRAM) { oldValue = ((int8_t*) memory->savedata.data)[address & (GBA_SIZE_SRAM - 1)]; ((int8_t*) memory->savedata.data)[address & (GBA_SIZE_SRAM - 1)] = value; } else { diff --git a/src/gba/overrides.c b/src/gba/overrides.c index ccc871e62e9..a63616e613b 100644 --- a/src/gba/overrides.c +++ b/src/gba/overrides.c @@ -13,213 +13,202 @@ static const struct GBACartridgeOverride _overrides[] = { // Advance Wars - { "AWRE", SAVEDATA_FLASH512, HW_NONE, 0x8038810, false }, - { "AWRP", SAVEDATA_FLASH512, HW_NONE, 0x8038810, false }, + { "AWRE", GBA_SAVEDATA_FLASH512, HW_NONE, 0x8038810 }, + { "AWRP", GBA_SAVEDATA_FLASH512, HW_NONE, 0x8038810 }, // Advance Wars 2: Black Hole Rising - { "AW2E", SAVEDATA_FLASH512, HW_NONE, 0x8036E08, false }, - { "AW2P", SAVEDATA_FLASH512, HW_NONE, 0x803719C, false }, + { "AW2E", GBA_SAVEDATA_FLASH512, HW_NONE, 0x8036E08 }, + { "AW2P", GBA_SAVEDATA_FLASH512, HW_NONE, 0x803719C }, // Boktai: The Sun is in Your Hand - { "U3IJ", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE, false }, - { "U3IE", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE, false }, - { "U3IP", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE, false }, + { "U3IJ", GBA_SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, GBA_IDLE_LOOP_NONE }, + { "U3IE", GBA_SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, GBA_IDLE_LOOP_NONE }, + { "U3IP", GBA_SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, GBA_IDLE_LOOP_NONE }, // Boktai 2: Solar Boy Django - { "U32J", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE, false }, - { "U32E", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE, false }, - { "U32P", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE, false }, + { "U32J", GBA_SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, GBA_IDLE_LOOP_NONE }, + { "U32E", GBA_SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, GBA_IDLE_LOOP_NONE }, + { "U32P", GBA_SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, GBA_IDLE_LOOP_NONE }, // Crash Bandicoot 2 - N-Tranced - { "AC8J", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "AC8E", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "AC8P", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, + { "AC8J", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "AC8E", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "AC8P", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, // DigiCommunication Nyo - Datou! Black Gemagema Dan - { "BDKJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, + { "BDKJ", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, // Dragon Ball Z - The Legacy of Goku - { "ALGP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, + { "ALGP", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, // Dragon Ball Z - The Legacy of Goku II - { "ALFJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "ALFE", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "ALFP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, + { "ALFJ", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "ALFE", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "ALFP", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, // Dragon Ball Z - Taiketsu - { "BDBE", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "BDBP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, + { "BDBE", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BDBP", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, // Drill Dozer - { "V49J", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false }, - { "V49E", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false }, - { "V49P", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false }, + { "V49J", GBA_SAVEDATA_SRAM, HW_RUMBLE, GBA_IDLE_LOOP_NONE }, + { "V49E", GBA_SAVEDATA_SRAM, HW_RUMBLE, GBA_IDLE_LOOP_NONE }, + { "V49P", GBA_SAVEDATA_SRAM, HW_RUMBLE, GBA_IDLE_LOOP_NONE }, // e-Reader - { "PEAJ", SAVEDATA_FLASH1M, HW_EREADER, IDLE_LOOP_NONE }, - { "PSAJ", SAVEDATA_FLASH1M, HW_EREADER, IDLE_LOOP_NONE }, - { "PSAE", SAVEDATA_FLASH1M, HW_EREADER, IDLE_LOOP_NONE }, + { "PEAJ", GBA_SAVEDATA_FLASH1M, HW_EREADER, GBA_IDLE_LOOP_NONE }, + { "PSAJ", GBA_SAVEDATA_FLASH1M, HW_EREADER, GBA_IDLE_LOOP_NONE }, + { "PSAE", GBA_SAVEDATA_FLASH1M, HW_EREADER, GBA_IDLE_LOOP_NONE }, // Final Fantasy Tactics Advance - { "AFXE", SAVEDATA_FLASH512, HW_NONE, 0x8000428, false }, + { "AFXE", GBA_SAVEDATA_FLASH512, HW_NONE, 0x8000428 }, // F-Zero - Climax - { "BFTJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, + { "BFTJ", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, // Goodboy Galaxy - { "2GBP", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false }, + { "2GBP", GBA_SAVEDATA_SRAM, HW_RUMBLE, GBA_IDLE_LOOP_NONE }, // Iridion II - { "AI2E", SAVEDATA_FORCE_NONE, HW_NONE, IDLE_LOOP_NONE, false }, - { "AI2P", SAVEDATA_FORCE_NONE, HW_NONE, IDLE_LOOP_NONE, false }, + { "AI2E", GBA_SAVEDATA_FORCE_NONE, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "AI2P", GBA_SAVEDATA_FORCE_NONE, HW_NONE, GBA_IDLE_LOOP_NONE }, // Game Boy Wars Advance 1+2 - { "BGWJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, + { "BGWJ", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, // Golden Sun: The Lost Age - { "AGFE", SAVEDATA_FLASH512, HW_NONE, 0x801353A, false }, + { "AGFE", GBA_SAVEDATA_FLASH512, HW_NONE, 0x801353A }, // Koro Koro Puzzle - Happy Panechu! - { "KHPJ", SAVEDATA_EEPROM, HW_TILT, IDLE_LOOP_NONE, false }, + { "KHPJ", GBA_SAVEDATA_EEPROM, HW_TILT, GBA_IDLE_LOOP_NONE }, // Legendz - Yomigaeru Shiren no Shima - { "BLJJ", SAVEDATA_FLASH512, HW_RTC, IDLE_LOOP_NONE, false }, - { "BLJK", SAVEDATA_FLASH512, HW_RTC, IDLE_LOOP_NONE, false }, + { "BLJJ", GBA_SAVEDATA_FLASH512, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "BLJK", GBA_SAVEDATA_FLASH512, HW_RTC, GBA_IDLE_LOOP_NONE }, // Legendz - Sign of Nekuromu - { "BLVJ", SAVEDATA_FLASH512, HW_RTC, IDLE_LOOP_NONE, false }, + { "BLVJ", GBA_SAVEDATA_FLASH512, HW_RTC, GBA_IDLE_LOOP_NONE }, // Mega Man Battle Network - { "AREE", SAVEDATA_SRAM, HW_NONE, 0x800032E, false }, + { "AREE", GBA_SAVEDATA_SRAM, HW_NONE, 0x800032E }, // Mega Man Zero - { "AZCE", SAVEDATA_SRAM, HW_NONE, 0x80004E8, false }, + { "AZCE", GBA_SAVEDATA_SRAM, HW_NONE, 0x80004E8 }, // Metal Slug Advance - { "BSME", SAVEDATA_EEPROM, HW_NONE, 0x8000290, false }, + { "BSME", GBA_SAVEDATA_EEPROM, HW_NONE, 0x8000290 }, // Pokemon Ruby - { "AXVJ", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXVE", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXVP", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXVI", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXVS", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXVD", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXVF", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, + { "AXVJ", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXVE", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXVP", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXVI", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXVS", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXVD", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXVF", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, // Pokemon Sapphire - { "AXPJ", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXPE", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXPP", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXPI", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXPS", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXPD", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, - { "AXPF", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, + { "AXPJ", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXPE", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXPP", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXPI", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXPS", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXPD", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, + { "AXPF", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, // Pokemon Emerald - { "BPEJ", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false }, - { "BPEE", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false }, - { "BPEP", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false }, - { "BPEI", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false }, - { "BPES", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false }, - { "BPED", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false }, - { "BPEF", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false }, + { "BPEJ", GBA_SAVEDATA_FLASH1M, HW_RTC, 0x80008C6 }, + { "BPEE", GBA_SAVEDATA_FLASH1M, HW_RTC, 0x80008C6 }, + { "BPEP", GBA_SAVEDATA_FLASH1M, HW_RTC, 0x80008C6 }, + { "BPEI", GBA_SAVEDATA_FLASH1M, HW_RTC, 0x80008C6 }, + { "BPES", GBA_SAVEDATA_FLASH1M, HW_RTC, 0x80008C6 }, + { "BPED", GBA_SAVEDATA_FLASH1M, HW_RTC, 0x80008C6 }, + { "BPEF", GBA_SAVEDATA_FLASH1M, HW_RTC, 0x80008C6 }, // Pokemon Mystery Dungeon - { "B24E", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "B24P", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, + { "B24E", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "B24P", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, // Pokemon FireRed - { "BPRJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPRE", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPRP", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPRI", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPRS", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPRD", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPRF", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, + { "BPRJ", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPRE", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPRP", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPRI", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPRS", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPRD", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPRF", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, // Pokemon LeafGreen - { "BPGJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPGE", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPGP", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPGI", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPGS", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPGD", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, - { "BPGF", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, + { "BPGJ", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPGE", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPGP", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPGI", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPGS", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPGD", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "BPGF", GBA_SAVEDATA_FLASH1M, HW_NONE, GBA_IDLE_LOOP_NONE }, // RockMan EXE 4.5 - Real Operation - { "BR4J", SAVEDATA_FLASH512, HW_RTC, IDLE_LOOP_NONE, false }, + { "BR4J", GBA_SAVEDATA_FLASH512, HW_RTC, GBA_IDLE_LOOP_NONE }, // Rocky - { "AR8E", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "AROP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, + { "AR8E", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "AROP", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, // Sennen Kazoku - { "BKAJ", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, + { "BKAJ", GBA_SAVEDATA_FLASH1M, HW_RTC, GBA_IDLE_LOOP_NONE }, // Shin Bokura no Taiyou: Gyakushuu no Sabata - { "U33J", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE, false }, + { "U33J", GBA_SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, GBA_IDLE_LOOP_NONE }, // Stuart Little 2 - { "ASLE", SAVEDATA_FORCE_NONE, HW_NONE, IDLE_LOOP_NONE, false }, - { "ASLF", SAVEDATA_FORCE_NONE, HW_NONE, IDLE_LOOP_NONE, false }, + { "ASLE", GBA_SAVEDATA_FORCE_NONE, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "ASLF", GBA_SAVEDATA_FORCE_NONE, HW_NONE, GBA_IDLE_LOOP_NONE }, // Super Mario Advance 2 - { "AA2J", SAVEDATA_EEPROM, HW_NONE, 0x800052E, false }, - { "AA2E", SAVEDATA_EEPROM, HW_NONE, 0x800052E, false }, - { "AA2P", SAVEDATA_AUTODETECT, HW_NONE, 0x800052E, false }, + { "AA2J", GBA_SAVEDATA_EEPROM, HW_NONE, 0x800052E }, + { "AA2E", GBA_SAVEDATA_EEPROM, HW_NONE, 0x800052E }, + { "AA2P", GBA_SAVEDATA_AUTODETECT, HW_NONE, 0x800052E }, // Super Mario Advance 3 - { "A3AJ", SAVEDATA_EEPROM, HW_NONE, 0x8002B9C, false }, - { "A3AE", SAVEDATA_EEPROM, HW_NONE, 0x8002B9C, false }, - { "A3AP", SAVEDATA_EEPROM, HW_NONE, 0x8002B9C, false }, + { "A3AJ", GBA_SAVEDATA_EEPROM, HW_NONE, 0x8002B9C }, + { "A3AE", GBA_SAVEDATA_EEPROM, HW_NONE, 0x8002B9C }, + { "A3AP", GBA_SAVEDATA_EEPROM, HW_NONE, 0x8002B9C }, // Super Mario Advance 4 - { "AX4J", SAVEDATA_FLASH1M, HW_NONE, 0x800072A, false }, - { "AX4E", SAVEDATA_FLASH1M, HW_NONE, 0x800072A, false }, - { "AX4P", SAVEDATA_FLASH1M, HW_NONE, 0x800072A, false }, + { "AX4J", GBA_SAVEDATA_FLASH1M, HW_NONE, 0x800072A }, + { "AX4E", GBA_SAVEDATA_FLASH1M, HW_NONE, 0x800072A }, + { "AX4P", GBA_SAVEDATA_FLASH1M, HW_NONE, 0x800072A }, // Super Monkey Ball Jr. - { "ALUE", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "ALUP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, + { "ALUE", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, + { "ALUP", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, // Top Gun - Combat Zones - { "A2YE", SAVEDATA_FORCE_NONE, HW_NONE, IDLE_LOOP_NONE, false }, + { "A2YE", GBA_SAVEDATA_FORCE_NONE, HW_NONE, GBA_IDLE_LOOP_NONE }, // Ueki no Housoku - Jingi Sakuretsu! Nouryokusha Battle - { "BUHJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, + { "BUHJ", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE }, // Wario Ware Twisted - { "RZWJ", SAVEDATA_SRAM, HW_RUMBLE | HW_GYRO, IDLE_LOOP_NONE, false }, - { "RZWE", SAVEDATA_SRAM, HW_RUMBLE | HW_GYRO, IDLE_LOOP_NONE, false }, - { "RZWP", SAVEDATA_SRAM, HW_RUMBLE | HW_GYRO, IDLE_LOOP_NONE, false }, + { "RZWJ", GBA_SAVEDATA_SRAM, HW_RUMBLE | HW_GYRO, GBA_IDLE_LOOP_NONE }, + { "RZWE", GBA_SAVEDATA_SRAM, HW_RUMBLE | HW_GYRO, GBA_IDLE_LOOP_NONE }, + { "RZWP", GBA_SAVEDATA_SRAM, HW_RUMBLE | HW_GYRO, GBA_IDLE_LOOP_NONE }, // Yoshi's Universal Gravitation - { "KYGJ", SAVEDATA_EEPROM, HW_TILT, IDLE_LOOP_NONE, false }, - { "KYGE", SAVEDATA_EEPROM, HW_TILT, IDLE_LOOP_NONE, false }, - { "KYGP", SAVEDATA_EEPROM, HW_TILT, IDLE_LOOP_NONE, false }, + { "KYGJ", GBA_SAVEDATA_EEPROM, HW_TILT, GBA_IDLE_LOOP_NONE }, + { "KYGE", GBA_SAVEDATA_EEPROM, HW_TILT, GBA_IDLE_LOOP_NONE }, + { "KYGP", GBA_SAVEDATA_EEPROM, HW_TILT, GBA_IDLE_LOOP_NONE }, // Aging cartridge - { "TCHK", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - - // Famicom Mini series 3 (FDS), some aren't mirrored (22 - 28) - // See https://forum.no-intro.org/viewtopic.php?f=2&t=4221 for discussion - { "FNMJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "FMRJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "FPTJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "FLBJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "FFMJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "FTKJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - { "FTUJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, - - { { 0, 0, 0, 0 }, 0, 0, IDLE_LOOP_NONE, false } + { "TCHK", GBA_SAVEDATA_EEPROM, HW_NONE, GBA_IDLE_LOOP_NONE, }, + + { { 0, 0, 0, 0 }, 0, 0, GBA_IDLE_LOOP_NONE, false } }; bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOverride* override) { - override->savetype = SAVEDATA_AUTODETECT; + override->savetype = GBA_SAVEDATA_AUTODETECT; override->hardware = HW_NONE; - override->idleLoop = IDLE_LOOP_NONE; - override->mirroring = false; + override->idleLoop = GBA_IDLE_LOOP_NONE; override->vbaBugCompat = false; bool found = false; @@ -233,7 +222,7 @@ bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOver } if (!found && override->id[0] == 'F') { // Classic NES Series - override->savetype = SAVEDATA_EEPROM; + override->savetype = GBA_SAVEDATA_EEPROM; found = true; } @@ -247,25 +236,25 @@ bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOver if (savetype) { if (strcasecmp(savetype, "SRAM") == 0) { found = true; - override->savetype = SAVEDATA_SRAM; + override->savetype = GBA_SAVEDATA_SRAM; } else if (strcasecmp(savetype, "SRAM512") == 0) { found = true; - override->savetype = SAVEDATA_SRAM512; + override->savetype = GBA_SAVEDATA_SRAM512; } else if (strcasecmp(savetype, "EEPROM") == 0) { found = true; - override->savetype = SAVEDATA_EEPROM; + override->savetype = GBA_SAVEDATA_EEPROM; } else if (strcasecmp(savetype, "EEPROM512") == 0) { found = true; - override->savetype = SAVEDATA_EEPROM512; + override->savetype = GBA_SAVEDATA_EEPROM512; } else if (strcasecmp(savetype, "FLASH512") == 0) { found = true; - override->savetype = SAVEDATA_FLASH512; + override->savetype = GBA_SAVEDATA_FLASH512; } else if (strcasecmp(savetype, "FLASH1M") == 0) { found = true; - override->savetype = SAVEDATA_FLASH1M; + override->savetype = GBA_SAVEDATA_FLASH1M; } else if (strcasecmp(savetype, "NONE") == 0) { found = true; - override->savetype = SAVEDATA_FORCE_NONE; + override->savetype = GBA_SAVEDATA_FORCE_NONE; } } @@ -295,28 +284,28 @@ void GBAOverrideSave(struct Configuration* config, const struct GBACartridgeOver snprintf(sectionName, sizeof(sectionName), "override.%c%c%c%c", override->id[0], override->id[1], override->id[2], override->id[3]); const char* savetype = 0; switch (override->savetype) { - case SAVEDATA_SRAM: + case GBA_SAVEDATA_SRAM: savetype = "SRAM"; break; - case SAVEDATA_SRAM512: + case GBA_SAVEDATA_SRAM512: savetype = "SRAM512"; break; - case SAVEDATA_EEPROM: + case GBA_SAVEDATA_EEPROM: savetype = "EEPROM"; break; - case SAVEDATA_EEPROM512: + case GBA_SAVEDATA_EEPROM512: savetype = "EEPROM512"; break; - case SAVEDATA_FLASH512: + case GBA_SAVEDATA_FLASH512: savetype = "FLASH512"; break; - case SAVEDATA_FLASH1M: + case GBA_SAVEDATA_FLASH1M: savetype = "FLASH1M"; break; - case SAVEDATA_FORCE_NONE: + case GBA_SAVEDATA_FORCE_NONE: savetype = "NONE"; break; - case SAVEDATA_AUTODETECT: + case GBA_SAVEDATA_AUTODETECT: break; } ConfigurationSetValue(config, sectionName, "savetype", savetype); @@ -327,7 +316,7 @@ void GBAOverrideSave(struct Configuration* config, const struct GBACartridgeOver ConfigurationClearValue(config, sectionName, "hardware"); } - if (override->idleLoop != IDLE_LOOP_NONE) { + if (override->idleLoop != GBA_IDLE_LOOP_NONE) { ConfigurationSetUIntValue(config, sectionName, "idleLoop", override->idleLoop); } else { ConfigurationClearValue(config, sectionName, "idleLoop"); @@ -335,7 +324,7 @@ void GBAOverrideSave(struct Configuration* config, const struct GBACartridgeOver } void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* override) { - if (override->savetype != SAVEDATA_AUTODETECT) { + if (override->savetype != GBA_SAVEDATA_AUTODETECT) { GBASavedataForceType(&gba->memory.savedata, override->savetype); } @@ -376,7 +365,7 @@ void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* overri } } - if (override->idleLoop != IDLE_LOOP_NONE) { + if (override->idleLoop != GBA_IDLE_LOOP_NONE) { gba->idleLoop = override->idleLoop; if (gba->idleOptimization == IDLE_LOOP_DETECT) { gba->idleOptimization = IDLE_LOOP_REMOVE; @@ -385,7 +374,7 @@ void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* overri } void GBAOverrideApplyDefaults(struct GBA* gba, const struct Configuration* overrides) { - struct GBACartridgeOverride override = { .idleLoop = IDLE_LOOP_NONE }; + struct GBACartridgeOverride override = { .idleLoop = GBA_IDLE_LOOP_NONE }; const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom; if (cart) { memcpy(override.id, &cart->id, sizeof(override.id)); @@ -429,7 +418,7 @@ void GBAOverrideApplyDefaults(struct GBA* gba, const struct Configuration* overr if (isPokemon && !isKnownPokemon) { // Enable FLASH1M and RTC on Pokémon ROM hacks - override.savetype = SAVEDATA_FLASH1M; + override.savetype = GBA_SAVEDATA_FLASH1M; override.hardware = HW_RTC; override.vbaBugCompat = true; GBAOverrideApply(gba, &override); diff --git a/src/gba/savedata.c b/src/gba/savedata.c index e7176650b72..11be3e0abfa 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -44,7 +44,7 @@ static void _ashesToAshes(struct mTiming* timing, void* user, uint32_t cyclesLat } void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) { - savedata->type = SAVEDATA_AUTODETECT; + savedata->type = GBA_SAVEDATA_AUTODETECT; savedata->data = 0; savedata->command = EEPROM_COMMAND_NULL; savedata->flashState = FLASH_STATE_RAW; @@ -72,35 +72,35 @@ void GBASavedataDeinit(struct GBASavedata* savedata) { savedata->vf = NULL; } else { switch (savedata->type) { - case SAVEDATA_SRAM: + case GBA_SAVEDATA_SRAM: mappedMemoryFree(savedata->data, GBA_SIZE_SRAM); break; - case SAVEDATA_SRAM512: + case GBA_SAVEDATA_SRAM512: mappedMemoryFree(savedata->data, GBA_SIZE_SRAM512); break; - case SAVEDATA_FLASH512: + case GBA_SAVEDATA_FLASH512: mappedMemoryFree(savedata->data, GBA_SIZE_FLASH512); break; - case SAVEDATA_FLASH1M: + case GBA_SAVEDATA_FLASH1M: mappedMemoryFree(savedata->data, GBA_SIZE_FLASH1M); break; - case SAVEDATA_EEPROM: + case GBA_SAVEDATA_EEPROM: mappedMemoryFree(savedata->data, GBA_SIZE_EEPROM); break; - case SAVEDATA_EEPROM512: + case GBA_SAVEDATA_EEPROM512: mappedMemoryFree(savedata->data, GBA_SIZE_EEPROM512); break; - case SAVEDATA_FORCE_NONE: - case SAVEDATA_AUTODETECT: + case GBA_SAVEDATA_FORCE_NONE: + case GBA_SAVEDATA_AUTODETECT: break; } } savedata->data = 0; - savedata->type = SAVEDATA_AUTODETECT; + savedata->type = GBA_SAVEDATA_AUTODETECT; } void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf, bool writeback) { - enum SavedataType type = savedata->type; + enum GBASavedataType type = savedata->type; struct VFile* oldVf = savedata->vf; GBASavedataDeinit(savedata); if (oldVf && oldVf != savedata->realVf) { @@ -116,7 +116,7 @@ void GBASavedataUnmask(struct GBASavedata* savedata) { if (!savedata->realVf || savedata->vf == savedata->realVf) { return; } - enum SavedataType type = savedata->type; + enum GBASavedataType type = savedata->type; struct VFile* vf = savedata->vf; GBASavedataDeinit(savedata); savedata->vf = savedata->realVf; @@ -132,20 +132,20 @@ void GBASavedataUnmask(struct GBASavedata* savedata) { bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) { if (savedata->data) { switch (savedata->type) { - case SAVEDATA_SRAM: + case GBA_SAVEDATA_SRAM: return out->write(out, savedata->data, GBA_SIZE_SRAM) == GBA_SIZE_SRAM; - case SAVEDATA_SRAM512: + case GBA_SAVEDATA_SRAM512: return out->write(out, savedata->data, GBA_SIZE_SRAM512) == GBA_SIZE_SRAM512; - case SAVEDATA_FLASH512: + case GBA_SAVEDATA_FLASH512: return out->write(out, savedata->data, GBA_SIZE_FLASH512) == GBA_SIZE_FLASH512; - case SAVEDATA_FLASH1M: + case GBA_SAVEDATA_FLASH1M: return out->write(out, savedata->data, GBA_SIZE_FLASH1M) == GBA_SIZE_FLASH1M; - case SAVEDATA_EEPROM: + case GBA_SAVEDATA_EEPROM: return out->write(out, savedata->data, GBA_SIZE_EEPROM) == GBA_SIZE_EEPROM; - case SAVEDATA_EEPROM512: + case GBA_SAVEDATA_EEPROM512: return out->write(out, savedata->data, GBA_SIZE_EEPROM512) == GBA_SIZE_EEPROM512; - case SAVEDATA_AUTODETECT: - case SAVEDATA_FORCE_NONE: + case GBA_SAVEDATA_AUTODETECT: + case GBA_SAVEDATA_FORCE_NONE: return true; } } else if (savedata->vf) { @@ -163,21 +163,21 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) { size_t GBASavedataSize(const struct GBASavedata* savedata) { switch (savedata->type) { - case SAVEDATA_SRAM: + case GBA_SAVEDATA_SRAM: return GBA_SIZE_SRAM; - case SAVEDATA_SRAM512: + case GBA_SAVEDATA_SRAM512: return GBA_SIZE_SRAM512; - case SAVEDATA_FLASH512: + case GBA_SAVEDATA_FLASH512: return GBA_SIZE_FLASH512; - case SAVEDATA_FLASH1M: + case GBA_SAVEDATA_FLASH1M: return GBA_SIZE_FLASH1M; - case SAVEDATA_EEPROM: + case GBA_SAVEDATA_EEPROM: return GBA_SIZE_EEPROM; - case SAVEDATA_EEPROM512: + case GBA_SAVEDATA_EEPROM512: return GBA_SIZE_EEPROM512; - case SAVEDATA_FORCE_NONE: + case GBA_SAVEDATA_FORCE_NONE: return 0; - case SAVEDATA_AUTODETECT: + case GBA_SAVEDATA_AUTODETECT: default: if (savedata->vf) { return savedata->vf->size(savedata->vf); @@ -188,7 +188,7 @@ size_t GBASavedataSize(const struct GBASavedata* savedata) { bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in) { if (savedata->data) { - if (!in || savedata->type == SAVEDATA_FORCE_NONE) { + if (!in || savedata->type == GBA_SAVEDATA_FORCE_NONE) { return false; } ssize_t size = GBASavedataSize(savedata); @@ -220,11 +220,11 @@ bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in) { return true; } -void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) { +void GBASavedataForceType(struct GBASavedata* savedata, enum GBASavedataType type) { if (savedata->type == type) { return; } - if (savedata->type != SAVEDATA_AUTODETECT) { + if (savedata->type != GBA_SAVEDATA_AUTODETECT) { struct VFile* vf = savedata->vf; int mapMode = savedata->mapMode; bool maskWriteback = savedata->maskWriteback; @@ -234,40 +234,40 @@ void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) savedata->maskWriteback = maskWriteback; } switch (type) { - case SAVEDATA_FLASH512: - case SAVEDATA_FLASH1M: + case GBA_SAVEDATA_FLASH512: + case GBA_SAVEDATA_FLASH1M: savedata->type = type; GBASavedataInitFlash(savedata); break; - case SAVEDATA_EEPROM: - case SAVEDATA_EEPROM512: + case GBA_SAVEDATA_EEPROM: + case GBA_SAVEDATA_EEPROM512: savedata->type = type; GBASavedataInitEEPROM(savedata); break; - case SAVEDATA_SRAM: + case GBA_SAVEDATA_SRAM: GBASavedataInitSRAM(savedata); break; - case SAVEDATA_SRAM512: + case GBA_SAVEDATA_SRAM512: GBASavedataInitSRAM512(savedata); break; - case SAVEDATA_FORCE_NONE: - savedata->type = SAVEDATA_FORCE_NONE; + case GBA_SAVEDATA_FORCE_NONE: + savedata->type = GBA_SAVEDATA_FORCE_NONE; break; - case SAVEDATA_AUTODETECT: + case GBA_SAVEDATA_AUTODETECT: break; } } void GBASavedataInitFlash(struct GBASavedata* savedata) { - if (savedata->type == SAVEDATA_AUTODETECT) { - savedata->type = SAVEDATA_FLASH512; + if (savedata->type == GBA_SAVEDATA_AUTODETECT) { + savedata->type = GBA_SAVEDATA_FLASH512; } - if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) { + if (savedata->type != GBA_SAVEDATA_FLASH512 && savedata->type != GBA_SAVEDATA_FLASH1M) { mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata"); return; } int32_t flashSize = GBA_SIZE_FLASH512; - if (savedata->type == SAVEDATA_FLASH1M) { + if (savedata->type == GBA_SAVEDATA_FLASH1M) { flashSize = GBA_SIZE_FLASH1M; } off_t end; @@ -289,14 +289,14 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) { } void GBASavedataInitEEPROM(struct GBASavedata* savedata) { - if (savedata->type == SAVEDATA_AUTODETECT) { - savedata->type = SAVEDATA_EEPROM512; - } else if (savedata->type != SAVEDATA_EEPROM512 && savedata->type != SAVEDATA_EEPROM) { + if (savedata->type == GBA_SAVEDATA_AUTODETECT) { + savedata->type = GBA_SAVEDATA_EEPROM512; + } else if (savedata->type != GBA_SAVEDATA_EEPROM512 && savedata->type != GBA_SAVEDATA_EEPROM) { mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata"); return; } int32_t eepromSize = GBA_SIZE_EEPROM512; - if (savedata->type == SAVEDATA_EEPROM) { + if (savedata->type == GBA_SAVEDATA_EEPROM) { eepromSize = GBA_SIZE_EEPROM; } off_t end; @@ -316,8 +316,8 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) { } void GBASavedataInitSRAM(struct GBASavedata* savedata) { - if (savedata->type == SAVEDATA_AUTODETECT) { - savedata->type = SAVEDATA_SRAM; + if (savedata->type == GBA_SAVEDATA_AUTODETECT) { + savedata->type = GBA_SAVEDATA_SRAM; } else { mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata"); return; @@ -340,8 +340,8 @@ void GBASavedataInitSRAM(struct GBASavedata* savedata) { } void GBASavedataInitSRAM512(struct GBASavedata* savedata) { - if (savedata->type == SAVEDATA_AUTODETECT) { - savedata->type = SAVEDATA_SRAM512; + if (savedata->type == GBA_SAVEDATA_AUTODETECT) { + savedata->type = GBA_SAVEDATA_SRAM512; } else { mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata"); return; @@ -365,11 +365,11 @@ void GBASavedataInitSRAM512(struct GBASavedata* savedata) { uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) { if (savedata->command == FLASH_COMMAND_ID) { - if (savedata->type == SAVEDATA_FLASH512) { + if (savedata->type == GBA_SAVEDATA_FLASH512) { if (address < 2) { return FLASH_MFG_PANASONIC >> (address * 8); } - } else if (savedata->type == SAVEDATA_FLASH1M) { + } else if (savedata->type == GBA_SAVEDATA_FLASH1M) { if (address < 2) { return FLASH_MFG_SANYO >> (address * 8); } @@ -472,10 +472,10 @@ static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) { if (size < GBA_SIZE_EEPROM512) { return; } - if (savedata->type == SAVEDATA_EEPROM) { + if (savedata->type == GBA_SAVEDATA_EEPROM) { return; } - savedata->type = SAVEDATA_EEPROM; + savedata->type = GBA_SAVEDATA_EEPROM; if (!savedata->vf) { return; } @@ -605,7 +605,7 @@ void GBASavedataRTCWrite(struct GBASavedata* savedata) { int bank = 0; if ((savedata->vf->size(savedata->vf) & 0xFF) != sizeof(buffer)) { // Writing past the end of the file can invalidate the file mapping - if (savedata->type == SAVEDATA_FLASH1M) { + if (savedata->type == GBA_SAVEDATA_FLASH1M) { bank = savedata->currentBank == &savedata->data[0x10000]; } savedata->vf->unmap(savedata->vf, savedata->data, size); @@ -614,9 +614,9 @@ void GBASavedataRTCWrite(struct GBASavedata* savedata) { savedata->vf->write(savedata->vf, &buffer, sizeof(buffer)); if (!savedata->data) { savedata->data = savedata->vf->map(savedata->vf, size, MAP_WRITE); - if (savedata->type == SAVEDATA_FLASH1M) { + if (savedata->type == GBA_SAVEDATA_FLASH1M) { savedata->currentBank = &savedata->data[bank << 16]; - } else if (savedata->type == SAVEDATA_FLASH512) { + } else if (savedata->type == GBA_SAVEDATA_FLASH512) { savedata->currentBank = savedata->data; } } @@ -730,7 +730,7 @@ void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerial LOAD_32(savedata->writeAddress, 0, &state->savedata.writeAddress); LOAD_16(savedata->settling, 0, &state->savedata.settlingSector); - if (savedata->type == SAVEDATA_FLASH1M) { + if (savedata->type == GBA_SAVEDATA_FLASH1M) { _flashSwitchBank(savedata, GBASerializedSavedataFlagsGetFlashBank(flags)); } @@ -743,9 +743,9 @@ void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerial void _flashSwitchBank(struct GBASavedata* savedata, int bank) { mLOG(GBA_SAVE, DEBUG, "Performing flash bank switch to bank %i", bank); - if (bank > 0 && savedata->type == SAVEDATA_FLASH512) { + if (bank > 0 && savedata->type == GBA_SAVEDATA_FLASH512) { mLOG(GBA_SAVE, INFO, "Updating flash chip from 512kb to 1Mb"); - savedata->type = SAVEDATA_FLASH1M; + savedata->type = GBA_SAVEDATA_FLASH1M; if (savedata->vf) { savedata->vf->unmap(savedata->vf, savedata->data, GBA_SIZE_FLASH512); if (savedata->vf->size(savedata->vf) < GBA_SIZE_FLASH1M) { @@ -764,7 +764,7 @@ void _flashErase(struct GBASavedata* savedata) { mLOG(GBA_SAVE, DEBUG, "Performing flash chip erase"); savedata->dirty |= mSAVEDATA_DIRT_NEW; size_t size = GBA_SIZE_FLASH512; - if (savedata->type == SAVEDATA_FLASH1M) { + if (savedata->type == GBA_SAVEDATA_FLASH1M) { size = GBA_SIZE_FLASH1M; } memset(savedata->data, 0xFF, size); @@ -774,7 +774,7 @@ void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) { mLOG(GBA_SAVE, DEBUG, "Performing flash sector erase at 0x%04x", sectorStart); savedata->dirty |= mSAVEDATA_DIRT_NEW; size_t size = 0x1000; - if (savedata->type == SAVEDATA_FLASH1M) { + if (savedata->type == GBA_SAVEDATA_FLASH1M) { mLOG(GBA_SAVE, DEBUG, "Performing unknown sector-size erase at 0x%04x", sectorStart); } savedata->settling = sectorStart >> 12; diff --git a/src/gba/sharkport.c b/src/gba/sharkport.c index 9e1e645181d..2f33e3d41a9 100644 --- a/src/gba/sharkport.c +++ b/src/gba/sharkport.c @@ -18,9 +18,9 @@ static const int GSV_PAYLOAD_OFFSET = 0x430; static bool _importSavedata(struct GBA* gba, void* payload, size_t size) { bool success = false; switch (gba->memory.savedata.type) { - case SAVEDATA_FLASH512: + case GBA_SAVEDATA_FLASH512: if (size > GBA_SIZE_FLASH512) { - GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M); + GBASavedataForceType(&gba->memory.savedata, GBA_SAVEDATA_FLASH1M); } // Fall through default: @@ -28,8 +28,8 @@ static bool _importSavedata(struct GBA* gba, void* payload, size_t size) { size = GBASavedataSize(&gba->memory.savedata); } break; - case SAVEDATA_FORCE_NONE: - case SAVEDATA_AUTODETECT: + case GBA_SAVEDATA_FORCE_NONE: + case GBA_SAVEDATA_AUTODETECT: goto cleanup; } @@ -276,7 +276,7 @@ bool GBASavedataExportSharkPort(const struct GBA* gba, struct VFile* vf) { checksum += buffer.c[i] << (checksum % 24); } - if (gba->memory.savedata.type == SAVEDATA_EEPROM) { + if (gba->memory.savedata.type == GBA_SAVEDATA_EEPROM) { for (i = 0; i < size; ++i) { char byte = gba->memory.savedata.data[i ^ 7]; checksum += byte << (checksum % 24); diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index 470c50db7d2..c85103bcecf 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -4,7 +4,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include #include #include #ifdef M_CORE_GBA @@ -47,8 +46,9 @@ static enum FilterMode { FM_NEAREST, FM_LINEAR_1x, FM_LINEAR_2x, + FM_LINEAR_3x, FM_MAX -} filterMode = FM_LINEAR_2x; +} filterMode = FM_LINEAR_3x; static enum DarkenMode { DM_NATIVE, @@ -60,7 +60,7 @@ static enum DarkenMode { #define _3DS_INPUT 0x3344534B -#define AUDIO_SAMPLES 384 +#define AUDIO_SAMPLES 1280 #define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 16) #define DSP_BUFFERS 4 @@ -97,9 +97,8 @@ static int bufferId = 0; static bool frameLimiter = true; static u32 frameCounter; -static C3D_RenderTarget* topScreen[2]; -static C3D_RenderTarget* bottomScreen[2]; -static int doubleBuffer = 0; +static C3D_RenderTarget* topScreen; +static C3D_RenderTarget* bottomScreen; static bool frameStarted = false; static C3D_RenderTarget* upscaleBuffer; @@ -115,28 +114,26 @@ static bool _initGpu(void) { } if (gfxIsWide()) { - topScreen[0] = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0); - topScreen[1] = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0); + topScreen = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0); } else { - topScreen[0] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0); - topScreen[1] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0); + topScreen = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0); } - bottomScreen[0] = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0); - bottomScreen[1] = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0); - if (!topScreen[0] || !topScreen[1] || !bottomScreen[0] || !bottomScreen[1]) { + bottomScreen = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0); + if (!topScreen || !bottomScreen) { return false; } C3D_FrameBegin(0); - C3D_FrameDrawOn(bottomScreen[0]); - C3D_RenderTargetClear(bottomScreen[0], C3D_CLEAR_COLOR, 0, 0); - C3D_FrameDrawOn(topScreen[0]); - C3D_RenderTargetClear(topScreen[0], C3D_CLEAR_COLOR, 0, 0); - C3D_RenderTargetSetOutput(topScreen[0], GFX_TOP, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)); - C3D_RenderTargetSetOutput(bottomScreen[0], GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)); + C3D_FrameDrawOn(bottomScreen); + C3D_RenderTargetClear(bottomScreen, C3D_CLEAR_COLOR, 0, 0); + C3D_FrameDrawOn(topScreen); + C3D_RenderTargetClear(topScreen, C3D_CLEAR_COLOR, 0, 0); + C3D_RenderTargetSetOutput(topScreen, GFX_TOP, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)); + C3D_RenderTargetSetOutput(bottomScreen, GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)); C3D_FrameEnd(0); - if (!C3D_TexInitVRAM(&upscaleBufferTex, 512, 512, GPU_RGB8)) { + if (!C3D_TexInitVRAM(&upscaleBufferTex, 1024, 512, GPU_RGB8)) { + __builtin_trap(); return false; } upscaleBuffer = C3D_RenderTargetCreateFromTex(&upscaleBufferTex, GPU_TEXFACE_2D, 0, 0); @@ -164,10 +161,8 @@ static void _cleanup(void) { screenshotBuffer = NULL; } - C3D_RenderTargetDelete(topScreen[0]); - C3D_RenderTargetDelete(topScreen[1]); - C3D_RenderTargetDelete(bottomScreen[0]); - C3D_RenderTargetDelete(bottomScreen[1]); + C3D_RenderTargetDelete(topScreen); + C3D_RenderTargetDelete(bottomScreen); C3D_RenderTargetDelete(upscaleBuffer); C3D_TexDelete(&upscaleBufferTex); C3D_TexDelete(&outputTexture[0]); @@ -194,7 +189,7 @@ static void _map3DSKey(struct mInputMap* map, int ctrKey, int key) { mInputBindKey(map, _3DS_INPUT, __builtin_ctz(ctrKey), key); } -static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right); +static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buffer); static void _drawStart(void) { if (frameStarted) { @@ -216,10 +211,10 @@ static void _drawStart(void) { C3D_FrameBegin(0); ctrStartFrame(); - C3D_FrameDrawOn(bottomScreen[doubleBuffer]); - C3D_RenderTargetClear(bottomScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0); - C3D_FrameDrawOn(topScreen[doubleBuffer]); - C3D_RenderTargetClear(topScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0); + C3D_FrameDrawOn(bottomScreen); + C3D_RenderTargetClear(bottomScreen, C3D_CLEAR_COLOR, 0, 0); + C3D_FrameDrawOn(topScreen); + C3D_RenderTargetClear(topScreen, C3D_CLEAR_COLOR, 0, 0); } static void _drawEnd(void) { @@ -227,12 +222,10 @@ static void _drawEnd(void) { return; } ctrEndFrame(); - C3D_RenderTargetSetOutput(topScreen[doubleBuffer], GFX_TOP, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)); - C3D_RenderTargetSetOutput(bottomScreen[doubleBuffer], GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)); + C3D_RenderTargetSetOutput(topScreen, GFX_TOP, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)); + C3D_RenderTargetSetOutput(bottomScreen, GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)); C3D_FrameEnd(0); frameStarted = false; - - doubleBuffer ^= 1; } static int _batteryState(void) { @@ -256,7 +249,7 @@ static int _batteryState(void) { } static void _guiPrepare(void) { - C3D_FrameDrawOn(bottomScreen[doubleBuffer]); + C3D_FrameDrawOn(bottomScreen); ctrSetViewportSize(320, 240, true); } @@ -349,13 +342,13 @@ static void _gameLoaded(struct mGUIRunner* runner) { } osSetSpeedupEnable(true); - double ratio = GBAAudioCalculateRatio(1, 268111856.f / 4481136.f, 1); - blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 32768 * ratio); - blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 32768 * ratio); if (hasSound != NO_SOUND) { audioPos = 0; } if (hasSound == DSP_SUPPORTED) { + unsigned sampleRate = runner->core->audioSampleRate(runner->core); + double fauxClock = mCoreCalculateFramerateRatio(runner->core, 16756991. / 280095.); + ndspChnSetRate(0, sampleRate * fauxClock); memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * 2 * sizeof(int16_t)); } unsigned mode; @@ -476,21 +469,33 @@ static u32 _setupTex(int out, bool faded) { static void _drawTex(struct mCore* core, bool faded, bool both) { unsigned screen_w, screen_h; bool isWide = screenMode >= SM_PA_TOP && gfxIsWide(); + + if (filterMode < FM_LINEAR_1x || filterMode > FM_LINEAR_3x) { + // Out-of-range filtering modes are not supported + filterMode = FM_LINEAR_3x; + } + int mult = 1 + filterMode - FM_LINEAR_1x; + switch (screenMode) { case SM_PA_BOTTOM: - C3D_FrameDrawOn(bottomScreen[doubleBuffer]); + C3D_FrameDrawOn(bottomScreen); screen_w = 320; screen_h = 240; break; case SM_PA_TOP: - C3D_FrameDrawOn(topScreen[doubleBuffer]); + C3D_FrameDrawOn(topScreen); screen_w = isWide ? 800 : 400; screen_h = 240; break; default: C3D_FrameDrawOn(upscaleBuffer); - screen_w = 512; - screen_h = 512; + // PICA200 erratum: if viewport X coord exceeds 1023, entire polygon + // is not rendered. If viewport Y coord exceeds 1016, GPU hangs. + // This can not be mitigated by scissor testing. + // C3D_FrameDrawOn sets the viewport dims to the texture's dims, + // thus we must re-set the viewport ourselves. + screen_w = 256 * mult; + screen_h = 256 * mult; break; } int wide = isWide ? 2 : 1; @@ -523,17 +528,13 @@ static void _drawTex(struct mCore* core, bool faded, bool both) { case SM_AF_BOTTOM: case SM_SF_TOP: case SM_SF_BOTTOM: - default: - if (filterMode == FM_LINEAR_1x) { - w = corew; - h = coreh; - } else { - w = corew * 2; - h = coreh * 2; - } + default: { + w = corew * mult; + h = coreh * mult; ctrSetViewportSize(screen_w, screen_h, false); break; } + } uint32_t color = _setupTex(activeOutputTexture, faded); ctrAddRectEx(color, x, y, w, h, 0, 0, corew, coreh, 0); @@ -549,10 +550,10 @@ static void _drawTex(struct mCore* core, bool faded, bool both) { coreh = h; screen_h = 240; if (screenMode < SM_PA_TOP) { - C3D_FrameDrawOn(bottomScreen[doubleBuffer]); + C3D_FrameDrawOn(bottomScreen); screen_w = 320; } else { - C3D_FrameDrawOn(topScreen[doubleBuffer]); + C3D_FrameDrawOn(topScreen); screen_w = isWide ? 800 : 400; } ctrSetViewportSize(screen_w, screen_h, true); @@ -606,8 +607,7 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) { GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1)); if (hasSound == NO_SOUND) { - blip_clear(runner->core->getAudioChannel(runner->core, 0)); - blip_clear(runner->core->getAudioChannel(runner->core, 1)); + mAudioBufferClear(runner->core->getAudioBuffer(runner->core)); } _drawTex(runner->core, faded, interframeBlending); @@ -702,7 +702,7 @@ static int32_t _readTiltY(struct mRotationSource* source) { static int32_t _readGyroZ(struct mRotationSource* source) { struct m3DSRotationSource* rotation = (struct m3DSRotationSource*) source; - return rotation->gyro.y << 18L; // Yes, y + return rotation->gyro.y << 17L; // Yes, y } static void _startRequestImage(struct mImageSource* source, unsigned w, unsigned h, int colorFormats) { @@ -774,15 +774,14 @@ static void _requestImage(struct mImageSource* source, const void** buffer, size CAMU_SetReceiving(&imageSource->handles[0], imageSource->buffer, PORT_CAM1, imageSource->bufferSize, imageSource->transferSize); } -static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { +static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buffer) { UNUSED(stream); if (hasSound == DSP_SUPPORTED) { int startId = bufferId; while (dspBuffer[bufferId].status == NDSP_WBUF_QUEUED || dspBuffer[bufferId].status == NDSP_WBUF_PLAYING) { bufferId = (bufferId + 1) & (DSP_BUFFERS - 1); if (bufferId == startId) { - blip_clear(left); - blip_clear(right); + mAudioBufferClear(buffer); return; } } @@ -790,8 +789,7 @@ static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* rig memset(&dspBuffer[bufferId], 0, sizeof(dspBuffer[bufferId])); dspBuffer[bufferId].data_pcm16 = tmpBuf; dspBuffer[bufferId].nsamples = AUDIO_SAMPLES; - blip_read_samples(left, dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES, true); - blip_read_samples(right, dspBuffer[bufferId].data_pcm16 + 1, AUDIO_SAMPLES, true); + mAudioBufferRead(buffer, dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES); DSP_FlushDataCache(dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES * 2 * sizeof(int16_t)); ndspChnWaveBufAdd(0, &dspBuffer[bufferId]); } @@ -856,7 +854,6 @@ int main(int argc, char* argv[]) { ndspChnReset(0); ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); ndspChnSetInterp(0, NDSP_INTERP_NONE); - ndspChnSetRate(0, 0x8000); ndspChnWaveBufClear(0); audioLeft = linearMemAlign(AUDIO_SAMPLES * DSP_BUFFERS * 2 * sizeof(int16_t), 0x80); memset(dspBuffer, 0, sizeof(dspBuffer)); @@ -979,13 +976,14 @@ int main(int argc, char* argv[]) { .title = "Filtering", .data = GUI_V_S("filterMode"), .submenu = 0, - .state = FM_LINEAR_2x, + .state = FM_LINEAR_3x, .validStates = (const char*[]) { NULL, // Disable choosing nearest neighbor; it always looks bad "Bilinear (smoother)", "Bilinear (pixelated)", + "Bilinear (ultrasharp)", }, - .nStates = 3 + .nStates = 4 }, { .title = "Screen darkening", diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index ca1698b01a6..26bd8235187 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -7,7 +7,6 @@ #include -#include #include #include #include @@ -43,7 +42,6 @@ FS_Archive sdmcArchive; #include "libretro_core_options.h" #define GB_SAMPLES 512 -#define SAMPLE_RATE 32768 /* An alpha factor of 1/180 is *somewhat* equivalent * to calculating the average for the last 180 * frames, or 3 seconds of runtime... */ @@ -69,7 +67,7 @@ static bool libretro_supports_bitmasks = false; static void GBARetroLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args); -static void _postAudioBuffer(struct mAVStream*, blip_t* left, blip_t* right); +static void _postAudioBuffer(struct mAVStream*, struct mAudioBuffer*); static void _setRumble(struct mRumble* rumble, int enable); static uint8_t _readLux(struct GBALuminanceSource* lux); static void _updateLux(struct GBALuminanceSource* lux); @@ -1349,7 +1347,7 @@ void retro_get_system_av_info(struct retro_system_av_info* info) { info->geometry.aspect_ratio = width / (double) height; info->timing.fps = core->frequency(core) / (float) core->frameCycles(core); - info->timing.sample_rate = SAMPLE_RATE; + info->timing.sample_rate = core->audioSampleRate(core); } void retro_init(void) { @@ -1690,9 +1688,8 @@ void retro_run(void) { #ifdef M_CORE_GBA if (core->platform(core) == mPLATFORM_GBA) { - blip_t *audioChannelLeft = core->getAudioChannel(core, 0); - blip_t *audioChannelRight = core->getAudioChannel(core, 1); - int samplesAvail = blip_samples_avail(audioChannelLeft); + struct mAudioBuffer *buffer = core->getAudioBuffer(core); + int samplesAvail = mAudioBufferAvailable(buffer); if (samplesAvail > 0) { /* Update 'running average' of number of * samples per frame. @@ -1709,8 +1706,7 @@ void retro_run(void) { audioSampleBufferSize = (samplesToRead * 2); audioSampleBuffer = realloc(audioSampleBuffer, audioSampleBufferSize * sizeof(int16_t)); } - int produced = blip_read_samples(audioChannelLeft, audioSampleBuffer, samplesToRead, true); - blip_read_samples(audioChannelRight, audioSampleBuffer + 1, samplesToRead, true); + int produced = mAudioBufferRead(buffer, audioSampleBuffer, samplesToRead); if (produced > 0) { if (audioLowPassEnabled) { _audioLowPassFilter(audioSampleBuffer, produced); @@ -2012,11 +2008,11 @@ bool retro_load_game(const struct retro_game_info* game) { * to nominal number of samples per frame. * Buffer will be resized as required in * retro_run(). */ - size_t audioSamplesPerFrame = (size_t)((float)SAMPLE_RATE * (float)core->frameCycles(core) / - (float)core->frequency(core)); - audioSampleBufferSize = audioSamplesPerFrame * 2; - audioSampleBuffer = malloc(audioSampleBufferSize * sizeof(int16_t)); - audioSamplesPerFrameAvg = (float)audioSamplesPerFrame; + size_t audioSamplesPerFrame = (size_t)((float) core->audioSampleRate(core) * (float) core->frameCycles(core) / + (float)core->frequency(core)); + audioSampleBufferSize = ceil(audioSamplesPerFrame) * 2; + audioSampleBuffer = malloc(audioSampleBufferSize * sizeof(int16_t)); + audioSamplesPerFrameAvg = (float) audioSamplesPerFrame; /* Internal audio buffer size should be * audioSamplesPerFrame, but number of samples * actually generated varies slightly on a @@ -2046,9 +2042,6 @@ bool retro_load_game(const struct retro_game_info* game) { core->setAudioBufferSize(core, GB_SAMPLES); } - blip_set_rates(core->getAudioChannel(core, 0), core->frequency(core), SAMPLE_RATE); - blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), SAMPLE_RATE); - core->setPeripheral(core, mPERIPH_RUMBLE, &rumble); core->setPeripheral(core, mPERIPH_ROTATION, &rotation); @@ -2315,7 +2308,7 @@ size_t retro_get_memory_size(unsigned id) { #ifdef M_CORE_GBA case mPLATFORM_GBA: switch (((struct GBA*) core->board)->memory.savedata.type) { - case SAVEDATA_AUTODETECT: + case GBA_SAVEDATA_AUTODETECT: return GBA_SIZE_FLASH1M; default: return GBASavedataSize(&((struct GBA*) core->board)->memory.savedata); @@ -2402,10 +2395,9 @@ void GBARetroLog(struct mLogger* logger, int category, enum mLogLevel level, con } /* Used only for GB/GBC content */ -static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { +static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buffer) { UNUSED(stream); - int produced = blip_read_samples(left, audioSampleBuffer, GB_SAMPLES, true); - blip_read_samples(right, audioSampleBuffer + 1, GB_SAMPLES, true); + int produced = mAudioBufferRead(buffer, audioSampleBuffer, GB_SAMPLES); if (produced > 0) { if (audioLowPassEnabled) { _audioLowPassFilter(audioSampleBuffer, produced); @@ -2554,7 +2546,7 @@ static void _updateRotation(struct mRotationSource* source) { tiltY = sensorGetCallback(0, RETRO_SENSOR_ACCELEROMETER_Y) * 2e8f; } if (gyroEnabled) { - gyroZ = sensorGetCallback(0, RETRO_SENSOR_GYROSCOPE_Z) * -1.1e9f; + gyroZ = sensorGetCallback(0, RETRO_SENSOR_GYROSCOPE_Z) * -5.5e8f; } } diff --git a/src/platform/openemu/Info.plist.in b/src/platform/openemu/Info.plist.in deleted file mode 100644 index b4ecfa95e7b..00000000000 --- a/src/platform/openemu/Info.plist.in +++ /dev/null @@ -1,52 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${PROJECT_NAME} - CFBundleIconFile - mGBA - CFBundleIdentifier - com.endrift.mgba - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - BNDL - CFBundleSignature - ???? - CFBundleVersion - ${VERSION_STRING} - NSPrincipalClass - OEGameCoreController - OEGameCoreClass - mGBAGameCore - OEGameCoreOptions - - openemu.system.gba - - OEGameCoreRewindBufferSeconds - 60 - OEGameCoreRewindInterval - 0 - OEGameCoreSupportsRewinding - - OEGameCoreSupportsCheatCode - - - - OEGameCorePlayerCount - 1 - OEProjectURL - https://mgba.io/ - OESystemIdentifiers - - openemu.system.gba - - SUEnableAutomaticChecks - 1 - SUFeedURL - https://raw.github.com/OpenEmu/OpenEmu-Update/master/mgba_appcast.xml - - diff --git a/src/platform/openemu/OEGBASystemResponderClient.h b/src/platform/openemu/OEGBASystemResponderClient.h deleted file mode 100644 index 4fc0179d101..00000000000 --- a/src/platform/openemu/OEGBASystemResponderClient.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright (c) 2011, OpenEmu Team - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the OpenEmu Team nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import - -@protocol OESystemResponderClient; - -typedef enum _OEGBAButton -{ - OEGBAButtonUp, - OEGBAButtonDown, - OEGBAButtonLeft, - OEGBAButtonRight, - OEGBAButtonA, - OEGBAButtonB, - OEGBAButtonL, - OEGBAButtonR, - OEGBAButtonStart, - OEGBAButtonSelect, - OEGBAButtonCount -} OEGBAButton; - -@protocol OEGBASystemResponderClient - -- (oneway void)didPushGBAButton:(OEGBAButton)button forPlayer:(NSUInteger)player; -- (oneway void)didReleaseGBAButton:(OEGBAButton)button forPlayer:(NSUInteger)player; - -@end diff --git a/src/platform/openemu/mGBAGameCore.h b/src/platform/openemu/mGBAGameCore.h deleted file mode 100644 index 744fdecfe7e..00000000000 --- a/src/platform/openemu/mGBAGameCore.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -OE_EXPORTED_CLASS -@interface mGBAGameCore : OEGameCore -@end diff --git a/src/platform/openemu/mGBAGameCore.m b/src/platform/openemu/mGBAGameCore.m deleted file mode 100644 index 7656a894599..00000000000 --- a/src/platform/openemu/mGBAGameCore.m +++ /dev/null @@ -1,298 +0,0 @@ -/* - Copyright (c) 2016, Jeffrey Pfau - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - */ - -#import "mGBAGameCore.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#import -#import "OEGBASystemResponderClient.h" -#import - -#define SAMPLES 1024 - -@interface mGBAGameCore () -{ - struct mCore* core; - void* outputBuffer; - NSMutableDictionary *cheatSets; -} -@end - -@implementation mGBAGameCore - -- (id)init -{ - if ((self = [super init])) - { - core = GBACoreCreate(); - mCoreInitConfig(core, nil); - - struct mCoreOptions opts = { - .useBios = true, - }; - mCoreConfigLoadDefaults(&core->config, &opts); - core->init(core); - outputBuffer = nil; - - unsigned width, height; - core->baseVideoSize(core, &width, &height); - outputBuffer = malloc(width * height * BYTES_PER_PIXEL); - core->setVideoBuffer(core, outputBuffer, width); - core->setAudioBufferSize(core, SAMPLES); - cheatSets = [[NSMutableDictionary alloc] init]; - } - - return self; -} - -- (void)dealloc -{ - mCoreConfigDeinit(&core->config); - core->deinit(core); - free(outputBuffer); -} - -#pragma mark - Execution - -- (BOOL)loadFileAtPath:(NSString *)path error:(NSError **)error -{ - NSString *batterySavesDirectory = [self batterySavesDirectoryPath]; - [[NSFileManager defaultManager] createDirectoryAtURL:[NSURL fileURLWithPath:batterySavesDirectory] - withIntermediateDirectories:YES - attributes:nil - error:nil]; - if (core->dirs.save) { - core->dirs.save->close(core->dirs.save); - } - core->dirs.save = VDirOpen([batterySavesDirectory fileSystemRepresentation]); - - if (!mCoreLoadFile(core, [path fileSystemRepresentation])) { - *error = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotLoadROMError userInfo:nil]; - return NO; - } - mCoreAutoloadSave(core); - - core->reset(core); - - return YES; -} - -- (void)executeFrame -{ - core->runFrame(core); - - int16_t samples[SAMPLES * 2]; - size_t available = 0; - available = blip_samples_avail(core->getAudioChannel(core, 0)); - blip_read_samples(core->getAudioChannel(core, 0), samples, available, true); - blip_read_samples(core->getAudioChannel(core, 1), samples + 1, available, true); - [[self ringBufferAtIndex:0] write:samples maxLength:available * 4]; -} - -- (void)resetEmulation -{ - core->reset(core); -} - -- (void)setupEmulation -{ - blip_set_rates(core->getAudioChannel(core, 0), core->frequency(core), 32768); - blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), 32768); -} - -#pragma mark - Video - -- (OEIntSize)aspectSize -{ - return OEIntSizeMake(3, 2); -} - -- (OEIntRect)screenRect -{ - unsigned width, height; - core->currentVideoSize(core, &width, &height); - return OEIntRectMake(0, 0, width, height); -} - -- (OEIntSize)bufferSize -{ - unsigned width, height; - core->baseVideoSize(core, &width, &height); - return OEIntSizeMake(width, height); -} - -- (const void *)getVideoBufferWithHint:(void *)hint -{ - OEIntSize bufferSize = [self bufferSize]; - - if (!hint) { - hint = outputBuffer; - } - - outputBuffer = hint; - core->setVideoBuffer(core, hint, bufferSize.width); - - return hint; -} - -- (GLenum)pixelFormat -{ - return GL_RGBA; -} - -- (GLenum)pixelType -{ - return GL_UNSIGNED_INT_8_8_8_8_REV; -} - -- (NSTimeInterval)frameInterval -{ - return core->frequency(core) / (double) core->frameCycles(core); -} - -#pragma mark - Audio - -- (NSUInteger)channelCount -{ - return 2; -} - -- (double)audioSampleRate -{ - return 32768; -} - -#pragma mark - Save State - -- (NSData *)serializeStateWithError:(NSError **)outError -{ - struct VFile* vf = VFileMemChunk(nil, 0); - if (!mCoreSaveStateNamed(core, vf, SAVESTATE_SAVEDATA)) { - *outError = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotLoadStateError userInfo:nil]; - vf->close(vf); - return nil; - } - size_t size = vf->size(vf); - void* data = vf->map(vf, size, MAP_READ); - NSData *nsdata = [NSData dataWithBytes:data length:size]; - vf->unmap(vf, data, size); - vf->close(vf); - return nsdata; -} - -- (BOOL)deserializeState:(NSData *)state withError:(NSError **)outError -{ - struct VFile* vf = VFileFromConstMemory(state.bytes, state.length); - if (!mCoreLoadStateNamed(core, vf, SAVESTATE_SAVEDATA)) { - *outError = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotLoadStateError userInfo:nil]; - vf->close(vf); - return NO; - } - vf->close(vf); - return YES; -} - -- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block -{ - struct VFile* vf = VFileOpen([fileName fileSystemRepresentation], O_CREAT | O_TRUNC | O_RDWR); - block(mCoreSaveStateNamed(core, vf, 0), nil); - vf->close(vf); -} - -- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block -{ - struct VFile* vf = VFileOpen([fileName fileSystemRepresentation], O_RDONLY); - block(mCoreLoadStateNamed(core, vf, 0), nil); - vf->close(vf); -} - -#pragma mark - Input - -const int GBAMap[] = { - GBA_KEY_UP, - GBA_KEY_DOWN, - GBA_KEY_LEFT, - GBA_KEY_RIGHT, - GBA_KEY_A, - GBA_KEY_B, - GBA_KEY_L, - GBA_KEY_R, - GBA_KEY_START, - GBA_KEY_SELECT -}; - -- (oneway void)didPushGBAButton:(OEGBAButton)button forPlayer:(NSUInteger)player -{ - UNUSED(player); - core->addKeys(core, 1 << GBAMap[button]); -} - -- (oneway void)didReleaseGBAButton:(OEGBAButton)button forPlayer:(NSUInteger)player -{ - UNUSED(player); - core->clearKeys(core, 1 << GBAMap[button]); -} - -#pragma mark - Cheats - -- (void)setCheat:(NSString *)code setType:(NSString *)type setEnabled:(BOOL)enabled -{ - code = [code stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - code = [code stringByReplacingOccurrencesOfString:@" " withString:@""]; - - NSString *codeId = [code stringByAppendingFormat:@"/%@", type]; - struct mCheatSet* cheatSet = [[cheatSets objectForKey:codeId] pointerValue]; - if (cheatSet) { - cheatSet->enabled = enabled; - return; - } - struct mCheatDevice* cheats = core->cheatDevice(core); - cheatSet = cheats->createSet(cheats, [codeId UTF8String]); - size_t size = mCheatSetsSize(&cheats->cheats); - if (size) { - cheatSet->copyProperties(cheatSet, *mCheatSetsGetPointer(&cheats->cheats, size - 1)); - } - int codeType = GBA_CHEAT_AUTODETECT; - NSArray *codeSet = [code componentsSeparatedByString:@"+"]; - for (id c in codeSet) { - mCheatAddLine(cheatSet, [c UTF8String], codeType); - } - cheatSet->enabled = enabled; - [cheatSets setObject:[NSValue valueWithPointer:cheatSet] forKey:codeId]; - mCheatAddSet(cheats, cheatSet); -} -@end - diff --git a/src/platform/opengl/gl.c b/src/platform/opengl/gl.c index fe24d04cf33..b0cef1f0d92 100644 --- a/src/platform/opengl/gl.c +++ b/src/platform/opengl/gl.c @@ -103,20 +103,29 @@ static void mGLContextDeinit(struct VideoBackend* v) { glDeleteTextures(VIDEO_LAYER_MAX, context->layers); } -static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) { +static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h, unsigned maxW, unsigned maxH) { unsigned drawW = w; unsigned drawH = h; - unsigned maxW; - unsigned maxH; - VideoBackendGetFrameSize(v, &maxW, &maxH); + + if (maxW && drawW > maxW) { + drawW = maxW; + } + + if (maxH && drawH > maxH) { + drawH = maxH; + } + + unsigned lockW; + unsigned lockH; + VideoBackendGetFrameSize(v, &lockW, &lockH); if (v->lockAspectRatio) { - lockAspectRatioUInt(maxW, maxH, &drawW, &drawH); + lockAspectRatioUInt(lockW, lockH, &drawW, &drawH); } if (v->lockIntegerScaling) { - lockIntegerRatioUInt(maxW, &drawW); - lockIntegerRatioUInt(maxH, &drawH); + lockIntegerRatioUInt(lockW, &drawW); + lockIntegerRatioUInt(lockH, &drawH); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index 51180813eb2..6350bd287a5 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -271,20 +271,28 @@ static void mGLES2ContextDeinit(struct VideoBackend* v) { free(context->initialShader.uniforms); } -static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h) { +static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h, unsigned maxW, unsigned maxH) { struct mGLES2Context* context = (struct mGLES2Context*) v; unsigned drawW = w; unsigned drawH = h; - unsigned maxW = context->width; - unsigned maxH = context->height; + if (maxW && drawW > maxW) { + drawW = maxW; + } + + if (maxH && drawH > maxH) { + drawH = maxH; + } + + unsigned lockW = context->width; + unsigned lockH = context->height; if (v->lockAspectRatio) { - lockAspectRatioUInt(maxW, maxH, &drawW, &drawH); + lockAspectRatioUInt(lockW, lockH, &drawW, &drawH); } if (v->lockIntegerScaling) { - lockIntegerRatioUInt(maxW, &drawW); - lockIntegerRatioUInt(maxH, &drawH); + lockIntegerRatioUInt(lockW, &drawW); + lockIntegerRatioUInt(lockH, &drawH); } size_t n; for (n = 0; n < context->nShaders; ++n) { diff --git a/src/platform/psp2/psp2-context.c b/src/platform/psp2/psp2-context.c index a7504f7cca9..36253aa071c 100644 --- a/src/platform/psp2/psp2-context.c +++ b/src/platform/psp2/psp2-context.c @@ -5,7 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "psp2-context.h" -#include #include #ifdef M_CORE_GBA @@ -18,9 +17,10 @@ #include "feature/gui/gui-runner.h" #include -#include +#include #include #include +#include #include #include #include @@ -65,7 +65,7 @@ static struct mSceRotationSource { static struct mSceRumble { struct mRumble d; - struct CircleBuffer history; + struct mCircleBuffer history; int current; } rumble; @@ -84,14 +84,21 @@ bool frameLimiter = true; extern const uint8_t _binary_backdrop_png_start[]; static vita2d_texture* backdrop = 0; +#define BUFFERS 16 #define PSP2_SAMPLES 512 -#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 16) +#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * BUFFERS) + +struct mPSP2AudioBuffer { + int16_t samples[PSP2_SAMPLES * 2] __attribute__((__aligned__(64))); + bool full; +}; static struct mPSP2AudioContext { - struct mStereoSample buffer[PSP2_AUDIO_BUFFER_SIZE]; - size_t writeOffset; - size_t readOffset; - size_t samples; + struct mPSP2AudioBuffer outputBuffers[BUFFERS]; + int currentAudioBuffer; + int nextAudioBuffer; + struct mAudioBuffer buffer; + struct mAudioResampler resampler; Mutex mutex; Condition cond; bool running; @@ -103,29 +110,26 @@ void mPSP2MapKey(struct mInputMap* map, int pspKey, int key) { static THREAD_ENTRY _audioThread(void* context) { struct mPSP2AudioContext* audio = (struct mPSP2AudioContext*) context; - uint32_t zeroBuffer[PSP2_SAMPLES] = {0}; - void* buffer = zeroBuffer; + const int16_t zeroBuffer[PSP2_SAMPLES * 2] __attribute__((__aligned__(64))) = {0}; + const void* buffer = zeroBuffer; int audioPort = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_SAMPLES, 48000, SCE_AUDIO_OUT_MODE_STEREO); + struct mPSP2AudioBuffer* outputBuffer = NULL; while (audio->running) { MutexLock(&audio->mutex); - if (buffer != zeroBuffer) { + if (outputBuffer) { // Can only happen in successive iterations - audio->samples -= PSP2_SAMPLES; + outputBuffer->full = false; ConditionWake(&audio->cond); } - if (audio->samples >= PSP2_SAMPLES) { - buffer = &audio->buffer[audio->readOffset]; - audio->readOffset += PSP2_SAMPLES; - if (audio->readOffset >= PSP2_AUDIO_BUFFER_SIZE) { - audio->readOffset = 0; - } - // Don't mark samples as read until the next loop iteration to prevent - // writing to the buffer while being read (see above) + outputBuffer = &audio->outputBuffers[audio->currentAudioBuffer]; + if (outputBuffer->full) { + buffer = outputBuffer->samples; + audio->currentAudioBuffer = (audio->currentAudioBuffer + 1) % BUFFERS; } else { buffer = zeroBuffer; + outputBuffer = NULL; } MutexUnlock(&audio->mutex); - sceAudioOutOutput(audioPort, buffer); } sceAudioOutReleasePort(audioPort); @@ -149,18 +153,18 @@ static int32_t _readTiltY(struct mRotationSource* source) { static int32_t _readGyroZ(struct mRotationSource* source) { struct mSceRotationSource* rotation = (struct mSceRotationSource*) source; - return rotation->state.gyro.z * -0x10000000; + return rotation->state.gyro.z * -0x8000000; } static void _setRumble(struct mRumble* source, int enable) { struct mSceRumble* rumble = (struct mSceRumble*) source; rumble->current += enable; - if (CircleBufferSize(&rumble->history) == RUMBLE_PWM) { + if (mCircleBufferSize(&rumble->history) == RUMBLE_PWM) { int8_t oldLevel; - CircleBufferRead8(&rumble->history, &oldLevel); + mCircleBufferRead8(&rumble->history, &oldLevel); rumble->current -= oldLevel; } - CircleBufferWrite8(&rumble->history, enable); + mCircleBufferWrite8(&rumble->history, enable); int small = (rumble->current << 21) / 65793; int big = ((rumble->current * rumble->current) << 18) / 65793; struct SceCtrlActuator state = { @@ -243,25 +247,21 @@ static void _requestImage(struct mImageSource* source, const void** buffer, size sceCameraRead(imageSource->cam - 1, &read); } -static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { +static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buf) { UNUSED(stream); MutexLock(&audioContext.mutex); - while (audioContext.samples + PSP2_SAMPLES >= PSP2_AUDIO_BUFFER_SIZE) { - if (!frameLimiter) { - blip_clear(left); - blip_clear(right); - MutexUnlock(&audioContext.mutex); - return; + mAudioResamplerProcess(&audioContext.resampler); + while (mAudioBufferAvailable(&audioContext.buffer) >= PSP2_SAMPLES) { + struct mPSP2AudioBuffer* buffer = &audioContext.outputBuffers[audioContext.nextAudioBuffer]; + while (buffer->full) { + if (!frameLimiter) { + break; + } + ConditionWait(&audioContext.cond, &audioContext.mutex); } - ConditionWait(&audioContext.cond, &audioContext.mutex); - } - struct mStereoSample* samples = &audioContext.buffer[audioContext.writeOffset]; - blip_read_samples(left, &samples[0].left, PSP2_SAMPLES, true); - blip_read_samples(right, &samples[0].right, PSP2_SAMPLES, true); - audioContext.samples += PSP2_SAMPLES; - audioContext.writeOffset += PSP2_SAMPLES; - if (audioContext.writeOffset >= PSP2_AUDIO_BUFFER_SIZE) { - audioContext.writeOffset = 0; + mAudioBufferRead(&audioContext.buffer, buffer->samples, PSP2_SAMPLES); + buffer->full = true; + audioContext.nextAudioBuffer = (audioContext.nextAudioBuffer + 1) % BUFFERS; } MutexUnlock(&audioContext.mutex); } @@ -294,7 +294,11 @@ void mPSP2SetFrameLimiter(struct mGUIRunner* runner, bool limit) { UNUSED(runner); if (!frameLimiter && limit) { MutexLock(&audioContext.mutex); - while (audioContext.samples) { + while (true) { + struct mPSP2AudioBuffer* buffer = &audioContext.outputBuffers[audioContext.currentAudioBuffer]; + if (!buffer->full) { + break; + } ConditionWait(&audioContext.cond, &audioContext.mutex); } MutexUnlock(&audioContext.mutex); @@ -334,6 +338,9 @@ void mPSP2Setup(struct mGUIRunner* runner) { runner->core->setVideoBuffer(runner->core, vita2d_texture_get_datap(tex[currentTex]), 256); runner->core->setAudioBufferSize(runner->core, PSP2_SAMPLES); + mAudioBufferInit(&audioContext.buffer, PSP2_AUDIO_BUFFER_SIZE, 2); + mAudioResamplerInit(&audioContext.resampler, mINTERPOLATOR_COSINE); + mAudioResamplerSetDestination(&audioContext.resampler, &audioContext.buffer, 48000); rotation.d.sample = _sampleRotation; rotation.d.readTiltX = _readTiltX; @@ -342,7 +349,7 @@ void mPSP2Setup(struct mGUIRunner* runner) { runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d); rumble.d.setRumble = _setRumble; - CircleBufferInit(&rumble.history, RUMBLE_PWM); + mCircleBufferInit(&rumble.history, RUMBLE_PWM); runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d); camera.d.startRequestImage = _startRequestImage; @@ -374,12 +381,6 @@ void mPSP2Setup(struct mGUIRunner* runner) { } void mPSP2LoadROM(struct mGUIRunner* runner) { - float rate = 60.0f / 1.001f; - sceDisplayGetRefreshRate(&rate); - double ratio = GBAAudioCalculateRatio(1, rate, 1); - blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio); - blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio); - switch (runner->core->platform(runner->core)) { #ifdef M_CORE_GBA case mPLATFORM_GBA: @@ -415,10 +416,17 @@ void mPSP2LoadROM(struct mGUIRunner* runner) { MutexInit(&audioContext.mutex); ConditionInit(&audioContext.cond); - memset(audioContext.buffer, 0, sizeof(audioContext.buffer)); - audioContext.readOffset = 0; - audioContext.writeOffset = 0; + mAudioBufferClear(&audioContext.buffer); + audioContext.nextAudioBuffer = 0; + audioContext.currentAudioBuffer = 0; audioContext.running = true; + + float rate = 60.0f / 1.001f; + sceDisplayGetRefreshRate(&rate); + double ratio = mCoreCalculateFramerateRatio(runner->core, rate); + unsigned sampleRate = runner->core->audioSampleRate(runner->core); + mAudioBufferClear(&audioContext.buffer); + mAudioResamplerSetSource(&audioContext.resampler, runner->core->getAudioBuffer(runner->core), sampleRate / ratio, true); ThreadCreate(&audioThread, _audioThread, &audioContext); } @@ -482,7 +490,9 @@ void mPSP2Unpaused(struct mGUIRunner* runner) { void mPSP2Teardown(struct mGUIRunner* runner) { UNUSED(runner); - CircleBufferDeinit(&rumble.history); + mCircleBufferDeinit(&rumble.history); + mAudioResamplerDeinit(&audioContext.resampler); + mAudioBufferDeinit(&audioContext.buffer); vita2d_free_texture(tex[0]); vita2d_free_texture(tex[1]); vita2d_free_texture(screenshot); diff --git a/src/platform/python/_builder.h b/src/platform/python/_builder.h index eef4cf0461b..0b51eb19d96 100644 --- a/src/platform/python/_builder.h +++ b/src/platform/python/_builder.h @@ -36,7 +36,6 @@ void free(void*); #include -#include #include #include #include @@ -67,7 +66,7 @@ void free(void*); #include #include #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS #include #include #endif diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py index 1e21cc82f8f..1defca53791 100644 --- a/src/platform/python/_builder.py +++ b/src/platform/python/_builder.py @@ -21,7 +21,6 @@ #define MGBA_EXPORT #include #define OPAQUE_THREADING -#include #include #include #include diff --git a/src/platform/python/engine.c b/src/platform/python/engine.c index 67d1d7b49cb..86ec2cf5c84 100644 --- a/src/platform/python/engine.c +++ b/src/platform/python/engine.c @@ -9,7 +9,7 @@ #include #include -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS #include #endif @@ -23,7 +23,7 @@ static bool mPythonScriptEngineLoadScript(struct mScriptEngine*, const char* nam static void mPythonScriptEngineRun(struct mScriptEngine*); static bool mPythonScriptEngineLookupSymbol(struct mScriptEngine*, const char* name, int32_t* out); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS static void mPythonScriptDebuggerEntered(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); #endif @@ -41,7 +41,7 @@ struct mPythonScriptEngine* mPythonCreateScriptEngine(void) { engine->d.loadScript = mPythonScriptEngineLoadScript; engine->d.run = mPythonScriptEngineRun; engine->d.lookupSymbol = mPythonScriptEngineLookupSymbol; -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS engine->d.debuggerEntered = mPythonScriptDebuggerEntered; #endif engine->sb = NULL; @@ -82,7 +82,7 @@ bool mPythonScriptEngineLoadScript(struct mScriptEngine* se, const char* name, s void mPythonScriptEngineRun(struct mScriptEngine* se) { struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se; -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS struct mDebugger* debugger = mScriptBridgeGetDebugger(engine->sb); if (debugger) { mPythonSetDebugger(debugger); @@ -97,7 +97,7 @@ bool mPythonScriptEngineLookupSymbol(struct mScriptEngine* se, const char* name, return mPythonLookupSymbol(name, out); } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void mPythonScriptDebuggerEntered(struct mScriptEngine* se, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) { struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se; diff --git a/src/platform/python/lib.h b/src/platform/python/lib.h index 0dd8754ed76..d0893101f5b 100644 --- a/src/platform/python/lib.h +++ b/src/platform/python/lib.h @@ -6,7 +6,7 @@ extern bool mPythonLoadScript(const char*, struct VFile*); extern void mPythonRunPending(); extern bool mPythonLookupSymbol(const char* name, int32_t* out); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS extern void mPythonSetDebugger(struct mDebugger*); extern void mPythonDebuggerEntered(enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); #endif diff --git a/src/platform/python/mgba/audio.py b/src/platform/python/mgba/audio.py deleted file mode 100644 index a833d082793..00000000000 --- a/src/platform/python/mgba/audio.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2013-2018 Jeffrey Pfau -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -from ._pylib import ffi, lib # pylint: disable=no-name-in-module - - -class Buffer(object): - def __init__(self, native, internal_rate): - self._native = native - self._internal_rate = internal_rate - - @property - def available(self): - return lib.blip_samples_avail(self._native) - - def set_rate(self, rate): - lib.blip_set_rates(self._native, self._internal_rate, rate) - - def read(self, samples): - buffer = ffi.new("short[%i]" % samples) - count = self.read_into(buffer, samples, 1, 0) - return buffer[:count] - - def read_into(self, buffer, samples, channels=1, interleave=0): - return lib.blip_read_samples(self._native, ffi.addressof(buffer, interleave), samples, channels == 2) - - def clear(self): - lib.blip_clear(self._native) - - -class StereoBuffer(object): - def __init__(self, left, right): - self._left = left - self._right = right - - @property - def available(self): - return min(self._left.available, self._right.available) - - def set_rate(self, rate): - self._left.set_rate(rate) - self._right.set_rate(rate) - - def read(self, samples): - buffer = ffi.new("short[%i]" % (2 * samples)) - count = self.read_into(buffer, samples) - return buffer[0:2 * count] - - def read_into(self, buffer, samples): - samples = self._left.read_into(buffer, samples, 2, 0) - return self._right.read_into(buffer, samples, 2, 1) - - def clear(self): - self._left.clear() - self._right.clear() diff --git a/src/platform/python/mgba/core.py b/src/platform/python/mgba/core.py index 51f543d3a01..22965b3f9c7 100644 --- a/src/platform/python/mgba/core.py +++ b/src/platform/python/mgba/core.py @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from ._pylib import ffi, lib # pylint: disable=no-name-in-module -from . import tile, audio +from . import tile from cached_property import cached_property from functools import wraps @@ -242,22 +242,6 @@ def desired_video_dimensions(self): def set_video_buffer(self, image): self._core.setVideoBuffer(self._core, image.buffer, image.stride) - @protected - def set_audio_buffer_size(self, size): - self._core.setAudioBufferSize(self._core, size) - - @property - def audio_buffer_size(self): - return self._core.getAudioBufferSize(self._core) - - @protected - def get_audio_channels(self): - return audio.StereoBuffer(self.get_audio_channel(0), self.get_audio_channel(1)); - - @protected - def get_audio_channel(self, channel): - return audio.Buffer(self._core.getAudioChannel(self._core, channel), self.frequency) - @protected def reset(self): self._core.reset(self._core) diff --git a/src/platform/qt/AudioDevice.cpp b/src/platform/qt/AudioDevice.cpp index 74f86344495..bf027cd43e8 100644 --- a/src/platform/qt/AudioDevice.cpp +++ b/src/platform/qt/AudioDevice.cpp @@ -7,11 +7,12 @@ #include "LogController.h" -#include #include #include #include +#include + using namespace QGBA; AudioDevice::AudioDevice(QObject* parent) @@ -19,6 +20,13 @@ AudioDevice::AudioDevice(QObject* parent) , m_context(nullptr) { setOpenMode(ReadOnly); + mAudioBufferInit(&m_buffer, 0x4000, 2); + mAudioResamplerInit(&m_resampler, mINTERPOLATOR_SINC); +} + +AudioDevice::~AudioDevice() { + mAudioResamplerDeinit(&m_resampler); + mAudioBufferDeinit(&m_buffer); } void AudioDevice::setFormat(const QAudioFormat& format) { @@ -26,15 +34,18 @@ void AudioDevice::setFormat(const QAudioFormat& format) { LOG(QT, INFO) << tr("Can't set format of context-less audio device"); return; } - double fauxClock = GBAAudioCalculateRatio(1, m_context->impl->sync.fpsTarget, 1); mCoreSyncLockAudio(&m_context->impl->sync); - blip_set_rates(m_context->core->getAudioChannel(m_context->core, 0), - m_context->core->frequency(m_context->core), format.sampleRate() * fauxClock); - blip_set_rates(m_context->core->getAudioChannel(m_context->core, 1), - m_context->core->frequency(m_context->core), format.sampleRate() * fauxClock); + mCore* core = m_context->core; + mAudioResamplerSetSource(&m_resampler, core->getAudioBuffer(core), core->audioSampleRate(core), true); + m_format = format; + adjustResampler(); mCoreSyncUnlockAudio(&m_context->impl->sync); } +void AudioDevice::setBufferSamples(int samples) { + m_samples = samples; +} + void AudioDevice::setInput(mCoreThread* input) { m_context = input; } @@ -45,15 +56,25 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) { return 0; } - maxSize /= sizeof(mStereoSample); + if (!maxSize) { + return 0; + } + mCoreSyncLockAudio(&m_context->impl->sync); - int available = std::min({ - blip_samples_avail(m_context->core->getAudioChannel(m_context->core, 0)), - maxSize, + mAudioResamplerProcess(&m_resampler); + if (mAudioBufferAvailable(&m_buffer) < 128) { + mCoreSyncConsumeAudio(&m_context->impl->sync); + // Audio is running slow...let's wait a tiny bit for more to come in + QThread::usleep(100); + mCoreSyncLockAudio(&m_context->impl->sync); + mAudioResamplerProcess(&m_resampler); + } + quint64 available = std::min({ + mAudioBufferAvailable(&m_buffer), + static_cast(maxSize / sizeof(mStereoSample)), std::numeric_limits::max() }); - blip_read_samples(m_context->core->getAudioChannel(m_context->core, 0), &reinterpret_cast(data)->left, available, true); - blip_read_samples(m_context->core->getAudioChannel(m_context->core, 1), &reinterpret_cast(data)->right, available, true); + mAudioBufferRead(&m_buffer, reinterpret_cast(data), available); mCoreSyncConsumeAudio(&m_context->impl->sync); return available * sizeof(mStereoSample); } @@ -62,3 +83,35 @@ qint64 AudioDevice::writeData(const char*, qint64) { LOG(QT, WARN) << tr("Writing data to read-only audio device"); return 0; } + +bool AudioDevice::atEnd() const { + return false; +} + +qint64 AudioDevice::bytesAvailable() const { + if (!m_context->core) { + return true; + } + int available = mAudioBufferAvailable(&m_buffer); + return available * sizeof(mStereoSample); +} + +qint64 AudioDevice::bytesAvailable() { + if (!m_context->core) { + return true; + } + mCoreSyncLockAudio(&m_context->impl->sync); + adjustResampler(); + mAudioResamplerProcess(&m_resampler); + int available = mAudioBufferAvailable(&m_buffer); + mCoreSyncUnlockAudio(&m_context->impl->sync); + return available * sizeof(mStereoSample); +} + +void AudioDevice::adjustResampler() { + mCore* core = m_context->core; + double fauxClock = mCoreCalculateFramerateRatio(m_context->core, m_context->impl->sync.fpsTarget); + mAudioResamplerSetDestination(&m_resampler, &m_buffer, m_format.sampleRate() * fauxClock); + m_context->impl->sync.audioHighWater = m_samples + m_resampler.highWaterMark + m_resampler.lowWaterMark; + m_context->impl->sync.audioHighWater *= core->audioSampleRate(core) / (m_format.sampleRate() * fauxClock); +} diff --git a/src/platform/qt/AudioDevice.h b/src/platform/qt/AudioDevice.h index 0794b37431d..04247cf9894 100644 --- a/src/platform/qt/AudioDevice.h +++ b/src/platform/qt/AudioDevice.h @@ -8,6 +8,9 @@ #include #include +#include +#include + struct mCoreThread; namespace QGBA { @@ -17,16 +20,28 @@ Q_OBJECT public: AudioDevice(QObject* parent = nullptr); + virtual ~AudioDevice(); void setInput(mCoreThread* input); void setFormat(const QAudioFormat& format); + void setBufferSamples(int samples); + bool atEnd() const override; + qint64 bytesAvailable() const override; + qint64 bytesAvailable(); + bool isSequential() const override { return true; } protected: virtual qint64 readData(char* data, qint64 maxSize) override; virtual qint64 writeData(const char* data, qint64 maxSize) override; private: + size_t m_samples = 512; + QAudioFormat m_format; mCoreThread* m_context; + mAudioBuffer m_buffer; + mAudioResampler m_resampler; + + void adjustResampler(); }; } diff --git a/src/platform/qt/AudioProcessorQt.cpp b/src/platform/qt/AudioProcessorQt.cpp index 8d12cd935b8..87ad5b5ac19 100644 --- a/src/platform/qt/AudioProcessorQt.cpp +++ b/src/platform/qt/AudioProcessorQt.cpp @@ -9,6 +9,9 @@ #include "LogController.h" #include +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#include +#endif #include #include @@ -18,6 +21,10 @@ using namespace QGBA; AudioProcessorQt::AudioProcessorQt(QObject* parent) : AudioProcessor(parent) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + m_recheckTimer.setInterval(1); + connect(&m_recheckTimer, &QTimer::timeout, this, &AudioProcessorQt::recheckUnderflow); +#endif } void AudioProcessorQt::setInput(std::shared_ptr controller) { @@ -31,10 +38,16 @@ void AudioProcessorQt::setInput(std::shared_ptr controller) { } void AudioProcessorQt::stop() { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + m_recheckTimer.stop(); +#endif + if (m_audioOutput) { + m_audioOutput->stop(); + m_audioOutput.reset(); + } if (m_device) { m_device.reset(); } - pause(); AudioProcessor::stop(); } @@ -52,34 +65,61 @@ bool AudioProcessorQt::start() { QAudioFormat format; format.setSampleRate(m_sampleRate); format.setChannelCount(2); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) format.setSampleSize(16); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::Endian(QSysInfo::ByteOrder)); format.setSampleType(QAudioFormat::SignedInt); - m_audioOutput = new QAudioOutput(format, this); + m_audioOutput = std::make_unique(format); m_audioOutput->setCategory("game"); +#else + format.setSampleFormat(QAudioFormat::Int16); + + QAudioDevice device(QMediaDevices::defaultAudioOutput()); + m_audioOutput = std::make_unique(device, format); + LOG(QT, INFO) << "Audio outputting to " << device.description(); + connect(m_audioOutput.get(), &QAudioSink::stateChanged, this, [this](QAudio::State state) { + if (state != QAudio::IdleState) { + return; + } + recheckUnderflow(); + m_recheckTimer.start(); + }); +#endif } - m_device->setInput(input()); - m_device->setFormat(m_audioOutput->format()); - - m_audioOutput->start(m_device.get()); - return m_audioOutput->state() == QAudio::ActiveState; + if (m_audioOutput->state() == QAudio::SuspendedState) { + m_audioOutput->resume(); + } else { + m_device->setBufferSamples(m_samples); + m_device->setInput(input()); + m_device->setFormat(m_audioOutput->format()); + m_audioOutput->start(m_device.get()); + } + return m_audioOutput->state() == QAudio::ActiveState && m_audioOutput->error() == QAudio::NoError; } void AudioProcessorQt::pause() { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + m_recheckTimer.stop(); +#endif if (m_audioOutput) { - m_audioOutput->stop(); + m_audioOutput->suspend(); } } -void AudioProcessorQt::setBufferSamples(int) { +void AudioProcessorQt::setBufferSamples(int samples) { + m_samples = samples; + if (m_device) { + m_device->setBufferSamples(samples); + } } void AudioProcessorQt::inputParametersChanged() { if (m_device) { m_device->setFormat(m_audioOutput->format()); + m_device->setBufferSamples(m_samples); } } @@ -98,3 +138,16 @@ unsigned AudioProcessorQt::sampleRate() const { } return m_audioOutput->format().sampleRate(); } + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +void AudioProcessorQt::recheckUnderflow() { + if (!m_device) { + m_recheckTimer.stop(); + return; + } + if (m_device->bytesAvailable()) { + start(); + m_recheckTimer.stop(); + } +} +#endif diff --git a/src/platform/qt/AudioProcessorQt.h b/src/platform/qt/AudioProcessorQt.h index 0e56907e106..bc01a8c07df 100644 --- a/src/platform/qt/AudioProcessorQt.h +++ b/src/platform/qt/AudioProcessorQt.h @@ -7,6 +7,13 @@ #include "AudioProcessor.h" +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#else +#include +#include +#endif + class QAudioOutput; namespace QGBA { @@ -32,9 +39,19 @@ public slots: virtual void requestSampleRate(unsigned) override; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +private slots: + void recheckUnderflow(); + +private: + QTimer m_recheckTimer; + std::unique_ptr m_audioOutput; +#else private: - QAudioOutput* m_audioOutput = nullptr; + std::unique_ptr m_audioOutput; +#endif std::unique_ptr m_device; + size_t m_samples = 1024; unsigned m_sampleRate = 44100; }; diff --git a/src/platform/qt/AudioProcessorSDL.h b/src/platform/qt/AudioProcessorSDL.h index c2d5d0ee1e7..2f86c0e166a 100644 --- a/src/platform/qt/AudioProcessorSDL.h +++ b/src/platform/qt/AudioProcessorSDL.h @@ -9,6 +9,7 @@ #ifdef BUILD_SDL +#define SDL_MAIN_HANDLED #include "platform/sdl/sdl-audio.h" namespace QGBA { diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 610fa362dbf..041ad0830a6 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -25,12 +25,24 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(QT_LIBRARIES) -set(QT_V 5) -find_package(Qt${QT_V} COMPONENTS Core Widgets Network OPTIONAL_COMPONENTS Multimedia) -if(QT_V GREATER_EQUAL 6) - find_package(Qt${QT_V} COMPONENTS OpenGL OpenGLWidgets) -endif() -set(QT Qt${QT_V}) +option(FORCE_QT_VERSION "Force the used Qt version" OFF) + +if(FORCE_QT_VERSION EQUAL 5 OR FORCE_QT_VERSION EQUAL 6) + set(QT_VERSIONS ${FORCE_QT_VERSION}) +else() + set(QT_VERSIONS 6 5) +endif() +foreach(V ${QT_VERSIONS}) + set(QT Qt${V}) + set(QT_V ${V}) + find_package(${QT} COMPONENTS Core Widgets Network OPTIONAL_COMPONENTS Multimedia) + if(QT_V GREATER_EQUAL 6) + find_package(${QT} COMPONENTS OpenGL OpenGLWidgets) + endif() + if(${${QT}Widgets_FOUND}) + break() + endif() +endforeach() if(NOT BUILD_GL AND NOT BUILD_GLES2 AND NOT BUILD_GLES3) message(WARNING "OpenGL is recommended to build the Qt port") @@ -225,11 +237,8 @@ endif() if(${QT}Multimedia_FOUND) list(APPEND AUDIO_SRC AudioProcessorQt.cpp - AudioDevice.cpp) - if(QT_V LESS 6) - list(APPEND SOURCE_FILES - VideoDumper.cpp) - endif() + AudioDevice.cpp + VideoDumper.cpp) if (WIN32 AND QT_STATIC) list(APPEND QT_LIBRARIES ${QT}::QWindowsAudioPlugin ${QT}::DSServicePlugin ${QT}::QWindowsVistaStylePlugin ${QT}::QJpegPlugin strmiids mfuuid mfplat mf ksguid dxva2 evr d3d9) @@ -245,7 +254,7 @@ if(NOT AUDIO_SRC) return() endif() -if(USE_DEBUGGERS) +if(ENABLE_DEBUGGERS) list(APPEND SOURCE_FILES DebuggerController.cpp DebuggerConsole.cpp @@ -253,7 +262,7 @@ if(USE_DEBUGGERS) MemoryAccessLogView.cpp) endif() -if(USE_GDB_STUB) +if(ENABLE_GDB_STUB) list(APPEND SOURCE_FILES GDBController.cpp GDBWindow.cpp) endif() @@ -335,7 +344,6 @@ if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY) install(DIRECTORY ${PROJECT_SOURCE_DIR}/res/shaders DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) else() file(GLOB SHADERS ${PROJECT_SOURCE_DIR}/res/shaders/*.shader) - message(STATUS ${SHADERS}) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/shaders) foreach(SHADER_DIR ${SHADERS}) get_filename_component(SHADER ${SHADER_DIR} NAME) diff --git a/src/platform/qt/ColorPicker.cpp b/src/platform/qt/ColorPicker.cpp index 22d995c0f3d..8cebab5207a 100644 --- a/src/platform/qt/ColorPicker.cpp +++ b/src/platform/qt/ColorPicker.cpp @@ -44,13 +44,11 @@ bool ColorPicker::eventFilter(QObject* obj, QEvent* event) { return false; } - QWidget* swatch = static_cast(obj); - QColorDialog* colorPicker = new QColorDialog; colorPicker->setAttribute(Qt::WA_DeleteOnClose); colorPicker->setCurrentColor(m_defaultColor); colorPicker->open(); - connect(colorPicker, &QColorDialog::colorSelected, [this, swatch](const QColor& color) { + connect(colorPicker, &QColorDialog::colorSelected, [this](const QColor& color) { setColor(color); emit colorChanged(color); }); diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index e1bf354086f..91ecf8e7a7b 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -13,7 +13,7 @@ #include #ifdef M_CORE_GB -#include +#include #endif static const mOption s_frontendOptions[] = { diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 2e2bd5fed7c..660de1f574f 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -49,7 +49,7 @@ CoreController::CoreController(mCore* core, QObject* parent) GBASIODolphinCreate(&m_dolphin); #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS mDebuggerInit(&m_debugger); #endif @@ -218,7 +218,7 @@ CoreController::~CoreController() { mCoreThreadJoin(&m_threadContext); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS mDebuggerDeinit(&m_debugger); #endif @@ -331,7 +331,7 @@ void CoreController::loadConfig(ConfigController* config) { #endif } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void CoreController::attachDebugger(bool interrupt) { Interrupter interrupter(this); if (!m_threadContext.core->debugger) { @@ -478,7 +478,7 @@ void CoreController::start() { void CoreController::stop() { setSync(false); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS detachDebugger(); #endif setPaused(false); @@ -556,11 +556,7 @@ void CoreController::rewind(int states) { if (!states) { states = INT_MAX; } - for (int i = 0; i < states; ++i) { - if (!mCoreRewindRestore(&m_threadContext.impl->rewind, m_threadContext.core)) { - break; - } - } + mCoreRewindRestore(&m_threadContext.impl->rewind, m_threadContext.core, states); interrupter.resume(); emit frameAvailable(); emit rewound(); diff --git a/src/platform/qt/CoreController.h b/src/platform/qt/CoreController.h index c46b4d6e953..684d864d051 100644 --- a/src/platform/qt/CoreController.h +++ b/src/platform/qt/CoreController.h @@ -107,7 +107,7 @@ Q_OBJECT mCheatDevice* cheatDevice() { return m_threadContext.core->cheatDevice(m_threadContext.core); } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS mDebugger* debugger() { return &m_debugger; } void attachDebugger(bool interrupt = true); void detachDebugger(); @@ -305,7 +305,7 @@ public slots: bool m_autoload; int m_autosaveCounter = 0; -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS struct mDebugger m_debugger; #endif diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index 8bec92f284b..2cc7df66ec1 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -33,49 +33,33 @@ QGBA::Display* QGBA::Display::create(QWidget* parent) { switch (s_driver) { #if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) case Driver::OPENGL: -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { - format.setVersion(2, 0); - } else { - format.setVersion(3, 3); - } - format.setProfile(QSurfaceFormat::CoreProfile); - if (DisplayGL::supportsFormat(format)) { - QSurfaceFormat::setDefaultFormat(format); - } else { -#ifdef BUILD_GL - LOG(QT, WARN) << ("Failed to create an OpenGL Core context, trying old-style..."); - format.setVersion(1, 4); - format.setOption(QSurfaceFormat::DeprecatedFunctions); - if (!DisplayGL::supportsFormat(format)) { - return nullptr; - } -#else - return nullptr; -#endif + default: + if (DisplayGL::highestCompatible(format)) { + return new DisplayGL(format, parent); } - return new DisplayGL(format, parent); -#endif + break; #endif #ifdef BUILD_GL case Driver::OPENGL1: - format.setVersion(1, 4); - if (!DisplayGL::supportsFormat(format)) { - return nullptr; + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + format.setVersion(1, 4); + } else { + format.setVersion(1, 1); + } + if (DisplayGL::supportsFormat(format)) { + return new DisplayGL(format, parent); } - return new DisplayGL(format, parent); + break; #endif case Driver::QT: - return new DisplayQt(parent); - +#if !defined(BUILD_GL) && !defined(BUILD_GLES2) && !defined(BUILD_GLES3) && !defined(USE_EPOXY) default: -#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) - return new DisplayGL(format, parent); -#else - return new DisplayQt(parent); #endif + return new DisplayQt(parent); + } + return nullptr; } QGBA::Display::Display(QWidget* parent) diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index ff6f0c23b7e..bb16b268e4f 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -57,6 +57,7 @@ Q_OBJECT virtual void setVideoScale(int) {} virtual void setBackgroundImage(const QImage&) = 0; virtual QSize contentSize() const = 0; + virtual void setMaximumSize(const QSize& size) = 0; virtual void setVideoProxy(std::shared_ptr proxy) { m_videoProxy = std::move(proxy); } std::shared_ptr videoProxy() { return m_videoProxy; } @@ -105,6 +106,7 @@ public slots: bool m_filter = false; QTimer m_mouseTimer; std::shared_ptr m_videoProxy; + QSize m_maxSize; }; } diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index e31437cad9e..5b60eb46e79 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -53,6 +53,12 @@ typedef struct _XDisplay Display; #define OVERHEAD_NSEC 300000 #endif +// Legacy define from X11/X.h +#ifdef Unsorted +#undef Unsorted +#endif + +#include "LogController.h" #include "OpenGLBug.h" #include "utils.h" @@ -277,6 +283,47 @@ void DisplayGL::startDrawing(std::shared_ptr controller) { QTimer::singleShot(8, this, &DisplayGL::updateContentSize); } +bool DisplayGL::highestCompatible(QSurfaceFormat& format) { +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + format.setVersion(3, 3); + format.setProfile(QSurfaceFormat::CoreProfile); + if (DisplayGL::supportsFormat(format)) { + return true; + } + } else { +#if defined(BUILD_GLES3) || defined(USE_EPOXY) + format.setVersion(3, 1); + if (DisplayGL::supportsFormat(format)) { + return true; + } +#endif +#if defined(BUILD_GLES2) || defined(USE_EPOXY) + format.setVersion(2, 0); + if (DisplayGL::supportsFormat(format)) { + return true; + } +#endif + } +#endif + +#ifdef BUILD_GL +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY) + LOG(QT, WARN) << tr("Failed to create an OpenGL 3 context, trying old-style..."); +#endif + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + format.setVersion(1, 4); + } else { + format.setVersion(1, 1); + } + format.setOption(QSurfaceFormat::DeprecatedFunctions); + if (DisplayGL::supportsFormat(format)) { + return true; + } +#endif + return false; +} + bool DisplayGL::supportsFormat(const QSurfaceFormat& format) { if (!s_supports.contains(format)) { QOpenGLContext context; @@ -477,6 +524,10 @@ int DisplayGL::framebufferHandle() { return m_painter->glTex(); } +void DisplayGL::setMaximumSize(const QSize& size) { + QMetaObject::invokeMethod(m_painter.get(), "setMaximumSize", Q_ARG(const QSize&, size)); +} + PainterGL::PainterGL(QWindow* window, mGLWidget* widget, const QSurfaceFormat& format) : m_window(window) , m_format(format) @@ -673,6 +724,11 @@ void PainterGL::resize(const QSize& size) { } } +void PainterGL::setMaximumSize(const QSize& size) { + m_maxSize = size; + resizeContext(); +} + void PainterGL::lockAspectRatio(bool lock) { m_backend->lockAspectRatio = lock; resize(m_size); @@ -864,7 +920,11 @@ void PainterGL::unpause() { void PainterGL::performDraw() { float r = m_window->devicePixelRatio(); - m_backend->contextResized(m_backend, m_size.width() * r, m_size.height() * r); + QSize maxSize = m_maxSize; + if (!maxSize.isValid()) { + maxSize = QSize(0, 0); + } + m_backend->contextResized(m_backend, m_size.width() * r, m_size.height() * r, maxSize.width() * r, maxSize.height() * r); if (m_buffer) { m_backend->setImage(m_backend, VIDEO_LAYER_IMAGE, m_buffer); } diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index d94a68621b2..5b0aab2cd4d 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -95,7 +95,9 @@ Q_OBJECT void setVideoProxy(std::shared_ptr) override; int framebufferHandle() override; QSize contentSize() const override { return m_cachedContentSize; } + void setMaximumSize(const QSize& size) override; + static bool highestCompatible(QSurfaceFormat&); static bool supportsFormat(const QSurfaceFormat&); public slots: @@ -171,6 +173,7 @@ public slots: void pause(); void unpause(); void resize(const QSize& size); + void setMaximumSize(const QSize& size); void lockAspectRatio(bool lock); void lockIntegerScaling(bool lock); void interframeBlending(bool enable); @@ -229,6 +232,7 @@ private slots: VideoBackend* m_backend = nullptr; QSize m_size; QSize m_dims; + QSize m_maxSize; MessagePainter* m_messagePainter = nullptr; QElapsedTimer m_delayTimer; std::shared_ptr m_videoProxy; diff --git a/src/platform/qt/DisplayQt.cpp b/src/platform/qt/DisplayQt.cpp index 1d9efebecde..3723a844524 100644 --- a/src/platform/qt/DisplayQt.cpp +++ b/src/platform/qt/DisplayQt.cpp @@ -130,7 +130,24 @@ void DisplayQt::paintEvent(QPaintEvent*) { struct mRectangle frame; VideoBackendGetFrame(&m_backend, &frame); QPoint origin(-frame.x, -frame.y); - QRect full(clampSize(contentSize(), size(), isAspectRatioLocked(), isIntegerScalingLocked())); + QSize drawSize(contentSize()); + if (!drawSize.isValid() || drawSize.width() < 1 || drawSize.height() < 1) { + return; + } + QSize usedSize = size(); + QPoint screenOrigin(0, 0); + if (m_maxSize.isValid()) { + if (m_maxSize.width() < usedSize.width()) { + screenOrigin.setX((usedSize.width() - m_maxSize.width()) / 2); + usedSize.setWidth(m_maxSize.width()); + } + if (m_maxSize.height() < usedSize.height()) { + screenOrigin.setY((usedSize.height() - m_maxSize.height()) / 2); + usedSize.setHeight(m_maxSize.height()); + } + } + QRect full(clampSize(contentSize(), usedSize, isAspectRatioLocked(), isIntegerScalingLocked())); + full.translate(screenOrigin); painter.save(); painter.translate(full.topLeft()); painter.scale(full.width() / static_cast(frame.width), full.height() / static_cast(frame.height)); @@ -216,7 +233,7 @@ void DisplayQt::swap(struct VideoBackend*) { void DisplayQt::clear(struct VideoBackend*) { } -void DisplayQt::contextResized(struct VideoBackend*, unsigned, unsigned) { +void DisplayQt::contextResized(struct VideoBackend*, unsigned, unsigned, unsigned, unsigned) { } void DisplayQt::setImageSize(struct VideoBackend* v, enum VideoLayer layer, int w, int h) { diff --git a/src/platform/qt/DisplayQt.h b/src/platform/qt/DisplayQt.h index c1363dede9d..a6816a18b3b 100644 --- a/src/platform/qt/DisplayQt.h +++ b/src/platform/qt/DisplayQt.h @@ -27,6 +27,8 @@ Q_OBJECT VideoShader* shaders() override { return nullptr; } QSize contentSize() const override; VideoBackend* videoBackend() override { return &m_backend; } + void setMaximumSize(const QSize& size) override { m_maxSize = size; } + public slots: void stopDrawing() override; @@ -56,7 +58,7 @@ public slots: static void layerDimensions(const struct VideoBackend*, enum VideoLayer, struct mRectangle*); static void swap(struct VideoBackend*); static void clear(struct VideoBackend*); - static void contextResized(struct VideoBackend*, unsigned w, unsigned h); + static void contextResized(struct VideoBackend*, unsigned w, unsigned h, unsigned maxW, unsigned maxH); static void setImageSize(struct VideoBackend*, enum VideoLayer, int w, int h); static void imageSize(struct VideoBackend*, enum VideoLayer, int* w, int* h); static void setImage(struct VideoBackend*, enum VideoLayer, const void* frame); @@ -69,6 +71,7 @@ public slots: int m_width = -1; int m_height = -1; QImage m_oldBacking{nullptr}; + QSize m_maxSize; std::shared_ptr m_context = nullptr; }; diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index 56e20201970..0934f1c69b2 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -232,9 +232,9 @@ bool ForwarderController::toolInstalled(const QString& tool) { void ForwarderController::connectReply(QNetworkReply* reply, Download download, void (ForwarderController::*next)(QNetworkReply*)) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) - connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() { + connect(reply, &QNetworkReply::errorOccurred, this, [this]() { #else - connect(reply, qOverload<>(&QNetworkReply::error), this, [this, reply]() { + connect(reply, qOverload<>(&QNetworkReply::error), this, [this]() { #endif emit buildFailed(); }); diff --git a/src/platform/qt/ForwarderGenerator3DS.h b/src/platform/qt/ForwarderGenerator3DS.h index b2258506341..8713be1bf51 100644 --- a/src/platform/qt/ForwarderGenerator3DS.h +++ b/src/platform/qt/ForwarderGenerator3DS.h @@ -23,7 +23,7 @@ Q_OBJECT System system() const override { return System::N3DS; } QString extension() const override { return QLatin1String("cia"); } - virtual QStringList externalTools() const { return {"bannertool", "3dstool", "ctrtool", "makerom"}; } + virtual QStringList externalTools() const override { return {"bannertool", "3dstool", "ctrtool", "makerom"}; } void rebuild(const QString& source, const QString& target) override; diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 88963afa294..8a8427973b1 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -18,6 +18,7 @@ #include "KeyEditor.h" #ifdef BUILD_SDL +#define SDL_MAIN_HANDLED #include "platform/sdl/sdl-events.h" #endif @@ -53,7 +54,7 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& refresh(); #ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { + if (type == SDL_BINDING_BUTTON || type == SDL_BINDING_CONTROLLER) { m_profileSelect = new QComboBox(this); connect(m_profileSelect, static_cast(&QComboBox::currentIndexChanged), this, &GBAKeyEditor::selectGamepad); @@ -121,6 +122,7 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& connect(key, &KeyEditor::valueChanged, this, &GBAKeyEditor::setNext); connect(key, &KeyEditor::axisChanged, this, &GBAKeyEditor::setNext); connect(key, &KeyEditor::hatChanged, this, &GBAKeyEditor::setNext); + key->setInputController(m_controller); key->installEventFilter(this); } @@ -264,7 +266,7 @@ void GBAKeyEditor::refresh() { void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, int key) { #ifdef BUILD_SDL - if (m_type == SDL_BINDING_BUTTON) { + if (m_type == SDL_BINDING_BUTTON || m_type == SDL_BINDING_CONTROLLER) { int value = mInputQueryBinding(map, m_type, key); keyEditor->setValueButton(value); return; @@ -328,7 +330,7 @@ void GBAKeyEditor::lookupHats(const mInputMap* map) { void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, int key) { InputMapper mapper = m_controller->mapper(m_type); #ifdef BUILD_SDL - if (m_type == SDL_BINDING_BUTTON && keyEditor->axis() >= 0) { + if ((m_type == SDL_BINDING_BUTTON || m_type == SDL_BINDING_CONTROLLER) && keyEditor->axis() >= 0) { mapper.bindAxis(keyEditor->axis(), keyEditor->direction(), key); } if (m_type == SDL_BINDING_BUTTON && keyEditor->hat() >= 0) { diff --git a/src/platform/qt/GBAOverride.h b/src/platform/qt/GBAOverride.h index 477af001423..b93cb1953f3 100644 --- a/src/platform/qt/GBAOverride.h +++ b/src/platform/qt/GBAOverride.h @@ -7,7 +7,7 @@ #include "Override.h" -#include +#include namespace QGBA { diff --git a/src/platform/qt/GBOverride.h b/src/platform/qt/GBOverride.h index e967211def7..fc1a7d2c927 100644 --- a/src/platform/qt/GBOverride.h +++ b/src/platform/qt/GBOverride.h @@ -7,7 +7,7 @@ #include "Override.h" -#include +#include namespace QGBA { diff --git a/src/platform/qt/GDBController.h b/src/platform/qt/GDBController.h index 6bf0a713277..50c0fb927de 100644 --- a/src/platform/qt/GDBController.h +++ b/src/platform/qt/GDBController.h @@ -7,7 +7,7 @@ #include "DebuggerController.h" -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB #include diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 337e7df6b2b..3deca07f392 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -16,8 +16,13 @@ #include #include #ifdef BUILD_QT_MULTIMEDIA +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #include #include +#else +#include +#include +#endif #endif #include @@ -50,6 +55,10 @@ InputController::InputController(QWidget* topLevel, QObject* parent) m_gamepadTimer.start(); #ifdef BUILD_QT_MULTIMEDIA +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + m_captureSession.setVideoSink(&m_videoSink); + connect(&m_videoSink, &QVideoSink::videoFrameChanged, &m_videoDumper, &VideoDumper::present); +#endif connect(&m_videoDumper, &VideoDumper::imageAvailable, this, &InputController::setCamImage); #endif @@ -90,18 +99,30 @@ InputController::InputController(QWidget* topLevel, QObject* parent) #ifdef BUILD_QT_MULTIMEDIA image->p->m_cameraActive = true; QByteArray camera = image->p->m_config->getQtOption("camera").toByteArray(); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) if (!camera.isNull()) { image->p->m_cameraDevice = camera; } +#else + if (!camera.isNull()) { + for (const auto& cam : QMediaDevices::videoInputs()) { + if (cam.id() == camera) { + image->p->m_cameraDevice = cam; + } + } + } +#endif QMetaObject::invokeMethod(image->p, "setupCam"); #endif }; m_image.stopRequestImage = [](mImageSource* context) { - InputControllerImage* image = static_cast(context); #ifdef BUILD_QT_MULTIMEDIA + InputControllerImage* image = static_cast(context); image->p->m_cameraActive = false; QMetaObject::invokeMethod(image->p, "teardownCam"); +#else + UNUSED(context); #endif }; @@ -151,15 +172,16 @@ void InputController::setConfiguration(ConfigController* config) { } bool InputController::loadConfiguration(uint32_t type) { - if (!mInputMapLoad(&m_inputMap, type, m_config->input())) { - return false; - } + bool loaded = mInputMapLoad(&m_inputMap, type, m_config->input()); auto driver = m_inputDrivers.value(type); if (!driver) { return false; } + if (!loaded) { + driver->bindDefaults(this); + } driver->loadConfiguration(m_config); - return true; + return loaded; } bool InputController::loadProfile(uint32_t type, const QString& profile) { @@ -595,9 +617,13 @@ void InputController::setCamImage(const QImage& image) { QList> InputController::listCameras() const { QList> out; #ifdef BUILD_QT_MULTIMEDIA - QList cams = QCameraInfo::availableCameras(); - for (const auto& cam : cams) { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + for (const auto& cam : QCameraInfo::availableCameras()) { out.append(qMakePair(cam.deviceName().toLatin1(), cam.description())); +#else + for (const auto& cam : QMediaDevices::videoInputs()) { + out.append(qMakePair(cam.id(), cam.description())); +#endif } #endif return out; @@ -639,6 +665,7 @@ void InputController::setupCam() { return; } +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) if (!m_camera) { m_camera = std::make_unique(m_cameraDevice); connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection); @@ -650,17 +677,31 @@ void InputController::setupCam() { m_camera->setCaptureMode(QCamera::CaptureVideo); m_camera->setViewfinder(&m_videoDumper); m_camera->load(); +#else + if (!m_camera) { + m_camera = std::make_unique(m_cameraDevice); + m_captureSession.setCamera(m_camera.get()); + } + prepareCamFormat(); +#endif #endif } #ifdef BUILD_QT_MULTIMEDIA +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) void InputController::prepareCamSettings(QCamera::Status status) { if (status != QCamera::LoadedStatus || m_camera->state() == QCamera::ActiveState) { return; } + prepareCamFormat(); +} +#endif + +void InputController::prepareCamFormat() { #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) - QCameraViewfinderSettings settings; QSize size(1280, 720); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QCameraViewfinderSettings settings; auto cameraRes = m_camera->supportedViewfinderResolutions(settings); for (auto& cameraSize : cameraRes) { if (cameraSize.width() < m_image.w || cameraSize.height() < m_image.h) { @@ -689,6 +730,25 @@ void InputController::prepareCamSettings(QCamera::Status status) { } } m_camera->setViewfinderSettings(settings); +#else + bool goodFormatFound = false; + auto goodFormats = m_videoDumper.supportedPixelFormats(); + QCameraFormat bestFormat; + for (const auto& format : m_cameraDevice.videoFormats()) { + if (!goodFormats.contains(format.pixelFormat())) { + continue; + } + if (format.resolution().width() <= size.width() && format.resolution().height() <= size.height()) { + size = format.resolution(); + bestFormat = format; + goodFormatFound = true; + } + } + if (!goodFormatFound) { + LOG(QT, WARN) << "Could not find a valid camera format!"; + } + m_camera->setCameraFormat(bestFormat); +#endif #endif m_camera->start(); } @@ -697,7 +757,11 @@ void InputController::prepareCamSettings(QCamera::Status status) { void InputController::teardownCam() { #ifdef BUILD_QT_MULTIMEDIA if (m_camera) { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) m_camera->unload(); +#else + m_captureSession.setCamera(nullptr); +#endif m_camera.reset(); } #endif @@ -705,6 +769,7 @@ void InputController::teardownCam() { void InputController::setCamera(const QByteArray& name) { #ifdef BUILD_QT_MULTIMEDIA +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) if (m_cameraDevice == name) { return; } @@ -712,8 +777,20 @@ void InputController::setCamera(const QByteArray& name) { if (m_camera && m_camera->state() == QCamera::ActiveState) { teardownCam(); } +#else + if (m_cameraDevice.id() == name) { + return; + } + for (const auto& cam : QMediaDevices::videoInputs()) { + if (cam.id() == name) { + m_cameraDevice = cam; + } + } +#endif if (m_cameraActive) { setupCam(); } +#else + UNUSED(name); #endif } diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 06871ff1ee5..07e752fd7c5 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -25,10 +25,13 @@ #include #ifdef BUILD_QT_MULTIMEDIA -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #include "VideoDumper.h" -#endif + #include +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#include +#include +#endif #endif struct mRotationSource; @@ -126,7 +129,7 @@ public slots: void setCamera(const QByteArray& id); private slots: -#ifdef BUILD_QT_MULTIMEDIA +#if defined(BUILD_QT_MULTIMEDIA) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) void prepareCamSettings(QCamera::Status); #endif void setupCam(); @@ -150,6 +153,10 @@ private slots: QSet> activeGamepadAxes(uint32_t type); QSet> activeGamepadHats(uint32_t type); +#if defined(BUILD_QT_MULTIMEDIA) + void prepareCamFormat(); +#endif + struct InputControllerLux : GBALuminanceSource { InputController* p; uint8_t value; @@ -168,10 +175,14 @@ private slots: #ifdef BUILD_QT_MULTIMEDIA bool m_cameraActive = false; - QByteArray m_cameraDevice; std::unique_ptr m_camera; -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) VideoDumper m_videoDumper; +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QByteArray m_cameraDevice; +#else + QCameraDevice m_cameraDevice; + QMediaCaptureSession m_captureSession; + QVideoSink m_videoSink; #endif #endif diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index 7d3ba1990db..50702da731c 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "KeyEditor.h" +#include "InputController.h" +#include "input/Gamepad.h" #include "input/GamepadAxisEvent.h" #include "input/GamepadButtonEvent.h" #include "ShortcutController.h" @@ -18,6 +20,7 @@ using namespace QGBA; KeyEditor::KeyEditor(QWidget* parent) : QLineEdit(parent) + , m_controller(nullptr) , m_direction(GamepadAxisEvent::NEUTRAL) , m_hatDirection(GamepadHatEvent::CENTER) { @@ -26,6 +29,13 @@ KeyEditor::KeyEditor(QWidget* parent) m_lastKey.setSingleShot(true); } +void KeyEditor::setInputController(InputController* controller) { + m_controller = controller; + if (m_button) { + updateButtonText(); + } +} + void KeyEditor::setValue(int key) { m_key = key; if (m_button) { @@ -204,10 +214,28 @@ void KeyEditor::updateButtonText() { } } if (m_key >= 0) { - text.append(QString::number(m_key)); + std::shared_ptr gamepad; + if (m_controller && m_controller->gamepadDriver()) { + gamepad = m_controller->gamepadDriver()->activeGamepad(); + } + if (!gamepad) { + text.append(QString::number(m_key)); + } else { + text.append(gamepad->buttonHumanName(m_key)); + } } if (m_direction != GamepadAxisEvent::NEUTRAL) { - text.append((m_direction == GamepadAxisEvent::NEGATIVE ? "-" : "+") + QString::number(m_axis)); + QString name; + std::shared_ptr gamepad; + if (m_controller && m_controller->gamepadDriver()) { + gamepad = m_controller->gamepadDriver()->activeGamepad(); + } + if (!gamepad) { + name = QString::number(m_axis); + } else { + name = gamepad->axisHumanName(m_axis); + } + text.append((m_direction == GamepadAxisEvent::NEGATIVE ? "-" : "+") + name); } if (text.isEmpty()) { setText(tr("---")); diff --git a/src/platform/qt/KeyEditor.h b/src/platform/qt/KeyEditor.h index c1bb56fae3b..93af927ac98 100644 --- a/src/platform/qt/KeyEditor.h +++ b/src/platform/qt/KeyEditor.h @@ -13,12 +13,16 @@ namespace QGBA { +class InputController; + class KeyEditor : public QLineEdit { Q_OBJECT public: KeyEditor(QWidget* parent = nullptr); + void setInputController(InputController* controller); + int value() const { return m_key; } GamepadAxisEvent::Direction direction() const { return m_direction; } @@ -57,6 +61,7 @@ public slots: int m_axis = -1; int m_hat = -1; bool m_button = false; + InputController* m_controller; GamepadAxisEvent::Direction m_direction; GamepadHatEvent::Direction m_hatDirection; QTimer m_lastKey; diff --git a/src/platform/qt/OverrideView.cpp b/src/platform/qt/OverrideView.cpp index 07e114c1c11..bec404a0091 100644 --- a/src/platform/qt/OverrideView.cpp +++ b/src/platform/qt/OverrideView.cpp @@ -153,14 +153,13 @@ void OverrideView::updateOverrides() { if (m_ui.tabWidget->currentWidget() == m_ui.tabGBA) { auto gba = std::make_unique(); memset(gba->override.id, 0, 4); - gba->override.savetype = static_cast(m_ui.savetype->currentIndex() - 1); + gba->override.savetype = static_cast(m_ui.savetype->currentIndex() - 1); gba->override.hardware = HW_NO_OVERRIDE; - gba->override.idleLoop = IDLE_LOOP_NONE; - gba->override.mirroring = false; + gba->override.idleLoop = GBA_IDLE_LOOP_NONE; gba->override.vbaBugCompat = false; gba->vbaBugCompatSet = false; - if (gba->override.savetype != SAVEDATA_AUTODETECT) { + if (gba->override.savetype != GBA_SAVEDATA_AUTODETECT) { hasOverride = true; } if (!m_ui.hwAutodetect->isChecked()) { @@ -246,7 +245,7 @@ void OverrideView::gameStarted() { m_ui.hwGBPlayer->setChecked(gba->memory.hw.devices & HW_GB_PLAYER_DETECTION); m_ui.vbaBugCompat->setChecked(gba->vbaBugCompat); - if (gba->idleLoop != IDLE_LOOP_NONE) { + if (gba->idleLoop != GBA_IDLE_LOOP_NONE) { m_ui.idleLoop->setText(QString::number(gba->idleLoop, 16)); } else { m_ui.idleLoop->clear(); diff --git a/src/platform/qt/SaveConverter.cpp b/src/platform/qt/SaveConverter.cpp index 44e061c8f48..d16eeeea4f5 100644 --- a/src/platform/qt/SaveConverter.cpp +++ b/src/platform/qt/SaveConverter.cpp @@ -170,8 +170,8 @@ void SaveConverter::detectFromSavestate(VFile* vf) { switch (platform) { #ifdef M_CORE_GBA case mPLATFORM_GBA: - save.gba.type = static_cast(state.at(offsetof(GBASerializedState, savedata.type))); - if (save.gba.type == SAVEDATA_EEPROM || save.gba.type == SAVEDATA_EEPROM512) { + save.gba.type = static_cast(state.at(offsetof(GBASerializedState, savedata.type))); + if (save.gba.type == GBA_SAVEDATA_EEPROM || save.gba.type == GBA_SAVEDATA_EEPROM512) { save.endianness = Endian::LITTLE; } break; @@ -198,25 +198,25 @@ void SaveConverter::detectFromSize(std::shared_ptr vf) { switch (vf->size()) { case GBA_SIZE_SRAM: case GBA_SIZE_SRAM + 16: - m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, vf}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_SRAM, vf}); break; case GBA_SIZE_FLASH512: case GBA_SIZE_FLASH512 + 16: - m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH512, vf}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_FLASH512, vf}); break; case GBA_SIZE_FLASH1M: case GBA_SIZE_FLASH1M + 16: - m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, vf}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_FLASH1M, vf}); break; case GBA_SIZE_EEPROM: case GBA_SIZE_EEPROM + 16: - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, vf, Endian::LITTLE}); - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, vf, Endian::BIG}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_EEPROM, vf, Endian::LITTLE}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_EEPROM, vf, Endian::BIG}); break; case GBA_SIZE_EEPROM512: case GBA_SIZE_EEPROM512 + 16: - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, vf, Endian::LITTLE}); - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, vf, Endian::BIG}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_EEPROM512, vf, Endian::LITTLE}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_EEPROM512, vf, Endian::BIG}); break; } #endif @@ -272,12 +272,12 @@ void SaveConverter::detectFromHeaders(std::shared_ptr vf) { QByteArray bytes = QByteArray::fromRawData(static_cast(data), size); bytes.data(); // Trigger a deep copy before we delete the backing if (size == GBA_SIZE_FLASH1M) { - m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::SHARKPORT}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::SHARKPORT}); } else { - m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, std::make_shared(bytes.left(GBA_SIZE_SRAM)), Endian::NONE, Container::SHARKPORT}); - m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH512, std::make_shared(bytes.left(GBA_SIZE_FLASH512)), Endian::NONE, Container::SHARKPORT}); - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, std::make_shared(bytes.left(GBA_SIZE_EEPROM)), Endian::BIG, Container::SHARKPORT}); - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, std::make_shared(bytes.left(GBA_SIZE_EEPROM512)), Endian::BIG, Container::SHARKPORT}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_SRAM, std::make_shared(bytes.left(GBA_SIZE_SRAM)), Endian::NONE, Container::SHARKPORT}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_FLASH512, std::make_shared(bytes.left(GBA_SIZE_FLASH512)), Endian::NONE, Container::SHARKPORT}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_EEPROM, std::make_shared(bytes.left(GBA_SIZE_EEPROM)), Endian::BIG, Container::SHARKPORT}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_EEPROM512, std::make_shared(bytes.left(GBA_SIZE_EEPROM512)), Endian::BIG, Container::SHARKPORT}); } free(data); } @@ -289,20 +289,20 @@ void SaveConverter::detectFromHeaders(std::shared_ptr vf) { bytes.data(); // Trigger a deep copy before we delete the backing switch (size) { case GBA_SIZE_FLASH1M: - m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::GSV}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::GSV}); break; case GBA_SIZE_FLASH512: - m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH512, std::make_shared(bytes), Endian::NONE, Container::GSV}); - m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::GSV}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_FLASH512, std::make_shared(bytes), Endian::NONE, Container::GSV}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::GSV}); break; case GBA_SIZE_SRAM: - m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, std::make_shared(bytes.left(GBA_SIZE_SRAM)), Endian::NONE, Container::GSV}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_SRAM, std::make_shared(bytes.left(GBA_SIZE_SRAM)), Endian::NONE, Container::GSV}); break; case GBA_SIZE_EEPROM: - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, std::make_shared(bytes.left(GBA_SIZE_EEPROM)), Endian::BIG, Container::GSV}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_EEPROM, std::make_shared(bytes.left(GBA_SIZE_EEPROM)), Endian::BIG, Container::GSV}); break; case GBA_SIZE_EEPROM512: - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, std::make_shared(bytes.left(GBA_SIZE_EEPROM512)), Endian::BIG, Container::GSV}); + m_validSaves.append(AnnotatedSave{GBA_SAVEDATA_EEPROM512, std::make_shared(bytes.left(GBA_SIZE_EEPROM512)), Endian::BIG, Container::GSV}); break; } free(data); @@ -401,7 +401,7 @@ SaveConverter::AnnotatedSave::AnnotatedSave(mPlatform platform, std::shared_ptr< } #ifdef M_CORE_GBA -SaveConverter::AnnotatedSave::AnnotatedSave(SavedataType type, std::shared_ptr vf, Endian endianness, Container container) +SaveConverter::AnnotatedSave::AnnotatedSave(GBASavedataType type, std::shared_ptr vf, Endian endianness, Container container) : container(container) , platform(mPLATFORM_GBA) , size(vf->size()) @@ -468,15 +468,15 @@ SaveConverter::AnnotatedSave::operator QString() const { #ifdef M_CORE_GBA case mPLATFORM_GBA: switch (gba.type) { - case SAVEDATA_SRAM: + case GBA_SAVEDATA_SRAM: typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "SRAM"); break; - case SAVEDATA_FLASH512: - case SAVEDATA_FLASH1M: + case GBA_SAVEDATA_FLASH512: + case GBA_SAVEDATA_FLASH1M: typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "%1 flash"); break; - case SAVEDATA_EEPROM: - case SAVEDATA_EEPROM512: + case GBA_SAVEDATA_EEPROM: + case GBA_SAVEDATA_EEPROM512: typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "%1 EEPROM"); break; default: @@ -664,8 +664,8 @@ QByteArray SaveConverter::AnnotatedSave::convertTo(const SaveConverter::Annotate #ifdef M_CORE_GBA case mPLATFORM_GBA: switch (gba.type) { - case SAVEDATA_EEPROM: - case SAVEDATA_EEPROM512: + case GBA_SAVEDATA_EEPROM: + case GBA_SAVEDATA_EEPROM512: if (endianness == target.endianness) { break; } diff --git a/src/platform/qt/SaveConverter.h b/src/platform/qt/SaveConverter.h index 2fa13307176..764e13d9c17 100644 --- a/src/platform/qt/SaveConverter.h +++ b/src/platform/qt/SaveConverter.h @@ -48,7 +48,7 @@ private slots: private: #ifdef M_CORE_GBA struct GBASave { - SavedataType type; + GBASavedataType type; }; #endif #ifdef M_CORE_GB @@ -66,7 +66,7 @@ private slots: AnnotatedSave(); AnnotatedSave(mPlatform, std::shared_ptr, Endian = Endian::NONE, Container = Container::NONE); #ifdef M_CORE_GBA - AnnotatedSave(SavedataType, std::shared_ptr, Endian = Endian::NONE, Container = Container::NONE); + AnnotatedSave(GBASavedataType, std::shared_ptr, Endian = Endian::NONE, Container = Container::NONE); #endif #ifdef M_CORE_GB AnnotatedSave(GBMemoryBankControllerType, std::shared_ptr, Endian = Endian::NONE, Container = Container::NONE); diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 7f0fef53f99..66c125466af 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -18,7 +18,7 @@ #ifdef M_CORE_GB #include "GameBoy.h" -#include +#include #endif #include @@ -26,6 +26,7 @@ #include #ifdef BUILD_SDL +#define SDL_MAIN_HANDLED #include "platform/sdl/sdl-events.h" #endif @@ -333,8 +334,13 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC GBAKeyEditor* buttonEditor = nullptr; #ifdef BUILD_SDL +#if SDL_VERSION_ATLEAST(2, 0, 0) + QString profile = inputController->profileForType(SDL_BINDING_CONTROLLER); + buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_CONTROLLER, profile); +#else QString profile = inputController->profileForType(SDL_BINDING_BUTTON); buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile); +#endif addPage(tr("Controllers"), buttonEditor, Page::CONTROLLERS); connect(m_ui.buttonBox, &QDialogButtonBox::accepted, buttonEditor, &GBAKeyEditor::save); #endif @@ -601,12 +607,6 @@ void SettingsView::updateConfig() { emit languageChanged(); } - bool oldAudioHle = m_controller->getOption("gba.audioHle", "0") != "0"; - if (oldAudioHle != m_ui.audioHle->isChecked()) { - saveSetting("gba.audioHle", m_ui.audioHle); - emit audioHleChanged(); - } - if (m_ui.multiplayerAudioAll->isChecked()) { m_controller->setQtOption("multiplayerAudio", "all"); } else if (m_ui.multiplayerAudio1->isChecked()) { @@ -734,7 +734,6 @@ void SettingsView::reloadConfig() { loadSetting("logToStdout", m_ui.logToStdout); loadSetting("logFile", m_ui.logFile); loadSetting("useDiscordPresence", m_ui.useDiscordPresence); - loadSetting("gba.audioHle", m_ui.audioHle); loadSetting("dynamicTitle", m_ui.dynamicTitle, true); loadSetting("gba.forceGbp", m_ui.forceGbp); loadSetting("vbaBugCompat", m_ui.vbaBugCompat, true); diff --git a/src/platform/qt/SettingsView.h b/src/platform/qt/SettingsView.h index 3543419fc5a..010b4ef0d8d 100644 --- a/src/platform/qt/SettingsView.h +++ b/src/platform/qt/SettingsView.h @@ -63,7 +63,6 @@ Q_OBJECT void pathsChanged(); void languageChanged(); void libraryCleared(); - void audioHleChanged(); public slots: void selectPage(Page); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index 42e4af5a00a..8539e6bdb75 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -1307,14 +1307,7 @@ - - - - XQ GBA audio (experimental) - - - - + OpenGL enhancements diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index d1165a9ba79..db7f27e6a92 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -143,7 +143,7 @@ bool ShortcutController::eventFilter(QObject* obj, QEvent* event) { QKeyEvent* keyEvent = static_cast(event); #ifdef ENABLE_SCRIPTING if (m_scripting) { - m_scripting->event(obj, event); + m_scripting->scriptingEvent(obj, event); } #endif if (keyEvent->isAutoRepeat()) { @@ -165,7 +165,7 @@ bool ShortcutController::eventFilter(QObject* obj, QEvent* event) { if (event->type() == GamepadButtonEvent::Down()) { #ifdef ENABLE_SCRIPTING if (m_scripting) { - m_scripting->event(obj, event); + m_scripting->scriptingEvent(obj, event); } #endif auto item = m_buttons.find(static_cast(event)->value()); @@ -186,7 +186,7 @@ bool ShortcutController::eventFilter(QObject* obj, QEvent* event) { if (event->type() == GamepadButtonEvent::Up()) { #ifdef ENABLE_SCRIPTING if (m_scripting) { - m_scripting->event(obj, event); + m_scripting->scriptingEvent(obj, event); } #endif auto item = m_buttons.find(static_cast(event)->value()); @@ -224,7 +224,7 @@ bool ShortcutController::eventFilter(QObject* obj, QEvent* event) { #ifdef ENABLE_SCRIPTING if (event->type() == GamepadHatEvent::Type()) { if (m_scripting) { - m_scripting->event(obj, event); + m_scripting->scriptingEvent(obj, event); } } #endif diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index 5efbc4d6295..f457263c578 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -54,6 +54,7 @@ void ShortcutView::setInputController(InputController* controller) { } m_input = controller; m_input->stealFocus(this); + m_ui.keyEdit->setInputController(controller); } void ShortcutView::load(const QModelIndex& index) { diff --git a/src/platform/qt/VideoDumper.cpp b/src/platform/qt/VideoDumper.cpp index 7fdb433bb18..913406a70ee 100644 --- a/src/platform/qt/VideoDumper.cpp +++ b/src/platform/qt/VideoDumper.cpp @@ -6,58 +6,66 @@ #include "VideoDumper.h" #include -#include using namespace QGBA; VideoDumper::VideoDumper(QObject* parent) +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) : QAbstractVideoSurface(parent) +#else + : QObject(parent) +#endif { } bool VideoDumper::present(const QVideoFrame& frame) { QVideoFrame mappedFrame(frame); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) if (!mappedFrame.map(QAbstractVideoBuffer::ReadOnly)) { return false; } - QVideoFrame::PixelFormat vFormat = mappedFrame.pixelFormat(); - QImage::Format format = QVideoFrame::imageFormatFromPixelFormat(vFormat); +#else + if (!mappedFrame.map(QVideoFrame::ReadOnly)) { + return false; + } +#endif + PixelFormat vFormat = mappedFrame.pixelFormat(); + QImage::Format format = imageFormatFromPixelFormat(vFormat); bool swap = false; #ifdef USE_FFMPEG bool useScaler = false; #endif if (format == QImage::Format_Invalid) { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) if (vFormat < QVideoFrame::Format_BGRA5658_Premultiplied) { vFormat = static_cast(vFormat - QVideoFrame::Format_BGRA32 + QVideoFrame::Format_ARGB32); - format = QVideoFrame::imageFormatFromPixelFormat(vFormat); - if (format == QImage::Format_ARGB32) { - format = QImage::Format_RGBA8888; - } else if (format == QImage::Format_ARGB32_Premultiplied) { - format = QImage::Format_RGBA8888_Premultiplied; - } +#else + if (vFormat < PixelFormat::Format_AYUV) { +#endif + format = imageFormatFromPixelFormat(vFormat); swap = true; } else { #ifdef USE_FFMPEG enum AVPixelFormat pixelFormat; switch (vFormat) { - case QVideoFrame::Format_YUV420P: + case VideoDumper::PixelFormat::Format_YUV420P: pixelFormat = AV_PIX_FMT_YUV420P; break; #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - case QVideoFrame::Format_YUV422P: + case VideoDumper::PixelFormat::Format_YUV422P: pixelFormat = AV_PIX_FMT_YUV422P; break; #endif - case QVideoFrame::Format_YUYV: + case VideoDumper::PixelFormat::Format_YUYV: pixelFormat = AV_PIX_FMT_YUYV422; break; - case QVideoFrame::Format_UYVY: + case VideoDumper::PixelFormat::Format_UYVY: pixelFormat = AV_PIX_FMT_UYVY422; break; - case QVideoFrame::Format_NV12: + case VideoDumper::PixelFormat::Format_NV12: pixelFormat = AV_PIX_FMT_NV12; break; - case QVideoFrame::Format_NV21: + case VideoDumper::PixelFormat::Format_NV21: pixelFormat = AV_PIX_FMT_NV12; break; default: @@ -80,11 +88,15 @@ bool VideoDumper::present(const QVideoFrame& frame) { #endif } } - uchar* bits = mappedFrame.bits(); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + Direction direction = surfaceFormat().scanLineDirection(); +#else + Direction direction = mappedFrame.surfaceFormat().scanLineDirection(); +#endif #ifdef USE_FFMPEG QImage image; if (!useScaler) { - image = QImage(bits, mappedFrame.width(), mappedFrame.height(), mappedFrame.bytesPerLine(), format); + image = QImage(mappedFrame.bits(0), mappedFrame.width(), mappedFrame.height(), mappedFrame.bytesPerLine(0), format); } if (useScaler) { image = QImage(mappedFrame.width(), mappedFrame.height(), format); @@ -99,14 +111,14 @@ bool VideoDumper::present(const QVideoFrame& frame) { sws_scale(m_scaler, planes, strides, 0, mappedFrame.height(), &outBits, &outStride); } else #else - QImage image(bits, mappedFrame.width(), mappedFrame.height(), mappedFrame.bytesPerLine(), format); + QImage image(mappedFrame.bits(0), mappedFrame.width(), mappedFrame.height(), mappedFrame.bytesPerLine(0), format); #endif if (swap) { image = image.rgbSwapped(); - } else if (surfaceFormat().scanLineDirection() != QVideoSurfaceFormat::BottomToTop) { + } else if (direction != Direction::BottomToTop) { image = image.copy(); // Create a deep copy of the bits } - if (surfaceFormat().scanLineDirection() == QVideoSurfaceFormat::BottomToTop) { + if (direction == Direction::BottomToTop) { image = image.mirrored(); } mappedFrame.unmap(); @@ -114,29 +126,63 @@ bool VideoDumper::present(const QVideoFrame& frame) { return true; } +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QList VideoDumper::supportedPixelFormats(QAbstractVideoBuffer::HandleType) const { - QList list; - list.append(QVideoFrame::Format_RGB32); - list.append(QVideoFrame::Format_ARGB32); - list.append(QVideoFrame::Format_RGB24); - list.append(QVideoFrame::Format_ARGB32_Premultiplied); - list.append(QVideoFrame::Format_RGB565); - list.append(QVideoFrame::Format_RGB555); - list.append(QVideoFrame::Format_BGR32); - list.append(QVideoFrame::Format_BGRA32); - list.append(QVideoFrame::Format_BGR24); - list.append(QVideoFrame::Format_BGRA32_Premultiplied); - list.append(QVideoFrame::Format_BGR565); - list.append(QVideoFrame::Format_BGR555); + return VideoDumper::supportedPixelFormats(); +} +#endif + +QList VideoDumper::supportedPixelFormats() { + QList list{{ +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + VideoDumper::PixelFormat::Format_RGB32, + VideoDumper::PixelFormat::Format_ARGB32, + VideoDumper::PixelFormat::Format_RGB24, + VideoDumper::PixelFormat::Format_ARGB32_Premultiplied, + VideoDumper::PixelFormat::Format_RGB565, + VideoDumper::PixelFormat::Format_RGB555, + VideoDumper::PixelFormat::Format_BGR32, + VideoDumper::PixelFormat::Format_BGRA32, + VideoDumper::PixelFormat::Format_BGR24, + VideoDumper::PixelFormat::Format_BGRA32_Premultiplied, + VideoDumper::PixelFormat::Format_BGR565, + VideoDumper::PixelFormat::Format_BGR555, +#else + VideoDumper::PixelFormat::Format_XRGB8888, + VideoDumper::PixelFormat::Format_ARGB8888, + VideoDumper::PixelFormat::Format_ARGB8888_Premultiplied, + VideoDumper::PixelFormat::Format_RGBX8888, + VideoDumper::PixelFormat::Format_RGBA8888, + VideoDumper::PixelFormat::Format_XBGR8888, + VideoDumper::PixelFormat::Format_ABGR8888, + VideoDumper::PixelFormat::Format_BGRX8888, + VideoDumper::PixelFormat::Format_BGRA8888, + VideoDumper::PixelFormat::Format_BGRA8888_Premultiplied, +#endif #ifdef USE_FFMPEG - list.append(QVideoFrame::Format_YUYV); - list.append(QVideoFrame::Format_UYVY); + VideoDumper::PixelFormat::Format_YUYV, + VideoDumper::PixelFormat::Format_UYVY, #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - list.append(QVideoFrame::Format_YUV422P); + VideoDumper::PixelFormat::Format_YUV422P, #endif - list.append(QVideoFrame::Format_YUV420P); - list.append(QVideoFrame::Format_NV12); - list.append(QVideoFrame::Format_NV21); + VideoDumper::PixelFormat::Format_YUV420P, + VideoDumper::PixelFormat::Format_NV12, + VideoDumper::PixelFormat::Format_NV21, #endif + }}; return list; } + +QImage::Format VideoDumper::imageFormatFromPixelFormat(VideoDumper::PixelFormat vFormat) { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QImage::Format format = QVideoFrame::imageFormatFromPixelFormat(vFormat); +#else + QImage::Format format = QVideoFrameFormat::imageFormatFromPixelFormat(vFormat); +#endif + if (format == QImage::Format_ARGB32) { + format = QImage::Format_RGBA8888; + } else if (format == QImage::Format_ARGB32_Premultiplied) { + format = QImage::Format_RGBA8888_Premultiplied; + } + return format; +} diff --git a/src/platform/qt/VideoDumper.h b/src/platform/qt/VideoDumper.h index c22688c2b08..2dcceac0f6c 100644 --- a/src/platform/qt/VideoDumper.h +++ b/src/platform/qt/VideoDumper.h @@ -5,7 +5,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once +#include +#include +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #include +#include +#else +#include +#endif #ifdef USE_FFMPEG extern "C" { @@ -15,14 +22,38 @@ extern "C" { namespace QGBA { -class VideoDumper : public QAbstractVideoSurface { +class VideoDumper : public +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QAbstractVideoSurface +#else +QObject +#endif +{ Q_OBJECT public: +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + using PixelFormat = QVideoFrame::PixelFormat; + using Direction = QVideoSurfaceFormat::Direction; +#else + using PixelFormat = QVideoFrameFormat::PixelFormat; + using Direction = QVideoFrameFormat::Direction; +#endif + VideoDumper(QObject* parent = nullptr); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QList supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const override; +#endif + static QList supportedPixelFormats(); + static QImage::Format imageFormatFromPixelFormat(PixelFormat); + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) bool present(const QVideoFrame& frame) override; - QList supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const override; +#else +public slots: + bool present(const QVideoFrame& frame); +#endif signals: void imageAvailable(const QImage& image); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 6b77a1ef393..c75a163efbf 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -177,8 +177,13 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi #ifdef BUILD_SDL m_inputController.addInputDriver(std::make_shared(&m_inputController)); +#if SDL_VERSION_ATLEAST(2, 0, 0) + m_inputController.setGamepadDriver(SDL_BINDING_CONTROLLER); + m_inputController.setSensorDriver(SDL_BINDING_CONTROLLER); +#else m_inputController.setGamepadDriver(SDL_BINDING_BUTTON); m_inputController.setSensorDriver(SDL_BINDING_BUTTON); +#endif #endif m_shortcutController->setConfigController(m_config); @@ -206,7 +211,7 @@ void Window::argumentsPassed() { m_pendingState = args->savestate; } -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB if (args->debugGdb) { if (!m_gdbController) { m_gdbController = new GDBController(this); @@ -219,7 +224,7 @@ void Window::argumentsPassed() { } #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (args->debugCli) { consoleOpen(); } @@ -247,6 +252,9 @@ void Window::argumentsPassed() { void Window::resizeFrame(const QSize& size) { QSize newSize(size); + if (!m_config->getOption("lockFrameSize").toInt()) { + m_savedSize = size; + } if (windowHandle()) { QRect geom = windowHandle()->screen()->availableGeometry(); if (newSize.width() > geom.width()) { @@ -535,15 +543,6 @@ void Window::openSettingsWindow(SettingsView::Page page) { connect(settingsWindow, &SettingsView::videoRendererChanged, this, &Window::changeRenderer); connect(settingsWindow, &SettingsView::languageChanged, this, &Window::mustRestart); connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig); - connect(settingsWindow, &SettingsView::audioHleChanged, this, [this]() { - if (!m_controller) { - return; - } - if (m_controller->platform() != mPLATFORM_GBA) { - return; - } - mustReset(); - }); #ifdef USE_SQLITE3 connect(settingsWindow, &SettingsView::libraryCleared, m_libraryView, &LibraryController::clear); #endif @@ -602,7 +601,7 @@ std::function Window::openNamedControllerTView(std::unique_ptr* name, }; } -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB void Window::gdbOpen() { if (!m_gdbController) { m_gdbController = new GDBController(this); @@ -614,7 +613,7 @@ void Window::gdbOpen() { } #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void Window::consoleOpen() { if (!m_console) { m_console = new DebuggerConsoleController(this); @@ -1093,7 +1092,9 @@ void Window::reloadAudioDriver() { m_audioProcessor = std::unique_ptr(AudioProcessor::create()); m_audioProcessor->setInput(m_controller); m_audioProcessor->configure(m_config); - m_audioProcessor->start(); + if (!m_audioProcessor->start()) { + LOG(QT, WARN) << "Failed to start audio processor"; + } } void Window::changeRenderer() { @@ -1515,15 +1516,30 @@ void Window::setupMenu(QMenuBar* menubar) { for (int i = 1; i <= 8; ++i) { auto setSize = m_actions.addAction(tr("%1×").arg(QString::number(i)), QString("frame.%1x").arg(QString::number(i)), [this, i]() { auto setSize = m_frameSizes[i]; - showNormal(); - QSize size(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); + bool lockFrameSize = m_config->getOption("lockFrameSize").toInt(); + if (!lockFrameSize) { + showNormal(); + } +#if defined(M_CORE_GBA) + QSize minimumSize = QSize(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); +#elif defined(M_CORE_GB) + QSize minimumSize = QSize(GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS); +#endif + QSize size; if (m_display) { size = m_display->contentSize(); } + if (size.isNull()) { + size = minimumSize; + } size *= i; m_savedScale = i; m_config->setOption("scaleMultiplier", i); // TODO: Port to other + m_savedSize = size; resizeFrame(size); + if (lockFrameSize) { + m_display->setMaximumSize(size); + } setSize->setActive(true); }, "frame"); setSize->setExclusive(true); @@ -1538,8 +1554,22 @@ void Window::setupMenu(QMenuBar* menubar) { #else fullscreenKeys = QKeySequence("Ctrl+F"); #endif + m_actions.addSeparator("frame"); m_actions.addAction(tr("Toggle fullscreen"), "fullscreen", this, &Window::toggleFullScreen, "frame", fullscreenKeys); + ConfigOption* lockFrameSize = m_config->addOption("lockFrameSize"); + lockFrameSize->addBoolean(tr("&Lock frame size"), &m_actions, "frame"); + lockFrameSize->connect([this](const QVariant& value) { + if (m_display) { + if (value.toBool()) { + m_display->setMaximumSize(m_display->size()); + } else { + m_display->setMaximumSize({}); + } + } + }, this); + m_config->updateOption("lockFrameSize"); + ConfigOption* lockAspectRatio = m_config->addOption("lockAspectRatio"); lockAspectRatio->addBoolean(tr("Lock aspect ratio"), &m_actions, "av"); lockAspectRatio->connect([this](const QVariant& value) { @@ -1670,14 +1700,14 @@ void Window::setupMenu(QMenuBar* menubar) { m_actions.addAction(tr("Make portable"), "makePortable", this, &Window::tryMakePortable, "tools"); m_actions.addSeparator("tools"); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS m_actions.addAction(tr("Open debugger console..."), "debuggerWindow", this, &Window::consoleOpen, "tools"); -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB auto gdbWindow = addGameAction(tr("Start &GDB server..."), "gdbWindow", this, &Window::gdbOpen, "tools"); m_platformActions.insert(mPLATFORM_GBA, gdbWindow); #endif #endif -#if defined(USE_DEBUGGERS) || defined(ENABLE_SCRIPTING) +#if defined(ENABLE_DEBUGGERS) || defined(ENABLE_SCRIPTING) m_actions.addSeparator("tools"); #endif @@ -1707,7 +1737,7 @@ void Window::setupMenu(QMenuBar* menubar) { addGameAction(tr("Search memory..."), "memorySearch", openControllerTView(), "stateViews"); addGameAction(tr("View &I/O registers..."), "ioViewer", openControllerTView(), "stateViews"); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS addGameAction(tr("Log memory &accesses..."), "memoryAccessView", openControllerTView(), "tools"); #endif @@ -2125,13 +2155,13 @@ void Window::setController(CoreController* controller, const QString& fname) { } #endif -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB if (m_gdbController) { m_gdbController->setController(m_controller); } #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (m_console) { m_console->setController(m_controller); } @@ -2201,6 +2231,11 @@ void Window::setController(CoreController* controller, const QString& fname) { void Window::attachDisplay() { m_display->attach(m_controller); connect(m_display.get(), &QGBA::Display::drawingStarted, this, &Window::changeRenderer); + if (m_config->getOption("lockFrameSize").toInt()) { + m_display->setMaximumSize(m_savedSize); + } else { + m_display->setMaximumSize({}); + } m_display->startDrawing(m_controller); #ifdef ENABLE_SCRIPTING diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index b2e8de82b75..f91e3ff308b 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -108,11 +108,11 @@ public slots: void startVideoLog(); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void consoleOpen(); #endif -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB void gdbOpen(); #endif @@ -194,6 +194,7 @@ private slots: std::unique_ptr m_display; QSize m_initialSize; + QSize m_savedSize; int m_savedScale; // TODO: Move these to a new class @@ -206,7 +207,7 @@ private slots: LogController m_log{0}; LogView* m_logView; -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS DebuggerConsoleController* m_console = nullptr; #endif LoadSaveState* m_stateWindow = nullptr; @@ -249,7 +250,7 @@ private slots: std::unique_ptr m_gifView; #endif -#ifdef USE_GDB_STUB +#ifdef ENABLE_GDB_STUB GDBController* m_gdbController = nullptr; #endif diff --git a/src/platform/qt/input/Gamepad.h b/src/platform/qt/input/Gamepad.h index 1781885cd8d..9a1cf19ec7d 100644 --- a/src/platform/qt/input/Gamepad.h +++ b/src/platform/qt/input/Gamepad.h @@ -22,6 +22,9 @@ Q_OBJECT virtual QList currentAxes() = 0; virtual QList currentHats() = 0; + virtual QString buttonHumanName(int) const = 0; + virtual QString axisHumanName(int) const = 0; + virtual int buttonCount() const = 0; virtual int axisCount() const = 0; virtual int hatCount() const = 0; diff --git a/src/platform/qt/input/SDLInputDriver.cpp b/src/platform/qt/input/SDLInputDriver.cpp index e3a77094fc8..72d148057a1 100644 --- a/src/platform/qt/input/SDLInputDriver.cpp +++ b/src/platform/qt/input/SDLInputDriver.cpp @@ -257,13 +257,20 @@ QList SDLGamepad::currentButtons() { return {}; } - SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; QList buttons; +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_GameController* controller = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->controller; + for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) { + buttons.append(SDL_GameControllerGetButton(controller, static_cast(i))); + } +#else + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; int numButtons = SDL_JoystickNumButtons(joystick); for (int i = 0; i < numButtons; ++i) { buttons.append(SDL_JoystickGetButton(joystick, i)); } +#endif return buttons; } @@ -273,13 +280,20 @@ QList SDLGamepad::currentAxes() { return {}; } - SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; QList axes; +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_GameController* controller = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->controller; + for (int i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) { + axes.append(SDL_GameControllerGetAxis(controller, static_cast(i))); + } +#else + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; int numAxes = SDL_JoystickNumAxes(joystick); for (int i = 0; i < numAxes; ++i) { axes.append(SDL_JoystickGetAxis(joystick, i)); } +#endif return axes; } @@ -289,24 +303,52 @@ QList SDLGamepad::currentHats() { return {}; } - SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; QList hats; +#if !SDL_VERSION_ATLEAST(2, 0, 0) + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; int numHats = SDL_JoystickNumHats(joystick); for (int i = 0; i < numHats; ++i) { hats.append(static_cast(SDL_JoystickGetHat(joystick, i))); } +#endif return hats; } +QString SDLGamepad::buttonHumanName(int button) const { +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_GameController* controller = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->controller; + const char* name = mSDLButtonName(controller, static_cast(button)); + if (name) { + return QString::fromUtf8(name); + } +#endif + return QString::number(button); +} + +QString SDLGamepad::axisHumanName(int axis) const { +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_GameController* controller = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->controller; + const char* name = mSDLAxisName(controller, static_cast(axis)); + if (name) { + return QString::fromUtf8(name); + } +#endif + return QString::number(axis); +} + int SDLGamepad::buttonCount() const { if (!verify()) { return -1; } +#if SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_CONTROLLER_BUTTON_MAX; +#else SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; return SDL_JoystickNumButtons(joystick); +#endif } int SDLGamepad::axisCount() const { @@ -314,8 +356,12 @@ int SDLGamepad::axisCount() const { return -1; } +#if SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_CONTROLLER_AXIS_MAX; +#else SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; return SDL_JoystickNumAxes(joystick); +#endif } int SDLGamepad::hatCount() const { @@ -323,8 +369,12 @@ int SDLGamepad::hatCount() const { return -1; } +#if SDL_VERSION_ATLEAST(2, 0, 0) + return 0; +#else SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; return SDL_JoystickNumHats(joystick); +#endif } QString SDLGamepad::name() const { diff --git a/src/platform/qt/input/SDLInputDriver.h b/src/platform/qt/input/SDLInputDriver.h index 61f1e6238e0..217dc40a7e5 100644 --- a/src/platform/qt/input/SDLInputDriver.h +++ b/src/platform/qt/input/SDLInputDriver.h @@ -8,6 +8,8 @@ #include "input/Gamepad.h" #include "input/InputDriver.h" +#define SDL_MAIN_HANDLED + #include "platform/sdl/sdl-events.h" #include @@ -29,7 +31,11 @@ Q_OBJECT SDLInputDriver(InputController*, QObject* parent = nullptr); ~SDLInputDriver(); +#if SDL_VERSION_ATLEAST(2, 0, 0) + uint32_t type() const override { return SDL_BINDING_CONTROLLER; } +#else uint32_t type() const override { return SDL_BINDING_BUTTON; } +#endif QString visibleName() const override { return QLatin1String("SDL"); } QString currentProfile() const override; @@ -87,6 +93,9 @@ Q_OBJECT int axisCount() const override; int hatCount() const override; + QString buttonHumanName(int) const override; + QString axisHumanName(int) const override; + QString name() const override; QString visibleName() const override; diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index c245cff5316..62967bf1a07 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -109,8 +109,9 @@ int main(int argc, char* argv[]) { #endif QTranslator qtTranslator; - qtTranslator.load(locale, "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath)); - application.installTranslator(&qtTranslator); + if (qtTranslator.load(locale, "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { + application.installTranslator(&qtTranslator); + } #ifdef QT_STATIC QTranslator qtStaticTranslator; @@ -119,8 +120,9 @@ int main(int argc, char* argv[]) { #endif QTranslator langTranslator; - langTranslator.load(locale, binaryName, "-", ":/translations/"); - application.installTranslator(&langTranslator); + if (langTranslator.load(locale, binaryName, "-", ":/translations/")) { + application.installTranslator(&langTranslator); + } Window* w = application.newWindow(); w->loadConfig(); diff --git a/src/platform/qt/scripting/ScriptingController.cpp b/src/platform/qt/scripting/ScriptingController.cpp index 065754bf183..b1d68e22ab9 100644 --- a/src/platform/qt/scripting/ScriptingController.cpp +++ b/src/platform/qt/scripting/ScriptingController.cpp @@ -92,6 +92,7 @@ void ScriptingController::setInputController(InputController* input) { } void ScriptingController::setVideoBackend(VideoBackend* backend) { + m_videoBackend = backend; mScriptCanvasUpdateBackend(&m_scriptContext, backend); } @@ -170,11 +171,11 @@ void ScriptingController::flushStorage() { } bool ScriptingController::eventFilter(QObject* obj, QEvent* ev) { - event(obj, ev); + scriptingEvent(obj, ev); return false; } -void ScriptingController::event(QObject* obj, QEvent* event) { +void ScriptingController::scriptingEvent(QObject* obj, QEvent* event) { if (!m_controller) { return; } @@ -188,7 +189,7 @@ void ScriptingController::event(QObject* obj, QEvent* event) { return; case QEvent::KeyPress: case QEvent::KeyRelease: { - struct mScriptKeyEvent ev{mSCRIPT_EV_TYPE_KEY}; + struct mScriptKeyEvent ev{{mSCRIPT_EV_TYPE_KEY}}; auto keyEvent = static_cast(event); ev.state = event->type() == QEvent::KeyRelease ? mSCRIPT_INPUT_STATE_UP : static_cast(event)->isAutoRepeat() ? mSCRIPT_INPUT_STATE_HELD : mSCRIPT_INPUT_STATE_DOWN; @@ -199,7 +200,7 @@ void ScriptingController::event(QObject* obj, QEvent* event) { } case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: { - struct mScriptMouseButtonEvent ev{mSCRIPT_EV_TYPE_MOUSE_BUTTON}; + struct mScriptMouseButtonEvent ev{{mSCRIPT_EV_TYPE_MOUSE_BUTTON}}; auto mouseEvent = static_cast(event); ev.mouse = 0; ev.state = event->type() == QEvent::MouseButtonPress ? mSCRIPT_INPUT_STATE_DOWN : mSCRIPT_INPUT_STATE_UP; @@ -208,7 +209,7 @@ void ScriptingController::event(QObject* obj, QEvent* event) { return; } case QEvent::MouseMove: { - struct mScriptMouseMoveEvent ev{mSCRIPT_EV_TYPE_MOUSE_MOVE}; + struct mScriptMouseMoveEvent ev{{mSCRIPT_EV_TYPE_MOUSE_MOVE}}; auto mouseEvent = static_cast(event); QPoint pos = mouseEvent->pos(); pos = static_cast(obj)->normalizedPoint(m_controller.get(), pos); @@ -219,7 +220,7 @@ void ScriptingController::event(QObject* obj, QEvent* event) { return; } case QEvent::Wheel: { - struct mScriptMouseWheelEvent ev{mSCRIPT_EV_TYPE_MOUSE_WHEEL}; + struct mScriptMouseWheelEvent ev{{mSCRIPT_EV_TYPE_MOUSE_WHEEL}}; auto wheelEvent = static_cast(event); QPoint adelta = wheelEvent->angleDelta(); QPoint pdelta = wheelEvent->pixelDelta(); @@ -240,7 +241,7 @@ void ScriptingController::event(QObject* obj, QEvent* event) { auto type = event->type(); if (type == GamepadButtonEvent::Down() || type == GamepadButtonEvent::Up()) { - struct mScriptGamepadButtonEvent ev{mSCRIPT_EV_TYPE_GAMEPAD_BUTTON}; + struct mScriptGamepadButtonEvent ev{{mSCRIPT_EV_TYPE_GAMEPAD_BUTTON}}; auto gamepadEvent = static_cast(event); ev.pad = 0; ev.state = event->type() == GamepadButtonEvent::Down() ? mSCRIPT_INPUT_STATE_DOWN : mSCRIPT_INPUT_STATE_UP; @@ -248,7 +249,7 @@ void ScriptingController::event(QObject* obj, QEvent* event) { mScriptContextFireEvent(&m_scriptContext, &ev.d); } if (type == GamepadHatEvent::Type()) { - struct mScriptGamepadHatEvent ev{mSCRIPT_EV_TYPE_GAMEPAD_HAT}; + struct mScriptGamepadHatEvent ev{{mSCRIPT_EV_TYPE_GAMEPAD_HAT}}; updateGamepad(); auto gamepadEvent = static_cast(event); ev.pad = 0; @@ -301,7 +302,7 @@ void ScriptingController::updateGamepad() { void ScriptingController::attach() { CoreController::Interrupter interrupter(m_controller); mScriptContextAttachCore(&m_scriptContext, m_controller->thread()->core); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS m_controller->attachDebugger(false); #endif } @@ -339,6 +340,7 @@ void ScriptingController::init() { mScriptContextAttachLogger(&m_scriptContext, &m_logger); m_bufferModel->attachToContext(&m_scriptContext); + mScriptCanvasUpdateBackend(&m_scriptContext, m_videoBackend); HashTableEnumerate(&m_scriptContext.engines, [](const char* key, void* engine, void* context) { ScriptingController* self = static_cast(context); diff --git a/src/platform/qt/scripting/ScriptingController.h b/src/platform/qt/scripting/ScriptingController.h index a44e52b2eae..5c579a7682f 100644 --- a/src/platform/qt/scripting/ScriptingController.h +++ b/src/platform/qt/scripting/ScriptingController.h @@ -43,7 +43,7 @@ Q_OBJECT bool loadFile(const QString& path); bool load(VFileDevice& vf, const QString& name); - void event(QObject* obj, QEvent* ev); + void scriptingEvent(QObject* obj, QEvent* ev); mScriptContext* context() { return &m_scriptContext; } ScriptingTextBufferModel* textBufferModel() const { return m_bufferModel; } @@ -87,6 +87,7 @@ private slots: mScriptEngineContext* m_activeEngine = nullptr; QHash m_engines; ScriptingTextBufferModel* m_bufferModel; + VideoBackend* m_videoBackend = nullptr; mScriptGamepad m_gamepad; diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts index a1d1b2e1588..ce7f38c30a4 100644 --- a/src/platform/qt/ts/mgba-de.ts +++ b/src/platform/qt/ts/mgba-de.ts @@ -70,7 +70,7 @@ Game Boy Advance ist eine eingetragene Marke von Nintendo Co., Ltd. An update to %1 is available. - Ein Update für %1 ist verfügbar. + Ein Update auf %1 ist verfügbar. @@ -132,7 +132,7 @@ Download-Größe: %3 (None) - (keiner) + (Nichts) @@ -145,7 +145,7 @@ Download-Größe: %3 Loading... - Laden … + Lädt … @@ -198,7 +198,7 @@ Download-Größe: %3 Audio device is missing its core - Dem Audio-Gerät fehlt ein Core + Dem Audio-Gerät fehlt sein Core @@ -395,7 +395,7 @@ Download-Größe: %3 Rewinding not currently enabled - Rücklauf ist derzeit nicht aktiviert + Zurückspulen ist derzeit nicht aktiviert @@ -716,7 +716,7 @@ Download-Größe: %3 Image files (*.png *.jpg *.bmp) - + Bilddateien (*.png *.jpg *.bmp) @@ -724,7 +724,7 @@ Download-Größe: %3 Inspect frame - Bild beobachten + Frame untersuchen @@ -734,7 +734,7 @@ Download-Größe: %3 Freeze frame - Bild einfrieren + Frame einfrieren @@ -3752,17 +3752,17 @@ Download-Größe: %3 Trying to detach a multiplayer player that's not attached - + Versuch Multiplayer-Spieler zu trennen der nicht verbunden ist Trying to get player ID for a multiplayer player that's not attached - + Versuch Spieler-ID festzustellen von einem Multiplayer-Spieler der nicht verbunden ist Trying to get save ID for a multiplayer player that's not attached - + Versuch Speicherstand-ID festzustellen von einem Multiplayer-Spieler der nicht verbunden ist @@ -4310,7 +4310,7 @@ Download-Größe: %3 Save file: - + Speicherstand: @@ -4477,7 +4477,7 @@ Download-Größe: %3 + RTC - + + RTC @@ -4759,12 +4759,12 @@ Download-Größe: %3 Select image - Bild auswählen + Bild auswählen Image file (*.png *.jpg *.jpeg) - Bilddatei (*.png *.jpg *.jpeg) + Bilddatei (*.png *.jpg *.jpeg) @@ -5074,7 +5074,7 @@ Download-Größe: %3 Custom border: - + Benutzerdefinierter Rand: @@ -5119,7 +5119,7 @@ Download-Größe: %3 Rewind speed: - + Zurückspulgeschwindigkeit: @@ -5457,7 +5457,7 @@ Download-Größe: %3 Enable rewind - Rücklauf aktivieren + Zurückspulen aktivieren @@ -5467,7 +5467,7 @@ Download-Größe: %3 Rewind history: - Rücklauf-Verlauf: + Zurückspulverlauf: @@ -6317,12 +6317,12 @@ Download-Größe: %3 Increase fast forward speed - + Vorspulgeschwindigkeit erhöhen Decrease fast forward speed - + Vorspulgeschwindigkeit senken @@ -6771,17 +6771,17 @@ Download-Größe: %3 Super (L) - Super (L) + Super (L) Super (R) - Super (R) + Super (R) Menu - Menü + Menü diff --git a/src/platform/qt/ts/mgba-es.ts b/src/platform/qt/ts/mgba-es.ts index 83c4d5d989d..7dfe054c7e1 100644 --- a/src/platform/qt/ts/mgba-es.ts +++ b/src/platform/qt/ts/mgba-es.ts @@ -656,7 +656,7 @@ Tamaño de descarga: %3 Use default image - Utilizar la imagen por defecto + Utilizar imagen por defecto @@ -686,17 +686,17 @@ Tamaño de descarga: %3 Build finished - Construcción finalizada + Compilación finalizada Forwarder finished building - Transmisor terminado de construir + Transmisor terminado de compilar Build failed - Falló la construcción + Falló la compilación @@ -716,7 +716,7 @@ Tamaño de descarga: %3 Image files (*.png *.jpg *.bmp) - + Archivos de imagen (*.png *.jpg *.bmp) @@ -3752,17 +3752,17 @@ Tamaño de descarga: %3 Trying to detach a multiplayer player that's not attached - + Intentando desvincular a un multijugador que no está conectado Trying to get player ID for a multiplayer player that's not attached - + Intentando obtener el ID de un multijugador que no está conectado Trying to get save ID for a multiplayer player that's not attached - + Intentando obtener ID de guardado para un jugador multijugador que no está conectado @@ -3780,7 +3780,7 @@ Tamaño de descarga: %3 Export - Esportar + Exportar @@ -4228,7 +4228,7 @@ Tamaño de descarga: %3 Hurry up! - ¡Apúrate! + ¡Apresúrate! @@ -4310,7 +4310,7 @@ Tamaño de descarga: %3 Save file: - + Guardar: @@ -4477,7 +4477,7 @@ Tamaño de descarga: %3 + RTC - + + RTC @@ -4759,12 +4759,12 @@ Tamaño de descarga: %3 Select image - Seleccionar imagen + Seleccionar imagen Image file (*.png *.jpg *.jpeg) - Archivo de imagen (*.png *.jpg *.jpeg) + Archivo de imagen (*.png *.jpg *.jpeg) @@ -4792,14 +4792,16 @@ Tamaño de descarga: %3 Hace %n hora Hace %n horas + Hace %n horas %n day(s) ago - Hace %n dia - Hace %n dias + Hace %n día + Hace %n días + Hace %n días @@ -5089,7 +5091,7 @@ Tamaño de descarga: %3 Custom border: - + Borde personalizado: @@ -5134,7 +5136,7 @@ Tamaño de descarga: %3 Rewind speed: - + Velocidad de rebobinado: @@ -6106,7 +6108,7 @@ Tamaño de descarga: %3 Load ROM in archive... - Cargar ROM desde contenedor... + Cargar ROM desde archivo comprimido... @@ -6151,7 +6153,7 @@ Tamaño de descarga: %3 Recent - Cargar reciente + Reciente @@ -6388,12 +6390,12 @@ Tamaño de descarga: %3 Increase fast forward speed - + Aumentar la velocidad de avance rápido Decrease fast forward speed - + Disminuir velocidad de avance rápido @@ -6771,17 +6773,17 @@ Tamaño de descarga: %3 Super (L) - Super (L) + Súper (L) Super (R) - Super (R) + Súper (R) Menu - Menú + Menú diff --git a/src/platform/qt/ts/mgba-hu.ts b/src/platform/qt/ts/mgba-hu.ts index 4e5f7e1f08f..6db9b2da969 100644 --- a/src/platform/qt/ts/mgba-hu.ts +++ b/src/platform/qt/ts/mgba-hu.ts @@ -21,7 +21,7 @@ %1 Video Logs (*.mvl) - + %1 Videonaplók (*.mvl) @@ -50,8 +50,9 @@ © 2013 – {year} Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 Game Boy Advance is a registered trademark of Nintendo Co., Ltd. - © 2013 – {year} Jeffrey Pfau, a Mozilla Public License 2.0 alatt licencelve -A Game Boy Advance a Nintendo Co., Ltd. bejegyzett védjegye + © 2013 – {year} Jeffrey Pfau, a Mozilla Public License 2.0 alatt licencelve. + +A Game Boy Advance a Nintendo Co., Ltd. bejegyzett védjegye. @@ -64,47 +65,52 @@ A Game Boy Advance a Nintendo Co., Ltd. bejegyzett védjegye An update is available - + Egy frissítés érhető el An update to %1 is available. - + Egy frissítés elérhető %1 verzióra. + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + +Szeretné most letöleni és telepíteni? Az emulátor újraindítására lesz szükség a letöltés befejezésekor. Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + +Az automatikus frisítés ezen platformon nem elérhető, a frissítéseket manuálisan telepíthetők. Current version: %1 New version: %2 Download size: %3 - + Jelenlegi verzió: %1 +Új verzió: %2 +Letöltendő adat: %3 Downloading update... - + Frissítés letöltése... Downloading failed. Please update manually. - + A letöltés meghiúsult. Próbálja meg manuálisan frissíteni aprogramot. Downloading done. Press OK to restart %1 and install the update. - + A letöltés kész. Nyomja meg az OK gombot %1 újraindításához és a frissítés telepítéséhez. @@ -112,22 +118,22 @@ Download size: %3 Stable - + Stabil Development - + Fejlesztési Unknown - + Nem ismert (None) - + (nincs) @@ -148,7 +154,7 @@ Download size: %3 Tile # - + Cím # @@ -232,7 +238,7 @@ Download size: %3 Insert - + Beszúrás @@ -329,7 +335,7 @@ Download size: %3 Add New Code - + Új kód hozzáadása @@ -339,12 +345,12 @@ Download size: %3 Add Lines - + Sorok hozzáadása Code type - + Kód típusa @@ -365,7 +371,7 @@ Download size: %3 Autodetect (recommended) - + Automatikus felisnerés (javasolt) @@ -376,7 +382,7 @@ Download size: %3 Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. - + Néhány csalást nem sikerült betölteni. Ellenőrizze, hogy helyesen formázottak-e és/vagy próbáljon ki más csalástípust! @@ -390,7 +396,7 @@ Download size: %3 Rewinding not currently enabled - + Visszatekerés jelenleg nem engedélyezett @@ -451,7 +457,7 @@ Download size: %3 Debugger - Hibakereső + Hibakereső @@ -477,7 +483,7 @@ Download size: %3 Connect to Dolphin - Csatlakozás Dolphin emulátorhoz + Csatlakozás Dolphin emulátorhoz @@ -512,12 +518,12 @@ Download size: %3 Couldn't Connect - + Nem sikerült csatlakozni Could not connect to Dolphin. - + Nem sikerült csatlakozni Dolphin emulátorhoz. @@ -719,7 +725,7 @@ Download size: %3 Inspect frame - Képkocka vizsgáló + Képkocka vizsgálata @@ -965,22 +971,22 @@ Download size: %3 Autodetect - Automatikus észlelés + Automatikus észlelés Game Boy (DMG) - + Game Boy (DMG) Game Boy Pocket (MGB) - + Game Boy Pocket (MGB) Super Game Boy (SGB) - + Super Game Boy (SGB) @@ -1138,7 +1144,7 @@ Download size: %3 0x0000 - + 0x0000 diff --git a/src/platform/qt/ts/mgba-it.ts b/src/platform/qt/ts/mgba-it.ts index a0a0deb5edd..12a018db737 100644 --- a/src/platform/qt/ts/mgba-it.ts +++ b/src/platform/qt/ts/mgba-it.ts @@ -530,12 +530,12 @@ Dimensione del download: %3 3DS - + 3DS Vita - + Vita @@ -543,12 +543,12 @@ Dimensione del download: %3 Icon - + Icona Banner - + Striscia @@ -556,7 +556,7 @@ Dimensione del download: %3 Bubble - + Bolla @@ -566,7 +566,7 @@ Dimensione del download: %3 Startup - + Avvio @@ -574,17 +574,17 @@ Dimensione del download: %3 Create forwarder - + Crea Icona di avvio Files - + File ROM file: - + File ROM: @@ -596,127 +596,127 @@ Dimensione del download: %3 Output filename: - + Nome file in uscita: Forwarder base: - + Base dell'Icona di avvio: Latest stable version - + Ultima versione stabile Latest development build - + Ultima development build Specific file - + File specifico Base file: - + File base: System - + Sistema 3DS - + 3DS Vita - + Vita Presentation - + Presentazione Title: - + Titolo: Images: - + Immagini: Use default image - + Usa immagine predefinita Preferred size: - + Dimensione preferita: Select image file - + Seleziona file immagine Select ROM file - + Seleziona file ROM Select output filename - + Selezione nome file in uscita Select base file - + Selezione file base Build finished - + Build completata Forwarder finished building - + Icona di avvio compilazione completata Build failed - + Build fallita Failed to build forwarder - + Impossibile compilare Icona di avvio %1 installable package (*.%2) - + %1 pacchetto installabile (*.%2) Select an image - + Seleziona una immagine Image files (*.png *.jpg *.bmp) - + File immagine (*.png *.jpg *.bmp) @@ -1085,12 +1085,12 @@ Dimensione del download: %3 NT (old 1) - + NT (vec. 1) NT (old 2) - + NT (vec. 2) @@ -1115,12 +1115,12 @@ Dimensione del download: %3 GGB-81 - + GGB-81 Li Cheng - + Li Cheng @@ -3752,17 +3752,17 @@ Dimensione del download: %3 Trying to detach a multiplayer player that's not attached - + Tentativo di scollegare un giocatore in multi che non è collegato Trying to get player ID for a multiplayer player that's not attached - + Cerco di ottenere l'ID giocatore per un giocatore in multi non collegato Trying to get save ID for a multiplayer player that's not attached - + Cerco di ottenere l'ID di salvataggio per un giocatore in multi non collegato @@ -4310,7 +4310,7 @@ Dimensione del download: %3 Save file: - + Salva file: @@ -4477,7 +4477,7 @@ Dimensione del download: %3 + RTC - + + RTC @@ -4779,7 +4779,7 @@ Dimensione del download: %3 Just now - Solo adesso + Proprio adesso @@ -5079,7 +5079,7 @@ Dimensione del download: %3 Custom border: - + Bordo personalizzato: @@ -5109,7 +5109,7 @@ Dimensione del download: %3 Last checked: - Vista l'ultima volta: + Ultimo controllo: @@ -5124,7 +5124,7 @@ Dimensione del download: %3 Rewind speed: - + Velocità riavvolgimento: @@ -5891,7 +5891,7 @@ Dimensione del download: %3 WavPack - + WavPack @@ -6312,12 +6312,12 @@ Dimensione del download: %3 Increase fast forward speed - + Aumenta la velocità di avvolgimento veloce Decrease fast forward speed - + Diminuisci la velocità di avvolgimento veloce @@ -6447,7 +6447,7 @@ Dimensione del download: %3 Create forwarder... - + Crea Icona di avvio... diff --git a/src/platform/qt/ts/mgba-ko.ts b/src/platform/qt/ts/mgba-ko.ts index a0955e5005a..05a8dc60410 100644 --- a/src/platform/qt/ts/mgba-ko.ts +++ b/src/platform/qt/ts/mgba-ko.ts @@ -716,7 +716,7 @@ Download size: %3 Image files (*.png *.jpg *.bmp) - + 이미지 파일 (*.png *.jpg *.bmp) @@ -3752,17 +3752,17 @@ Download size: %3 Trying to detach a multiplayer player that's not attached - + 연결되지 않은 멀티플레이어 플레이어를 분리하려고 함 Trying to get player ID for a multiplayer player that's not attached - + 연결되지 않은 멀티플레이어 플레이어의 플레이어 ID를 가져오려고 함 Trying to get save ID for a multiplayer player that's not attached - + 연결되지 않은 멀티플레이어 플레이어의 저장 ID를 얻으려고 함 @@ -4310,7 +4310,7 @@ Download size: %3 Save file: - + 저장 파일: @@ -4477,7 +4477,7 @@ Download size: %3 + RTC - + + RTC @@ -4759,12 +4759,12 @@ Download size: %3 Select image - 이미지 선택 + 이미지 선택 Image file (*.png *.jpg *.jpeg) - 이미지 파일 (*.png *.jpg *.jpeg) + 이미지 파일 (*.png *.jpg *.jpeg) @@ -5067,7 +5067,7 @@ Download size: %3 Custom border: - + 커스텀 테두리: @@ -5112,7 +5112,7 @@ Download size: %3 Rewind speed: - + 되감기 속도: @@ -6325,12 +6325,12 @@ Download size: %3 Increase fast forward speed - + 빨리 감기 속도 향상 Decrease fast forward speed - + 빨리 감기 속도 줄이기 @@ -6769,17 +6769,17 @@ Download size: %3 Super (L) - 슈퍼 (L) + 슈퍼 (L) Super (R) - 슈퍼 (R) + 슈퍼 (R) Menu - 메뉴 + 메뉴 diff --git a/src/platform/qt/ts/mgba-nb_NO.ts b/src/platform/qt/ts/mgba-nb_NO.ts index 128bee3ac7f..4a97f0c6073 100644 --- a/src/platform/qt/ts/mgba-nb_NO.ts +++ b/src/platform/qt/ts/mgba-nb_NO.ts @@ -16,7 +16,7 @@ All ROMs (%1) - + Alle ROM-filer (%1) @@ -64,7 +64,7 @@ Game Boy Advance er et registrert varemerke tilhørende Nintendo Co., Ltd. An update is available - + En oppdatering er tilgjengelig @@ -96,7 +96,7 @@ Nedlastningsstørrelse: %3 Downloading update... - Laster ned ny versjon … + Laster ned oppdatering … @@ -114,22 +114,22 @@ Nedlastningsstørrelse: %3 Stable - + Stabil Development - + Utvikling Unknown - Ukjent + Ukjent (None) - + (Ingen) @@ -142,7 +142,7 @@ Nedlastningsstørrelse: %3 Loading... - Laster inn … + Laster inn ... @@ -264,7 +264,7 @@ Nedlastningsstørrelse: %3 Inserted - + Satt inn @@ -331,7 +331,7 @@ Nedlastningsstørrelse: %3 Add New Code - + Legg til ny kode @@ -341,7 +341,7 @@ Nedlastningsstørrelse: %3 Add Lines - + Legg til linjer @@ -367,7 +367,7 @@ Nedlastningsstørrelse: %3 Autodetect (recommended) - + Auto-oppdag (anbefales) @@ -463,7 +463,7 @@ Nedlastningsstørrelse: %3 Break - + Stopp @@ -514,12 +514,12 @@ Nedlastningsstørrelse: %3 Couldn't Connect - + Kunne ikke koble til Could not connect to Dolphin. - + Klarte ikke å koble til Dolphin. @@ -527,7 +527,7 @@ Nedlastningsstørrelse: %3 3DS - + 3DS @@ -540,7 +540,7 @@ Nedlastningsstørrelse: %3 Icon - + Ikon @@ -553,17 +553,17 @@ Nedlastningsstørrelse: %3 Bubble - + Boble Background - Bakgrunn + Bakgrunn Startup - + Oppstart @@ -576,12 +576,12 @@ Nedlastningsstørrelse: %3 Files - + Filer ROM file: - + ROM-fil: @@ -593,7 +593,7 @@ Nedlastningsstørrelse: %3 Output filename: - + Utdata-filnavn: @@ -603,7 +603,7 @@ Nedlastningsstørrelse: %3 Latest stable version - + Nyeste stabile versjon @@ -613,42 +613,42 @@ Nedlastningsstørrelse: %3 Specific file - + Spesifikk fil Base file: - + Grunnfil: System - + System 3DS - + 3DS Vita - + Vita Presentation - + Presentasjon Title: - + Tittel: Images: - + Bilder: @@ -658,17 +658,17 @@ Nedlastningsstørrelse: %3 Preferred size: - + Ønsket størrelse: Select image file - + Velg bildefil Select ROM file - + Velg ROM-fil @@ -678,7 +678,7 @@ Nedlastningsstørrelse: %3 Select base file - + Velg grunnfil @@ -708,12 +708,12 @@ Nedlastningsstørrelse: %3 Select an image - + Velg et bilde Image files (*.png *.jpg *.bmp) - + Bildefiler (*.png *.jpg *.bmp) @@ -736,7 +736,7 @@ Nedlastningsstørrelse: %3 Backdrop color - + Bakgrunnsfarge @@ -791,12 +791,12 @@ Nedlastningsstørrelse: %3 Backdrop - + Bakgrunn Frame - + Ramme @@ -850,7 +850,7 @@ Nedlastningsstørrelse: %3 Bind address - + Bindingsadresse @@ -875,7 +875,7 @@ Nedlastningsstørrelse: %3 Break - + Stopp @@ -967,7 +967,7 @@ Nedlastningsstørrelse: %3 Autodetect - + Auto-oppdag @@ -1037,7 +1037,7 @@ Nedlastningsstørrelse: %3 MBC5 + Rumble - + MBC5 + risting @@ -1047,7 +1047,7 @@ Nedlastningsstørrelse: %3 MBC7 (Tilt) - + MBC7 (Tilt) @@ -1112,12 +1112,12 @@ Nedlastningsstørrelse: %3 GGB-81 - + GGB-81 Li Cheng - + Li Cheng @@ -1135,12 +1135,12 @@ Nedlastningsstørrelse: %3 I/O Viewer - + I/O-visning 0x0000 - + 0x0000 @@ -1232,7 +1232,7 @@ Nedlastningsstørrelse: %3 Enable OBJ - + Skru på OBJ @@ -1252,17 +1252,17 @@ Nedlastningsstørrelse: %3 Currently in VBlank - + For øyeblikket i VBlank Currently in HBlank - + For øyeblikket i HBlank Currently in VCounter - + For øyeblikket i VCounter @@ -1319,7 +1319,7 @@ Nedlastningsstørrelse: %3 Enable 256-color - + Skru på 256-fargemodus @@ -1742,7 +1742,7 @@ Nedlastningsstørrelse: %3 Initial volume - + Volum ved begynnelse @@ -1801,28 +1801,28 @@ Nedlastningsstørrelse: %3 0% - 0% + 0 % 100% - 100% + 100% 50% - 50% + 50% 25% - 25% + 25% @@ -1830,7 +1830,7 @@ Nedlastningsstørrelse: %3 75% - 75% + 75% @@ -1848,13 +1848,13 @@ Nedlastningsstørrelse: %3 15 - 15 + 15 7 - 7 + 7 @@ -1954,7 +1954,7 @@ Nedlastningsstørrelse: %3 0 - 0 + 0 @@ -1967,7 +1967,7 @@ Nedlastningsstørrelse: %3 1 - 1 + 1 @@ -2137,22 +2137,22 @@ Nedlastningsstørrelse: %3 Right/A - + Høyre/A Left/B - + Venstre/B Up/Select - + Opp/Select Down/Start - + Ned/Start @@ -2162,7 +2162,7 @@ Nedlastningsstørrelse: %3 Active face buttons - + Skru på hovedknappene @@ -2182,12 +2182,12 @@ Nedlastningsstørrelse: %3 Divider - + Skillelinje 1/16 - 1/16 + 1/16 @@ -2479,7 +2479,7 @@ Nedlastningsstørrelse: %3 Red - Rød + Rød @@ -2618,7 +2618,7 @@ Nedlastningsstørrelse: %3 Immediate - + Umiddelbart @@ -2681,7 +2681,7 @@ Nedlastningsstørrelse: %3 Video Capture - + Videoopptak @@ -2715,7 +2715,7 @@ Nedlastningsstørrelse: %3 1/64 - 1/64 + 1/64 @@ -2724,7 +2724,7 @@ Nedlastningsstørrelse: %3 1/256 - 1/256 + 1/256 @@ -2733,14 +2733,14 @@ Nedlastningsstørrelse: %3 1/1024 - 1/1024 + 1/1024 Cascade - + Cascade @@ -2891,7 +2891,7 @@ Nedlastningsstørrelse: %3 Gamepak - + Spillkassett @@ -2905,7 +2905,7 @@ Nedlastningsstørrelse: %3 4 - 4 + 4 @@ -2913,7 +2913,7 @@ Nedlastningsstørrelse: %3 3 - 3 + 3 @@ -2922,7 +2922,7 @@ Nedlastningsstørrelse: %3 2 - 2 + 2 @@ -2931,7 +2931,7 @@ Nedlastningsstørrelse: %3 8 - 8 + 8 @@ -2966,7 +2966,7 @@ Nedlastningsstørrelse: %3 PHI terminal - + PHI-terminal @@ -2977,17 +2977,17 @@ Nedlastningsstørrelse: %3 4.19MHz - + 4,19MHz 8.38MHz - + 8,38MHz 16.78MHz - + 16,78MHz @@ -3006,7 +3006,7 @@ Nedlastningsstørrelse: %3 --- - + --- @@ -3019,7 +3019,7 @@ Nedlastningsstørrelse: %3 Location - + Sted @@ -3034,7 +3034,7 @@ Nedlastningsstørrelse: %3 CRC32 - + CRC32 @@ -3111,12 +3111,12 @@ Nedlastningsstørrelse: %3 Load State - + Last inn tilstand Save State - + Lagre tilstand @@ -3170,12 +3170,12 @@ Nedlastningsstørrelse: %3 Stub - + Stump Game Error - Spillfeil + Spillfeil @@ -3183,7 +3183,7 @@ Nedlastningsstørrelse: %3 [%1] %2: %3 - + [%1] %2: %3 @@ -3208,7 +3208,7 @@ Nedlastningsstørrelse: %3 WARN - + ADVARSEL @@ -3246,7 +3246,7 @@ Nedlastningsstørrelse: %3 Stub - + Stump @@ -3304,7 +3304,7 @@ Nedlastningsstørrelse: %3 Export - Eksporter + Eksporter @@ -3352,12 +3352,12 @@ Nedlastningsstørrelse: %3 Mirror - + Speilvend None - Ingen + Ingen @@ -3367,12 +3367,12 @@ Nedlastningsstørrelse: %3 Horizontal - + Horisontal Vertical - + Vertikal @@ -3389,7 +3389,7 @@ Nedlastningsstørrelse: %3 Portable Network Graphics (*.png) - + Portable Network Graphics (*.png) @@ -3440,7 +3440,7 @@ Nedlastningsstørrelse: %3 Paste - Lim inn + Lim inn @@ -3450,7 +3450,7 @@ Nedlastningsstørrelse: %3 All - + Alle @@ -3480,12 +3480,12 @@ Nedlastningsstørrelse: %3 TBL - + TBL ISO-8859-1 - + ISO-8859-1 @@ -3519,7 +3519,7 @@ Nedlastningsstørrelse: %3 Numeric - + Numerisk @@ -3535,7 +3535,7 @@ Nedlastningsstørrelse: %3 Guess - + Gjett @@ -3555,17 +3555,17 @@ Nedlastningsstørrelse: %3 Number type - + Nummertype Decimal - + Desimal Hexadecimal - + Heksadesimal @@ -3640,17 +3640,17 @@ Nedlastningsstørrelse: %3 (%0/%1×) - + (%0/%1×) (⅟%0×) - + (⅟%0×) (%0×) - + (%0×) @@ -3741,7 +3741,7 @@ Nedlastningsstørrelse: %3 Frame %1 - + Bilde %1 @@ -3807,17 +3807,17 @@ Nedlastningsstørrelse: %3 Export - Eksporter + Eksporter Attributes - + Attributter Transform - + Transformer @@ -3851,13 +3851,13 @@ Nedlastningsstørrelse: %3 H Short for horizontal - + H V Short for vertical - + V @@ -3878,7 +3878,7 @@ Nedlastningsstørrelse: %3 Enabled - + Skrudd på @@ -3894,7 +3894,7 @@ Nedlastningsstørrelse: %3 0x%0 - + 0x%0 @@ -3906,7 +3906,7 @@ Nedlastningsstørrelse: %3 --- - + --- @@ -3916,12 +3916,12 @@ Nedlastningsstørrelse: %3 OBJWIN - + OBJWIN Invalid - + Ugyldig @@ -3945,7 +3945,7 @@ Nedlastningsstørrelse: %3 Game Overrides - + Overstyringer for spill @@ -3958,7 +3958,7 @@ Nedlastningsstørrelse: %3 Autodetect - + Auto-oppdag @@ -3973,7 +3973,7 @@ Nedlastningsstørrelse: %3 Tilt - + Tilt @@ -4013,7 +4013,7 @@ Nedlastningsstørrelse: %3 EEPROM 8kB - + EEPROM 8kB @@ -4033,7 +4033,7 @@ Nedlastningsstørrelse: %3 Game Boy Player features - + Game Boy Player-funksjoner @@ -4043,12 +4043,12 @@ Nedlastningsstørrelse: %3 Game Boy - + Game Boy Game Boy model - + Game Boy-modell @@ -4073,7 +4073,7 @@ Nedlastningsstørrelse: %3 Palette preset - + Forhåndsvalgt fargepalett @@ -4096,7 +4096,7 @@ Nedlastningsstørrelse: %3 Palette - Palett + Palett @@ -4151,17 +4151,17 @@ Nedlastningsstørrelse: %3 Export OBJ - + Eksporter OBJ #%0 - + #%0 0x%0 - + 0x%0 @@ -4169,7 +4169,7 @@ Nedlastningsstørrelse: %3 0x%0 (%1) - 0x%0 (%1) + 0x%0 (%1) @@ -4197,7 +4197,7 @@ Nedlastningsstørrelse: %3 All - + Alle @@ -4207,12 +4207,12 @@ Nedlastningsstørrelse: %3 X - + X Y - + Y @@ -4277,32 +4277,32 @@ Nedlastningsstørrelse: %3 ROM Info - + ROM-info Game name: - + Spillets navn: Internal name: - + Internt navn: Game ID: - + Spill-ID: File size: - + Filstørrelse: CRC32: - + CRC32: @@ -4340,7 +4340,7 @@ Nedlastningsstørrelse: %3 Save - Lagre + Lagre @@ -4350,7 +4350,7 @@ Nedlastningsstørrelse: %3 Include save file - + Inkluder lagrefilen @@ -4378,7 +4378,7 @@ Nedlastningsstørrelse: %3 Select save game - + Velg lagrefil @@ -4439,7 +4439,7 @@ Nedlastningsstørrelse: %3 Output file - + Utdatafil @@ -4459,7 +4459,7 @@ Nedlastningsstørrelse: %3 SRAM - SRAM + SRAM @@ -4469,7 +4469,7 @@ Nedlastningsstørrelse: %3 %1 EEPROM - + %1 EEPROM @@ -4509,17 +4509,17 @@ Nedlastningsstørrelse: %3 MBC6 SRAM - + MBC6-SRAM TAMA5 - TAMA5 + TAMA5 %1 (%2) - + %1 (%2) @@ -4555,12 +4555,12 @@ Nedlastningsstørrelse: %3 Run - + Kjør File - + Fil @@ -4570,17 +4570,17 @@ Nedlastningsstørrelse: %3 Load script... - + Last inn skript... &Reset - + &Omstart 0 - 0 + 0 @@ -4595,7 +4595,7 @@ Nedlastningsstørrelse: %3 All files (*.*) - + Alle filer (*.*) @@ -4603,12 +4603,12 @@ Nedlastningsstørrelse: %3 Sensors - + Sensorer Realtime clock - Sanntidsklokke + Sanntidsklokke @@ -4618,7 +4618,7 @@ Nedlastningsstørrelse: %3 System time - + Systemtid @@ -4628,7 +4628,7 @@ Nedlastningsstørrelse: %3 Now - + @@ -4638,27 +4638,27 @@ Nedlastningsstørrelse: %3 sec - + sek MM/dd/yy hh:mm:ss AP - + dd/mm/YYYY hh:mm:ss AP Light sensor - Lyssensor + Lyssensor Brightness - + Lysstyrke Tilt sensor - + Tiltsensor @@ -4675,12 +4675,12 @@ Nedlastningsstørrelse: %3 Gyroscope - Gyroskop + Gyroskop Sensitivity - + Følsomhet @@ -4715,12 +4715,12 @@ Nedlastningsstørrelse: %3 None - Ingen + Ingen None (Still Image) - + Ingen (stillbilde) @@ -4756,32 +4756,32 @@ Nedlastningsstørrelse: %3 Select image - Velg bilde + Velg bilde Image file (*.png *.jpg *.jpeg) - + Bildefil (*.png *.jpg *.jpeg) (%1×%2) - + (%1×%2) Never - + Aldri Just now - + Akkurat nå Less than an hour ago - + Mindre enn én time siden @@ -4802,12 +4802,12 @@ Nedlastningsstørrelse: %3 Settings - + Innstillinger Audio/Video - + Lyd/Video @@ -4817,93 +4817,93 @@ Nedlastningsstørrelse: %3 Interface - + Grensesnitt Update - + Oppdater Emulation - + Emulering Enhancements - + Forbedringer BIOS - + BIOS Paths - + Filbaner Logging - + Loggføring Game Boy - + Game Boy Audio driver: - + Lyddriver: Audio buffer: - + Lydbuffer: 1536 - 1536 + 1536 512 - 512 + 512 768 - 768 + 768 1024 - 1024 + 1024 2048 - 2048 + 2048 3072 - 3072 + 3072 4096 - 4096 + 4096 samples - + datapunkter @@ -4914,32 +4914,32 @@ Nedlastningsstørrelse: %3 44100 - 44100 + 44100 22050 - 22050 + 22050 32000 - 32000 + 32000 48000 - 48000 + 48000 Hz - + Hz Volume: - + Volum: @@ -4947,7 +4947,7 @@ Nedlastningsstørrelse: %3 Mute - + Demp lyd @@ -4962,7 +4962,7 @@ Nedlastningsstørrelse: %3 All windows - + Alle vinduer @@ -4977,7 +4977,7 @@ Nedlastningsstørrelse: %3 Display driver: - + Skjermdriver: @@ -4987,45 +4987,45 @@ Nedlastningsstørrelse: %3 Skip every - + Hopp over hver frames - + bilder FPS target: - + Siktemål for bildefrekvens: frames per second - + bilder per sekund Sync: - + Synkroniser: Video - + Video Audio - + Lyd Lock aspect ratio - + Lås visningsforhold @@ -5035,7 +5035,7 @@ Nedlastningsstørrelse: %3 Bilinear filtering - + Bilineær filtrering @@ -5046,7 +5046,7 @@ Nedlastningsstørrelse: %3 Pause - + Pause @@ -5056,7 +5056,7 @@ Nedlastningsstørrelse: %3 On loading a game: - + Når det lastes inn et spill: @@ -5081,32 +5081,32 @@ Nedlastningsstørrelse: %3 Current channel: - + Nåværende kanal: Current version: - + Nåværende versjon: Update channel: - + Oppdateringskanal: Available version: - + Tilgjengelig versjon: (Unknown) - + (Ukjent) Last checked: - + Sist sjekket: @@ -5116,7 +5116,7 @@ Nedlastningsstørrelse: %3 Check now - + Sjekk nå @@ -5126,37 +5126,37 @@ Nedlastningsstørrelse: %3 SGB color palette if available - + SGB-fargepalett hvis tilgjengelig GBC color palette if available - + GBC-fargepalett hvis tilgjengelig SGB (preferred) or GBC color palette if available - + SGB (foretrukket) eller GBC-fargepalett hvis tilgjengelig Game Boy Camera - + Game Boy Camera Driver: - + Driver: Source: - + Kilde: Native (59.7275) - + Systemstandard (59.7275) @@ -5166,32 +5166,32 @@ Nedlastningsstørrelse: %3 Language - + Språk Library: - + Bibliotek: List view - + Listevisning Tree view - + Tre-visning Show when no game open - + Vis når ingen spill er åpne Clear cache - + Tøm mellomlageret @@ -5231,7 +5231,7 @@ Nedlastningsstørrelse: %3 Show FPS in title bar - + Vis bildefrekvensen i tittellinjen @@ -5272,7 +5272,7 @@ Nedlastningsstørrelse: %3 Enable rewind - + Skru på tilbakespoling @@ -5292,12 +5292,12 @@ Nedlastningsstørrelse: %3 Run all - + Kjør alle Remove known - + Fjern kjente @@ -5328,54 +5328,54 @@ Nedlastningsstørrelse: %3 Models - + Modeller GB only: - + Kun GB: SGB compatible: - + SGB-kompatibel: GBC only: - + Kun GBC: GBC compatible: - + GBC-kompatibel: SGB and GBC compatible: - + SGB- og GBC-kompatible: Game Boy palette - + Game Boy-fargepalett Preset: - + Forhåndsinnstilling: Screenshot - + Skjermbilde Cheat codes - + Juksekoder @@ -5395,7 +5395,7 @@ Nedlastningsstørrelse: %3 Software - + Programvare @@ -5410,7 +5410,7 @@ Nedlastningsstørrelse: %3 (240×160) - + (240×160) @@ -5444,22 +5444,22 @@ Nedlastningsstørrelse: %3 Skip BIOS intro - + Hopp over BIOS-introen GBA BIOS file: - + GBA-BIOS-fil: GBC BIOS file: - + GBC-BIOS-fil: SGB BIOS file: - + SGB-BIOS-fil: @@ -5488,7 +5488,7 @@ Nedlastningsstørrelse: %3 Patches - + Patcher @@ -5498,7 +5498,7 @@ Nedlastningsstørrelse: %3 Log to file - + Loggfør til en fil @@ -5528,7 +5528,7 @@ Nedlastningsstørrelse: %3 Super Game Boy borders - + Super Game Boy-kanter @@ -5567,7 +5567,7 @@ Nedlastningsstørrelse: %3 Shaders - Skyggeleggere + Skygger @@ -5577,17 +5577,17 @@ Nedlastningsstørrelse: %3 Name - Navn + Navn Author - + Skaper Description - + Beskrivelse @@ -5615,7 +5615,7 @@ Nedlastningsstørrelse: %3 Gamepad - + Kontroller @@ -5628,17 +5628,17 @@ Nedlastningsstørrelse: %3 Keyboard - Tastatur + Tastatur Gamepad - + Kontroller Clear - Tøm + Tøm @@ -5672,17 +5672,17 @@ Nedlastningsstørrelse: %3 Export All - + Eksporter alle 256 colors - + 256 farger Palette - Palett + Palett @@ -5697,7 +5697,7 @@ Nedlastningsstørrelse: %3 Fit to window - + Tilpass til vinduet @@ -5717,7 +5717,7 @@ Nedlastningsstørrelse: %3 Both - Begge + Begge @@ -5727,7 +5727,7 @@ Nedlastningsstørrelse: %3 Copy All - + Kopier alle @@ -5755,44 +5755,44 @@ Nedlastningsstørrelse: %3 Start - Start + Begynn Stop - Stopp + Stopp Select File - Velg fil + Velg fil Presets - + Forhåndsinnstillinger High &Quality - + Høy &kvalitet &YouTube - + &YouTube WebM - + WebM MP4 - + MP4 @@ -5802,22 +5802,22 @@ Nedlastningsstørrelse: %3 4K - 4K + 4K &1080p - + &1080p &720p - + &720p &480p - + &480p @@ -5827,47 +5827,47 @@ Nedlastningsstørrelse: %3 Format - + Format MKV - + MKV AVI - + AVI H.264 - + H.264 H.264 (NVENC) - + H.264 (NVENC) HEVC - + HEVC HEVC (NVENC) - + HEVC (NVENC) VP8 - + VP8 VP9 - + VP9 @@ -5878,12 +5878,12 @@ Nedlastningsstørrelse: %3 None - Ingen + Ingen FLAC - + FLAC @@ -5893,27 +5893,27 @@ Nedlastningsstørrelse: %3 Opus - + Opus Vorbis - + Vorbis MP3 - + MP3 AAC - + AAC Uncompressed - + Ukomprimert @@ -5923,32 +5923,32 @@ Nedlastningsstørrelse: %3 ABR - + ABR VBR - + VBR CRF - + CRF Dimensions - Dimensjoner + Dimensjoner Lock aspect ratio - + Lås visningsforhold Show advanced - Vis avanserte innstillinger + Vis avansert @@ -5974,7 +5974,7 @@ Nedlastningsstørrelse: %3 Select save - + Velg lagrefil @@ -5989,12 +5989,12 @@ Nedlastningsstørrelse: %3 Select e-Reader dotcode - + Velg e-Reader-punktkode e-Reader card (*.raw *.bin *.bmp) - + e-Reader-kort (*.raw *.bin *.bmp) @@ -6019,7 +6019,7 @@ Nedlastningsstørrelse: %3 Video logs (*.mvl) - + Videologgføringer (*.mvl) @@ -6036,12 +6036,12 @@ Nedlastningsstørrelse: %3 Couldn't Start - + Klarte ikke å starte opp Could not start game. - + Klarte ikke å starte opp spillet. @@ -6071,7 +6071,7 @@ Nedlastningsstørrelse: %3 Restart needed - + Gjennomfør omstart @@ -6086,12 +6086,12 @@ Nedlastningsstørrelse: %3 %1 - %2 - + %1 - %2 %1 - %2 - %3 - + %1 - %2 - %3 @@ -6101,12 +6101,12 @@ Nedlastningsstørrelse: %3 &File - + &Fil Load &ROM... - + Last inn &ROM... @@ -6126,7 +6126,7 @@ Nedlastningsstørrelse: %3 Select save game - + Velg lagrefil @@ -6142,12 +6142,12 @@ Nedlastningsstørrelse: %3 Select e-Reader card images - + Velg e-Reader-kortavbildninger Image file (*.png *.jpg *.jpeg) - + Bildefil (*.png *.jpg *.jpeg) @@ -6177,17 +6177,17 @@ Nedlastningsstørrelse: %3 Boot BIOS - + Oppstarts-BIOS Replace ROM... - + Bytt ROM.... Scan e-Reader dotcodes... - + Skann e-Reader-punktkoder... @@ -6202,7 +6202,7 @@ Nedlastningsstørrelse: %3 Recent - + Nylig @@ -6212,7 +6212,7 @@ Nedlastningsstørrelse: %3 &Load state - + &Last inn tilstand @@ -6222,7 +6222,7 @@ Nedlastningsstørrelse: %3 &Save state - + &Lagre en tilstand @@ -6323,47 +6323,47 @@ Nedlastningsstørrelse: %3 Connect to Dolphin... - + Koble til Dolphin... Report bug... - + Rapporter inn feil... About... - + Om … E&xit - + A&vslutt &Emulation - + &Emulering &Reset - + &Omstart Sh&utdown - + Skr&u av Yank game pak - + Dra ut spillkassetten &Pause - + &Pause @@ -6393,7 +6393,7 @@ Nedlastningsstørrelse: %3 %0x - %0x + %0x @@ -6408,7 +6408,7 @@ Nedlastningsstørrelse: %3 Rewind (held) - + Spol tilbake (holdt) @@ -6423,37 +6423,37 @@ Nedlastningsstørrelse: %3 Solar sensor - + Solsensor Increase solar level - + Øke solnivået Decrease solar level - + Reduser solnivået Brightest solar level - + Lyseste solnivå Darkest solar level - + Mørkeste solnivå Brightness %1 - + Lysstyrke %1 Game Boy Printer... - + Game Boy Printer... @@ -6468,22 +6468,22 @@ Nedlastningsstørrelse: %3 Frame size - + Rammestørrelse %1× - %1× + %1× Toggle fullscreen - + Skru på/av fullskjerm Lock aspect ratio - + Lås visningsforhold @@ -6498,7 +6498,7 @@ Nedlastningsstørrelse: %3 Bilinear filtering - + Bilineær filtrering @@ -6508,7 +6508,7 @@ Nedlastningsstørrelse: %3 Mute - + Demp lyd @@ -6518,17 +6518,17 @@ Nedlastningsstørrelse: %3 Native (59.7275) - + Systemstandard (59.7275) Take &screenshot - + Ta &skjermbilde F12 - + F12 @@ -6548,7 +6548,7 @@ Nedlastningsstørrelse: %3 Audio channels - + Lydkanaler @@ -6558,7 +6558,7 @@ Nedlastningsstørrelse: %3 &Tools - + Verk&tøy @@ -6573,7 +6573,7 @@ Nedlastningsstørrelse: %3 Game Pak sensors... - + Game Pak-sensorer ... @@ -6588,7 +6588,7 @@ Nedlastningsstørrelse: %3 Settings... - + Innstillinger… @@ -6613,7 +6613,7 @@ Nedlastningsstørrelse: %3 View &palette... - + Vis &palett ... @@ -6628,7 +6628,7 @@ Nedlastningsstørrelse: %3 View &map... - + Vis &kart … @@ -6663,7 +6663,7 @@ Nedlastningsstørrelse: %3 Exit fullscreen - + Gå ut av fullskjerm @@ -6728,7 +6728,7 @@ Nedlastningsstørrelse: %3 Clear - Tøm + Tøm @@ -6736,47 +6736,47 @@ Nedlastningsstørrelse: %3 %1 byte - + %1 byte %1 kiB - + %1 kiB %1 MiB - + %1 MiB GBA - + GBA GB - + GB ? - + ? Super (L) - + Super (L) Super (R) - + Super (R) Menu - Meny + Meny @@ -6784,22 +6784,22 @@ Nedlastningsstørrelse: %3 Shift - + Shift Control - + Kontroll Alt - + Alt Meta - + Meta diff --git a/src/platform/qt/ts/mgba-pl.ts b/src/platform/qt/ts/mgba-pl.ts index 180b32630a7..650563e2e03 100644 --- a/src/platform/qt/ts/mgba-pl.ts +++ b/src/platform/qt/ts/mgba-pl.ts @@ -716,7 +716,7 @@ Rozmiar pobierania: %3 Image files (*.png *.jpg *.bmp) - + Obrazy (*.png *.jpg *.bmp) @@ -3752,17 +3752,17 @@ Rozmiar pobierania: %3 Trying to detach a multiplayer player that's not attached - + Próba odłączenia gracza multiplayer, który nie jest dołączony Trying to get player ID for a multiplayer player that's not attached - + Próba uzyskania ID gracza w trybie wieloosobowym, który nie jest połączony Trying to get save ID for a multiplayer player that's not attached - + Próba uzyskania identyfikatora zapisu dla gracza multiplayer, który nie jest połączony @@ -4310,7 +4310,7 @@ Rozmiar pobierania: %3 Save file: - + Zapisz plik: @@ -4477,7 +4477,7 @@ Rozmiar pobierania: %3 + RTC - + + RTC @@ -4759,12 +4759,12 @@ Rozmiar pobierania: %3 Select image - Wybierz obraz + Wybierz obraz Image file (*.png *.jpg *.jpeg) - Plik graficzny (*.png *.jpg *.jpeg) + Plik graficzny (*.png *.jpg *.jpeg) @@ -5251,7 +5251,7 @@ Rozmiar pobierania: %3 Custom border: - + Niestandardowa ramka: @@ -5287,7 +5287,7 @@ Rozmiar pobierania: %3 Rewind speed: - + Prędkość przewijania: @@ -6405,12 +6405,12 @@ Rozmiar pobierania: %3 Increase fast forward speed - + Zwiększenie prędkości przewijania do przodu Decrease fast forward speed - + Zmiejszenie prędkości przewijania do przodu @@ -6773,17 +6773,17 @@ Rozmiar pobierania: %3 Super (L) - Super (L) + Super (L) Super (R) - Super (R) + Super (R) Menu - Menu + Menu diff --git a/src/platform/qt/ts/mgba-pt.ts b/src/platform/qt/ts/mgba-pt.ts index 72af03abedf..cddb1dd8cf0 100644 --- a/src/platform/qt/ts/mgba-pt.ts +++ b/src/platform/qt/ts/mgba-pt.ts @@ -716,7 +716,7 @@ Tamanho da descarga: %3 Image files (*.png *.jpg *.bmp) - + Ficheiro da imagem (*.png *.jpg *.bmp) @@ -3752,17 +3752,17 @@ Tamanho da descarga: %3 Trying to detach a multiplayer player that's not attached - + A tentar desconectar um jogador multiplayer que não está conectado Trying to get player ID for a multiplayer player that's not attached - + A tentar obter a ID do jogador para um jogador multiplayer que não está conectado Trying to get save ID for a multiplayer player that's not attached - + A tentar obter a ID gravada para um jogador multiplayer que não está conectado @@ -4310,7 +4310,7 @@ Tamanho da descarga: %3 Save file: - + Ficheiro do save: @@ -4477,7 +4477,7 @@ Tamanho da descarga: %3 + RTC - + + RTC @@ -4759,12 +4759,12 @@ Tamanho da descarga: %3 Select image - Selecionar imagem + Selecionar imagem Image file (*.png *.jpg *.jpeg) - Ficheiro da imagem (*.png *.jpg *.jpeg) + Ficheiro da imagem (*.png *.jpg *.jpeg) @@ -4792,6 +4792,7 @@ Tamanho da descarga: %3 %n hora atrás %n horas atrás + %n horas atrás @@ -4800,6 +4801,7 @@ Tamanho da descarga: %3 %n dia atrás %n dias atrás + %n dias atrás @@ -5249,7 +5251,7 @@ Tamanho da descarga: %3 Custom border: - + Borda personalizada: @@ -5285,7 +5287,7 @@ Tamanho da descarga: %3 Rewind speed: - + Velocidade do retrocesso: @@ -6403,12 +6405,12 @@ Tamanho da descarga: %3 Increase fast forward speed - + Aumentar a velocidade do avanço rápido Decrease fast forward speed - + Diminuir a velocidade do avanço rápido @@ -6771,17 +6773,17 @@ Tamanho da descarga: %3 Super (L) - Super (E) + Super (E) Super (R) - Super (D) + Super (D) Menu - Menu + Menu diff --git a/src/platform/qt/ts/mgba-pt_BR.ts b/src/platform/qt/ts/mgba-pt_BR.ts index 2cbfde5287f..77573eb1ba2 100644 --- a/src/platform/qt/ts/mgba-pt_BR.ts +++ b/src/platform/qt/ts/mgba-pt_BR.ts @@ -716,7 +716,7 @@ Tamanho do download: %3 Image files (*.png *.jpg *.bmp) - + Arquivo da imagem (*.png *.jpg *.bmp) @@ -3752,17 +3752,17 @@ Tamanho do download: %3 Trying to detach a multiplayer player that's not attached - + Tentando desconectar um jogador multiplayer que não está conectado Trying to get player ID for a multiplayer player that's not attached - + Tentando obter a ID do jogador pra um jogador multiplayer que não está conectado Trying to get save ID for a multiplayer player that's not attached - + Tentando obter a ID salva pra um jogador multiplayer que não está conectado @@ -4310,7 +4310,7 @@ Tamanho do download: %3 Save file: - + Arquivo do save: @@ -4477,7 +4477,7 @@ Tamanho do download: %3 + RTC - + + RTC @@ -4759,12 +4759,12 @@ Tamanho do download: %3 Select image - Selecionar imagem + Selecionar imagem Image file (*.png *.jpg *.jpeg) - Arquivo da imagem (*.png *.jpg *.jpeg) + Arquivo da imagem (*.png *.jpg *.jpeg) @@ -4792,6 +4792,7 @@ Tamanho do download: %3 %n hora atrás %n horas atrás + @@ -4800,6 +4801,7 @@ Tamanho do download: %3 %n dia atrás %n dias atrás + @@ -5089,7 +5091,7 @@ Tamanho do download: %3 Custom border: - + Borda personalizada: @@ -5134,7 +5136,7 @@ Tamanho do download: %3 Rewind speed: - + Velocidade do retrocesso: @@ -6388,12 +6390,12 @@ Tamanho do download: %3 Increase fast forward speed - + Aumentar a velocidade do avanço rápido Decrease fast forward speed - + Diminuir a velocidade do avanço rápido @@ -6771,17 +6773,17 @@ Tamanho do download: %3 Super (L) - Super (E) + Super (E) Super (R) - Super (D) + Super (D) Menu - Menu + Menu diff --git a/src/platform/qt/ts/mgba-tr.ts b/src/platform/qt/ts/mgba-tr.ts index d239b59c2d8..a98e0639b0c 100644 --- a/src/platform/qt/ts/mgba-tr.ts +++ b/src/platform/qt/ts/mgba-tr.ts @@ -6,22 +6,22 @@ Game Boy Advance ROMs (%1) - Game Boy Advance ROMları (%1) + Game Boy Advance ROM'ları (%1) Game Boy ROMs (%1) - Game Boy ROMları (%1) + Game Boy ROM'ları (%1) All ROMs (%1) - Bütün ROMlar (%1) + Bütün ROM'lar (%1) %1 Video Logs (*.mvl) - + %1 Video Günlükleri (*.mvl) @@ -64,47 +64,52 @@ Game Boy Advance, Nintendo Co., Ltd.'nin tescilli ticari markasıdır. An update is available - + Bir güncelleme mevcut An update to %1 is available. - + %1 için bir güncelleme mevcut. + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + +Şimdi indirip yüklemek istiyor musunuz? İndirme işlemi tamamlandığında öykünücüyü yeniden başlatmanız gerekecektir. Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + +Otomatik güncelleme bu platformda mevcut değildir. Güncellemek istiyorsanız bunu manuel olarak yapmanız gerekecektir. Current version: %1 New version: %2 Download size: %3 - + Güncel sürüm: %1 +Yeni sürüm: %2 +İndirme boyutu: %3 Downloading update... - + Güncelleme indiriliyor... Downloading failed. Please update manually. - + İndirme başarısız oldu. Lütfen manuel olarak güncelleyin. Downloading done. Press OK to restart %1 and install the update. - + İndirme tamamlandı. %1 yeniden başlatmak ve güncellemeyi yüklemek için Tamam'a basın. @@ -112,22 +117,22 @@ Download size: %3 Stable - + Kararlı Development - + Geliştirme Unknown - Bilinmeyen + Bilinmeyen (None) - + (Yok) @@ -188,17 +193,17 @@ Download size: %3 Can't set format of context-less audio device - + Bağlamdan bağımsız ses cihazının formatı ayarlanamıyor Audio device is missing its core - + Ses cihazının çekirdeği eksik Writing data to read-only audio device - + Salt okunur ses cihazına veri yazma @@ -206,7 +211,7 @@ Download size: %3 Can't start an audio processor without input - + Girişsiz ses işlemcisi başlatılamaz @@ -214,7 +219,7 @@ Download size: %3 Can't start an audio processor without input - + Girişsiz ses işlemcisi başlatılamaz @@ -282,28 +287,28 @@ Download size: %3 BattleChip data missing - + BattleChip verisi yok BattleChip data is missing. BattleChip Gates will still work, but some graphics will be missing. Would you like to download the data now? - + BattleChip verisi yok. Bazı grafikler olmadan BattleChip Gates hala çalışır. Verileri şimdi indirmek istermisin? Select deck file - + Deste dosyası seç Incompatible deck - + Uyumsuz deste The selected deck is not compatible with this Chip Gate - + Seçilen deste bu Chip Gate ile uyumlu değildir @@ -329,7 +334,7 @@ Download size: %3 Add New Code - + Yeni Kod Ekle @@ -339,12 +344,12 @@ Download size: %3 Add Lines - + Satır ekle Code type - + Kod tipi @@ -365,7 +370,7 @@ Download size: %3 Autodetect (recommended) - + Otoseç (tavsiye edilir) @@ -376,7 +381,7 @@ Download size: %3 Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. - + Bazı hileler eklenemedi. Lütfen onların doğru formatlandığından emin ol ve/yada başa hile tiplerini dene. @@ -395,7 +400,7 @@ Download size: %3 Reset the game? - + Oyun sıfırlansım mı? @@ -6074,7 +6079,7 @@ Download size: %3 This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - Emülatörün yapılandırmasını yürütülebilir dosya ile aynı dizinden yüklemesini sağlar. Devam etmek istiyor musun? + Öyküncünün yapılandırmasını yürütülebilir dosya ile aynı dizinden yüklemesini sağlar. Devam etmek istiyor musunuz? @@ -6084,7 +6089,7 @@ Download size: %3 Some changes will not take effect until the emulator is restarted. - Bazı değişiklikler emülatör yeniden başlatılıncaya kadar etkili olmaz. + Bazı değişiklikler öyküncü yeniden başlatılıncaya kadar etkili olmaz. diff --git a/src/platform/qt/ts/mgba-zh_CN.ts b/src/platform/qt/ts/mgba-zh_CN.ts index 150ad5404fd..6992a62809e 100644 --- a/src/platform/qt/ts/mgba-zh_CN.ts +++ b/src/platform/qt/ts/mgba-zh_CN.ts @@ -4366,7 +4366,7 @@ Download size: %3 Save games and save states (%1) - 保存游戏和即时存档(%1) + 保存游戏和即时存档 (%1) @@ -4376,7 +4376,7 @@ Download size: %3 Save games (%1) - 保存游戏(%1) + 保存游戏 (%1) @@ -6124,7 +6124,7 @@ Download size: %3 Save games (%1) - 保存游戏(%1) + 保存游戏 (%1) @@ -6134,7 +6134,7 @@ Download size: %3 mGBA save state files (%1) - mGBA 即时存档文件(%1) + mGBA 即时存档文件 (%1) diff --git a/src/platform/sdl/gl-common.c b/src/platform/sdl/gl-common.c index 1ce9100e804..f2ccdd7e29b 100644 --- a/src/platform/sdl/gl-common.c +++ b/src/platform/sdl/gl-common.c @@ -15,7 +15,7 @@ #endif void mSDLGLDoViewport(int w, int h, struct VideoBackend* v) { - v->contextResized(v, w, h); + v->contextResized(v, w, h, 0, 0); v->clear(v); v->swap(v); v->clear(v); diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index f71f1cfc52c..1a1d3f6fc76 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -47,6 +47,7 @@ static void _loadState(struct mCoreThread* thread) { int main(int argc, char** argv) { #ifdef _WIN32 AttachConsole(ATTACH_PARENT_PROCESS); + freopen("CONOUT$", "w", stdout); #endif struct mSDLRenderer renderer = {0}; @@ -216,12 +217,12 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { #ifdef ENABLE_PYTHON mPythonSetup(bridge); #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS CLIDebuggerScriptEngineInstall(bridge); #endif #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS struct mDebugger debugger; mDebuggerInit(&debugger); bool hasDebugger = mArgumentsApplyDebugger(args, renderer->core, &debugger); @@ -291,7 +292,7 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { mScriptBridgeDestroy(bridge); #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (hasDebugger) { renderer->core->detachDebugger(renderer->core); mDebuggerDeinit(&debugger); diff --git a/src/platform/sdl/sdl-audio.c b/src/platform/sdl/sdl-audio.c index f1666aa4ee9..d90ad95d342 100644 --- a/src/platform/sdl/sdl-audio.c +++ b/src/platform/sdl/sdl-audio.c @@ -7,12 +7,6 @@ #include #include -#include -#include - -#include - -#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2) mLOG_DEFINE_CATEGORY(SDL_AUDIO, "SDL Audio", "platform.sdl.audio"); @@ -47,6 +41,10 @@ bool mSDLInitAudio(struct mSDLAudio* context, struct mCoreThread* threadContext) } context->core = 0; + mAudioBufferInit(&context->buffer, context->samples, context->obtainedSpec.channels); + mAudioResamplerInit(&context->resampler, mINTERPOLATOR_SINC); + mAudioResamplerSetDestination(&context->resampler, &context->buffer, context->obtainedSpec.freq); + if (threadContext) { context->core = threadContext->core; context->sync = &threadContext->impl->sync; @@ -70,6 +68,8 @@ void mSDLDeinitAudio(struct mSDLAudio* context) { SDL_PauseAudio(1); SDL_CloseAudio(); #endif + mAudioBufferDeinit(&context->buffer); + mAudioResamplerDeinit(&context->resampler); SDL_QuitSubSystem(SDL_INIT_AUDIO); } @@ -97,32 +97,25 @@ static void _mSDLAudioCallback(void* context, Uint8* data, int len) { memset(data, 0, len); return; } - blip_t* left = NULL; - blip_t* right = NULL; - int32_t clockRate = GBA_ARM7TDMI_FREQUENCY; + struct mAudioBuffer* buffer = NULL; + unsigned sampleRate = 32768; if (audioContext->core) { - left = audioContext->core->getAudioChannel(audioContext->core, 0); - right = audioContext->core->getAudioChannel(audioContext->core, 1); - clockRate = audioContext->core->frequency(audioContext->core); + buffer = audioContext->core->getAudioBuffer(audioContext->core); + sampleRate = audioContext->core->audioSampleRate(audioContext->core); } double fauxClock = 1; if (audioContext->sync) { - if (audioContext->sync->fpsTarget > 0) { - fauxClock = GBAAudioCalculateRatio(1, audioContext->sync->fpsTarget, 1); + if (audioContext->sync->fpsTarget > 0 && audioContext->core) { + fauxClock = mCoreCalculateFramerateRatio(audioContext->core, audioContext->sync->fpsTarget); } mCoreSyncLockAudio(audioContext->sync); + audioContext->sync->audioHighWater = audioContext->samples + audioContext->resampler.highWaterMark + audioContext->resampler.lowWaterMark; + audioContext->sync->audioHighWater *= sampleRate / (fauxClock * audioContext->obtainedSpec.freq); } - blip_set_rates(left, clockRate, audioContext->obtainedSpec.freq * fauxClock); - blip_set_rates(right, clockRate, audioContext->obtainedSpec.freq * fauxClock); + mAudioResamplerSetSource(&audioContext->resampler, buffer, sampleRate / fauxClock, true); + mAudioResamplerProcess(&audioContext->resampler); len /= 2 * audioContext->obtainedSpec.channels; - int available = blip_samples_avail(left); - if (available > len) { - available = len; - } - blip_read_samples(left, (short*) data, available, audioContext->obtainedSpec.channels == 2); - if (audioContext->obtainedSpec.channels == 2) { - blip_read_samples(right, ((short*) data) + 1, available, 1); - } + int available = mAudioBufferRead(&audioContext->buffer, (int16_t*) data, len); if (audioContext->sync) { mCoreSyncConsumeAudio(audioContext->sync); diff --git a/src/platform/sdl/sdl-audio.h b/src/platform/sdl/sdl-audio.h index 2ae920af408..b8fa00c85ef 100644 --- a/src/platform/sdl/sdl-audio.h +++ b/src/platform/sdl/sdl-audio.h @@ -11,6 +11,8 @@ CXX_GUARD_START #include +#include +#include #include // Altivec sometimes defines this @@ -30,6 +32,8 @@ struct mSDLAudio { unsigned sampleRate; // State + struct mAudioBuffer buffer; + struct mAudioResampler resampler; SDL_AudioSpec desiredSpec; SDL_AudioSpec obtainedSpec; #if SDL_VERSION_ATLEAST(2, 0, 0) diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index a675ff2c093..950bb94f3c4 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -167,12 +167,30 @@ void mSDLInitBindingsGBA(struct mInputMap* inputMap) { mInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RIGHT, GBA_KEY_RIGHT); #endif +#if SDL_VERSION_ATLEAST(2, 0, 0) + mInputBindKey(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_BUTTON_A, GBA_KEY_A); + mInputBindKey(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_BUTTON_B, GBA_KEY_B); + mInputBindKey(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, GBA_KEY_L); + mInputBindKey(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, GBA_KEY_R); + mInputBindKey(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_BUTTON_START, GBA_KEY_START); + mInputBindKey(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_BUTTON_BACK, GBA_KEY_SELECT); + mInputBindKey(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_BUTTON_DPAD_UP, GBA_KEY_UP); + mInputBindKey(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_BUTTON_DPAD_DOWN, GBA_KEY_DOWN); + mInputBindKey(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_BUTTON_DPAD_LEFT, GBA_KEY_LEFT); + mInputBindKey(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, GBA_KEY_RIGHT); + + struct mInputAxis description = (struct mInputAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x4000, -0x4000 }; + mInputBindAxis(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_AXIS_LEFTX, &description); + description = (struct mInputAxis) { GBA_KEY_DOWN, GBA_KEY_UP, 0x4000, -0x4000 }; + mInputBindAxis(inputMap, SDL_BINDING_CONTROLLER, SDL_CONTROLLER_AXIS_LEFTY, &description); +#else struct mInputAxis description = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x4000, -0x4000 }; mInputBindAxis(inputMap, SDL_BINDING_BUTTON, 0, &description); description = (struct mInputAxis) { GBA_KEY_DOWN, GBA_KEY_UP, 0x4000, -0x4000 }; mInputBindAxis(inputMap, SDL_BINDING_BUTTON, 1, &description); mInputBindHat(inputMap, SDL_BINDING_BUTTON, 0, &GBAInputInfo.hat); +#endif } bool mSDLAttachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) { @@ -184,7 +202,7 @@ bool mSDLAttachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) { #if SDL_VERSION_ATLEAST(2, 0, 0) player->rumble.d.setRumble = _mSDLSetRumble; - CircleBufferInit(&player->rumble.history, RUMBLE_PWM); + mCircleBufferInit(&player->rumble.history, RUMBLE_PWM); player->rumble.level = 0; player->rumble.activeLevel = 0; player->rumble.p = player; @@ -201,7 +219,7 @@ bool mSDLAttachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) { player->rotation.gyroY = 1; player->rotation.gyroZ = -1; player->rotation.zDelta = 0; - CircleBufferInit(&player->rotation.zHistory, sizeof(float) * GYRO_STEPS); + mCircleBufferInit(&player->rotation.zHistory, sizeof(float) * GYRO_STEPS); player->rotation.p = player; player->playerId = events->playersAttached; @@ -275,26 +293,28 @@ void mSDLDetachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) { } } --events->playersAttached; - CircleBufferDeinit(&player->rotation.zHistory); + mCircleBufferDeinit(&player->rotation.zHistory); #if SDL_VERSION_ATLEAST(2, 0, 0) - CircleBufferDeinit(&player->rumble.history); + mCircleBufferDeinit(&player->rumble.history); #endif } void mSDLPlayerLoadConfig(struct mSDLPlayer* context, const struct Configuration* config) { mInputMapLoad(context->bindings, SDL_BINDING_KEY, config); if (context->joystick) { - mInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config); #if SDL_VERSION_ATLEAST(2, 0, 0) + mInputMapLoad(context->bindings, SDL_BINDING_CONTROLLER, config); char name[34] = {0}; SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(context->joystick->joystick), name, sizeof(name)); + mInputProfileLoad(context->bindings, SDL_BINDING_CONTROLLER, config, name); #else + mInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config); const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick)); if (!name) { return; } -#endif mInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, name); +#endif const char* value; char* end; @@ -410,7 +430,7 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration* if (events->preferredJoysticks[i] && strcmp(events->preferredJoysticks[i], joystickName) == 0) { events->players[i]->joystick = joystick; if (config) { - mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_BUTTON, config, joystickName); + mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_CONTROLLER, config, joystickName); } return; } @@ -421,7 +441,7 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration* } events->players[i]->joystick = joystick; if (config && joystickName[0]) { - mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_BUTTON, config, joystickName); + mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_CONTROLLER, config, joystickName); } break; } @@ -485,7 +505,7 @@ static void _mSDLHandleKeypress(struct mCoreThread* context, struct mSDLPlayer* } if (event->type == SDL_KEYDOWN) { switch (event->keysym.sym) { -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS case SDLK_F11: if (context->core->debugger) { mDebuggerEnter(context->core->debugger, DEBUGGER_ENTER_MANUAL, NULL); @@ -574,6 +594,45 @@ static void _mSDLHandleKeypress(struct mCoreThread* context, struct mSDLPlayer* } } +#if SDL_VERSION_ATLEAST(2, 0, 0) +static void _mSDLHandleControllerButton(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const struct SDL_ControllerButtonEvent* event) { + int key = 0; + key = mInputMapKey(sdlContext->bindings, SDL_BINDING_CONTROLLER, event->button); + if (key == -1) { + return; + } + + mCoreThreadInterrupt(context); + if (event->type == SDL_CONTROLLERBUTTONDOWN) { + context->core->addKeys(context->core, 1 << key); + } else { + context->core->clearKeys(context->core, 1 << key); + } + mCoreThreadContinue(context); +} + +static void _mSDLHandleControllerAxis(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const struct SDL_ControllerAxisEvent* event) { + int clearKeys = ~mInputClearAxis(sdlContext->bindings, SDL_BINDING_CONTROLLER, event->axis, -1); + int newKeys = 0; + int key = mInputMapAxis(sdlContext->bindings, SDL_BINDING_CONTROLLER, event->axis, event->value); + if (key != -1) { + newKeys |= 1 << key; + } + clearKeys &= ~newKeys; + mCoreThreadInterrupt(context); + context->core->clearKeys(context->core, clearKeys); + context->core->addKeys(context->core, newKeys); + mCoreThreadContinue(context); +} + +static void _mSDLHandleWindowEvent(struct mSDLPlayer* sdlContext, const struct SDL_WindowEvent* event) { + switch (event->event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + sdlContext->windowUpdated = 1; + break; + } +} +#else static void _mSDLHandleJoyButton(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const struct SDL_JoyButtonEvent* event) { int key = 0; key = mInputMapKey(sdlContext->bindings, SDL_BINDING_BUTTON, event->button); @@ -616,16 +675,6 @@ static void _mSDLHandleJoyAxis(struct mCoreThread* context, struct mSDLPlayer* s context->core->clearKeys(context->core, clearKeys); context->core->addKeys(context->core, newKeys); mCoreThreadContinue(context); - -} - -#if SDL_VERSION_ATLEAST(2, 0, 0) -static void _mSDLHandleWindowEvent(struct mSDLPlayer* sdlContext, const struct SDL_WindowEvent* event) { - switch (event->event) { - case SDL_WINDOWEVENT_SIZE_CHANGED: - sdlContext->windowUpdated = 1; - break; - } } #endif @@ -638,17 +687,19 @@ void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, case SDL_WINDOWEVENT: _mSDLHandleWindowEvent(sdlContext, &event->window); break; + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + _mSDLHandleControllerButton(context, sdlContext, &event->cbutton); + break; + case SDL_CONTROLLERAXISMOTION: + _mSDLHandleControllerAxis(context, sdlContext, &event->caxis); + break; #else case SDL_VIDEORESIZE: sdlContext->newWidth = event->resize.w; sdlContext->newHeight = event->resize.h; sdlContext->windowUpdated = 1; break; -#endif - case SDL_KEYDOWN: - case SDL_KEYUP: - _mSDLHandleKeypress(context, sdlContext, &event->key); - break; case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: _mSDLHandleJoyButton(context, sdlContext, &event->jbutton); @@ -659,27 +710,41 @@ void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, case SDL_JOYAXISMOTION: _mSDLHandleJoyAxis(context, sdlContext, &event->jaxis); break; +#endif + case SDL_KEYDOWN: + case SDL_KEYUP: + _mSDLHandleKeypress(context, sdlContext, &event->key); + break; } } #if SDL_VERSION_ATLEAST(2, 0, 0) static void _mSDLSetRumble(struct mRumble* rumble, int enable) { struct mSDLRumble* sdlRumble = (struct mSDLRumble*) rumble; - if (!sdlRumble->p->joystick + if (!sdlRumble->p->joystick) { + return; + } + #if !SDL_VERSION_ATLEAST(2, 0, 9) - || !sdlRumble->p->joystick->haptic || !SDL_HapticRumbleSupported(sdlRumble->p->joystick->haptic) + if (!sdlRumble->p->joystick->haptic || !SDL_HapticRumbleSupported(sdlRumble->p->joystick->haptic)) { + return; + } #endif - ) { + +#if SDL_VERSION_ATLEAST(2, 0, 18) + if (!sdlRumble->p->joystick->controller || !SDL_GameControllerHasRumble(sdlRumble->p->joystick->controller)) { return; } +#endif + int8_t originalLevel = sdlRumble->level; sdlRumble->level += enable; - if (CircleBufferSize(&sdlRumble->history) == RUMBLE_PWM) { + if (mCircleBufferSize(&sdlRumble->history) == RUMBLE_PWM) { int8_t oldLevel; - CircleBufferRead8(&sdlRumble->history, &oldLevel); + mCircleBufferRead8(&sdlRumble->history, &oldLevel); sdlRumble->level -= oldLevel; } - CircleBufferWrite8(&sdlRumble->history, enable); + mCircleBufferWrite8(&sdlRumble->history, enable); if (sdlRumble->level == originalLevel) { return; } @@ -689,7 +754,11 @@ static void _mSDLSetRumble(struct mRumble* rumble, int enable) { } sdlRumble->activeLevel = activeLevel; #if SDL_VERSION_ATLEAST(2, 0, 9) - SDL_JoystickRumble(sdlRumble->p->joystick->joystick, activeLevel * 0xFFFF, activeLevel * 0xFFFF, 500); + if (sdlRumble->p->joystick->controller) { + SDL_GameControllerRumble(sdlRumble->p->joystick->controller, activeLevel * 0xFFFF, activeLevel * 0xFFFF, 500); + } else { + SDL_JoystickRumble(sdlRumble->p->joystick->joystick, activeLevel * 0xFFFF, activeLevel * 0xFFFF, 500); + } #else if (sdlRumble->activeLevel > 0.5 / RUMBLE_STEPS) { SDL_HapticRumbleStop(sdlRumble->p->joystick->haptic); @@ -754,14 +823,14 @@ static void _mSDLRotationSample(struct mRotationSource* source) { float theta[3]; int count = SDL_GameControllerGetSensorData(controller, SDL_SENSOR_GYRO, theta, 3); if (count >= 0) { - rotation->zDelta = theta[1] / -10.f; + rotation->zDelta = theta[1] / -20.f; } return; } } #endif if (rotation->gyroZ >= 0) { - rotation->zDelta = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroZ) / 1.e5f; + rotation->zDelta = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroZ) / 2.e5f; return; } @@ -782,10 +851,10 @@ static void _mSDLRotationSample(struct mRotationSource* source) { rotation->oldY = y; float oldZ = 0; - if (CircleBufferSize(&rotation->zHistory) == GYRO_STEPS * sizeof(float)) { - CircleBufferRead32(&rotation->zHistory, (int32_t*) &oldZ); + if (mCircleBufferSize(&rotation->zHistory) == GYRO_STEPS * sizeof(float)) { + mCircleBufferRead32(&rotation->zHistory, (int32_t*) &oldZ); } - CircleBufferWrite32(&rotation->zHistory, theta.i); + mCircleBufferWrite32(&rotation->zHistory, theta.i); rotation->zDelta += theta.f - oldZ; } @@ -817,4 +886,221 @@ void mSDLSetScreensaverSuspendable(struct mSDLEvents* events, bool suspendable) SDL_EnableScreenSaver(); } } + +static const char* const buttonNamesXbox360[SDL_CONTROLLER_BUTTON_MAX] = { + [SDL_CONTROLLER_BUTTON_A] = "A", + [SDL_CONTROLLER_BUTTON_B] = "B", + [SDL_CONTROLLER_BUTTON_X] = "X", + [SDL_CONTROLLER_BUTTON_Y] = "Y", + [SDL_CONTROLLER_BUTTON_BACK] = "Back", + [SDL_CONTROLLER_BUTTON_GUIDE] = "Xbox", + [SDL_CONTROLLER_BUTTON_START] = "Start", + [SDL_CONTROLLER_BUTTON_LEFTSTICK] = "LS", + [SDL_CONTROLLER_BUTTON_RIGHTSTICK] = "RS", + [SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = "LB", + [SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = "RB", +#if SDL_VERSION_ATLEAST(2, 0, 14) + [SDL_CONTROLLER_BUTTON_MISC1] = "Misc", + [SDL_CONTROLLER_BUTTON_PADDLE1] = "P1", + [SDL_CONTROLLER_BUTTON_PADDLE2] = "P2", + [SDL_CONTROLLER_BUTTON_PADDLE3] = "P3", + [SDL_CONTROLLER_BUTTON_PADDLE4] = "P4", + [SDL_CONTROLLER_BUTTON_TOUCHPAD] = "Touch", +#endif +}; + +#if SDL_VERSION_ATLEAST(2, 0, 12) +static const char* const buttonNamesXboxOne[SDL_CONTROLLER_BUTTON_MAX] = { + [SDL_CONTROLLER_BUTTON_A] = "A", + [SDL_CONTROLLER_BUTTON_B] = "B", + [SDL_CONTROLLER_BUTTON_X] = "X", + [SDL_CONTROLLER_BUTTON_Y] = "Y", + [SDL_CONTROLLER_BUTTON_BACK] = "View", + [SDL_CONTROLLER_BUTTON_GUIDE] = "Xbox", + [SDL_CONTROLLER_BUTTON_START] = "Menu", + [SDL_CONTROLLER_BUTTON_LEFTSTICK] = "LS", + [SDL_CONTROLLER_BUTTON_RIGHTSTICK] = "RS", + [SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = "LB", + [SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = "RB", +#if SDL_VERSION_ATLEAST(2, 0, 14) + [SDL_CONTROLLER_BUTTON_MISC1] = "Share", + [SDL_CONTROLLER_BUTTON_PADDLE1] = "P1", + [SDL_CONTROLLER_BUTTON_PADDLE2] = "P2", + [SDL_CONTROLLER_BUTTON_PADDLE3] = "P3", + [SDL_CONTROLLER_BUTTON_PADDLE4] = "P4", + [SDL_CONTROLLER_BUTTON_TOUCHPAD] = "Touch", +#endif +}; + +static const char* const buttonNamesPlayStation[SDL_CONTROLLER_BUTTON_MAX] = { + [SDL_CONTROLLER_BUTTON_A] = "×", + [SDL_CONTROLLER_BUTTON_B] = "○", + [SDL_CONTROLLER_BUTTON_X] = "□", + [SDL_CONTROLLER_BUTTON_Y] = "△", + [SDL_CONTROLLER_BUTTON_BACK] = "Share", + [SDL_CONTROLLER_BUTTON_GUIDE] = "PS", + [SDL_CONTROLLER_BUTTON_START] = "Options", + [SDL_CONTROLLER_BUTTON_LEFTSTICK] = "L3", + [SDL_CONTROLLER_BUTTON_RIGHTSTICK] = "R3", + [SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = "L1", + [SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = "R1", +#if SDL_VERSION_ATLEAST(2, 0, 14) + [SDL_CONTROLLER_BUTTON_MISC1] = "Misc", + [SDL_CONTROLLER_BUTTON_PADDLE1] = "P1", + [SDL_CONTROLLER_BUTTON_PADDLE2] = "P2", + [SDL_CONTROLLER_BUTTON_PADDLE3] = "P3", + [SDL_CONTROLLER_BUTTON_PADDLE4] = "P4", + [SDL_CONTROLLER_BUTTON_TOUCHPAD] = "Touch", +#endif +}; + +static const char* const buttonNamesNintedo[SDL_CONTROLLER_BUTTON_MAX] = { + [SDL_CONTROLLER_BUTTON_A] = "A", + [SDL_CONTROLLER_BUTTON_B] = "B", + [SDL_CONTROLLER_BUTTON_X] = "X", + [SDL_CONTROLLER_BUTTON_Y] = "Y", + [SDL_CONTROLLER_BUTTON_BACK] = "-", + [SDL_CONTROLLER_BUTTON_GUIDE] = "Home", + [SDL_CONTROLLER_BUTTON_START] = "+", + [SDL_CONTROLLER_BUTTON_LEFTSTICK] = "LS", + [SDL_CONTROLLER_BUTTON_RIGHTSTICK] = "RS", + [SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = "L", + [SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = "R", +#if SDL_VERSION_ATLEAST(2, 0, 14) + [SDL_CONTROLLER_BUTTON_MISC1] = "Share", + [SDL_CONTROLLER_BUTTON_PADDLE1] = "P1", + [SDL_CONTROLLER_BUTTON_PADDLE2] = "P2", + [SDL_CONTROLLER_BUTTON_PADDLE3] = "P3", + [SDL_CONTROLLER_BUTTON_PADDLE4] = "P4", + [SDL_CONTROLLER_BUTTON_TOUCHPAD] = "Touch", +#endif +}; + +static const char* const buttonNamesGeneric[SDL_CONTROLLER_BUTTON_MAX] = { + [SDL_CONTROLLER_BUTTON_A] = "A", + [SDL_CONTROLLER_BUTTON_B] = "B", + [SDL_CONTROLLER_BUTTON_X] = "X", + [SDL_CONTROLLER_BUTTON_Y] = "Y", + [SDL_CONTROLLER_BUTTON_BACK] = "Select", + [SDL_CONTROLLER_BUTTON_GUIDE] = "Guide", + [SDL_CONTROLLER_BUTTON_START] = "Start", + [SDL_CONTROLLER_BUTTON_LEFTSTICK] = "LS", + [SDL_CONTROLLER_BUTTON_RIGHTSTICK] = "RS", + [SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = "LB", + [SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = "RB", +#if SDL_VERSION_ATLEAST(2, 0, 14) + [SDL_CONTROLLER_BUTTON_MISC1] = "Misc", + [SDL_CONTROLLER_BUTTON_PADDLE1] = "P1", + [SDL_CONTROLLER_BUTTON_PADDLE2] = "P2", + [SDL_CONTROLLER_BUTTON_PADDLE3] = "P3", + [SDL_CONTROLLER_BUTTON_PADDLE4] = "P4", + [SDL_CONTROLLER_BUTTON_TOUCHPAD] = "Touch", +#endif +}; +#endif + +const char* mSDLButtonName(SDL_GameController* controller, SDL_GameControllerButton button) { + const char* const* buttonNames = buttonNamesXbox360; + +#if SDL_VERSION_ATLEAST(2, 0, 12) + switch (SDL_GameControllerGetType(controller)) { + case SDL_CONTROLLER_TYPE_XBOX360: + buttonNames = buttonNamesXbox360; + break; + case SDL_CONTROLLER_TYPE_XBOXONE: + buttonNames = buttonNamesXboxOne; + break; + case SDL_CONTROLLER_TYPE_PS3: + case SDL_CONTROLLER_TYPE_PS4: +#if SDL_VERSION_ATLEAST(2, 0, 14) + case SDL_CONTROLLER_TYPE_PS5: +#endif + buttonNames = buttonNamesPlayStation; + break; + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: + buttonNames = buttonNamesNintedo; + break; + default: + buttonNames = buttonNamesGeneric; + break; + } +#endif + + switch (button) { + case SDL_CONTROLLER_BUTTON_DPAD_UP: + return "D↑"; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + return "D↓"; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + return "D←"; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + return "D→"; + default: + return buttonNames[button]; + case SDL_CONTROLLER_BUTTON_INVALID: + case SDL_CONTROLLER_BUTTON_MAX: + break; + } + return NULL; +} + +static const char* const axisNamesXbox[SDL_CONTROLLER_AXIS_MAX] = { + [SDL_CONTROLLER_AXIS_TRIGGERLEFT] = "LT", + [SDL_CONTROLLER_AXIS_TRIGGERRIGHT] = "RT", +}; + +#if SDL_VERSION_ATLEAST(2, 0, 12) +static const char* const axisNamesPlayStation[SDL_CONTROLLER_AXIS_MAX] = { + [SDL_CONTROLLER_AXIS_TRIGGERLEFT] = "L3", + [SDL_CONTROLLER_AXIS_TRIGGERRIGHT] = "R3", +}; + +static const char* const axisNamesNintendo[SDL_CONTROLLER_AXIS_MAX] = { + [SDL_CONTROLLER_AXIS_TRIGGERLEFT] = "ZL", + [SDL_CONTROLLER_AXIS_TRIGGERRIGHT] = "ZR", +}; +#endif + +const char* mSDLAxisName(SDL_GameController* controller, SDL_GameControllerAxis axis) { + const char* const* axisNames = axisNamesXbox; + +#if SDL_VERSION_ATLEAST(2, 0, 12) + switch (SDL_GameControllerGetType(controller)) { + case SDL_CONTROLLER_TYPE_XBOX360: + case SDL_CONTROLLER_TYPE_XBOXONE: + default: + axisNames = axisNamesXbox; + break; + case SDL_CONTROLLER_TYPE_PS3: + case SDL_CONTROLLER_TYPE_PS4: +#if SDL_VERSION_ATLEAST(2, 0, 14) + case SDL_CONTROLLER_TYPE_PS5: +#endif + axisNames = axisNamesPlayStation; + break; + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: + axisNames = axisNamesNintendo; + break; + } +#endif + + switch (axis) { + case SDL_CONTROLLER_AXIS_LEFTX: + return "X"; + case SDL_CONTROLLER_AXIS_LEFTY: + return "Y"; + case SDL_CONTROLLER_AXIS_RIGHTX: + return "RX"; + case SDL_CONTROLLER_AXIS_RIGHTY: + return "RY"; + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + return axisNames[axis]; + case SDL_CONTROLLER_AXIS_INVALID: + case SDL_CONTROLLER_AXIS_MAX: + break; + } + return NULL; +} + #endif diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index 8bdbd7c0b04..877818a01a4 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -29,6 +29,7 @@ mLOG_DECLARE_CATEGORY(SDL_EVENTS); #define SDL_BINDING_KEY 0x53444C4BU #define SDL_BINDING_BUTTON 0x53444C42U +#define SDL_BINDING_CONTROLLER 0x53444C43U #define MAX_PLAYERS 4 @@ -75,7 +76,7 @@ struct mSDLPlayer { int level; float activeLevel; - struct CircleBuffer history; + struct mCircleBuffer history; } rumble; #else int newWidth; @@ -97,7 +98,7 @@ struct mSDLPlayer { int gyroY; int gyroZ; float gyroSensitivity; - struct CircleBuffer zHistory; + struct mCircleBuffer zHistory; int oldX; int oldY; float zDelta; @@ -125,6 +126,9 @@ void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, void mSDLSuspendScreensaver(struct mSDLEvents*); void mSDLResumeScreensaver(struct mSDLEvents*); void mSDLSetScreensaverSuspendable(struct mSDLEvents*, bool suspendable); + +const char* mSDLButtonName(SDL_GameController*, SDL_GameControllerButton); +const char* mSDLAxisName(SDL_GameController*, SDL_GameControllerAxis); #endif CXX_GUARD_END diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index ef3e12164b2..8307f981173 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -4,7 +4,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "feature/gui/gui-runner.h" -#include #include #include #include @@ -14,6 +13,7 @@ #include #include +#include #include #include #include @@ -21,15 +21,9 @@ #define AUTO_INPUT 0x4E585031 #define SAMPLES 0x200 -#define N_BUFFERS 4 +#define N_BUFFERS 6 #define ANALOG_DEADZONE 0x4000 -#if (SAMPLES * 4) < 0x1000 -#define BUFFER_SIZE 0x1000 -#else -#define BUFFER_SIZE (SAMPLES * 4) -#endif - TimeType __nx_time_type = TimeType_UserSystemClock; static EGLDisplay s_display; @@ -94,9 +88,9 @@ static struct mSwitchRumble { HidVibrationValue value; } rumble; static struct mRotationSource rotation = {0}; -static int audioBufferActive; -static AudioOutBuffer audoutBuffer[N_BUFFERS]; -static int enqueuedBuffers; +static AudioDriver audrenDriver; +static AudioDriverWaveBuf audrvBuffer[N_BUFFERS]; +static struct mStereoSample* audioBuffer[N_BUFFERS]; static bool frameLimiter = true; static unsigned framecount = 0; static unsigned framecap = 10; @@ -116,8 +110,6 @@ static float gyroZ = 0; static float tiltX = 0; static float tiltY = 0; -static struct mStereoSample audioBuffer[N_BUFFERS][BUFFER_SIZE / 4] __attribute__((__aligned__(0x1000))); - static enum ScreenMode { SM_PA, SM_AF, @@ -274,22 +266,6 @@ static void _updateRenderer(struct mGUIRunner* runner, bool gl) { } } -static int _audioWait(u64 timeout) { - AudioOutBuffer* releasedBuffers; - u32 nReleasedBuffers = 0; - Result rc; - if (timeout) { - rc = audoutWaitPlayFinish(&releasedBuffers, &nReleasedBuffers, timeout); - } else { - rc = audoutGetReleasedAudioOutBuffer(&releasedBuffers, &nReleasedBuffers); - } - if (R_FAILED(rc)) { - return 0; - } - enqueuedBuffers -= nReleasedBuffers; - return nReleasedBuffers; -} - static void _setup(struct mGUIRunner* runner) { _mapKey(&runner->core->inputMap, AUTO_INPUT, HidNpadButton_A, GBA_KEY_A); _mapKey(&runner->core->inputMap, AUTO_INPUT, HidNpadButton_B, GBA_KEY_B); @@ -325,15 +301,19 @@ static void _setup(struct mGUIRunner* runner) { } runner->core->setAudioBufferSize(runner->core, SAMPLES); + + u32 samplerate = runner->core->audioSampleRate(runner->core); + double ratio = mCoreCalculateFramerateRatio(runner->core, 60.0); + audrvVoiceInit(&audrenDriver, 0, 2, PcmFormat_Int16, samplerate / ratio); + audrvVoiceSetDestinationMix(&audrenDriver, 0, AUDREN_FINAL_MIX_ID); + audrvVoiceSetMixFactor(&audrenDriver, 0, 1.0f, 0, 0); + audrvVoiceSetMixFactor(&audrenDriver, 0, 0.0f, 0, 1); + audrvVoiceSetMixFactor(&audrenDriver, 0, 0.0f, 1, 0); + audrvVoiceSetMixFactor(&audrenDriver, 0, 1.0f, 1, 1); + audrvUpdate(&audrenDriver); } static void _gameLoaded(struct mGUIRunner* runner) { - u32 samplerate = audoutGetSampleRate(); - - double ratio = GBAAudioCalculateRatio(1, 60.0, 1); - blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), samplerate * ratio); - blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), samplerate * ratio); - mCoreConfigGetUIntValue(&runner->config, "fastForwardCap", &framecap); unsigned mode; @@ -382,12 +362,14 @@ static void _gameLoaded(struct mGUIRunner* runner) { } static void _gameUnloaded(struct mGUIRunner* runner) { + UNUSED(runner); HidVibrationValue values[4]; memcpy(&values[0], &vibrationStop, sizeof(rumble.value)); memcpy(&values[1], &vibrationStop, sizeof(rumble.value)); memcpy(&values[2], &vibrationStop, sizeof(rumble.value)); memcpy(&values[3], &vibrationStop, sizeof(rumble.value)); hidSendVibrationValues(vibrationDeviceHandles, values, 4); + audrvVoiceStop(&audrenDriver, 0); } static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded, bool blendTop) { @@ -407,6 +389,7 @@ static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, float aspectY = inheight / vheight; float max = 1.f; switch (screenMode) { + case SM_MAX: // This should never get hit, so just fall through to silence warning case SM_PA: if (aspectX > aspectY) { max = floor(1.f / aspectX); @@ -565,9 +548,7 @@ static void _incrementScreenMode(struct mGUIRunner* runner) { static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) { UNUSED(runner); if (!frameLimiter && limit) { - while (enqueuedBuffers > 2) { - _audioWait(100000000); - } + audrenWaitFrame(); } frameLimiter = limit; eglSwapInterval(s_surface, limit); @@ -591,29 +572,34 @@ static bool _running(struct mGUIRunner* runner) { return appletMainLoop(); } -static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { +static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buffer) { UNUSED(stream); - _audioWait(0); - while (enqueuedBuffers >= N_BUFFERS - 1) { + int i; + while (true) { + audrvUpdate(&audrenDriver); + for (i = 0; i < N_BUFFERS; ++i) { + if (audrvBuffer[i].state == AudioDriverWaveBufState_Free || audrvBuffer[i].state == AudioDriverWaveBufState_Done) { + break; + } + } + if (i < N_BUFFERS) { + break; + } if (!frameLimiter) { - blip_clear(left); - blip_clear(right); + mAudioBufferClear(buffer); return; } - _audioWait(10000000); + audrenWaitFrame(); } - if (enqueuedBuffers >= N_BUFFERS) { - blip_clear(left); - blip_clear(right); - return; + struct mStereoSample* samples = audioBuffer[i]; + mAudioBufferRead(buffer, (int16_t*) samples, SAMPLES); + armDCacheFlush(samples, SAMPLES * sizeof(struct mStereoSample)); + audrvVoiceAddWaveBuf(&audrenDriver, 0, &audrvBuffer[i]); + + if (!audrvVoiceIsPlaying(&audrenDriver, 0)) { + audrvVoiceStart(&audrenDriver, 0); } - struct mStereoSample* samples = audioBuffer[audioBufferActive]; - blip_read_samples(left, &samples[0].left, SAMPLES, true); - blip_read_samples(right, &samples[0].right, SAMPLES, true); - audoutAppendAudioOutBuffer(&audoutBuffer[audioBufferActive]); - ++audioBufferActive; - audioBufferActive %= N_BUFFERS; - ++enqueuedBuffers; + audrvUpdate(&audrenDriver); } void _setRumble(struct mRumble* rumble, int enable) { @@ -626,6 +612,7 @@ void _setRumble(struct mRumble* rumble, int enable) { } void _sampleRotation(struct mRotationSource* source) { + UNUSED(source); HidSixAxisSensorState sixaxis = {0}; u64 styles = padGetStyleSet(&pad); if (styles & HidNpadStyleTag_NpadHandheld) { @@ -644,7 +631,7 @@ void _sampleRotation(struct mRotationSource* source) { } tiltX = sixaxis.acceleration.x * 3e8f; tiltY = sixaxis.acceleration.y * -3e8f; - gyroZ = sixaxis.angular_velocity.z * -1.1e9f; + gyroZ = sixaxis.angular_velocity.z * -5.5e8f; } int32_t _readTiltX(struct mRotationSource* source) { @@ -865,9 +852,26 @@ int main(int argc, char* argv[]) { nxlinkStdio(); eglInit(); romfsInit(); - audoutInitialize(); psmInitialize(); + const AudioRendererConfig audren = { + .output_rate = AudioRendererOutputRate_48kHz, + .num_voices = 24, + .num_effects = 0, + .num_sinks = 1, + .num_mix_objs = 1, + .num_mix_buffers = 2, + }; + const u8 channels[] = { 0, 1 }; + audrenInitialize(&audren); + audrvCreate(&audrenDriver, &audren, 2); + struct mStereoSample* buffers = memalign(AUDREN_MEMPOOL_ALIGNMENT, SAMPLES * N_BUFFERS * sizeof(struct mStereoSample)); + int mempool = audrvMemPoolAdd(&audrenDriver, buffers, SAMPLES * N_BUFFERS * sizeof(struct mStereoSample)); + audrvMemPoolAttach(&audrenDriver, mempool); + audrvDeviceSinkAdd(&audrenDriver, AUDREN_DEFAULT_DEVICE_NAME, 2, channels); + audrvUpdate(&audrenDriver); + audrenStartAudioRenderer(); + font = GUIFontCreate(); vmode = appletGetOperationMode(); @@ -888,16 +892,13 @@ int main(int argc, char* argv[]) { stream.postAudioFrame = NULL; stream.postAudioBuffer = _postAudioBuffer; - memset(audioBuffer, 0, sizeof(audioBuffer)); - audioBufferActive = 0; - enqueuedBuffers = 0; size_t i; for (i = 0; i < N_BUFFERS; ++i) { - audoutBuffer[i].next = NULL; - audoutBuffer[i].buffer = audioBuffer[i]; - audoutBuffer[i].buffer_size = BUFFER_SIZE; - audoutBuffer[i].data_size = SAMPLES * 4; - audoutBuffer[i].data_offset = 0; + audrvBuffer[i].data_raw = buffers; + audrvBuffer[i].size = SAMPLES * N_BUFFERS * sizeof(struct mStereoSample); + audrvBuffer[i].start_sample_offset = SAMPLES * i; + audrvBuffer[i].end_sample_offset = SAMPLES * (i + 1); + audioBuffer[i] = &buffers[SAMPLES * i]; } bool illuminanceAvailable = false; @@ -1074,8 +1075,6 @@ int main(int argc, char* argv[]) { _mapKey(&runner.params.keyMap, AUTO_INPUT, HidNpadButton_Left, GUI_INPUT_LEFT); _mapKey(&runner.params.keyMap, AUTO_INPUT, HidNpadButton_Right, GUI_INPUT_RIGHT); - audoutStartAudioOut(); - if (argc > 0) { struct VFile* vf = VFileOpen("romfs:/fileassoc.cfg.in", O_RDONLY); if (vf) { @@ -1108,7 +1107,8 @@ int main(int argc, char* argv[]) { mGUIDeinit(&runner); - audoutStopAudioOut(); + audrvClose(&audrenDriver); + audrenExit(); GUIFontDestroy(font); glDeinit(); diff --git a/src/platform/test/fuzz-main.c b/src/platform/test/fuzz-main.c index eb42b449b2c..af3bcf0833e 100644 --- a/src/platform/test/fuzz-main.c +++ b/src/platform/test/fuzz-main.c @@ -3,7 +3,6 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include #include #include #include @@ -153,9 +152,6 @@ int main(int argc, char** argv) { savestate = 0; } - blip_set_rates(core->getAudioChannel(core, 0), core->frequency(core), 0x8000); - blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), 0x8000); - _fuzzRunloop(core, fuzzOpts.frames); if (hasDebugger) { @@ -188,8 +184,7 @@ static void _fuzzRunloop(struct mCore* core, int frames) { do { core->runFrame(core); --frames; - blip_clear(core->getAudioChannel(core, 0)); - blip_clear(core->getAudioChannel(core, 1)); + mAudioBufferClear(core->getAudioBuffer(core)); } while (frames > 0 && !_dispatchExiting); } diff --git a/src/platform/test/perf-main.c b/src/platform/test/perf-main.c index 6682052969c..38d50b30c19 100644 --- a/src/platform/test/perf-main.c +++ b/src/platform/test/perf-main.c @@ -3,7 +3,6 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include #include #include #include diff --git a/src/platform/test/rom-test-main.c b/src/platform/test/rom-test-main.c index 8c0d4fd3d02..8524a852f4b 100644 --- a/src/platform/test/rom-test-main.c +++ b/src/platform/test/rom-test-main.c @@ -139,7 +139,7 @@ int main(int argc, char * argv[]) { goto loadError; } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS struct mDebugger debugger; mDebuggerInit(&debugger); bool hasDebugger = mArgumentsApplyDebugger(&args, core, &debugger); @@ -165,7 +165,7 @@ int main(int argc, char * argv[]) { savestate->close(savestate); } -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (hasDebugger) { do { mDebuggerRun(&debugger); @@ -178,7 +178,7 @@ int main(int argc, char * argv[]) { core->unloadROM(core); -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS if (hasDebugger) { core->detachDebugger(core); mDebuggerDeinit(&debugger); diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index db3ed724296..f21c7e19b9d 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -14,13 +14,13 @@ #include -#include #include #include "feature/gui/gui-runner.h" #include #include #include #include +#include #include #include #include @@ -76,7 +76,7 @@ static enum VideoMode { static void _retraceCallback(u32 count); -static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right); +static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer*); static void _audioDMA(void); static void _setRumble(struct mRumble* rumble, int enable); static void _sampleRotation(struct mRotationSource* source); @@ -141,12 +141,14 @@ static void* framebuffer[2] = { 0, 0 }; static int whichFb = 0; static struct AudioBuffer { - struct mStereoSample samples[SAMPLES] __attribute__((__aligned__(32))); - volatile size_t size; -} audioBuffer[BUFFERS] = {0}; + int16_t samples[SAMPLES * 2] __attribute__((__aligned__(32))); + volatile bool full; +} audioBuffers[BUFFERS] = {0}; +static struct mAudioBuffer audioBuffer; static volatile int currentAudioBuffer = 0; static volatile int nextAudioBuffer = 0; static double audioSampleRate = 60.0 / 1.001; +static struct mAudioResampler resampler; static struct GUIFont* font; @@ -246,11 +248,6 @@ static void reconfigureScreen(struct mGUIRunner* runner) { if (runner) { runner->params.width = vmode->fbWidth * guiScale * wAdjust; runner->params.height = vmode->efbHeight * guiScale * hAdjust; - if (runner->core) { - double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1); - blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio); - blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio); - } } } @@ -269,7 +266,10 @@ int main(int argc, char* argv[]) { AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); AUDIO_RegisterDMACallback(_audioDMA); - memset(audioBuffer, 0, sizeof(audioBuffer)); + memset(audioBuffers, 0, sizeof(audioBuffers)); + mAudioBufferInit(&audioBuffer, SAMPLES * BUFFERS, 2); + mAudioResamplerInit(&resampler, mINTERPOLATOR_COSINE); + mAudioResamplerSetDestination(&resampler, &audioBuffer, 48000); #ifdef FIXED_ROM_BUFFER romBufferSize = GBA_SIZE_ROM0; romBuffer = SYS_GetArena2Lo(); @@ -663,6 +663,9 @@ int main(int argc, char* argv[]) { VIDEO_WaitVSync(); mGUIDeinit(&runner); + mAudioResamplerDeinit(&resampler); + mAudioBufferDeinit(&audioBuffer); + free(fifo); free(texmem); free(rescaleTexmem); @@ -678,41 +681,38 @@ int main(int argc, char* argv[]) { } static void _audioDMA(void) { - struct AudioBuffer* buffer = &audioBuffer[currentAudioBuffer]; - if (buffer->size != SAMPLES) { + struct AudioBuffer* buffer = &audioBuffers[currentAudioBuffer]; + if (!buffer->full) { + printf("Recv %i %i%s", currentAudioBuffer, buffer->full, buffer->full ? "" : "!"); return; } DCFlushRange(buffer->samples, SAMPLES * sizeof(struct mStereoSample)); AUDIO_InitDMA((u32) buffer->samples, SAMPLES * sizeof(struct mStereoSample)); - buffer->size = 0; + buffer->full = false; currentAudioBuffer = (currentAudioBuffer + 1) % BUFFERS; } -static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { +static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buf) { UNUSED(stream); - + UNUSED(buf); + mAudioResamplerProcess(&resampler); u32 level = 0; + bool gotAudio = false; _CPU_ISR_Disable(level); - struct AudioBuffer* buffer = &audioBuffer[nextAudioBuffer]; - int available = blip_samples_avail(left); - if (available + buffer->size > SAMPLES) { - available = SAMPLES - buffer->size; - } - if (available > 0) { - // These appear to be reversed for AUDIO_InitDMA - blip_read_samples(left, &buffer->samples[buffer->size].right, available, true); - blip_read_samples(right, &buffer->samples[buffer->size].left, available, true); - buffer->size += available; - } - if (buffer->size == SAMPLES) { - int next = (nextAudioBuffer + 1) % BUFFERS; - if ((currentAudioBuffer + BUFFERS - next) % BUFFERS != 1) { - nextAudioBuffer = next; - } - if (!AUDIO_GetDMAEnableFlag()) { - _audioDMA(); - AUDIO_StartDMA(); + while (mAudioBufferAvailable(&audioBuffer) >= SAMPLES) { + struct AudioBuffer* buffer = &audioBuffers[nextAudioBuffer]; + if (buffer->full) { + printf("Send %i %i%s", nextAudioBuffer, buffer->full, buffer->full ? "!!" : ""); + break; } + mAudioBufferRead(&audioBuffer, buffer->samples, SAMPLES); + buffer->full = true; + nextAudioBuffer = (nextAudioBuffer + 1) % BUFFERS; + gotAudio = true; + } + if (gotAudio && !AUDIO_GetDMAEnableFlag()) { + _audioDMA(); + AUDIO_StartDMA(); } _CPU_ISR_Restore(level); } @@ -1416,15 +1416,11 @@ void _setup(struct mGUIRunner* runner) { nextAudioBuffer = 0; currentAudioBuffer = 0; - int i; - for (i = 0; i < BUFFERS; ++i) { - audioBuffer[i].size = 0; - } + memset(audioBuffers, 0, sizeof(audioBuffers)); runner->core->setAudioBufferSize(runner->core, SAMPLES); - double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1); - blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio); - blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio); + double ratio = mCoreCalculateFramerateRatio(runner->core, audioSampleRate); + mAudioResamplerSetSource(&resampler, runner->core->getAudioBuffer(runner->core), runner->core->audioSampleRate(runner->core) / ratio, true); frameLimiter = true; } @@ -1433,6 +1429,7 @@ void _gameUnloaded(struct mGUIRunner* runner) { UNUSED(runner); AUDIO_StopDMA(); frameLimiter = true; + mAudioBufferClear(&audioBuffer); VIDEO_SetBlack(true); VIDEO_Flush(); VIDEO_WaitVSync(); @@ -1731,7 +1728,7 @@ void _sampleRotation(struct mRotationSource* source) { return; } gyroZ = exp.mp.rz - 0x1FA0; - gyroZ <<= 18; + gyroZ <<= 17; } int32_t _readTiltX(struct mRotationSource* source) { diff --git a/src/script/context.c b/src/script/context.c index 090daf66d2a..47a4460f6e2 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -481,7 +481,7 @@ bool mScriptInvoke(const struct mScriptValue* val, struct mScriptFrame* frame) { return false; } const struct mScriptTypeFunction* signature = &val->type->details.function; - if (!mScriptCoerceFrame(&signature->parameters, &frame->arguments)) { + if (!mScriptCoerceFrame(&signature->parameters, &frame->arguments, &frame->arguments)) { return false; } const struct mScriptFunction* fn = val->value.opaque; diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 32ed0d13ef4..f6bdabea2aa 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -40,12 +40,12 @@ static void _freeFrame(struct mScriptList* frame); static void _autofreeFrame(struct mScriptContext* context, struct mScriptList* frame); struct mScriptEngineContextLua; -static bool _luaPushFrame(struct mScriptEngineContextLua*, struct mScriptList*); -static bool _luaPopFrame(struct mScriptEngineContextLua*, struct mScriptList*); +static bool _luaPushFrame(struct mScriptEngineContextLua*, lua_State*, struct mScriptList*); +static bool _luaPopFrame(struct mScriptEngineContextLua*, lua_State*, struct mScriptList*); static bool _luaInvoke(struct mScriptEngineContextLua*, struct mScriptFrame*); -static struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool pop); -static bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue*); +static struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, lua_State*, bool pop); +static bool _luaWrap(struct mScriptEngineContextLua* luaContext, lua_State*, struct mScriptValue*); static void _luaDeref(struct mScriptValue*); @@ -516,14 +516,14 @@ bool _luaIsScript(struct mScriptEngineContext* ctx, const char* name, struct VFi struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext* ctx, const char* name) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; lua_getglobal(luaContext->lua, name); - return _luaCoerce(luaContext, true); + return _luaCoerce(luaContext, luaContext->lua, true); } bool _luaSetGlobal(struct mScriptEngineContext* ctx, const char* name, struct mScriptValue* value) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; if (!value) { lua_pushnil(luaContext->lua); - } else if (!_luaWrap(luaContext, value)) { + } else if (!_luaWrap(luaContext, luaContext->lua, value)) { return false; } lua_setglobal(luaContext->lua, name); @@ -540,7 +540,7 @@ struct mScriptValue* _luaRootScope(struct mScriptEngineContext* ctx) { struct mScriptValue* key; lua_pop(luaContext->lua, 1); - key = _luaCoerce(luaContext, false); + key = _luaCoerce(luaContext, luaContext->lua, false); mScriptValueWrap(key, mScriptListAppend(list->value.list)); mScriptValueRef(key); mScriptContextFillPool(luaContext->d.context, key); @@ -550,60 +550,60 @@ struct mScriptValue* _luaRootScope(struct mScriptEngineContext* ctx) { return list; } -struct mScriptValue* _luaCoerceFunction(struct mScriptEngineContextLua* luaContext) { +struct mScriptValue* _luaCoerceFunction(struct mScriptEngineContextLua* luaContext, lua_State* lua) { struct mScriptValue* value = mScriptValueAlloc(&mSTLuaFunc); struct mScriptFunction* fn = calloc(1, sizeof(*fn)); struct mScriptEngineContextLuaRef* ref = calloc(1, sizeof(*ref)); fn->call = _luaCall; fn->context = ref; ref->context = luaContext; - ref->ref = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); + ref->ref = luaL_ref(lua, LUA_REGISTRYINDEX); value->value.opaque = fn; return value; } -struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext, struct Table* markedObjects) { +struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext, lua_State* lua, struct Table* markedObjects) { struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); bool isList = true; - lua_pushnil(luaContext->lua); + lua_pushnil(lua); const void* tablePointer; - while (lua_next(luaContext->lua, -2) != 0) { + while (lua_next(lua, -2) != 0) { struct mScriptValue* value = NULL; - int type = lua_type(luaContext->lua, -1); + int type = lua_type(lua, -1); switch (type) { case LUA_TNUMBER: case LUA_TBOOLEAN: case LUA_TSTRING: case LUA_TFUNCTION: - value = _luaCoerce(luaContext, true); + value = _luaCoerce(luaContext, lua, true); break; case LUA_TTABLE: - tablePointer = lua_topointer(luaContext->lua, -1); + tablePointer = lua_topointer(lua, -1); // Ensure this table doesn't contain any cycles if (!HashTableLookupBinary(markedObjects, &tablePointer, sizeof(tablePointer))) { HashTableInsertBinary(markedObjects, &tablePointer, sizeof(tablePointer), (void*) tablePointer); - value = _luaCoerceTable(luaContext, markedObjects); + value = _luaCoerceTable(luaContext, lua, markedObjects); } default: break; } if (!value) { - lua_pop(luaContext->lua, type == LUA_TTABLE ? 2 : 3); + lua_pop(lua, type == LUA_TTABLE ? 2 : 3); mScriptValueDeref(table); return NULL; } struct mScriptValue* key = NULL; - type = lua_type(luaContext->lua, -1); + type = lua_type(lua, -1); switch (type) { case LUA_TBOOLEAN: case LUA_TSTRING: isList = false; // Fall through case LUA_TNUMBER: - key = _luaCoerce(luaContext, false); + key = _luaCoerce(luaContext, lua, false); break; default: // Limit keys to hashable types @@ -611,7 +611,7 @@ struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext, } if (!key) { - lua_pop(luaContext->lua, 2); + lua_pop(lua, 2); mScriptValueDeref(table); return false; } @@ -619,7 +619,7 @@ struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext, mScriptValueDeref(key); mScriptValueDeref(value); } - lua_pop(luaContext->lua, 1); + lua_pop(lua, 1); size_t len = mScriptTableSize(table); if (!isList || !len) { @@ -652,9 +652,9 @@ struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext, return list; } -struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool pop) { - if (lua_isnone(luaContext->lua, -1)) { - lua_pop(luaContext->lua, 1); +struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, lua_State* lua, bool pop) { + if (lua_isnone(lua, -1)) { + lua_pop(lua, 1); return NULL; } @@ -662,27 +662,27 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool const void* buffer; struct Table markedObjects; struct mScriptValue* value = NULL; - switch (lua_type(luaContext->lua, -1)) { + switch (lua_type(lua, -1)) { case LUA_TNIL: value = &mScriptValueNull; break; case LUA_TNUMBER: #if LUA_VERSION_NUM >= 503 - if (lua_isinteger(luaContext->lua, -1)) { + if (lua_isinteger(lua, -1)) { value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S64); - value->value.s64 = lua_tointeger(luaContext->lua, -1); + value->value.s64 = lua_tointeger(lua, -1); break; } #endif value = mScriptValueAlloc(mSCRIPT_TYPE_MS_F64); - value->value.f64 = lua_tonumber(luaContext->lua, -1); + value->value.f64 = lua_tonumber(lua, -1); break; case LUA_TBOOLEAN: value = mScriptValueAlloc(mSCRIPT_TYPE_MS_BOOL); - value->value.u32 = lua_toboolean(luaContext->lua, -1); + value->value.u32 = lua_toboolean(lua, -1); break; case LUA_TSTRING: - buffer = lua_tolstring(luaContext->lua, -1, &size); + buffer = lua_tolstring(lua, -1, &size); value = mScriptStringCreateFromBytes(buffer, size); break; case LUA_TFUNCTION: @@ -690,35 +690,35 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool if (!pop) { break; } - return _luaCoerceFunction(luaContext); + return _luaCoerceFunction(luaContext, lua); case LUA_TTABLE: // This function pops the value internally if (!pop) { break; } HashTableInit(&markedObjects, 0, NULL); - value = _luaCoerceTable(luaContext, &markedObjects); + value = _luaCoerceTable(luaContext, lua, &markedObjects); HashTableDeinit(&markedObjects); return value; case LUA_TUSERDATA: - if (!lua_getmetatable(luaContext->lua, -1)) { + if (!lua_getmetatable(lua, -1)) { break; } - luaL_getmetatable(luaContext->lua, "mSTStruct"); - if (!lua_rawequal(luaContext->lua, -1, -2)) { - lua_pop(luaContext->lua, 1); - luaL_getmetatable(luaContext->lua, "mSTList"); - if (!lua_rawequal(luaContext->lua, -1, -2)) { - lua_pop(luaContext->lua, 1); - luaL_getmetatable(luaContext->lua, "mSTTable"); - if (!lua_rawequal(luaContext->lua, -1, -2)) { - lua_pop(luaContext->lua, 2); + luaL_getmetatable(lua, "mSTStruct"); + if (!lua_rawequal(lua, -1, -2)) { + lua_pop(lua, 1); + luaL_getmetatable(lua, "mSTList"); + if (!lua_rawequal(lua, -1, -2)) { + lua_pop(lua, 1); + luaL_getmetatable(lua, "mSTTable"); + if (!lua_rawequal(lua, -1, -2)) { + lua_pop(lua, 2); break; } } } - lua_pop(luaContext->lua, 2); - value = lua_touserdata(luaContext->lua, -1); + lua_pop(lua, 2); + value = lua_touserdata(lua, -1); value = mScriptContextAccessWeakref(luaContext->d.context, value); if (value->type->base == mSCRIPT_TYPE_WRAPPER) { value = mScriptValueUnwrap(value); @@ -729,14 +729,14 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool break; } if (pop) { - lua_pop(luaContext->lua, 1); + lua_pop(lua, 1); } return value; } -bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* value) { +bool _luaWrap(struct mScriptEngineContextLua* luaContext, lua_State* lua, struct mScriptValue* value) { if (!value) { - lua_pushnil(luaContext->lua); + lua_pushnil(lua); return true; } uint32_t weakref; @@ -744,7 +744,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v if (value->type->base == mSCRIPT_TYPE_WRAPPER) { value = mScriptValueUnwrap(value); if (!value) { - lua_pushnil(luaContext->lua); + lua_pushnil(lua); return true; } mScriptContextFillPool(luaContext->d.context, value); @@ -778,7 +778,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v weakref = value->value.u32; value = mScriptContextAccessWeakref(luaContext->d.context, value); if (!value) { - lua_pushnil(luaContext->lua); + lua_pushnil(lua); return true; } needsWeakref = true; @@ -787,95 +787,95 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v struct mScriptValue* newValue; switch (value->type->base) { case mSCRIPT_TYPE_VOID: - lua_pushnil(luaContext->lua); + lua_pushnil(lua); break; case mSCRIPT_TYPE_SINT: if (value->type->size <= 4) { - lua_pushinteger(luaContext->lua, value->value.s32); + lua_pushinteger(lua, value->value.s32); } else if (value->type->size == 8) { - lua_pushinteger(luaContext->lua, value->value.s64); + lua_pushinteger(lua, value->value.s64); } else { ok = false; } break; case mSCRIPT_TYPE_UINT: if (value->type == mSCRIPT_TYPE_MS_BOOL) { - lua_pushboolean(luaContext->lua, !!value->value.u32); + lua_pushboolean(lua, !!value->value.u32); } else if (value->type->size <= 4) { - lua_pushinteger(luaContext->lua, value->value.u32); + lua_pushinteger(lua, value->value.u32); } else if (value->type->size == 8) { - lua_pushinteger(luaContext->lua, value->value.u64); + lua_pushinteger(lua, value->value.u64); } else { ok = false; } break; case mSCRIPT_TYPE_FLOAT: if (value->type->size == 4) { - lua_pushnumber(luaContext->lua, value->value.f32); + lua_pushnumber(lua, value->value.f32); } else if (value->type->size == 8) { - lua_pushnumber(luaContext->lua, value->value.f64); + lua_pushnumber(lua, value->value.f64); } else { ok = false; } break; case mSCRIPT_TYPE_STRING: if (!value->value.string) { - lua_pushnil(luaContext->lua); + lua_pushnil(lua); break; } if (value->type == mSCRIPT_TYPE_MS_STR) { - lua_pushlstring(luaContext->lua, value->value.string->buffer, value->value.string->size); + lua_pushlstring(lua, value->value.string->buffer, value->value.string->size); break; } if (value->type == mSCRIPT_TYPE_MS_CHARP) { - lua_pushstring(luaContext->lua, value->value.copaque); + lua_pushstring(lua, value->value.copaque); break; } ok = false; break; case mSCRIPT_TYPE_LIST: - newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); + newValue = lua_newuserdata(lua, sizeof(*newValue)); if (needsWeakref) { *newValue = mSCRIPT_MAKE(WEAKREF, weakref); } else { mScriptValueRef(value); mScriptValueWrap(value, newValue); } - lua_getfield(luaContext->lua, LUA_REGISTRYINDEX, "mSTList"); - lua_setmetatable(luaContext->lua, -2); + lua_getfield(lua, LUA_REGISTRYINDEX, "mSTList"); + lua_setmetatable(lua, -2); break; case mSCRIPT_TYPE_TABLE: - newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); + newValue = lua_newuserdata(lua, sizeof(*newValue)); if (needsWeakref) { *newValue = mSCRIPT_MAKE(WEAKREF, weakref); } else { mScriptValueRef(value); mScriptValueWrap(value, newValue); } - lua_getfield(luaContext->lua, LUA_REGISTRYINDEX, "mSTTable"); - lua_setmetatable(luaContext->lua, -2); + lua_getfield(lua, LUA_REGISTRYINDEX, "mSTTable"); + lua_setmetatable(lua, -2); break; case mSCRIPT_TYPE_FUNCTION: - newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); + newValue = lua_newuserdata(lua, sizeof(*newValue)); newValue->type = value->type; newValue->refs = mSCRIPT_VALUE_UNREF; newValue->type->alloc(newValue); - lua_pushcclosure(luaContext->lua, _luaThunk, 1); + lua_pushcclosure(lua, _luaThunk, 1); break; case mSCRIPT_TYPE_OBJECT: if (!value->value.opaque) { - lua_pushnil(luaContext->lua); + lua_pushnil(lua); break; } - newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); + newValue = lua_newuserdata(lua, sizeof(*newValue)); if (needsWeakref) { *newValue = mSCRIPT_MAKE(WEAKREF, weakref); } else { mScriptValueRef(value); mScriptValueWrap(value, newValue); } - lua_getfield(luaContext->lua, LUA_REGISTRYINDEX, "mSTStruct"); - lua_setmetatable(luaContext->lua, -2); + lua_getfield(lua, LUA_REGISTRYINDEX, "mSTStruct"); + lua_setmetatable(lua, -2); break; default: ok = false; @@ -1032,31 +1032,31 @@ const char* _luaGetError(struct mScriptEngineContext* context) { return luaContext->lastError; } -bool _luaPushFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList* frame) { +bool _luaPushFrame(struct mScriptEngineContextLua* luaContext, lua_State* lua, struct mScriptList* frame) { bool ok = true; if (frame) { size_t i; for (i = 0; i < mScriptListSize(frame); ++i) { struct mScriptValue* value = mScriptListGetPointer(frame, i); - if (!_luaWrap(luaContext, value)) { + if (!_luaWrap(luaContext, lua, value)) { ok = false; break; } } } if (!ok) { - lua_pop(luaContext->lua, lua_gettop(luaContext->lua)); + lua_pop(lua, lua_gettop(lua)); } return ok; } -bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList* frame) { - int count = lua_gettop(luaContext->lua); +bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, lua_State* lua, struct mScriptList* frame) { + int count = lua_gettop(lua); bool ok = true; if (frame) { int i; for (i = 0; i < count; ++i) { - struct mScriptValue* value = _luaCoerce(luaContext, true); + struct mScriptValue* value = _luaCoerce(luaContext, lua, true); if (!value) { ok = false; break; @@ -1068,7 +1068,7 @@ bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList } } if (count > i) { - lua_pop(luaContext->lua, count - i); + lua_pop(lua, count - i); } if (ok) { @@ -1127,7 +1127,7 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* return false; } - if (frame && !_luaPushFrame(luaContext, &frame->arguments)) { + if (frame && !_luaPushFrame(luaContext, luaContext->lua, &frame->arguments)) { mScriptContextDeactivate(luaContext->d.context); return false; } @@ -1151,7 +1151,7 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* return false; } - if (frame && !_luaPopFrame(luaContext, &frame->returnValues)) { + if (frame && !_luaPopFrame(luaContext, luaContext->lua, &frame->returnValues)) { mScriptContextDrainPool(luaContext->d.context); return false; } @@ -1184,10 +1184,6 @@ static struct mScriptEngineContextLua* _luaGetContext(lua_State* lua) { struct mScriptEngineContextLua* luaContext = lua_touserdata(lua, -1); lua_pop(lua, 1); - if (luaContext->lua != lua) { - lua_pushliteral(lua, "Function called from invalid context"); - lua_error(lua); - } return luaContext; } @@ -1195,7 +1191,7 @@ int _luaThunk(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); struct mScriptFrame frame; mScriptFrameInit(&frame); - if (!_luaPopFrame(luaContext, &frame.arguments)) { + if (!_luaPopFrame(luaContext, lua, &frame.arguments)) { _freeFrame(&frame.arguments); mScriptContextDrainPool(luaContext->d.context); mScriptFrameDeinit(&frame); @@ -1212,7 +1208,7 @@ int _luaThunk(lua_State* lua) { return lua_error(lua); } - bool ok = _luaPushFrame(luaContext, &frame.returnValues); + bool ok = _luaPushFrame(luaContext, lua, &frame.returnValues); mScriptContextDrainPool(luaContext->d.context); mScriptFrameDeinit(&frame); if (!ok) { @@ -1220,7 +1216,7 @@ int _luaThunk(lua_State* lua) { return lua_error(lua); } - return lua_gettop(luaContext->lua); + return lua_gettop(lua); } int _luaGetObject(lua_State* lua) { @@ -1251,7 +1247,7 @@ int _luaGetObject(lua_State* lua) { return lua_error(lua); } - if (!_luaWrap(luaContext, &val)) { + if (!_luaWrap(luaContext, lua, &val)) { luaL_traceback(lua, lua, "Error translating value from runtime", 1); return lua_error(lua); } @@ -1263,7 +1259,7 @@ int _luaSetObject(lua_State* lua) { char key[MAX_KEY_SIZE]; const char* keyPtr = lua_tostring(lua, -2); struct mScriptValue* obj = lua_touserdata(lua, -3); - struct mScriptValue* val = _luaCoerce(luaContext, true); + struct mScriptValue* val = _luaCoerce(luaContext, lua, true); if (!keyPtr) { lua_pop(lua, 2); @@ -1312,12 +1308,12 @@ static int _luaGcObject(lua_State* lua) { int _luaGetTable(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); char key[MAX_KEY_SIZE]; - int type = lua_type(luaContext->lua, -1); + int type = lua_type(lua, -1); const char* keyPtr = NULL; int64_t intKey; switch (type) { case LUA_TNUMBER: - intKey = lua_tointeger(luaContext->lua, -1); + intKey = lua_tointeger(lua, -1); break; case LUA_TSTRING: keyPtr = lua_tostring(lua, -1); @@ -1355,7 +1351,7 @@ int _luaGetTable(lua_State* lua) { return 0; } - if (!_luaWrap(luaContext, val)) { + if (!_luaWrap(luaContext, lua, val)) { luaL_traceback(lua, lua, "Error translating value from runtime", 1); return lua_error(lua); } @@ -1378,7 +1374,7 @@ int _luaLenTable(lua_State* lua) { struct mScriptValue val = mSCRIPT_MAKE_U64(mScriptTableSize(obj)); - if (!_luaWrap(luaContext, &val)) { + if (!_luaWrap(luaContext, lua, &val)) { luaL_traceback(lua, lua, "Error translating value from runtime", 1); return lua_error(lua); } @@ -1388,12 +1384,12 @@ int _luaLenTable(lua_State* lua) { static int _luaNextTable(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); char key[MAX_KEY_SIZE]; - int type = lua_type(luaContext->lua, -1); + int type = lua_type(lua, -1); const char* keyPtr = NULL; struct mScriptValue keyVal = {0}; switch (type) { case LUA_TNUMBER: - keyVal = mSCRIPT_MAKE_S64(lua_tointeger(luaContext->lua, -1)); + keyVal = mSCRIPT_MAKE_S64(lua_tointeger(lua, -1)); break; case LUA_TSTRING: keyPtr = lua_tostring(lua, -1); @@ -1429,12 +1425,12 @@ static int _luaNextTable(lua_State* lua) { } } - if (!_luaWrap(luaContext, mScriptTableIteratorGetKey(table, &iter))) { + if (!_luaWrap(luaContext, lua, mScriptTableIteratorGetKey(table, &iter))) { luaL_traceback(lua, lua, "Iteration error", 1); return lua_error(lua); } - if (!_luaWrap(luaContext, mScriptTableIteratorGetValue(table, &iter))) { + if (!_luaWrap(luaContext, lua, mScriptTableIteratorGetValue(table, &iter))) { luaL_traceback(lua, lua, "Iteration error", 1); return lua_error(lua); } @@ -1453,9 +1449,9 @@ int _luaGetList(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); ssize_t index; #if LUA_VERSION_NUM >= 503 - index = lua_tointeger(luaContext->lua, -1); + index = lua_tointeger(lua, -1); #else - index = lua_tonumber(luaContext->lua, -1); + index = lua_tonumber(lua, -1); #endif struct mScriptValue* obj = lua_touserdata(lua, -2); lua_pop(lua, 2); @@ -1481,7 +1477,7 @@ int _luaGetList(lua_State* lua) { --index; struct mScriptValue* val = mScriptListGetPointer(list, index); - if (!_luaWrap(luaContext, val)) { + if (!_luaWrap(luaContext, lua, val)) { luaL_traceback(lua, lua, "Error translating value from runtime", 1); return lua_error(lua); } @@ -1509,21 +1505,21 @@ static int _luaLenList(lua_State* lua) { static int _luaRequireShim(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); - int oldtop = lua_gettop(luaContext->lua); + int oldtop = lua_gettop(lua); const char* path = lua_tostring(lua, lua_upvalueindex(1)); - lua_getglobal(luaContext->lua, "package"); + lua_getglobal(lua, "package"); - lua_pushliteral(luaContext->lua, "path"); - lua_pushstring(luaContext->lua, path); - lua_pushliteral(luaContext->lua, "/?.lua;"); - lua_pushstring(luaContext->lua, path); - lua_pushliteral(luaContext->lua, "/?/init.lua;"); - lua_pushliteral(luaContext->lua, "path"); - lua_gettable(luaContext->lua, -7); - char* oldpath = strdup(lua_tostring(luaContext->lua, -1)); - lua_concat(luaContext->lua, 5); - lua_settable(luaContext->lua, -3); + lua_pushliteral(lua, "path"); + lua_pushstring(lua, path); + lua_pushliteral(lua, "/?.lua;"); + lua_pushstring(lua, path); + lua_pushliteral(lua, "/?/init.lua;"); + lua_pushliteral(lua, "path"); + lua_gettable(lua, -7); + char* oldpath = strdup(lua_tostring(lua, -1)); + lua_concat(lua, 5); + lua_settable(lua, -3); #ifdef _WIN32 #define DLL "dll" @@ -1532,42 +1528,42 @@ static int _luaRequireShim(lua_State* lua) { #else #define DLL "so" #endif - lua_pushliteral(luaContext->lua, "cpath"); - lua_pushstring(luaContext->lua, path); - lua_pushliteral(luaContext->lua, "/?." DLL ";"); - lua_pushstring(luaContext->lua, path); - lua_pushliteral(luaContext->lua, "/?/init." DLL ";"); - lua_pushliteral(luaContext->lua, "cpath"); - lua_gettable(luaContext->lua, -7); - char* oldcpath = strdup(lua_tostring(luaContext->lua, -1)); - lua_concat(luaContext->lua, 5); - lua_settable(luaContext->lua, -3); + lua_pushliteral(lua, "cpath"); + lua_pushstring(lua, path); + lua_pushliteral(lua, "/?." DLL ";"); + lua_pushstring(lua, path); + lua_pushliteral(lua, "/?/init." DLL ";"); + lua_pushliteral(lua, "cpath"); + lua_gettable(lua, -7); + char* oldcpath = strdup(lua_tostring(lua, -1)); + lua_concat(lua, 5); + lua_settable(lua, -3); - lua_pop(luaContext->lua, 1); + lua_pop(lua, 1); - lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->require); - lua_insert(luaContext->lua, -2); - int ret = lua_pcall(luaContext->lua, 1, LUA_MULTRET, 0); + lua_rawgeti(lua, LUA_REGISTRYINDEX, luaContext->require); + lua_insert(lua, -2); + int ret = lua_pcall(lua, 1, LUA_MULTRET, 0); - lua_getglobal(luaContext->lua, "package"); + lua_getglobal(lua, "package"); - lua_pushliteral(luaContext->lua, "path"); - lua_pushstring(luaContext->lua, oldpath); - lua_settable(luaContext->lua, -3); + lua_pushliteral(lua, "path"); + lua_pushstring(lua, oldpath); + lua_settable(lua, -3); - lua_pushliteral(luaContext->lua, "cpath"); - lua_pushstring(luaContext->lua, oldcpath); - lua_settable(luaContext->lua, -3); + lua_pushliteral(lua, "cpath"); + lua_pushstring(lua, oldcpath); + lua_settable(lua, -3); - lua_pop(luaContext->lua, 1); + lua_pop(lua, 1); free(oldpath); free(oldcpath); if (ret) { - return lua_error(luaContext->lua); + return lua_error(lua); } - int newtop = lua_gettop(luaContext->lua); + int newtop = lua_gettop(lua); return newtop - oldtop + 1; } diff --git a/src/script/image.c b/src/script/image.c index f056c7d4938..2bd5a8736e8 100644 --- a/src/script/image.c +++ b/src/script/image.c @@ -131,6 +131,7 @@ static struct mScriptValue* _mScriptPainterGet(struct mScriptPainter* painter, c void _mScriptPainterDeinit(struct mScriptPainter* painter) { mScriptValueDeref(painter->image); + free(painter); } mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, setBlend, _mPainterSetBlend, 1, BOOL, enable); @@ -141,6 +142,7 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, setStrokeColor, _mPainterSetStrokeC mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, drawRectangle, mPainterDrawRectangle, 4, S32, x, S32, y, S32, width, S32, height); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, drawLine, mPainterDrawLine, 4, S32, x1, S32, y1, S32, x2, S32, y2); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, drawCircle, mPainterDrawCircle, 3, S32, x, S32, y, S32, diameter); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, drawMask, mPainterDrawMask, 3, CS(mImage), mask, S32, x, S32, y); mSCRIPT_DEFINE_STRUCT(mPainter) mSCRIPT_DEFINE_CLASS_DOCSTRING( @@ -162,6 +164,13 @@ mSCRIPT_DEFINE_STRUCT(mPainter) mSCRIPT_DEFINE_STRUCT_METHOD(mPainter, drawLine) mSCRIPT_DEFINE_DOCSTRING("Draw a circle with the specified diameter with the given origin at the top-left corner of the bounding box") mSCRIPT_DEFINE_STRUCT_METHOD(mPainter, drawCircle) + mSCRIPT_DEFINE_DOCSTRING( + "Draw a mask image with each color channel multiplied by the current fill color. This can " + "be useful for displaying graphics with dynamic colors. By making a grayscale template " + "image on a transparent background in advance, a script can set the fill color to a desired " + "target color and use this function to draw it into a destination image." + ) + mSCRIPT_DEFINE_STRUCT_METHOD(mPainter, drawMask) mSCRIPT_DEFINE_END; mSCRIPT_DECLARE_STRUCT_METHOD(mScriptPainter, W(mPainter), _get, _mScriptPainterGet, 1, CHARP, name); diff --git a/src/script/input.c b/src/script/input.c index c103fa82cd8..1cb9f488b81 100644 --- a/src/script/input.c +++ b/src/script/input.c @@ -35,20 +35,28 @@ struct mScriptInputContext { }; static void _mScriptInputDeinit(struct mScriptInputContext*); -static bool _mScriptInputIsKeyActive(const struct mScriptInputContext*, struct mScriptValue*); +static bool _mScriptInputIsKeyActiveStr(const struct mScriptInputContext*, const char*); +static bool _mScriptInputIsKeyActiveNum(const struct mScriptInputContext*, uint32_t); static struct mScriptValue* _mScriptInputActiveKeys(const struct mScriptInputContext*); mSCRIPT_DECLARE_STRUCT(mScriptInputContext); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptInputContext, _deinit, _mScriptInputDeinit, 0); -mSCRIPT_DECLARE_STRUCT_C_METHOD(mScriptInputContext, BOOL, isKeyActive, _mScriptInputIsKeyActive, 1, WRAPPER, key); +mSCRIPT_DECLARE_STRUCT_C_METHOD(mScriptInputContext, BOOL, isKeyActiveStr, _mScriptInputIsKeyActiveStr, 1, CHARP, key); +mSCRIPT_DECLARE_STRUCT_C_METHOD(mScriptInputContext, BOOL, isKeyActiveNum, _mScriptInputIsKeyActiveNum, 1, U32, key); +mSCRIPT_DECLARE_STRUCT_OVERLOADED_C_METHOD(mScriptInputContext, BOOL, isKeyActive); mSCRIPT_DECLARE_STRUCT_C_METHOD(mScriptInputContext, WLIST, activeKeys, _mScriptInputActiveKeys, 0); +mSCRIPT_DEFINE_STRUCT_METHOD_OVERLOADS(mScriptInputContext, isKeyActive) + mSCRIPT_DEFINE_STRUCT_METHOD_OVERLOAD(mScriptInputContext, isKeyActiveStr) + mSCRIPT_DEFINE_STRUCT_METHOD_OVERLOAD(mScriptInputContext, isKeyActiveNum) +mSCRIPT_DEFINE_OVERLOADS_END; + mSCRIPT_DEFINE_STRUCT(mScriptInputContext) mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptInputContext) mSCRIPT_DEFINE_DOCSTRING("Sequence number of the next event to be emitted") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptInputContext, U64, seq) mSCRIPT_DEFINE_DOCSTRING("Check if a given keyboard key is currently held. The input can be either the printable character for a key, the numerical Unicode codepoint, or a special value from C.KEY") - mSCRIPT_DEFINE_STRUCT_METHOD(mScriptInputContext, isKeyActive) + mSCRIPT_DEFINE_STRUCT_OVERLOADED_METHOD(mScriptInputContext, isKeyActive) mSCRIPT_DEFINE_DOCSTRING("Get a list of the currently active keys. The values are Unicode codepoints or special key values from C.KEY, not strings, so make sure to convert as needed") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptInputContext, activeKeys) mSCRIPT_DEFINE_DOCSTRING("The currently active gamepad, if any") @@ -319,33 +327,18 @@ void _mScriptInputDeinit(struct mScriptInputContext* context) { TableDeinit(&context->activeKeys); } -bool _mScriptInputIsKeyActive(const struct mScriptInputContext* context, struct mScriptValue* value) { +bool _mScriptInputIsKeyActiveStr(const struct mScriptInputContext* context, const char* value) { uint32_t key; - struct mScriptValue intValue; - size_t length; - const char* strbuf; - - switch (value->type->base) { - case mSCRIPT_TYPE_SINT: - case mSCRIPT_TYPE_UINT: - case mSCRIPT_TYPE_FLOAT: - if (!mScriptCast(mSCRIPT_TYPE_MS_U32, value, &intValue)) { - return false; - } - key = intValue.value.u32; - break; - case mSCRIPT_TYPE_STRING: - if (value->value.string->length > 1) { - return false; - } - strbuf = value->value.string->buffer; - length = value->value.string->size; - key = utf8Char(&strbuf, &length); - break; - default: + size_t length = strlen(value); + key = utf8Char(&value, &length); + if (length > 0) { return false; } + void* down = TableLookup(&context->activeKeys, key); + return down != NULL; +} +bool _mScriptInputIsKeyActiveNum(const struct mScriptInputContext* context, uint32_t key) { void* down = TableLookup(&context->activeKeys, key); return down != NULL; } diff --git a/src/script/stdlib.c b/src/script/stdlib.c index 33ec0b751e8..6e81cfd8634 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -160,7 +160,7 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mSCRIPT_KV_SENTINEL }); #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS mScriptContextExportConstants(context, "WATCHPOINT_TYPE", (struct mScriptKVPair[]) { mSCRIPT_CONSTANT_PAIR(WATCHPOINT, WRITE), mSCRIPT_CONSTANT_PAIR(WATCHPOINT, READ), diff --git a/src/script/test/classes.c b/src/script/test/classes.c index f7c5eb12871..e0ce39d47a5 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -57,6 +57,11 @@ struct TestH { int32_t j; }; +struct TestI { + uint32_t num; + const char* str; +}; + static int32_t testAi0(struct TestA* a) { return a->i; } @@ -114,6 +119,14 @@ static void testSetC(struct TestG* g, const char* name, const char* val) { g->c = val; } +static void callNum(struct TestI* i, uint32_t num) { + i->num = num; +} + +static void callStr(struct TestI* i, const char* str) { + i->str = str; +} + #define MEMBER_A_DOCSTRING "Member a" mSCRIPT_DECLARE_STRUCT(TestA); @@ -201,12 +214,25 @@ mSCRIPT_DEFINE_STRUCT(TestG) mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TestG, setC) mSCRIPT_DEFINE_END; - mSCRIPT_DEFINE_STRUCT(TestH) mSCRIPT_DEFINE_STRUCT_MEMBER(TestH, S32, i) mSCRIPT_DEFINE_STRUCT_CONST_MEMBER(TestH, S32, j) mSCRIPT_DEFINE_END; +mSCRIPT_DECLARE_STRUCT(TestI); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestI, callStr, callStr, 1, CHARP, value); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestI, callNum, callNum, 1, U32, value); +mSCRIPT_DECLARE_STRUCT_OVERLOADED_VOID_METHOD(TestI, call); + +mSCRIPT_DEFINE_STRUCT_METHOD_OVERLOADS(TestI, call) + mSCRIPT_DEFINE_STRUCT_METHOD_OVERLOAD(TestI, callStr) + mSCRIPT_DEFINE_STRUCT_METHOD_OVERLOAD(TestI, callNum) +mSCRIPT_DEFINE_OVERLOADS_END; + +mSCRIPT_DEFINE_STRUCT(TestI) + mSCRIPT_DEFINE_STRUCT_OVERLOADED_METHOD(TestI, call) +mSCRIPT_DEFINE_END; + M_TEST_DEFINE(testALayout) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; assert_false(cls->init); @@ -1184,6 +1210,44 @@ M_TEST_DEFINE(testHSet) { assert_false(cls->init); } +M_TEST_DEFINE(testOverloadsBasic) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestI)->details.cls; + assert_false(cls->init); + mScriptClassInit(cls); + assert_true(cls->init); + + struct TestI s = { + .num = 0, + .str = NULL, + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestI, &s); + struct mScriptValue fn; + struct mScriptFrame frame; + + assert_true(mScriptObjectGet(&sval, "call", &fn)); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestI), &s); + mSCRIPT_PUSH(&frame.arguments, U32, 1); + assert_true(mScriptInvoke(&fn, &frame)); + mScriptFrameDeinit(&frame); + assert_int_equal(s.num, 1); + assert_null(s.str); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestI), &s); + mSCRIPT_PUSH(&frame.arguments, CHARP, "called"); + assert_true(mScriptInvoke(&fn, &frame)); + mScriptFrameDeinit(&frame); + assert_int_equal(s.num, 1); + assert_string_equal(s.str, "called"); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testALayout), cmocka_unit_test(testASignatures), @@ -1201,4 +1265,5 @@ M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testFDeinit), cmocka_unit_test(testGSet), cmocka_unit_test(testHSet), + cmocka_unit_test(testOverloadsBasic), ) diff --git a/src/script/test/input.c b/src/script/test/input.c index 4c689a6efa5..641e1c6ab37 100644 --- a/src/script/test/input.c +++ b/src/script/test/input.c @@ -172,6 +172,43 @@ M_TEST_DEFINE(clearKeys) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(numericKeys) { + SETUP_LUA; + + TEST_PROGRAM("assert(not input:isKeyActive(C.KEY.F1))"); + + TEST_PROGRAM( + "activeKey = false\n" + "state = nil\n" + "function cb(ev)\n" + " assert(ev.type == C.EV_TYPE.KEY)\n" + " activeKey = ev.key\n" + " state = ev.state\n" + "end\n" + "id = callbacks:add('key', cb)\n" + "assert(id)\n" + "assert(not activeKey)\n" + ); + + struct mScriptKeyEvent keyEvent = { + .d = { .type = mSCRIPT_EV_TYPE_KEY }, + .state = mSCRIPT_INPUT_STATE_DOWN, + .key = mSCRIPT_KEY_F1 + }; + mScriptContextFireEvent(&context, &keyEvent.d); + + TEST_PROGRAM("assert(input:isKeyActive(C.KEY.F1))"); + TEST_PROGRAM("assert(activeKey == C.KEY.F1)"); + TEST_PROGRAM("assert(state == C.INPUT_STATE.DOWN)"); + + keyEvent.state = mSCRIPT_INPUT_STATE_UP; + mScriptContextFireEvent(&context, &keyEvent.d); + + TEST_PROGRAM("assert(not input:isKeyActive(C.KEY.F1))"); + TEST_PROGRAM("assert(state == C.INPUT_STATE.UP)"); + + mScriptContextDeinit(&context); +} M_TEST_DEFINE(gamepadExport) { SETUP_LUA; @@ -219,5 +256,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptInput, cmocka_unit_test(fireKey), cmocka_unit_test(activeKeys), cmocka_unit_test(clearKeys), + cmocka_unit_test(numericKeys), cmocka_unit_test(gamepadExport), ) diff --git a/src/script/test/types.c b/src/script/test/types.c index 5c66b3f7693..67b6fba9a54 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -372,25 +372,25 @@ M_TEST_DEFINE(wrongConst) { mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, S(Test), &a); signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); - assert_true(mScriptCoerceFrame(&signature, &frame.arguments)); + assert_true(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test); - assert_true(mScriptCoerceFrame(&signature, &frame.arguments)); + assert_true(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, S(Test), &a); signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test); - assert_true(mScriptCoerceFrame(&signature, &frame.arguments)); + assert_true(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); - assert_false(mScriptCoerceFrame(&signature, &frame.arguments)); + assert_false(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); @@ -402,7 +402,7 @@ M_TEST_DEFINE(wrongConst) { mSCRIPT_PUSH(&frame.arguments, S(Test), &a); assert_false(mScriptPopCSTest(&frame.arguments, &cb)); signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test); - assert_true(mScriptCoerceFrame(&signature, &frame.arguments)); + assert_true(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); assert_true(mScriptPopCSTest(&frame.arguments, &cb)); mScriptFrameDeinit(&frame); @@ -410,7 +410,7 @@ M_TEST_DEFINE(wrongConst) { mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); assert_false(mScriptPopSTest(&frame.arguments, &b)); signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); - assert_false(mScriptCoerceFrame(&signature, &frame.arguments)); + assert_false(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); assert_false(mScriptPopSTest(&frame.arguments, &b)); mScriptFrameDeinit(&frame); diff --git a/src/script/types.c b/src/script/types.c index 20f9458397c..d860d40c0c3 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -1238,6 +1238,9 @@ static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScript member->docstring = docstring; docstring = NULL; } + if (detail->info.member.type->base != mSCRIPT_TYPE_FUNCTION) { + abort(); + } if (detail->info.member.type->details.function.parameters.count != 3) { abort(); } @@ -1773,21 +1776,24 @@ bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* inpu return false; } -bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame) { - if (types->count < mScriptListSize(frame) && !types->variable) { +bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, const struct mScriptList* input, struct mScriptList* output) { + if (types->count < mScriptListSize(input) && !types->variable) { return false; } - if (types->count > mScriptListSize(frame) && !types->variable && !types->defaults) { + if (types->count > mScriptListSize(input) && !types->variable && !types->defaults) { return false; } + if (output) { + mScriptListResize(output, mScriptListSize(input) - mScriptListSize(output)); + } size_t i; - for (i = 0; i < mScriptListSize(frame) && i < types->count; ++i) { - if (types->entries[i] == mScriptListGetPointer(frame, i)->type) { + for (i = 0; i < mScriptListSize(input) && i < types->count; ++i) { + if (types->entries[i] == mScriptListGetConstPointer(input, i)->type) { continue; } - struct mScriptValue* unwrapped = NULL; - if (mScriptListGetPointer(frame, i)->type->base == mSCRIPT_TYPE_WRAPPER) { - unwrapped = mScriptValueUnwrap(mScriptListGetPointer(frame, i)); + const struct mScriptValue* unwrapped = NULL; + if (mScriptListGetConstPointer(input, i)->type->base == mSCRIPT_TYPE_WRAPPER) { + unwrapped = mScriptValueUnwrapConst(mScriptListGetConstPointer(input, i)); if (types->entries[i]->base == mSCRIPT_TYPE_WRAPPER) { if (types->entries[i]->details.type == unwrapped->type) { continue; @@ -1796,7 +1802,12 @@ bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList continue; } } - if (!mScriptCast(types->entries[i], mScriptListGetPointer(frame, i), mScriptListGetPointer(frame, i))) { + struct mScriptValue fakeVal; + struct mScriptValue* castTo = &fakeVal; + if (output) { + castTo = mScriptListGetPointer(output, i); + } + if (!mScriptCast(types->entries[i], mScriptListGetConstPointer(input, i), castTo)) { return false; } } @@ -1808,11 +1819,26 @@ bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList if (!types->defaults[i].type) { return false; } - memcpy(mScriptListAppend(frame), &types->defaults[i], sizeof(struct mScriptValue)); + if (output) { + memcpy(mScriptListAppend(output), &types->defaults[i], sizeof(struct mScriptValue)); + } } return true; } +const struct mScriptFunctionOverload* mScriptFunctionFindOverload(const struct mScriptFunctionOverload* overloads, struct mScriptList* frame) { + size_t i; + for (i = 0; overloads[i].type; ++i) { + if (overloads[i].type->base != mSCRIPT_TYPE_FUNCTION) { + continue; + } + if (mScriptCoerceFrame(&overloads[i].type->details.function.parameters, frame, NULL)) { + return &overloads[i]; + } + } + return NULL; +} + static void addTypesFromTuple(struct Table* types, const struct mScriptTypeTuple* tuple) { size_t i; for (i = 0; i < tuple->count; ++i) { diff --git a/src/third-party/blip_buf/blip_buf.c b/src/third-party/blip_buf/blip_buf.c deleted file mode 100644 index 6bc7da8bd05..00000000000 --- a/src/third-party/blip_buf/blip_buf.c +++ /dev/null @@ -1,344 +0,0 @@ -/* blip_buf 1.1.0. http://www.slack.net/~ant/ */ - -#include - -#include -#include -#include -#include - -/* Library Copyright (C) 2003-2009 Shay Green. This library is free software; -you can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -library is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#if defined (BLARGG_TEST) && BLARGG_TEST - #include "blargg_test.h" -#endif - -/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000. -Avoids constants that don't fit in 32 bits. */ -#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF - typedef unsigned long fixed_t; - enum { pre_shift = 32 }; - -#elif defined(ULLONG_MAX) - typedef unsigned long long fixed_t; - enum { pre_shift = 32 }; - -#else - typedef unsigned fixed_t; - enum { pre_shift = 0 }; - -#endif - -enum { time_bits = pre_shift + 20 }; - -static fixed_t const time_unit = (fixed_t) 1 << time_bits; - -enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */ -enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */ - -enum { half_width = 8 }; -enum { buf_extra = half_width*2 + end_frame_extra }; -enum { phase_bits = 5 }; -enum { phase_count = 1 << phase_bits }; -enum { delta_bits = 15 }; -enum { delta_unit = 1 << delta_bits }; -enum { frac_bits = time_bits - pre_shift }; - -/* We could eliminate avail and encode whole samples in offset, but that would -limit the total buffered samples to blip_max_frame. That could only be -increased by decreasing time_bits, which would reduce resample ratio accuracy. -*/ - -/** Sample buffer that resamples to output rate and accumulates samples -until they're read out */ -struct blip_t -{ - fixed_t factor; - fixed_t offset; - int avail; - int size; - int integrator; -}; - -typedef int buf_t; - -/* probably not totally portable */ -#define SAMPLES( buf ) ((buf_t*) ((buf) + 1)) - -/* Arithmetic (sign-preserving) right shift */ -#define ARITH_SHIFT( n, shift ) \ - ((n) >> (shift)) - -enum { max_sample = +32767 }; -enum { min_sample = -32768 }; - -#define CLAMP( n ) \ - {\ - if ( (short) n != n )\ - n = ARITH_SHIFT( n, 16 ) ^ max_sample;\ - } - -static void check_assumptions( void ) -{ - int n; - - #if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF - #error "int must be at least 32 bits" - #endif - - assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */ - - n = max_sample * 2; - CLAMP( n ); - assert( n == max_sample ); - - n = min_sample * 2; - CLAMP( n ); - assert( n == min_sample ); - - assert( blip_max_ratio <= time_unit ); - assert( blip_max_frame <= (fixed_t) -1 >> time_bits ); -} - -blip_t* blip_new( int size ) -{ - blip_t* m; - assert( size >= 0 ); - - m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) ); - if ( m ) - { - m->factor = time_unit / blip_max_ratio; - m->size = size; - blip_clear( m ); - check_assumptions(); - } - return m; -} - -void blip_delete( blip_t* m ) -{ - if ( m != NULL ) - { - /* Clear fields in case user tries to use after freeing */ - memset( m, 0, sizeof *m ); - free( m ); - } -} - -void blip_set_rates( blip_t* m, double clock_rate, double sample_rate ) -{ - double factor = time_unit * sample_rate / clock_rate; - m->factor = (fixed_t) factor; - - /* Fails if clock_rate exceeds maximum, relative to sample_rate */ - assert( 0 <= factor - m->factor && factor - m->factor < 1 ); - - /* Avoid requiring math.h. Equivalent to - m->factor = (int) ceil( factor ) */ - if ( m->factor < factor ) - m->factor++; - - /* At this point, factor is most likely rounded up, but could still - have been rounded down in the floating-point calculation. */ -} - -void blip_clear( blip_t* m ) -{ - /* We could set offset to 0, factor/2, or factor-1. 0 is suitable if - factor is rounded up. factor-1 is suitable if factor is rounded down. - Since we don't know rounding direction, factor/2 accommodates either, - with the slight loss of showing an error in half the time. Since for - a 64-bit factor this is years, the halving isn't a problem. */ - - m->offset = m->factor / 2; - m->avail = 0; - m->integrator = 0; - memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) ); -} - -int blip_clocks_needed( const blip_t* m, int samples ) -{ - fixed_t needed; - - /* Fails if buffer can't hold that many more samples */ - assert( samples >= 0 && m->avail + samples <= m->size ); - - needed = (fixed_t) samples * time_unit; - if ( needed < m->offset ) - return 0; - - return (needed - m->offset + m->factor - 1) / m->factor; -} - -void blip_end_frame( blip_t* m, unsigned t ) -{ - fixed_t off = t * m->factor + m->offset; - m->avail += off >> time_bits; - m->offset = off & (time_unit - 1); - - /* Fails if buffer size was exceeded */ - assert( m->avail <= m->size ); -} - -int blip_samples_avail( const blip_t* m ) -{ - return m->avail; -} - -static void remove_samples( blip_t* m, int count ) -{ - buf_t* buf = SAMPLES( m ); - int remain = m->avail + buf_extra - count; - m->avail -= count; - - memmove( &buf [0], &buf [count], remain * sizeof buf [0] ); - memset( &buf [remain], 0, count * sizeof buf [0] ); -} - -int blip_read_samples( blip_t* m, short out [], int count, int stereo ) -{ - assert( count >= 0 ); - - if ( count > m->avail ) - count = m->avail; - - if ( count ) - { - int const step = stereo ? 2 : 1; - buf_t const* in = SAMPLES( m ); - buf_t const* end = in + count; - int sum = m->integrator; - do - { - /* Eliminate fraction */ - int s = ARITH_SHIFT( sum, delta_bits ); - - sum += *in++; - - CLAMP( s ); - - *out = s; - out += step; - - /* High-pass filter */ - sum -= s << (delta_bits - bass_shift); - } - while ( in != end ); - m->integrator = sum; - - remove_samples( m, count ); - } - - return count; -} - -/* Things that didn't help performance on x86: - __attribute__((aligned(128))) - #define short int - restrict -*/ - -/* Sinc_Generator( 0.9, 0.55, 4.5 ) */ -static short const bl_step [phase_count + 1] [half_width] = -{ -{ 43, -115, 350, -488, 1136, -914, 5861,21022}, -{ 44, -118, 348, -473, 1076, -799, 5274,21001}, -{ 45, -121, 344, -454, 1011, -677, 4706,20936}, -{ 46, -122, 336, -431, 942, -549, 4156,20829}, -{ 47, -123, 327, -404, 868, -418, 3629,20679}, -{ 47, -122, 316, -375, 792, -285, 3124,20488}, -{ 47, -120, 303, -344, 714, -151, 2644,20256}, -{ 46, -117, 289, -310, 634, -17, 2188,19985}, -{ 46, -114, 273, -275, 553, 117, 1758,19675}, -{ 44, -108, 255, -237, 471, 247, 1356,19327}, -{ 43, -103, 237, -199, 390, 373, 981,18944}, -{ 42, -98, 218, -160, 310, 495, 633,18527}, -{ 40, -91, 198, -121, 231, 611, 314,18078}, -{ 38, -84, 178, -81, 153, 722, 22,17599}, -{ 36, -76, 157, -43, 80, 824, -241,17092}, -{ 34, -68, 135, -3, 8, 919, -476,16558}, -{ 32, -61, 115, 34, -60, 1006, -683,16001}, -{ 29, -52, 94, 70, -123, 1083, -862,15422}, -{ 27, -44, 73, 106, -184, 1152,-1015,14824}, -{ 25, -36, 53, 139, -239, 1211,-1142,14210}, -{ 22, -27, 34, 170, -290, 1261,-1244,13582}, -{ 20, -20, 16, 199, -335, 1301,-1322,12942}, -{ 18, -12, -3, 226, -375, 1331,-1376,12293}, -{ 15, -4, -19, 250, -410, 1351,-1408,11638}, -{ 13, 3, -35, 272, -439, 1361,-1419,10979}, -{ 11, 9, -49, 292, -464, 1362,-1410,10319}, -{ 9, 16, -63, 309, -483, 1354,-1383, 9660}, -{ 7, 22, -75, 322, -496, 1337,-1339, 9005}, -{ 6, 26, -85, 333, -504, 1312,-1280, 8355}, -{ 4, 31, -94, 341, -507, 1278,-1205, 7713}, -{ 3, 35, -102, 347, -506, 1238,-1119, 7082}, -{ 1, 40, -110, 350, -499, 1190,-1021, 6464}, -{ 0, 43, -115, 350, -488, 1136, -914, 5861} -}; - -/* Shifting by pre_shift allows calculation using unsigned int rather than -possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient. -And by having pre_shift 32, a 32-bit platform can easily do the shift by -simply ignoring the low half. */ - -void blip_add_delta( blip_t* m, unsigned time, int delta ) -{ - unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); - buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits); - - int const phase_shift = frac_bits - phase_bits; - int phase = fixed >> phase_shift & (phase_count - 1); - short const* in = bl_step [phase]; - short const* rev = bl_step [phase_count - phase]; - - int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1); - int delta2 = (delta * interp) >> delta_bits; - delta -= delta2; - - /* Fails if buffer size was exceeded */ - assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] ); - - out [0] += in[0]*delta + in[half_width+0]*delta2; - out [1] += in[1]*delta + in[half_width+1]*delta2; - out [2] += in[2]*delta + in[half_width+2]*delta2; - out [3] += in[3]*delta + in[half_width+3]*delta2; - out [4] += in[4]*delta + in[half_width+4]*delta2; - out [5] += in[5]*delta + in[half_width+5]*delta2; - out [6] += in[6]*delta + in[half_width+6]*delta2; - out [7] += in[7]*delta + in[half_width+7]*delta2; - - in = rev; - out [ 8] += in[7]*delta + in[7-half_width]*delta2; - out [ 9] += in[6]*delta + in[6-half_width]*delta2; - out [10] += in[5]*delta + in[5-half_width]*delta2; - out [11] += in[4]*delta + in[4-half_width]*delta2; - out [12] += in[3]*delta + in[3-half_width]*delta2; - out [13] += in[2]*delta + in[2-half_width]*delta2; - out [14] += in[1]*delta + in[1-half_width]*delta2; - out [15] += in[0]*delta + in[0-half_width]*delta2; -} - -void blip_add_delta_fast( blip_t* m, unsigned time, int delta ) -{ - unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); - buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits); - - int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1); - int delta2 = delta * interp; - - /* Fails if buffer size was exceeded */ - assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] ); - - out [7] += delta * delta_unit - delta2; - out [8] += delta2; -} diff --git a/src/third-party/blip_buf/license.txt b/src/third-party/blip_buf/license.txt deleted file mode 100644 index 5faba9d48ce..00000000000 --- a/src/third-party/blip_buf/license.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/src/tools/docgen.c b/src/tools/docgen.c index f57fb8d4428..cdabb472c77 100644 --- a/src/tools/docgen.c +++ b/src/tools/docgen.c @@ -190,6 +190,14 @@ void explainClass(struct mScriptTypeClass* cls, int level) { fprintf(out, "%s readonly: true\n", indent); } fprintf(out, "%s type: %s\n", indent, details->info.member.type->name); + if (details->info.member.overloads) { + fprintf(out, "%s overloads:\n", indent); + size_t i; + for (i = 0; details->info.member.overloads[i].type; ++i) { + mScriptTypeAdd(&types, details->info.member.overloads[i].type); + fprintf(out, "%s - %s\n", indent, details->info.member.overloads[i].type->name); + } + } break; case mSCRIPT_CLASS_INIT_END: break; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index eb82215d703..66cd6ca4761 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -13,12 +13,15 @@ set(BASE_SOURCE_FILES set(SOURCE_FILES ${BASE_SOURCE_FILES} + audio-buffer.c + audio-resampler.c convolve.c elf-read.c geometry.c image.c image/export.c image/png-io.c + interpolator.c patch.c patch-fast.c patch-ips.c @@ -35,6 +38,7 @@ set(GUI_FILES gui/menu.c) set(TEST_FILES + test/circle-buffer.c test/color.c test/geometry.c test/image.c diff --git a/src/util/audio-buffer.c b/src/util/audio-buffer.c new file mode 100644 index 00000000000..5d67827fbc8 --- /dev/null +++ b/src/util/audio-buffer.c @@ -0,0 +1,59 @@ +/* Copyright (c) 2013-2024 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +#include + +void mAudioBufferInit(struct mAudioBuffer* buffer, size_t capacity, unsigned channels) { + mCircleBufferInit(&buffer->data, capacity * channels * sizeof(int16_t)); + buffer->channels = channels; +} + +void mAudioBufferDeinit(struct mAudioBuffer* buffer) { + mCircleBufferDeinit(&buffer->data); +} + +size_t mAudioBufferAvailable(const struct mAudioBuffer* buffer) { + return mCircleBufferSize(&buffer->data) / (buffer->channels * sizeof(int16_t)); +} + +size_t mAudioBufferCapacity(const struct mAudioBuffer* buffer) { + return mCircleBufferCapacity(&buffer->data) / (buffer->channels * sizeof(int16_t)); +} + +void mAudioBufferClear(struct mAudioBuffer* buffer) { + mCircleBufferClear(&buffer->data); +} + +int16_t mAudioBufferPeek(const struct mAudioBuffer* buffer, unsigned channel, size_t offset) { + int16_t sample; + if (!mCircleBufferDump(&buffer->data, &sample, sizeof(int16_t), (offset * buffer->channels + channel) * sizeof(int16_t))) { + return 0; + } + return sample; +} + +size_t mAudioBufferDump(const struct mAudioBuffer* buffer, int16_t* samples, size_t count, size_t offset) { + return mCircleBufferDump(&buffer->data, + samples, + count * buffer->channels * sizeof(int16_t), + offset * buffer->channels * sizeof(int16_t)) / + (buffer->channels * sizeof(int16_t)); +} + +size_t mAudioBufferRead(struct mAudioBuffer* buffer, int16_t* samples, size_t count) { + return mCircleBufferRead(&buffer->data, samples, count * buffer->channels * sizeof(int16_t)) / + (buffer->channels * sizeof(int16_t)); +} + +size_t mAudioBufferWrite(struct mAudioBuffer* buffer, const int16_t* samples, size_t count) { + size_t free = mCircleBufferCapacity(&buffer->data) - mCircleBufferSize(&buffer->data); + if (count * buffer->channels * sizeof(int16_t) > free) { + count = free / (buffer->channels * sizeof(int16_t)); + } + return mCircleBufferWrite(&buffer->data, samples, count * buffer->channels * sizeof(int16_t)) / + (buffer->channels * sizeof(int16_t)); +} diff --git a/src/util/audio-resampler.c b/src/util/audio-resampler.c new file mode 100644 index 00000000000..5b7a069e436 --- /dev/null +++ b/src/util/audio-resampler.c @@ -0,0 +1,111 @@ +/* Copyright (c) 2013-2024 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +#include + +#define MAX_CHANNELS 2 + +struct mAudioResamplerData { + struct mAudioResampler* resampler; + unsigned channel; +}; + +static int16_t _sampleAt(int index, const void* context) { + const struct mAudioResamplerData* data = context; + if (index < 0) { + return 0; + } + return mAudioBufferPeek(data->resampler->source, data->channel, index); +} + +void mAudioResamplerInit(struct mAudioResampler* resampler, enum mInterpolatorType interpType) { + memset(resampler, 0, sizeof(*resampler)); + resampler->interpType = interpType; + switch (interpType) { + case mINTERPOLATOR_SINC: + mInterpolatorSincInit(&resampler->sinc, 0, 0); + resampler->lowWaterMark = resampler->sinc.width; + resampler->highWaterMark = resampler->sinc.width; + break; + case mINTERPOLATOR_COSINE: + mInterpolatorCosineInit(&resampler->cosine, 0); + resampler->lowWaterMark = 0; + resampler->highWaterMark = 1; + break; + } +} + +void mAudioResamplerDeinit(struct mAudioResampler* resampler) { + switch (resampler->interpType) { + case mINTERPOLATOR_SINC: + mInterpolatorSincDeinit(&resampler->sinc); + break; + case mINTERPOLATOR_COSINE: + mInterpolatorCosineDeinit(&resampler->cosine); + break; + } + resampler->source = NULL; + resampler->destination = NULL; +} + +void mAudioResamplerSetSource(struct mAudioResampler* resampler, struct mAudioBuffer* source, double rate, bool consume) { + resampler->source = source; + resampler->sourceRate = rate; + resampler->consume = consume; +} + +void mAudioResamplerSetDestination(struct mAudioResampler* resampler, struct mAudioBuffer* destination, double rate) { + resampler->destination = destination; + resampler->destRate = rate; +} + +size_t mAudioResamplerProcess(struct mAudioResampler* resampler) { + int16_t sampleBuffer[MAX_CHANNELS] = {0}; + double timestep = resampler->sourceRate / resampler->destRate; + double timestamp = resampler->timestamp; + struct mInterpolator* interp = &resampler->interp; + struct mAudioResamplerData context = { + .resampler = resampler, + }; + struct mInterpolationData data = { + .at = _sampleAt, + .context = &context, + }; + + size_t read = 0; + if (resampler->source->channels > MAX_CHANNELS) { + abort(); + } + + while (true) { + if (timestamp + resampler->highWaterMark >= mAudioBufferAvailable(resampler->source)) { + break; + } + if (mAudioBufferAvailable(resampler->destination) == mAudioBufferCapacity(resampler->destination)) { + break; + } + + size_t channel; + for (channel = 0; channel < resampler->source->channels; ++channel) { + context.channel = channel; + sampleBuffer[channel] = interp->interpolate(interp, &data, timestamp, timestep); + } + if (!mAudioBufferWrite(resampler->destination, sampleBuffer, 1)) { + break; + } + timestamp += timestep; + ++read; + } + + if (resampler->consume && timestamp > resampler->lowWaterMark) { + size_t drop = timestamp - resampler->lowWaterMark; + drop = mAudioBufferRead(resampler->source, NULL, drop); + timestamp -= drop; + } + resampler->timestamp = timestamp; + return read; +} diff --git a/src/util/circle-buffer.c b/src/util/circle-buffer.c index c77a71ebadf..ec14bfc4f88 100644 --- a/src/util/circle-buffer.c +++ b/src/util/circle-buffer.c @@ -6,7 +6,7 @@ #include #ifndef NDEBUG -static int _checkIntegrity(struct CircleBuffer* buffer) { +static int _checkIntegrity(struct mCircleBuffer* buffer) { if ((int8_t*) buffer->writePtr - (int8_t*) buffer->readPtr == (ssize_t) buffer->size) { return 1; } @@ -20,32 +20,32 @@ static int _checkIntegrity(struct CircleBuffer* buffer) { } #endif -void CircleBufferInit(struct CircleBuffer* buffer, unsigned capacity) { +void mCircleBufferInit(struct mCircleBuffer* buffer, unsigned capacity) { buffer->data = malloc(capacity); buffer->capacity = capacity; - CircleBufferClear(buffer); + mCircleBufferClear(buffer); } -void CircleBufferDeinit(struct CircleBuffer* buffer) { +void mCircleBufferDeinit(struct mCircleBuffer* buffer) { free(buffer->data); buffer->data = 0; } -size_t CircleBufferSize(const struct CircleBuffer* buffer) { +size_t mCircleBufferSize(const struct mCircleBuffer* buffer) { return buffer->size; } -size_t CircleBufferCapacity(const struct CircleBuffer* buffer) { +size_t mCircleBufferCapacity(const struct mCircleBuffer* buffer) { return buffer->capacity; } -void CircleBufferClear(struct CircleBuffer* buffer) { +void mCircleBufferClear(struct mCircleBuffer* buffer) { buffer->size = 0; buffer->readPtr = buffer->data; buffer->writePtr = buffer->data; } -int CircleBufferWrite8(struct CircleBuffer* buffer, int8_t value) { +int mCircleBufferWrite8(struct mCircleBuffer* buffer, int8_t value) { int8_t* data = buffer->writePtr; if (buffer->size + sizeof(int8_t) > buffer->capacity) { return 0; @@ -67,17 +67,17 @@ int CircleBufferWrite8(struct CircleBuffer* buffer, int8_t value) { return 1; } -int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value) { +int mCircleBufferWrite32(struct mCircleBuffer* buffer, int32_t value) { int32_t* data = buffer->writePtr; if (buffer->size + sizeof(int32_t) > buffer->capacity) { return 0; } - if ((intptr_t) data & 0x3) { + if (((intptr_t) data & 0x3) || (uintptr_t) data - (uintptr_t) buffer->data > buffer->capacity - sizeof(int32_t)) { int written = 0; - written += CircleBufferWrite8(buffer, ((int8_t*) &value)[0]); - written += CircleBufferWrite8(buffer, ((int8_t*) &value)[1]); - written += CircleBufferWrite8(buffer, ((int8_t*) &value)[2]); - written += CircleBufferWrite8(buffer, ((int8_t*) &value)[3]); + written += mCircleBufferWrite8(buffer, ((int8_t*) &value)[0]); + written += mCircleBufferWrite8(buffer, ((int8_t*) &value)[1]); + written += mCircleBufferWrite8(buffer, ((int8_t*) &value)[2]); + written += mCircleBufferWrite8(buffer, ((int8_t*) &value)[3]); return written; } *data = value; @@ -97,15 +97,15 @@ int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value) { return 4; } -int CircleBufferWrite16(struct CircleBuffer* buffer, int16_t value) { +int mCircleBufferWrite16(struct mCircleBuffer* buffer, int16_t value) { int16_t* data = buffer->writePtr; if (buffer->size + sizeof(int16_t) > buffer->capacity) { return 0; } - if ((intptr_t) data & 0x3) { + if (((intptr_t) data & 0x1) || (uintptr_t) data - (uintptr_t) buffer->data > buffer->capacity - sizeof(int16_t)) { int written = 0; - written += CircleBufferWrite8(buffer, ((int8_t*) &value)[0]); - written += CircleBufferWrite8(buffer, ((int8_t*) &value)[1]); + written += mCircleBufferWrite8(buffer, ((int8_t*) &value)[0]); + written += mCircleBufferWrite8(buffer, ((int8_t*) &value)[1]); return written; } *data = value; @@ -125,7 +125,7 @@ int CircleBufferWrite16(struct CircleBuffer* buffer, int16_t value) { return 2; } -size_t CircleBufferWrite(struct CircleBuffer* buffer, const void* input, size_t length) { +size_t mCircleBufferWrite(struct mCircleBuffer* buffer, const void* input, size_t length) { int8_t* data = buffer->writePtr; if (buffer->size + length > buffer->capacity) { return 0; @@ -153,7 +153,14 @@ size_t CircleBufferWrite(struct CircleBuffer* buffer, const void* input, size_t return length; } -int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value) { +size_t mCircleBufferWriteTruncate(struct mCircleBuffer* buffer, const void* input, size_t length) { + if (buffer->size + length > buffer->capacity) { + length = buffer->capacity - buffer->size; + } + return mCircleBufferWrite(buffer, input, length); +} + +int mCircleBufferRead8(struct mCircleBuffer* buffer, int8_t* value) { int8_t* data = buffer->readPtr; if (buffer->size < sizeof(int8_t)) { return 0; @@ -175,15 +182,15 @@ int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value) { return 1; } -int CircleBufferRead16(struct CircleBuffer* buffer, int16_t* value) { +int mCircleBufferRead16(struct mCircleBuffer* buffer, int16_t* value) { int16_t* data = buffer->readPtr; if (buffer->size < sizeof(int16_t)) { return 0; } - if ((intptr_t) data & 0x3) { + if (((intptr_t) data & 0x1) || (uintptr_t) data - (uintptr_t) buffer->data > buffer->capacity - sizeof(int16_t)) { int read = 0; - read += CircleBufferRead8(buffer, &((int8_t*) value)[0]); - read += CircleBufferRead8(buffer, &((int8_t*) value)[1]); + read += mCircleBufferRead8(buffer, &((int8_t*) value)[0]); + read += mCircleBufferRead8(buffer, &((int8_t*) value)[1]); return read; } *value = *data; @@ -203,17 +210,17 @@ int CircleBufferRead16(struct CircleBuffer* buffer, int16_t* value) { return 2; } -int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value) { +int mCircleBufferRead32(struct mCircleBuffer* buffer, int32_t* value) { int32_t* data = buffer->readPtr; if (buffer->size < sizeof(int32_t)) { return 0; } - if ((intptr_t) data & 0x3) { + if (((intptr_t) data & 0x3) || (uintptr_t) data - (uintptr_t) buffer->data > buffer->capacity - sizeof(int32_t)) { int read = 0; - read += CircleBufferRead8(buffer, &((int8_t*) value)[0]); - read += CircleBufferRead8(buffer, &((int8_t*) value)[1]); - read += CircleBufferRead8(buffer, &((int8_t*) value)[2]); - read += CircleBufferRead8(buffer, &((int8_t*) value)[3]); + read += mCircleBufferRead8(buffer, &((int8_t*) value)[0]); + read += mCircleBufferRead8(buffer, &((int8_t*) value)[1]); + read += mCircleBufferRead8(buffer, &((int8_t*) value)[2]); + read += mCircleBufferRead8(buffer, &((int8_t*) value)[3]); return read; } *value = *data; @@ -233,7 +240,7 @@ int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value) { return 4; } -size_t CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length) { +size_t mCircleBufferRead(struct mCircleBuffer* buffer, void* output, size_t length) { int8_t* data = buffer->readPtr; if (buffer->size == 0) { return 0; @@ -243,15 +250,19 @@ size_t CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length } size_t remaining = buffer->capacity - ((int8_t*) data - (int8_t*) buffer->data); if (length <= remaining) { - memcpy(output, data, length); + if (output) { + memcpy(output, data, length); + } if (length == remaining) { buffer->readPtr = buffer->data; } else { buffer->readPtr = (int8_t*) data + length; } } else { - memcpy(output, data, remaining); - memcpy((int8_t*) output + remaining, buffer->data, length - remaining); + if (output) { + memcpy(output, data, remaining); + memcpy((int8_t*) output + remaining, buffer->data, length - remaining); + } buffer->readPtr = (int8_t*) buffer->data + length - remaining; } @@ -264,15 +275,26 @@ size_t CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length return length; } -size_t CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length) { +size_t mCircleBufferDump(const struct mCircleBuffer* buffer, void* output, size_t length, size_t offset) { int8_t* data = buffer->readPtr; - if (buffer->size == 0) { + if (buffer->size <= offset) { return 0; } - if (length > buffer->size) { - length = buffer->size; + if (length > buffer->size - offset) { + length = buffer->size - offset; } - size_t remaining = buffer->capacity - ((int8_t*) data - (int8_t*) buffer->data); + size_t remaining = buffer->capacity - ((uintptr_t) data - (uintptr_t) buffer->data); + if (offset) { + if (remaining >= offset) { + data += offset; + remaining -= offset; + } else { + offset -= remaining; + data = buffer->data; + data += offset; + } + } + if (length <= remaining) { memcpy(output, data, length); } else { diff --git a/src/util/formatting.c b/src/util/formatting.c index e1e1c0bf001..2506ddc9c66 100644 --- a/src/util/formatting.c +++ b/src/util/formatting.c @@ -46,26 +46,26 @@ float strtof_l(const char* restrict str, char** restrict end, locale_t locale) { #endif int ftostr_u(char* restrict str, size_t size, float f) { -#if HAVE_LOCALE +#ifdef HAVE_LOCALE locale_t l = newlocale(LC_NUMERIC_MASK, "C", 0); #else locale_t l = "C"; #endif int res = ftostr_l(str, size, f, l); -#if HAVE_LOCALE +#ifdef HAVE_LOCALE freelocale(l); #endif return res; } float strtof_u(const char* restrict str, char** restrict end) { -#if HAVE_LOCALE +#ifdef HAVE_LOCALE locale_t l = newlocale(LC_NUMERIC_MASK, "C", 0); #else locale_t l = "C"; #endif float res = strtof_l(str, end, l); -#if HAVE_LOCALE +#ifdef HAVE_LOCALE freelocale(l); #endif return res; diff --git a/src/util/gui/menu.c b/src/util/gui/menu.c index 584f2d0d5e0..f0c624f1715 100644 --- a/src/util/gui/menu.c +++ b/src/util/gui/menu.c @@ -18,30 +18,33 @@ DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem); DEFINE_VECTOR(GUIMenuSavedList, struct GUIMenuSavedState); void _itemNext(struct GUIMenuItem* item, bool wrap) { - if (item->state < item->nStates - 1) { + if (wrap || item->state < item->nStates - 1) { unsigned oldState = item->state; do { ++item->state; + if (item->state >= item->nStates) { + item->state -= item->nStates; + } } while (!item->validStates[item->state] && item->state < item->nStates - 1); if (!item->validStates[item->state]) { item->state = oldState; } - } else if (wrap) { - item->state = 0; } } void _itemPrev(struct GUIMenuItem* item, bool wrap) { - if (item->state > 0) { + if (wrap || item->state > 0) { unsigned oldState = item->state; do { - --item->state; + if (item->state > 0) { + --item->state; + } else { + item->state = item->nStates - 1; + } } while (!item->validStates[item->state] && item->state > 0); if (!item->validStates[item->state]) { item->state = oldState; } - } else if (wrap) { - item->state = item->nStates - 1; } } diff --git a/src/util/image.c b/src/util/image.c index a0fefafe799..f1f1a0fef47 100644 --- a/src/util/image.c +++ b/src/util/image.c @@ -42,6 +42,53 @@ memcpy((void*) (DST), &_color, (DEPTH)); \ } while (0); +static uint32_t _mColorMultiply(uint32_t colorA, uint32_t colorB) { + uint32_t color = 0; + + uint32_t a, b; + a = colorA & 0xFF; + b = colorB & 0xFF; + b = a * b; + b /= 0xFF; + if (b > 0xFF) { + color |= 0xFF; + } else { + color |= b; + } + + a = (colorA >> 8) & 0xFF; + b = (colorB >> 8) & 0xFF; + b = a * b; + b /= 0xFF; + if (b > 0xFF) { + color |= 0xFF00; + } else { + color |= b << 8; + } + + a = (colorA >> 16) & 0xFF; + b = (colorB >> 16) & 0xFF; + b = a * b; + b /= 0xFF; + if (b > 0xFF) { + color |= 0xFF0000; + } else { + color |= b << 16; + } + + a = (colorA >> 24) & 0xFF; + b = (colorB >> 24) & 0xFF; + b = a * b; + b /= 0xFF; + if (b > 0xFF) { + color |= 0xFF000000; + } else { + color |= b << 24; + } + + return color; +} + struct mImage* mImageCreate(unsigned width, unsigned height, enum mColorFormat format) { return mImageCreateWithStride(width, height, width, format); } @@ -395,18 +442,18 @@ void mImageSetPaletteEntry(struct mImage* image, unsigned index, uint32_t color) image->palette[index] = color; } -#define COMPOSITE_BOUNDS_INIT \ +#define COMPOSITE_BOUNDS_INIT(SOURCE, DEST) \ struct mRectangle dstRect = { \ .x = 0, \ .y = 0, \ - .width = image->width, \ - .height = image->height \ + .width = (DEST)->width, \ + .height = (DEST)->height \ }; \ struct mRectangle srcRect = { \ .x = x, \ .y = y, \ - .width = source->width, \ - .height = source->height \ + .width = (SOURCE)->width, \ + .height = (SOURCE)->height \ }; \ if (!mRectangleIntersection(&srcRect, &dstRect)) { \ return; \ @@ -436,7 +483,7 @@ void mImageBlit(struct mImage* image, const struct mImage* source, int x, int y) return; } - COMPOSITE_BOUNDS_INIT; + COMPOSITE_BOUNDS_INIT(source, image); for (y = 0; y < srcRect.height; ++y) { uintptr_t srcPixel = (uintptr_t) PIXEL(source, srcStartX, srcStartY + y); @@ -461,7 +508,7 @@ void mImageComposite(struct mImage* image, const struct mImage* source, int x, i return; } - COMPOSITE_BOUNDS_INIT; + COMPOSITE_BOUNDS_INIT(source, image); for (y = 0; y < srcRect.height; ++y) { uintptr_t srcPixel = (uintptr_t) PIXEL(source, srcStartX, srcStartY + y); @@ -498,7 +545,7 @@ void mImageCompositeWithAlpha(struct mImage* image, const struct mImage* source, alpha = 256; } - COMPOSITE_BOUNDS_INIT; + COMPOSITE_BOUNDS_INIT(source, image); int fixedAlpha = alpha * 0x200; @@ -821,6 +868,33 @@ void mPainterDrawCircle(struct mPainter* painter, int x, int y, int diameter) { } } +void mPainterDrawMask(struct mPainter* painter, const struct mImage* mask, int x, int y) { + if (!painter->fill) { + return; + } + + COMPOSITE_BOUNDS_INIT(mask, painter->backing); + + for (y = 0; y < srcRect.height; ++y) { + uintptr_t dstPixel = (uintptr_t) PIXEL(painter->backing, dstStartX, dstStartY + y); + uintptr_t maskPixel = (uintptr_t) PIXEL(mask, srcStartX, srcStartY + y); + for (x = 0; x < srcRect.width; ++x, dstPixel += painter->backing->depth, maskPixel += mask->depth) { + uint32_t color; + GET_PIXEL(color, maskPixel, mask->depth); + color = mColorConvert(color, mask->format, mCOLOR_ARGB8); + color = _mColorMultiply(painter->fillColor, color); + if (painter->blend || painter->fillColor < 0xFF000000) { + uint32_t current; + GET_PIXEL(current, dstPixel, painter->backing->depth); + current = mColorConvert(current, painter->backing->format, mCOLOR_ARGB8); + color = mColorMixARGB8(color, current); + } + color = mColorConvert(color, mCOLOR_ARGB8, painter->backing->format); + PUT_PIXEL(color, dstPixel, painter->backing->depth); + } + } +} + uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to) { if (from == to) { return color; diff --git a/src/util/interpolator.c b/src/util/interpolator.c new file mode 100644 index 00000000000..a69d19e8185 --- /dev/null +++ b/src/util/interpolator.c @@ -0,0 +1,116 @@ +/* Copyright (c) 2013-2024 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +enum { + mSINC_RESOLUTION = 8192, + mSINC_WIDTH = 8, + + mCOSINE_RESOLUTION = 8192, +}; + +static int16_t mInterpolatorSincInterpolate(const struct mInterpolator*, const struct mInterpolationData*, double time, double sampleStep); +static int16_t mInterpolatorCosineInterpolate(const struct mInterpolator*, const struct mInterpolationData*, double time, double sampleStep); + +void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution, unsigned width) { + interp->d.interpolate = mInterpolatorSincInterpolate; + + if (!resolution) { + resolution = mSINC_RESOLUTION; + } + if (!width) { + width = mSINC_WIDTH; + } + unsigned samples = resolution * width; + double dy = M_PI / samples; + double y = dy; + double dx = dy * width; + double x = dx; + + interp->sincLut = calloc(samples + 1, sizeof(double)); + interp->windowLut = calloc(samples + 1, sizeof(double)); + + interp->sincLut[0] = 0; + interp->windowLut[0] = 1; + + interp->width = width; + interp->resolution = resolution; + + unsigned i; + for (i = 1; i <= samples; ++i, x += dx, y += dy) { + interp->sincLut[i] = x < width ? sin(x) / x : 0.0; + // Three term Nuttall window with continuous first derivative + interp->windowLut[i] = 0.40897 + 0.5 * cos(y) + 0.09103 * cos(2 * y); + } +} + +void mInterpolatorSincDeinit(struct mInterpolatorSinc* interp) { + free(interp->sincLut); + free(interp->windowLut); +} + +int16_t mInterpolatorSincInterpolate(const struct mInterpolator* interpolator, const struct mInterpolationData* data, double time, double sampleStep) { + struct mInterpolatorSinc* interp = (struct mInterpolatorSinc*) interpolator; + int index = time; + double subsample = time - floor(time); + unsigned step = sampleStep < 1 ? interp->resolution * sampleStep : interp->resolution; + unsigned yShift = subsample * step; + unsigned xShift = subsample * interp->resolution; + double sum = 0.0; + double kernelSum = 0.0; + double kernel; + + int i; + for (i = 1 - (int) interp->width; i <= (int) interp->width; ++i) { + unsigned window = (i >= 0 ? i : -i) * interp->resolution; + if (yShift > window) { + window = yShift - window; + } else { + window -= yShift; + } + + unsigned sinc = (i >= 0 ? i : -i) * step; + if (xShift > sinc) { + sinc = xShift - sinc; + } else { + sinc -= xShift; + } + + kernel = interp->sincLut[sinc] * interp->windowLut[window]; + kernelSum += kernel; + sum += data->at(index + i, data->context) * kernel; + } + return sum / kernelSum; +} + +void mInterpolatorCosineInit(struct mInterpolatorCosine* interp, unsigned resolution) { + interp->d.interpolate = mInterpolatorCosineInterpolate; + + if (!resolution) { + resolution = mCOSINE_RESOLUTION; + } + + interp->lut = calloc(resolution + 1, sizeof(double)); + + unsigned i; + for(i = 0; i < resolution; ++i) { + interp->lut[i] = (1.0 - cos(M_PI * i / resolution) * M_PI) * 0.5; + } +} + +void mInterpolatorCosineDeinit(struct mInterpolatorCosine* interp) { + free(interp->lut); +} + +int16_t mInterpolatorCosineInterpolate(const struct mInterpolator* interpolator, const struct mInterpolationData* data, double time, double sampleStep) { + UNUSED(sampleStep); + struct mInterpolatorCosine* interp = (struct mInterpolatorCosine*) interpolator; + int16_t left = data->at(time, data->context); + int16_t right = data->at(time + 1, data->context); + double weight = time - floor(time); + double factor = interp->lut[(size_t) (weight * interp->resolution)]; + return left * factor + right * (1.0 - factor); +} diff --git a/src/util/patch-ups.c b/src/util/patch-ups.c index bce88e74d8a..72d0e14a833 100644 --- a/src/util/patch-ups.c +++ b/src/util/patch-ups.c @@ -23,7 +23,7 @@ static size_t _UPSOutputSize(struct Patch* patch, size_t inSize); static bool _UPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* out, size_t outSize); static bool _BPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* out, size_t outSize); -static size_t _decodeLength(struct VFile* vf, struct CircleBuffer* buffer); +static size_t _decodeLength(struct VFile* vf, struct mCircleBuffer* buffer); bool loadPatchUPS(struct Patch* patch) { patch->vf->seek(patch->vf, 0, SEEK_SET); @@ -77,42 +77,42 @@ bool _UPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* ou return false; } - struct CircleBuffer buffer; + struct mCircleBuffer buffer; memcpy(out, in, inSize > outSize ? outSize : inSize); size_t offset = 0; size_t alreadyRead = 0; uint8_t* buf = out; - CircleBufferInit(&buffer, BUFFER_SIZE); + mCircleBufferInit(&buffer, BUFFER_SIZE); while (alreadyRead < filesize + IN_CHECKSUM) { offset += _decodeLength(patch->vf, &buffer); int8_t byte; while (true) { - if (!CircleBufferSize(&buffer)) { + if (!mCircleBufferSize(&buffer)) { uint8_t block[BUFFER_SIZE]; ssize_t read = patch->vf->read(patch->vf, block, sizeof(block)); if (read < 1) { - CircleBufferDeinit(&buffer); + mCircleBufferDeinit(&buffer); return false; } - CircleBufferWrite(&buffer, block, read); + mCircleBufferWrite(&buffer, block, read); } - CircleBufferRead8(&buffer, &byte); + mCircleBufferRead8(&buffer, &byte); if (!byte) { break; } if (offset >= outSize) { - CircleBufferDeinit(&buffer); + mCircleBufferDeinit(&buffer); return false; } buf[offset] ^= byte; ++offset; } ++offset; - alreadyRead = patch->vf->seek(patch->vf, 0, SEEK_CUR) - CircleBufferSize(&buffer); + alreadyRead = patch->vf->seek(patch->vf, 0, SEEK_CUR) - mCircleBufferSize(&buffer); } - CircleBufferDeinit(&buffer); + mCircleBufferDeinit(&buffer); uint32_t goodCrc32; patch->vf->seek(patch->vf, OUT_CHECKSUM, SEEK_END); @@ -223,21 +223,21 @@ bool _BPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* ou return true; } -size_t _decodeLength(struct VFile* vf, struct CircleBuffer* buffer) { +size_t _decodeLength(struct VFile* vf, struct mCircleBuffer* buffer) { size_t shift = 1; size_t value = 0; uint8_t byte; while (true) { if (buffer) { - if (!CircleBufferSize(buffer)) { + if (!mCircleBufferSize(buffer)) { uint8_t block[BUFFER_SIZE]; ssize_t read = vf->read(vf, block, sizeof(block)); if (read < 1) { return false; } - CircleBufferWrite(buffer, block, read); + mCircleBufferWrite(buffer, block, read); } - CircleBufferRead8(buffer, (int8_t*) &byte); + mCircleBufferRead8(buffer, (int8_t*) &byte); } else { if (vf->read(vf, &byte, 1) != 1) { break; diff --git a/src/util/string.c b/src/util/string.c index 1a2ffe7a49a..4a9e35de53a 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -177,16 +177,12 @@ size_t toUtf8(uint32_t unichar, char* buffer) { buffer[2] = (unichar & 0x3F) | 0x80; return 3; } - if (unichar < 0x200000) { - buffer[0] = (unichar >> 18) | 0xF0; - buffer[1] = ((unichar >> 12) & 0x3F) | 0x80; - buffer[2] = ((unichar >> 6) & 0x3F) | 0x80; - buffer[3] = (unichar & 0x3F) | 0x80; - return 4; - } - // This shouldn't be possible - return 0; + buffer[0] = (unichar >> 18) | 0xF0; + buffer[1] = ((unichar >> 12) & 0x3F) | 0x80; + buffer[2] = ((unichar >> 6) & 0x3F) | 0x80; + buffer[3] = (unichar & 0x3F) | 0x80; + return 4; } size_t toUtf16(uint32_t unichar, uint16_t* buffer) { @@ -289,9 +285,6 @@ char* latin1ToUtf8(const char* latin1, size_t length) { size_t utf8TotalBytes = 0; size_t utf8Length = 0; for (offset = 0; offset < length; ++offset) { - if (length == 0) { - break; - } uint8_t unichar = latin1[offset]; size_t bytes = toUtf8(unichar, buffer); utf8Length += bytes; diff --git a/src/util/test/circle-buffer.c b/src/util/test/circle-buffer.c new file mode 100644 index 00000000000..bff0c9485f3 --- /dev/null +++ b/src/util/test/circle-buffer.c @@ -0,0 +1,995 @@ +/* Copyright (c) 2013-2024 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/test/suite.h" + +#include + +M_TEST_DEFINE(basicCircle) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + int8_t i; + for (i = 0; i < 63; ++i) { + assert_int_equal(mCircleBufferWrite8(&buffer, i), 1); + } + for (i = 0; i < 63; ++i) { + int8_t value; + assert_int_equal(mCircleBufferRead8(&buffer, &value), 1); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(basicAlignment16) { + struct mCircleBuffer buffer; + int8_t i8; + + mCircleBufferInit(&buffer, 64); + + // Aligned buffer + int16_t i; + for (i = 0; i < 31; ++i) { + assert_int_equal(mCircleBufferWrite16(&buffer, i), 2); + } + for (i = 0; i < 31; ++i) { + int16_t value; + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + assert_int_equal(value, i); + } + + // Misaligned buffer + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 31; ++i) { + assert_int_equal(mCircleBufferWrite16(&buffer, i), 2); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 31; ++i) { + int16_t value; + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(basicAlignment32) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + // Aligned buffer + int32_t i; + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i), 4); + } + for (i = 0; i < 15; ++i) { + int32_t value; + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i); + } + + // Singly misaligned buffer + int8_t i8; + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i), 4); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 15; ++i) { + int32_t value; + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i); + } + + // Doubly misaligned buffer + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 1), 1); + + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i), 4); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + + for (i = 0; i < 15; ++i) { + int32_t value; + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i); + } + + // Triply misaligned buffer + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 1), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 2), 1); + + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i), 4); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + + for (i = 0; i < 15; ++i) { + int32_t value; + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(capacity) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + int8_t i; + for (i = 0; i < 64; ++i) { + assert_int_equal(mCircleBufferWrite8(&buffer, i), 1); + } + for (i = 0; i < 64; ++i) { + int8_t value; + assert_int_equal(mCircleBufferRead8(&buffer, &value), 1); + assert_int_equal(value, i); + } + + for (i = 0; i < 64; ++i) { + assert_int_equal(mCircleBufferWrite8(&buffer, i), 1); + } + assert_int_equal(mCircleBufferWrite8(&buffer, 64), 0); + + for (i = 0; i < 64; ++i) { + int8_t value; + assert_int_equal(mCircleBufferRead8(&buffer, &value), 1); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(overflowWrap8) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + int8_t value; + int8_t i; + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &value), 1); + for (i = 0; i < 63; ++i) { + assert_int_equal(mCircleBufferWrite8(&buffer, i), 1); + } + assert_int_equal(mCircleBufferRead8(&buffer, &value), 1); + for (i = 0; i < 63; ++i) { + assert_int_equal(mCircleBufferRead8(&buffer, &value), 1); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(overflowWrap16) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + int16_t value; + int16_t i; + assert_int_equal(mCircleBufferWrite16(&buffer, 0), 2); + assert_int_equal(mCircleBufferWrite16(&buffer, 0), 2); + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + for (i = 0; i < 31; ++i) { + assert_int_equal(mCircleBufferWrite16(&buffer, i), 2); + } + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + for (i = 0; i < 31; ++i) { + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(overflowWrap16_1) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + int8_t i8; + int16_t value; + int16_t i; + assert_int_equal(mCircleBufferWrite16(&buffer, 0), 2); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + for (i = 0; i < 31; ++i) { + assert_int_equal(mCircleBufferWrite16(&buffer, i), 2); + } + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 31; ++i) { + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(overflowWrap32) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + int32_t value; + int32_t i; + assert_int_equal(mCircleBufferWrite32(&buffer, 0), 4); + assert_int_equal(mCircleBufferWrite32(&buffer, 0), 4); + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i), 4); + } + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(overflowWrap32_1) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + int8_t i8; + int32_t value; + int32_t i; + assert_int_equal(mCircleBufferWrite32(&buffer, 0), 4); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i), 4); + } + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(overflowWrap32_2) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + int16_t i16; + int32_t value; + int32_t i; + assert_int_equal(mCircleBufferWrite32(&buffer, 0), 4); + assert_int_equal(mCircleBufferWrite16(&buffer, 0), 2); + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i), 4); + } + assert_int_equal(mCircleBufferRead16(&buffer, &i16), 2); + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(overflowWrap32_3) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + int8_t i8; + int32_t value; + int32_t i; + assert_int_equal(mCircleBufferWrite32(&buffer, 0), 4); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i), 4); + } + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 15; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(weirdSize16) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 15); + + // Aligned, no overflow wrap + int16_t value; + int16_t i; + for (i = 0; i < 7; ++i) { + assert_int_equal(mCircleBufferWrite16(&buffer, i * 0x102), 2); + } + assert_int_equal(mCircleBufferWrite16(&buffer, 7), 0); + for (i = 0; i < 7; ++i) { + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + assert_int_equal(value, i * 0x102); + } + + // Misaligned, no overflow wrap + mCircleBufferClear(&buffer); + int8_t i8; + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 7; ++i) { + assert_int_equal(mCircleBufferWrite16(&buffer, i * 0x102), 2); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 7; ++i) { + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + assert_int_equal(value, i * 0x102); + } + + // Aligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 6; ++i) { + assert_int_equal(mCircleBufferWrite16(&buffer, i * 0x102), 2); + } + assert_int_equal(mCircleBufferWrite16(&buffer, 6 * 0x102), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite16(&buffer, 6 * 0x102), 2); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 7; ++i) { + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + assert_int_equal(value, i * 0x102); + } + + // Misaligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 6; ++i) { + assert_int_equal(mCircleBufferWrite16(&buffer, i * 0x102), 2); + } + assert_int_equal(mCircleBufferWrite16(&buffer, 6 * 0x102), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite16(&buffer, 6 * 0x102), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite16(&buffer, 6 * 0x102), 2); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 7; ++i) { + assert_int_equal(mCircleBufferRead16(&buffer, &value), 2); + assert_int_equal(value, i * 0x102); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(weirdSize32_1) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 13); + + // Aligned, no overflow wrap + int32_t value; + int32_t i; + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 3), 0); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Misaligned, no overflow wrap + mCircleBufferClear(&buffer); + int8_t i8; + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Aligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 2; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 4); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Misaligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 2; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 4); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(weirdSize32_2) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 14); + + // Aligned, no overflow wrap + int32_t value; + int8_t i8; + int32_t i; + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 3), 0); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Singly misaligned, no overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Doubly misaligned, no overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Aligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 2; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 4); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Singly misaligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 2; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 4); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Doubly misaligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 2; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 4); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(weirdSize32_3) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 15); + + // Aligned, no overflow wrap + int32_t value; + int8_t i8; + int32_t i; + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 3), 0); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Singly misaligned, no overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Doubly misaligned, no overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Triply misaligned, no overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Aligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 2; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 4); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Singly misaligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 2; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 4); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Doubly misaligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 2; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 4); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + // Triply misaligned, overflow wrap + mCircleBufferClear(&buffer); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + assert_int_equal(mCircleBufferWrite8(&buffer, 0), 1); + + for (i = 0; i < 2; ++i) { + assert_int_equal(mCircleBufferWrite32(&buffer, i * 0x1020304), 4); + } + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 0); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferWrite32(&buffer, 2 * 0x1020304), 4); + + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + assert_int_equal(mCircleBufferRead8(&buffer, &i8), 1); + for (i = 0; i < 3; ++i) { + assert_int_equal(mCircleBufferRead32(&buffer, &value), 4); + assert_int_equal(value, i * 0x1020304); + } + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(overCapacity16) { + struct mCircleBuffer buffer; + + mCircleBufferInit(&buffer, 64); + + int8_t i; + for (i = 0; i < 63; ++i) { + assert_int_equal(mCircleBufferWrite8(&buffer, i), 1); + } + assert_int_equal(mCircleBufferWrite16(&buffer, 0xFFFF), 0); + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(writeLenCapacity) { + struct mCircleBuffer buffer; + const char* data = " Lorem ipsum dolor sit amet, consectetur adipiscing elit placerat."; + char databuf[64]; + + mCircleBufferInit(&buffer, 64); + + assert_int_equal(mCircleBufferWrite(&buffer, data, 64), 64); + assert_int_equal(mCircleBufferSize(&buffer), 64); + assert_int_equal(mCircleBufferRead(&buffer, databuf, 64), 64); + assert_int_equal(mCircleBufferSize(&buffer), 0); + assert_memory_equal(data, databuf, 64); + + assert_int_equal(mCircleBufferWrite(&buffer, data, 48), 48); + assert_int_equal(mCircleBufferSize(&buffer), 48); + assert_int_equal(mCircleBufferWrite(&buffer, data, 48), 0); + assert_int_equal(mCircleBufferSize(&buffer), 48); + assert_int_equal(mCircleBufferRead(&buffer, databuf, 64), 48); + assert_memory_equal(data, databuf, 48); + + assert_int_equal(mCircleBufferWrite(&buffer, data, 48), 48); + assert_int_equal(mCircleBufferSize(&buffer), 48); + assert_int_equal(mCircleBufferWrite(&buffer, data, 16), 16); + assert_int_equal(mCircleBufferSize(&buffer), 64); + assert_int_equal(mCircleBufferRead(&buffer, databuf, 64), 64); + assert_int_equal(mCircleBufferSize(&buffer), 0); + assert_memory_equal(data, databuf, 48); + assert_memory_equal(data, &databuf[48], 16); + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(writeTruncate) { + struct mCircleBuffer buffer; + const char* data = " Lorem ipsum dolor sit amet, consectetur adipiscing elit placerat."; + char databuf[64]; + + mCircleBufferInit(&buffer, 64); + + assert_int_equal(mCircleBufferWriteTruncate(&buffer, data, 64), 64); + assert_int_equal(mCircleBufferSize(&buffer), 64); + assert_int_equal(mCircleBufferRead(&buffer, databuf, 64), 64); + assert_int_equal(mCircleBufferSize(&buffer), 0); + assert_memory_equal(data, databuf, 64); + + assert_int_equal(mCircleBufferWriteTruncate(&buffer, data, 48), 48); + assert_int_equal(mCircleBufferSize(&buffer), 48); + assert_int_equal(mCircleBufferWrite(&buffer, data, 48), 0); + assert_int_equal(mCircleBufferSize(&buffer), 48); + assert_int_equal(mCircleBufferWriteTruncate(&buffer, data, 48), 16); + assert_int_equal(mCircleBufferSize(&buffer), 64); + assert_int_equal(mCircleBufferRead(&buffer, databuf, 64), 64); + assert_memory_equal(data, databuf, 48); + assert_memory_equal(data, &databuf[48], 16); + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(dumpBasic) { + struct mCircleBuffer buffer; + const char* data = " Lorem ipsum dolor sit amet, consectetur adipiscing elit placerat."; + char databuf[64]; + + mCircleBufferInit(&buffer, 64); + + assert_int_equal(mCircleBufferWrite(&buffer, data, 64), 64); + assert_int_equal(mCircleBufferSize(&buffer), 64); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 64, 0), 64); + assert_int_equal(mCircleBufferSize(&buffer), 64); + assert_memory_equal(data, databuf, 64); + assert_int_equal(mCircleBufferRead(&buffer, databuf, 64), 64); + assert_int_equal(mCircleBufferSize(&buffer), 0); + assert_memory_equal(data, databuf, 64); + + assert_int_equal(mCircleBufferWrite(&buffer, data, 48), 48); + assert_int_equal(mCircleBufferSize(&buffer), 48); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 48, 0), 48); + assert_int_equal(mCircleBufferSize(&buffer), 48); + assert_memory_equal(data, databuf, 48); + assert_int_equal(mCircleBufferRead(&buffer, databuf, 16), 16); + assert_int_equal(mCircleBufferSize(&buffer), 32); + assert_memory_equal(data, databuf, 16); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 48, 0), 32); + assert_int_equal(mCircleBufferSize(&buffer), 32); + assert_memory_equal(&data[16], databuf, 32); + + assert_int_equal(mCircleBufferWrite(&buffer, data, 32), 32); + assert_int_equal(mCircleBufferSize(&buffer), 64); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 64, 0), 64); + assert_int_equal(mCircleBufferSize(&buffer), 64); + assert_memory_equal(&data[16], databuf, 32); + assert_memory_equal(data, &databuf[32], 32); + assert_int_equal(mCircleBufferRead(&buffer, databuf, 64), 64); + assert_memory_equal(&data[16], databuf, 32); + assert_memory_equal(data, &databuf[32], 32); + assert_int_equal(mCircleBufferSize(&buffer), 0); + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(dumpOffset) { + struct mCircleBuffer buffer; + const char* data = " Lorem ipsum dolor sit amet, consectetur adipiscing elit placerat."; + char databuf[64]; + + mCircleBufferInit(&buffer, 64); + + assert_int_equal(mCircleBufferWrite(&buffer, data, 48), 48); + assert_int_equal(mCircleBufferSize(&buffer), 48); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 32, 0), 32); + assert_memory_equal(data, databuf, 32); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 32, 16), 32); + assert_memory_equal(&data[16], databuf, 32); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 32, 32), 16); + assert_memory_equal(&data[32], databuf, 16); + + assert_int_equal(mCircleBufferRead(&buffer, databuf, 16), 16); + assert_int_equal(mCircleBufferSize(&buffer), 32); + assert_memory_equal(data, databuf, 16); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 32, 0), 32); + assert_memory_equal(&data[16], databuf, 32); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 32, 16), 16); + assert_memory_equal(&data[32], databuf, 16); + + assert_int_equal(mCircleBufferWrite(&buffer, data, 32), 32); + assert_int_equal(mCircleBufferSize(&buffer), 64); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 32, 0), 32); + assert_memory_equal(&data[16], databuf, 32); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 32, 16), 32); + assert_memory_equal(&data[32], databuf, 16); + assert_memory_equal(data, &databuf[16], 16); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 32, 32), 32); + assert_memory_equal(data, databuf, 32); + assert_int_equal(mCircleBufferDump(&buffer, databuf, 32, 48), 16); + assert_memory_equal(&data[16], databuf, 16); + + mCircleBufferDeinit(&buffer); +} + +M_TEST_DEFINE(dumpOffsetWrap) { + struct mCircleBuffer buffer; + const char* data = " Lorem ipsum dolor sit amet, consectetur adipiscing elit placerat."; + char databuf[64]; + + mCircleBufferInit(&buffer, 64); + + assert_int_equal(mCircleBufferWrite(&buffer, data, 64), 64); + assert_int_equal(mCircleBufferSize(&buffer), 64); + assert_int_equal(mCircleBufferRead(&buffer, databuf, 48), 48); + assert_memory_equal(data, databuf, 48); + assert_int_equal(mCircleBufferSize(&buffer), 16); + assert_int_equal(mCircleBufferWrite(&buffer, data, 16), 16); + assert_int_equal(mCircleBufferSize(&buffer), 32); + + assert_int_equal(mCircleBufferDump(&buffer, databuf, 64, 0), 32); + assert_memory_equal(&data[48], databuf, 16); + assert_memory_equal(data, &databuf[16], 16); + + assert_int_equal(mCircleBufferDump(&buffer, databuf, 64, 8), 24); + assert_memory_equal(&data[56], databuf, 8); + assert_memory_equal(data, &databuf[8], 16); + + assert_int_equal(mCircleBufferDump(&buffer, databuf, 64, 16), 16); + assert_memory_equal(data, databuf, 16); + + assert_int_equal(mCircleBufferDump(&buffer, databuf, 64, 24), 8); + assert_memory_equal(&data[8], databuf, 8); + + mCircleBufferDeinit(&buffer); +} + +M_TEST_SUITE_DEFINE(mCircleBuffer, + cmocka_unit_test(basicCircle), + cmocka_unit_test(basicAlignment16), + cmocka_unit_test(basicAlignment32), + cmocka_unit_test(capacity), + cmocka_unit_test(overflowWrap8), + cmocka_unit_test(overflowWrap16), + cmocka_unit_test(overflowWrap16_1), + cmocka_unit_test(overflowWrap32), + cmocka_unit_test(overflowWrap32_1), + cmocka_unit_test(overflowWrap32_2), + cmocka_unit_test(overflowWrap32_3), + cmocka_unit_test(weirdSize16), + cmocka_unit_test(weirdSize32_1), + cmocka_unit_test(weirdSize32_2), + cmocka_unit_test(weirdSize32_3), + cmocka_unit_test(overCapacity16), + cmocka_unit_test(writeLenCapacity), + cmocka_unit_test(writeTruncate), + cmocka_unit_test(dumpBasic), + cmocka_unit_test(dumpOffset), + cmocka_unit_test(dumpOffsetWrap), +) diff --git a/src/util/test/image.c b/src/util/test/image.c index a27e53d5c36..e3ea16e2451 100644 --- a/src/util/test/image.c +++ b/src/util/test/image.c @@ -2038,6 +2038,97 @@ M_TEST_DEFINE(painterDrawCircleInvalid) { mImageDestroy(image); } +M_TEST_DEFINE(painterDrawMask) { + struct mImage* image; + struct mImage* mask; + struct mPainter painter; + + image = mImageCreate(4, 4, mCOLOR_XRGB8); + mPainterInit(&painter, image); + painter.blend = false; + painter.fill = true; + + mask = mImageCreate(2, 2, mCOLOR_XRGB8); + mImageSetPixel(mask, 0, 0, 0xFFFFFFFF); + mImageSetPixel(mask, 1, 0, 0xFFFF0000); + mImageSetPixel(mask, 0, 1, 0xFF00FF00); + mImageSetPixel(mask, 1, 1, 0xFF0000FF); + + painter.fillColor = 0xFFFFFFFF; + mPainterDrawMask(&painter, mask, 0, 0); + painter.fillColor = 0xFFFF0000; + mPainterDrawMask(&painter, mask, 2, 0); + painter.fillColor = 0xFF00FF00; + mPainterDrawMask(&painter, mask, 0, 2); + painter.fillColor = 0xFF0000FF; + mPainterDrawMask(&painter, mask, 2, 2); + + COMPARE4X(0xFFFFFF, 0xFF0000, 0xFF0000, 0xFF0000, + 0x00FF00, 0x0000FF, 0x000000, 0x000000, + 0x00FF00, 0x000000, 0x0000FF, 0x000000, + 0x00FF00, 0x000000, 0x000000, 0x0000FF); + + painter.fillColor = 0xFF808080; + mPainterDrawMask(&painter, mask, 0, 0); + painter.fillColor = 0xFFFFFF00; + mPainterDrawMask(&painter, mask, 2, 0); + painter.fillColor = 0xFF00FFFF; + mPainterDrawMask(&painter, mask, 0, 2); + painter.fillColor = 0xFFFF00FF; + mPainterDrawMask(&painter, mask, 2, 2); + + COMPARE4X(0x808080, 0x800000, 0xFFFF00, 0xFF0000, + 0x008000, 0x000080, 0x00FF00, 0x000000, + 0x00FFFF, 0x000000, 0xFF00FF, 0xFF0000, + 0x00FF00, 0x0000FF, 0x000000, 0x0000FF); + + painter.fillColor = 0xFFFFFFFF; + mPainterDrawMask(&painter, mask, -1, -1); + mPainterDrawMask(&painter, mask, 3, 3); + assert_int_equal(0xFF0000FF, mImageGetPixel(image, 0, 0)); + assert_int_equal(0xFFFFFFFF, mImageGetPixel(image, 3, 3)); + + mImageDestroy(image); + mImageDestroy(mask); +} + +M_TEST_DEFINE(painterDrawMaskBlend) { + struct mImage* image; + struct mImage* mask; + struct mPainter painter; + const uint8_t lut[4] = { 0x00, 0x55, 0xAA, 0xFF }; + int x, y; + + image = mImageCreate(4, 4, mCOLOR_XRGB8); + mPainterInit(&painter, image); + painter.blend = true; + painter.fill = true; + painter.fillColor = 0xFFFF8000; + + for (y = 0; y < 4; ++y) { + for (x = 0; x < 4; ++x) { + mImageSetPixel(image, x, y, 0xFF808080); + } + } + + mask = mImageCreate(4, 4, mCOLOR_ARGB8); + for (y = 0; y < 4; ++y) { + for (x = 0; x < 4; ++x) { + mImageSetPixel(mask, x, y, (lut[x] << 24) | (lut[y] * 0x010101)); + } + } + + mPainterDrawMask(&painter, mask, 0, 0); + + COMPARE4X(0x808080, 0x555555, 0x2A2A2A, 0x000000, + 0x808080, 0x716355, 0x63462A, 0x552A00, + 0x808080, 0x8E7155, 0x9C632A, 0xAA5500, + 0x808080, 0xAA8055, 0xD4802A, 0xFF8000); + + mImageDestroy(image); + mImageDestroy(mask); +} + #undef COMPARE3X #undef COMPARE3 #undef COMPARE4X @@ -2083,4 +2174,6 @@ M_TEST_SUITE_DEFINE(Image, cmocka_unit_test(painterDrawCircleOffset), cmocka_unit_test(painterDrawCircleBlend), cmocka_unit_test(painterDrawCircleInvalid), + cmocka_unit_test(painterDrawMask), + cmocka_unit_test(painterDrawMaskBlend), ) diff --git a/src/util/vfs/vfs-fifo.c b/src/util/vfs/vfs-fifo.c index 5b6af576042..c7bb5be0403 100644 --- a/src/util/vfs/vfs-fifo.c +++ b/src/util/vfs/vfs-fifo.c @@ -8,7 +8,7 @@ struct VFileFIFO { struct VFile d; - struct CircleBuffer* backing; + struct mCircleBuffer* backing; }; static bool _vffClose(struct VFile* vf); @@ -21,7 +21,7 @@ static void _vffTruncate(struct VFile* vf, size_t size); static ssize_t _vffSize(struct VFile* vf); static bool _vffSync(struct VFile* vf, void* buffer, size_t size); -struct VFile* VFileFIFO(struct CircleBuffer* backing) { +struct VFile* VFileFIFO(struct mCircleBuffer* backing) { if (!backing) { return NULL; } @@ -61,12 +61,12 @@ static off_t _vffSeek(struct VFile* vf, off_t offset, int whence) { static ssize_t _vffRead(struct VFile* vf, void* buffer, size_t size) { struct VFileFIFO* vff = (struct VFileFIFO*) vf; - return CircleBufferRead(vff->backing, buffer, size); + return mCircleBufferRead(vff->backing, buffer, size); } static ssize_t _vffWrite(struct VFile* vf, const void* buffer, size_t size) { struct VFileFIFO* vff = (struct VFileFIFO*) vf; - return CircleBufferWrite(vff->backing, buffer, size); + return mCircleBufferWrite(vff->backing, buffer, size); } static void* _vffMap(struct VFile* vf, size_t size, int flags) { @@ -85,13 +85,13 @@ static void _vffUnmap(struct VFile* vf, void* memory, size_t size) { static void _vffTruncate(struct VFile* vf, size_t size) { struct VFileFIFO* vff = (struct VFileFIFO*) vf; if (!size) { - CircleBufferClear(vff->backing); + mCircleBufferClear(vff->backing); } } static ssize_t _vffSize(struct VFile* vf) { struct VFileFIFO* vff = (struct VFileFIFO*) vf; - return CircleBufferSize(vff->backing); + return mCircleBufferSize(vff->backing); } static bool _vffSync(struct VFile* vf, void* buffer, size_t size) {