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..6249e67d8c7 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) diff --git a/CMakeLists.txt b/CMakeLists.txt index a1e730e0430..24caaaf9f32 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") @@ -217,13 +217,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 +301,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 +313,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 +345,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 +489,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 +541,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}) @@ -853,10 +851,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) @@ -1314,11 +1312,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}") 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/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..50f29893065 --- /dev/null +++ b/include/mgba-util/audio-resampler.h @@ -0,0 +1,37 @@ +/* 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; + struct mInterpolatorSinc interp; + bool consume; +}; + +void mAudioResamplerInit(struct mAudioResampler*); +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..f01048f3445 --- /dev/null +++ b/include/mgba-util/interpolator.h @@ -0,0 +1,36 @@ +/* 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 + +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; +}; + +void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution, unsigned width); +void mInterpolatorSincDeinit(struct mInterpolatorSinc* interp); + +CXX_GUARD_END + +#endif 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/core.h b/include/mgba/core/core.h index 052e367957f..7eeb1d8fbc3 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 @@ -78,6 +78,7 @@ struct mCore { void (*getPixels)(struct mCore*, const void** buffer, size_t* stride); void (*putPixels)(struct mCore*, const void* buffer, size_t stride); + unsigned (*audioSampleRate)(const struct mCore*); struct blip_t* (*getAudioChannel)(struct mCore*, int ch); void (*setAudioBufferSize)(struct mCore*, size_t samples); size_t (*getAudioBufferSize)(struct mCore*); @@ -146,7 +147,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 +217,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/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/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/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..6702f5ff478 100644 --- a/include/mgba/internal/gba/audio.h +++ b/include/mgba/internal/gba/audio.h @@ -253,7 +253,7 @@ struct GBAMP2kTrack { struct GBAMP2kMusicPlayerTrack track; struct GBAMP2kSoundChannel* channel; uint8_t lastCommand; - struct CircleBuffer buffer; + struct mCircleBuffer buffer; uint32_t samplePlaying; float currentOffset; bool waiting; @@ -313,8 +313,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/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/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/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..18c1c20e16b 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -42,18 +42,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 +103,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 +140,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 +149,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 +174,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 +194,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 +218,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 +303,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,38 +328,41 @@ 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) { @@ -386,14 +391,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 +431,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 +476,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); @@ -494,7 +502,7 @@ bool mCoreThreadStart(struct mCoreThread* threadContext) { 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..cf5445e1570 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -33,10 +33,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); @@ -192,7 +192,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 +243,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 +383,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) { @@ -877,9 +877,10 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { 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 +933,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 +969,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..9ee6d3f8454 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); } @@ -445,6 +445,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 +1065,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,6 +1307,7 @@ struct mCore* GBCoreCreate(void) { core->setVideoGLTex = _GBCoreSetVideoGLTex; core->getPixels = _GBCoreGetPixels; core->putPixels = _GBCorePutPixels; + core->audioSampleRate = _GBCoreAudioSampleRate; core->getAudioChannel = _GBCoreGetAudioChannel; core->setAudioBufferSize = _GBCoreSetAudioBufferSize; core->getAudioBufferSize = _GBCoreGetAudioBufferSize; @@ -1352,7 +1358,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/audio.c b/src/gba/audio.c index fb0e1b62c63..b25273f1680 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -587,7 +587,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..4fb801c4ab3 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -313,7 +313,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); } @@ -374,7 +374,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; @@ -559,6 +559,11 @@ static void _GBACorePutPixels(struct mCore* core, const void* buffer, size_t str gba->video.renderer->putPixels(gba->video.renderer, stride, buffer); } +static unsigned _GBACoreAudioSampleRate(const struct mCore* core) { + UNUSED(core); + return 65536; +} + static struct blip_t* _GBACoreGetAudioChannel(struct mCore* core, int ch) { struct GBA* gba = core->board; switch (ch) { @@ -1004,27 +1009,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 +1081,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 +1223,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 +1285,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,6 +1520,7 @@ struct mCore* GBACoreCreate(void) { core->setVideoGLTex = _GBACoreSetVideoGLTex; core->getPixels = _GBACoreGetPixels; core->putPixels = _GBACorePutPixels; + core->audioSampleRate = _GBACoreAudioSampleRate; core->getAudioChannel = _GBACoreGetAudioChannel; core->setAudioBufferSize = _GBACoreSetAudioBufferSize; core->getAudioBufferSize = _GBACoreGetAudioBufferSize; @@ -1565,7 +1571,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 index 2a14f1ce661..e01b609b407 100644 --- a/src/gba/extra/audio-mixer.c +++ b/src/gba/extra/audio-mixer.c @@ -43,7 +43,7 @@ void _mp2kInit(void* cpu, struct mCPUComponent* component) { 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); + mCircleBufferInit(&mixer->activeTracks[i].buffer, 0x10000); } } @@ -51,7 +51,7 @@ 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); + mCircleBufferDeinit(&mixer->activeTracks[i].buffer); } } @@ -130,8 +130,8 @@ static void _stepSample(struct GBAAudioMixer* mixer, struct GBAMP2kTrack* track) (sample * track->channel->rightVolume * track->channel->envelopeV) >> 9 }; - CircleBufferWrite16(&track->buffer, stereo.left); - CircleBufferWrite16(&track->buffer, stereo.right); + mCircleBufferWrite16(&track->buffer, stereo.left); + mCircleBufferWrite16(&track->buffer, stereo.right); sampleOffset += mixer->p->sampleInterval / OVERSAMPLE; while (sampleOffset > freq) { @@ -265,7 +265,7 @@ void _mp2kStep(struct GBAAudioMixer* mixer) { } else { track->currentOffset = 0; track->samplePlaying = 0; - CircleBufferClear(&track->buffer); + mCircleBufferClear(&track->buffer); } } mixer->frame -= VIDEO_TOTAL_LENGTH / mixer->tempo; @@ -281,9 +281,9 @@ void _mp2kStep(struct GBAAudioMixer* mixer) { continue; } int16_t value; - CircleBufferRead16(&mixer->activeTracks[track].buffer, &value); + mCircleBufferRead16(&mixer->activeTracks[track].buffer, &value); sample.left += value; - CircleBufferRead16(&mixer->activeTracks[track].buffer, &value); + mCircleBufferRead16(&mixer->activeTracks[track].buffer, &value); sample.right += value; } sample.left = (sample.left * mixer->p->masterVolume) >> 8; diff --git a/src/gba/gba.c b/src/gba/gba.c index 7586ae66c06..41eee7435f0 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 = { @@ -1053,7 +1053,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..455c70bdb6c 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 { @@ -1263,7 +1263,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 +1333,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 +1391,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..fb4f63a56ba 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -47,8 +47,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, @@ -97,9 +98,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 +115,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 +162,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]); @@ -216,10 +212,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 +223,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 +250,7 @@ static int _batteryState(void) { } static void _guiPrepare(void) { - C3D_FrameDrawOn(bottomScreen[doubleBuffer]); + C3D_FrameDrawOn(bottomScreen); ctrSetViewportSize(320, 240, true); } @@ -349,9 +343,8 @@ 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); + blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 32768); + blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 32768); if (hasSound != NO_SOUND) { audioPos = 0; } @@ -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); @@ -702,7 +703,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) { @@ -856,7 +857,7 @@ int main(int argc, char* argv[]) { ndspChnReset(0); ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); ndspChnSetInterp(0, NDSP_INTERP_NONE); - ndspChnSetRate(0, 0x8000); + ndspChnSetRate(0, 32822); ndspChnWaveBufClear(0); audioLeft = linearMemAlign(AUDIO_SAMPLES * DSP_BUFFERS * 2 * sizeof(int16_t), 0x80); memset(dspBuffer, 0, sizeof(dspBuffer)); @@ -979,13 +980,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..9e67dec19d8 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -2315,7 +2315,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); @@ -2554,7 +2554,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/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..755021f6f5f 100644 --- a/src/platform/psp2/psp2-context.c +++ b/src/platform/psp2/psp2-context.c @@ -65,7 +65,7 @@ static struct mSceRotationSource { static struct mSceRumble { struct mRumble d; - struct CircleBuffer history; + struct mCircleBuffer history; int current; } rumble; @@ -149,18 +149,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 = { @@ -342,7 +342,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; @@ -376,7 +376,7 @@ void mPSP2Setup(struct mGUIRunner* runner) { void mPSP2LoadROM(struct mGUIRunner* runner) { float rate = 60.0f / 1.001f; sceDisplayGetRefreshRate(&rate); - double ratio = GBAAudioCalculateRatio(1, rate, 1); + double ratio = mCoreCalculateFramerateRatio(runner->core, rate); 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); @@ -482,7 +482,7 @@ void mPSP2Unpaused(struct mGUIRunner* runner) { void mPSP2Teardown(struct mGUIRunner* runner) { UNUSED(runner); - CircleBufferDeinit(&rumble.history); + mCircleBufferDeinit(&rumble.history); 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..e06e4f5ccdc 100644 --- a/src/platform/python/_builder.h +++ b/src/platform/python/_builder.h @@ -67,7 +67,7 @@ void free(void*); #include #include #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS #include #include #endif 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/qt/AudioDevice.cpp b/src/platform/qt/AudioDevice.cpp index 74f86344495..ec6dd31333b 100644 --- a/src/platform/qt/AudioDevice.cpp +++ b/src/platform/qt/AudioDevice.cpp @@ -26,7 +26,7 @@ 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); + double fauxClock = mCoreCalculateFramerateRatio(m_context->core, m_context->impl->sync.fpsTarget); 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); @@ -62,3 +62,17 @@ qint64 AudioDevice::writeData(const char*, qint64) { LOG(QT, WARN) << tr("Writing data to read-only audio device"); return 0; } + +bool AudioDevice::atEnd() const { + return !bytesAvailable(); +} + +qint64 AudioDevice::bytesAvailable() const { + if (!m_context->core) { + return true; + } + mCoreSyncLockAudio(&m_context->impl->sync); + int available = blip_samples_avail(m_context->core->getAudioChannel(m_context->core, 0)); + mCoreSyncUnlockAudio(&m_context->impl->sync); + return available * sizeof(mStereoSample); +} diff --git a/src/platform/qt/AudioDevice.h b/src/platform/qt/AudioDevice.h index 0794b37431d..e4386bda293 100644 --- a/src/platform/qt/AudioDevice.h +++ b/src/platform/qt/AudioDevice.h @@ -20,6 +20,8 @@ Q_OBJECT void setInput(mCoreThread* input); void setFormat(const QAudioFormat& format); + bool atEnd() const override; + qint64 bytesAvailable() const override; protected: virtual qint64 readData(char* data, qint64 maxSize) override; diff --git a/src/platform/qt/AudioProcessorQt.cpp b/src/platform/qt/AudioProcessorQt.cpp index 8d12cd935b8..3e4c5474ec8 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,25 +65,45 @@ 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; + } + 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->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(); } } @@ -98,3 +131,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->atEnd()) { + start(); + m_recheckTimer.stop(); + } +} +#endif diff --git a/src/platform/qt/AudioProcessorQt.h b/src/platform/qt/AudioProcessorQt.h index 0e56907e106..bdfa17a7701 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,8 +39,17 @@ 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; 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..673170f0f68 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -25,12 +25,18 @@ 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}) +set(QT_VERSIONS 6 5) +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 +231,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 +248,7 @@ if(NOT AUDIO_SRC) return() endif() -if(USE_DEBUGGERS) +if(ENABLE_DEBUGGERS) list(APPEND SOURCE_FILES DebuggerController.cpp DebuggerConsole.cpp @@ -253,7 +256,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 +338,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..e95da696c2a 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 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..bd52b086293 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()) { @@ -602,7 +610,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 +622,7 @@ void Window::gdbOpen() { } #endif -#ifdef USE_DEBUGGERS +#ifdef ENABLE_DEBUGGERS void Window::consoleOpen() { if (!m_console) { m_console = new DebuggerConsoleController(this); @@ -1093,7 +1101,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 +1525,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 +1563,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 +1709,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 +1746,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 +2164,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 +2240,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..ff7ddd2dacb 100644 --- a/src/platform/sdl/sdl-audio.c +++ b/src/platform/sdl/sdl-audio.c @@ -99,16 +99,16 @@ static void _mSDLAudioCallback(void* context, Uint8* data, int len) { } blip_t* left = NULL; blip_t* right = NULL; - int32_t clockRate = GBA_ARM7TDMI_FREQUENCY; + int32_t clockRate = 1; if (audioContext->core) { + clockRate = audioContext->core->frequency(audioContext->core); left = audioContext->core->getAudioChannel(audioContext->core, 0); right = audioContext->core->getAudioChannel(audioContext->core, 1); - clockRate = audioContext->core->frequency(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); } 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..0dc866dfd1b 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -21,15 +22,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 +89,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 +111,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 +267,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 +302,22 @@ 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); + blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), samplerate); + blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), samplerate); + + 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 +366,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 +393,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 +552,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); @@ -593,27 +578,34 @@ static bool _running(struct mGUIRunner* runner) { static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { 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); return; } - _audioWait(10000000); - } - if (enqueuedBuffers >= N_BUFFERS) { - blip_clear(left); - blip_clear(right); - return; + audrenWaitFrame(); } - struct mStereoSample* samples = audioBuffer[audioBufferActive]; + struct mStereoSample* samples = audioBuffer[i]; 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; + armDCacheFlush(samples, SAMPLES * sizeof(struct mStereoSample)); + audrvVoiceAddWaveBuf(&audrenDriver, 0, &audrvBuffer[i]); + + if (!audrvVoiceIsPlaying(&audrenDriver, 0)) { + audrvVoiceStart(&audrenDriver, 0); + } + audrvUpdate(&audrenDriver); } void _setRumble(struct mRumble* rumble, int enable) { @@ -626,6 +618,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 +637,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 +858,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 +898,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 +1081,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 +1113,8 @@ int main(int argc, char* argv[]) { mGUIDeinit(&runner); - audoutStopAudioOut(); + audrvClose(&audrenDriver); + audrenExit(); GUIFontDestroy(font); glDeinit(); 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..4e955a0ad30 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -247,7 +247,7 @@ static void reconfigureScreen(struct mGUIRunner* runner) { runner->params.width = vmode->fbWidth * guiScale * wAdjust; runner->params.height = vmode->efbHeight * guiScale * hAdjust; if (runner->core) { - double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1); + double ratio = mCoreCalculateFramerateRatio(runner->core, audioSampleRate); 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); } @@ -1422,7 +1422,7 @@ void _setup(struct mGUIRunner* runner) { } runner->core->setAudioBufferSize(runner->core, SAMPLES); - double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1); + double ratio = mCoreCalculateFramerateRatio(runner->core, audioSampleRate); 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); @@ -1731,7 +1731,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/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..0f0147c348b --- /dev/null +++ b/src/util/audio-resampler.c @@ -0,0 +1,94 @@ +/* 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) { + memset(resampler, 0, sizeof(*resampler)); + mInterpolatorSincInit(&resampler->interp, 0, 0); + resampler->lowWaterMark = resampler->interp.width; + resampler->highWaterMark = 0; +} + +void mAudioResamplerDeinit(struct mAudioResampler* resampler) { + mInterpolatorSincDeinit(&resampler->interp); + 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.d; + 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..21dd91d2c0b --- /dev/null +++ b/src/util/interpolator.c @@ -0,0 +1,84 @@ +/* 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, +}; + +static int16_t mInterpolatorSincInterpolate(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; +} 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) {