diff --git a/.gitignore b/.gitignore index 20c19fc41..0e5d801be 100644 --- a/.gitignore +++ b/.gitignore @@ -10,10 +10,12 @@ *.a *.dylib *.dll +*.lib *.exe *.o *.so CMakeCache.txt CMakeFiles CMakeSettings.json +cmake_install.cmake version.c diff --git a/CHANGES b/CHANGES index f03b446c1..4d91d6ec9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,50 @@ -0.10.0: (Future) +0.11.0: (Future) +Features: + - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 + - Debugger: Add range watchpoints +Emulation fixes: + - GBA Memory: Make VRAM access stalls only apply to BG RAM + - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) +Other fixes: + - Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784) + - Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794) + - Qt: Disable sync while running scripts from main thread (fixes mgba.io/i/2738) + - Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560) +Misc: + - GB Serialize: Add missing savestate support for MBC6 and NT (newer) + - GBA: Improve detection of valid ELF ROMs + - Qt: Include wayland QPA in AppImage (fixes mgba.io/i/2796) + +0.10.1: (2023-01-10) +Emulation fixes: + - GB Audio: Fix channels 1/2 not playing when resetting volume (fixes mgba.io/i/2614) + - GB Audio: Fix channel 3 volume being changed between samples (fixes mgba.io/i/1896) + - GB Audio: Fix up boot sequence + - GB Audio: Fix updating channels other than 2 when writing NR5x + - GB Memory: Actually, HDMAs should start when LCD is off (fixes mgba.io/i/2662) + - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) + - GB SIO: Further fix bidirectional transfer starting + - GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716) + - GBA BIOS: Include timing in degenerate ArcTan2 cases (fixes mgba.io/i/2763) + - GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes mgba.io/i/2489) +Other fixes: + - GBA: Fix forceskip BIOS logic for multiboot ROMs (fixes mgba.io/i/2753) + - GBA Cheats: Fix issues detecting unencrypted cheats (fixes mgba.io/i/2724) + - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) + - Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693) + - Qt: Don't re-enable sync if GBA link modes aren't the same (fixes mgba.io/i/2044) + - Qt: Improve handling of multiplayer syncing (fixes mgba.io/i/2720) + - Qt: Fix initializing update revision info + - Qt: Redo stable branch detection heuristic (fixes mgba.io/i/2679) + - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) + - VFS: Fix minizip write returning 0 on success instead of size +Misc: + - macOS: Add category to plist (closes mgba.io/i/2691) + - macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700) + - Qt: Keep track of current palette preset name (fixes mgba.io/i/2680) + - Qt: Move OpenGL proxy onto its own thread (fixes mgba.io/i/2493) + +0.10.0: (2022-10-11) Features: - Preliminary Lua scripting support - Presets for Game Boy palettes @@ -6,7 +52,7 @@ Features: - Tool for converting scanned pictures of e-Reader cards to raw dotcode data - Options for muting when inactive, minimized, or for different players in multiplayer - Cheat code support in homebrew ports - - Acclerometer and gyro support for controllers on PC + - Accelerometer and gyro support for controllers on PC - Support for combo "Super Game Boy Color" SGB + GBC ROM hacks - Improved support for HuC-3 mapper, including RTC - Support for 64 kiB SRAM saves used in some bootlegs @@ -20,13 +66,13 @@ Emulation fixes: - ARM7: Fix unsigned multiply timing - GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378) - GB: Fix HALT breaking M-cycle alignment (fixes mgba.io/i/250) - - GB Audio: Fix channel 1/2 reseting edge cases (fixes mgba.io/i/1925) + - GB Audio: Fix channel 1/2 resetting edge cases (fixes mgba.io/i/1925) - GB Audio: Properly apply per-model audio differences - GB Audio: Revamp channel rendering - GB Audio: Fix APU re-enable timing glitch - GB I/O: Fix writing to WAVE RAM behavior (fixes mgba.io/i/1334) - - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) - GB MBC: Fix edge case with Pocket Cam register accesses (fixes mgba.io/i/2557) + - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) - GB Serialize: Fix loading MBC1 states that affect bank 0 (fixes mgba.io/i/2402) - GB SIO: Fix bidirectional transfer starting (fixes mgba.io/i/2290) - GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339) @@ -130,7 +176,7 @@ Emulation fixes: Other fixes: - ARM Decoder: Fix decoding of lsl r0 (fixes mgba.io/i/2349) - FFmpeg: Don't attempt to use YUV 4:2:0 for lossless videos (fixes mgba.io/i/2084) - - GB Video: Fix memory leak when reseting SGB games + - GB Video: Fix memory leak when resetting SGB games - GBA: Fix out of bounds ROM accesses on patched ROMs smaller than 32 MiB - GBA: Fix maximum tile ID in caching for 256-color modes - GBA Video: Fix cache updating with proxy and GL renderers @@ -241,7 +287,7 @@ Emulation fixes: - GBA BIOS: Implement dummy sound driver calls - GBA BIOS: Improve HLE BIOS timing - GBA BIOS: Fix reloading video registers after reset (fixes mgba.io/i/1808) - - GBA BIOS: Make HLE BIOS calls interruptable (fixes mgba.io/i/1711 and mgba.io/i/1823) + - GBA BIOS: Make HLE BIOS calls interruptible (fixes mgba.io/i/1711 and mgba.io/i/1823) - GBA BIOS: Fix invalid decompression bounds checking - GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320) - GBA DMA: Fix ordering and timing of overlapping DMAs @@ -257,7 +303,7 @@ Emulation fixes: - GBA Serialize: Fix alignment check when loading states - GBA SIO: Fix copying Normal mode transfer values - GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800) - - GBA SIO: Fix deseralizing SIO registers + - GBA SIO: Fix deserializing SIO registers - GBA SIO: Fix hanging on starting a second multiplayer window (fixes mgba.io/i/854) - GBA SIO: Fix Normal mode transfer start timing (fixes mgba.io/i/425) - GBA Timers: Fix toggling timer cascading while timer is active (fixes mgba.io/i/2043) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c0b987d1..42cff1ebc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ if(NOT MSVC) # mingw32 likes to complain about using the "wrong" format strings despite them actually working set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-format") endif() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=implicit-function-declaration") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=implicit-function-declaration -Werror=implicit-int") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}") else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-") @@ -235,6 +235,7 @@ elseif(UNIX) endif() if(APPLE) + execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK) add_definitions(-D_DARWIN_C_SOURCE) list(APPEND OS_LIB "-framework Foundation") if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "10.0") # Darwin 10.x is Mac OS X 10.6 @@ -413,7 +414,7 @@ endif() if(BUILD_GL) find_package(OpenGL QUIET) - if(NOT OPENGL_FOUND) + if(NOT OPENGL_FOUND OR (APPLE AND MACOSX_SDK VERSION_GREATER 10.14)) set(BUILD_GL OFF CACHE BOOL "OpenGL not found" FORCE) elseif(UNIX AND NOT APPLE AND TARGET OpenGL::GL) set(OPENGL_LIBRARY OpenGL::GL) @@ -430,6 +431,11 @@ if(NOT BUILD_GL AND NOT LIBMGBA_ONLY) endif() if(BUILD_GLES2 AND NOT BUILD_GL) + if(APPLE AND MACOSX_SDK VERSION_GREATER 10.14) + find_package(OpenGL QUIET) + set(OPENGLES2_INCLUDE_DIR ${OPENGL_INCLUDE_DIR}) + set(OPENGLES2_LIBRARY ${OPENGL_LIBRARY}) + endif() find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h) find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM) if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY) @@ -443,6 +449,11 @@ if(BUILD_GLES2) endif() if(BUILD_GLES3 AND NOT BUILD_GL) + if(APPLE AND MACOSX_SDK VERSION_GREATER 10.14) + find_package(OpenGL QUIET) + set(OPENGLES3_INCLUDE_DIR ${OPENGL_INCLUDE_DIR}) + set(OPENGLES3_LIBRARY ${OPENGL_LIBRARY}) + endif() find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h) find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2) if(NOT OPENGLES3_INCLUDE_DIR OR NOT OPENGLES3_LIBRARY) @@ -629,14 +640,15 @@ endif() if(USE_LIBZIP) if(TARGET libzip::zip) - list(APPEND DEPENDENCY_LIB libzip::zip) + set(ZIP_LIBRARIES libzip::zip) elseif(TARGET zip) - list(APPEND DEPENDENCY_LIB zip) + set(ZIP_LIBRARIES zip) else() include_directories(AFTER ${LIBZIP_INCLUDE_DIRS}) link_directories(${LIBZIP_LIBRARY_DIRS}) - list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES}) + set(ZIP_LIBRARIES ${LIBZIP_LIBRARIES}) endif() + list(APPEND DEPENDENCY_LIB ${ZIP_LIBRARIES}) list(APPEND FEATURES LIBZIP) list(APPEND VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-zip.c) string(REGEX MATCH "^[0-9]+" LIBZIP_VERSION_MAJOR "${libzip_VERSION}") @@ -650,6 +662,7 @@ if(USE_LIBZIP) elseif(USE_MINIZIP) include_directories(AFTER ${MINIZIP_INCLUDE_DIRS}) link_directories(${MINIZIP_LIBRARY_DIRS}) + set(ZIP_LIBRARIES ${MINIZIP_LIBRARIES}) list(APPEND DEPENDENCY_LIB ${MINIZIP_LIBRARIES}) list(APPEND FEATURES MINIZIP) list(APPEND VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-zip.c) @@ -705,8 +718,12 @@ if (USE_LZMA) endif() if(USE_EPOXY) - list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c) - list(APPEND FEATURE_DEFINES BUILD_GL BUILD_GLES2 BUILD_GLES3) + if(NOT APPLE OR NOT MACOSX_SDK VERSION_GREATER 10.14) + list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c) + list(APPEND FEATURE_DEFINES BUILD_GL) + endif() + list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c) + list(APPEND FEATURE_DEFINES BUILD_GLES2 BUILD_GLES3) list(APPEND FEATURES EPOXY) include_directories(AFTER ${EPOXY_INCLUDE_DIRS}) link_directories(${EPOXY_LIBRARY_DIRS}) @@ -778,6 +795,7 @@ add_subdirectory(src/sm83) add_subdirectory(src/util) list(APPEND GUI_SRC ${EXTRA_GUI_SRC}) +list(APPEND UTIL_BASE_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c) list(APPEND UTIL_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c) set(TEST_SRC ${CORE_TEST_SRC}) @@ -968,12 +986,12 @@ if(BUILD_QT AND (WIN32 OR APPLE OR CMAKE_SYSTEM_NAME STREQUAL "Linux")) endif() if(BUILD_UPDATER) - add_executable(updater-stub WIN32 ${CORE_VFS_SRC} ${OS_SRC} ${UTIL_SRC} ${THIRD_PARTY_SRC} + add_executable(updater-stub WIN32 ${CORE_VFS_SRC} ${VFS_SRC} ${OS_SRC} ${UTIL_BASE_SRC} ${THIRD_PARTY_SRC} ${CMAKE_CURRENT_SOURCE_DIR}/src/core/config.c ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater.c ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater-main.c) - target_link_libraries(updater-stub ${OS_LIB} ${PLATFORM_LIBRARY}) - set_target_properties(updater-stub PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FUNCTION_DEFINES};BUILD_STATIC") + target_link_libraries(updater-stub ${ZLIB_LIBRARY} ${ZLIB_LIBRARY} ${ZIP_LIBRARIES} ${OS_LIB} ${PLATFORM_LIBRARY}) + set_target_properties(updater-stub PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FUNCTION_DEFINES};${FEATURE_DEFINES};BUILD_STATIC") if(MSVC) set_target_properties(updater-stub PROPERTIES LINK_FLAGS /ENTRY:mainCRTStartup) else() diff --git a/README.md b/README.md index 2d8837c53..20507604e 100644 --- a/README.md +++ b/README.md @@ -50,15 +50,15 @@ The following mappers are fully supported: - MBC2 - MBC3 - MBC3+RTC +- MBC30 - MBC5 - MBC5+Rumble - MBC7 - Wisdom Tree (unlicensed) +- NT "old type" 1 and 2 (unlicensed multicart) - NT "new type" (unlicensed MBC5-like) - Pokémon Jade/Diamond (unlicensed) -- BBD (unlicensed MBC5-like) -- Hitek (unlicensed MBC5-like) -- Sachen MMC1 +- Sachen MMC1 (unlicensed) The following mappers are partially supported: @@ -69,6 +69,10 @@ The following mappers are partially supported: - HuC-1 (missing IR support) - HuC-3 (missing IR support) - Sachen MMC2 (missing alternate wiring support) +- BBD (missing logo switching) +- Hitek (missing logo switching) +- GGB-81 (missing logo switching) +- Li Cheng (missing logo switching) ### Planned features diff --git a/cinema/gba/blend/disabled-bg-semitrans-blend/baseline_0000.png b/cinema/gba/blend/disabled-bg-semitrans-blend/baseline_0000.png new file mode 100644 index 000000000..f90f448c4 Binary files /dev/null and b/cinema/gba/blend/disabled-bg-semitrans-blend/baseline_0000.png differ diff --git a/cinema/gba/blend/disabled-bg-semitrans-blend/config.ini b/cinema/gba/blend/disabled-bg-semitrans-blend/config.ini new file mode 100644 index 000000000..f26f6d8b1 --- /dev/null +++ b/cinema/gba/blend/disabled-bg-semitrans-blend/config.ini @@ -0,0 +1,3 @@ +[testinfo] +skip=10 +frames=1 diff --git a/cinema/gba/blend/disabled-bg-semitrans-blend/test.gba b/cinema/gba/blend/disabled-bg-semitrans-blend/test.gba new file mode 100644 index 000000000..91cb67e5f Binary files /dev/null and b/cinema/gba/blend/disabled-bg-semitrans-blend/test.gba differ diff --git a/cinema/gba/blend/gbg-blend/baseline_0000.png b/cinema/gba/blend/gbg-blend/baseline_0000.png new file mode 100644 index 000000000..2fbf58114 Binary files /dev/null and b/cinema/gba/blend/gbg-blend/baseline_0000.png differ diff --git a/cinema/gba/blend/gbg-blend/baseline_0001.png b/cinema/gba/blend/gbg-blend/baseline_0001.png new file mode 100644 index 000000000..5bc3c37a9 Binary files /dev/null and b/cinema/gba/blend/gbg-blend/baseline_0001.png differ diff --git a/cinema/gba/blend/gbg-blend/baseline_0002.png b/cinema/gba/blend/gbg-blend/baseline_0002.png new file mode 100644 index 000000000..1d38b9113 Binary files /dev/null and b/cinema/gba/blend/gbg-blend/baseline_0002.png differ diff --git a/cinema/gba/blend/gbg-blend/baseline_0003.png b/cinema/gba/blend/gbg-blend/baseline_0003.png new file mode 100644 index 000000000..368572a31 Binary files /dev/null and b/cinema/gba/blend/gbg-blend/baseline_0003.png differ diff --git a/cinema/gba/blend/gbg-blend/baseline_0004.png b/cinema/gba/blend/gbg-blend/baseline_0004.png new file mode 100644 index 000000000..25ac9a790 Binary files /dev/null and b/cinema/gba/blend/gbg-blend/baseline_0004.png differ diff --git a/cinema/gba/blend/gbg-blend/baseline_0005.png b/cinema/gba/blend/gbg-blend/baseline_0005.png new file mode 100644 index 000000000..c6d340652 Binary files /dev/null and b/cinema/gba/blend/gbg-blend/baseline_0005.png differ diff --git a/cinema/gba/blend/gbg-blend/test.mvl b/cinema/gba/blend/gbg-blend/test.mvl new file mode 100644 index 000000000..02be0a86a Binary files /dev/null and b/cinema/gba/blend/gbg-blend/test.mvl differ diff --git a/include/mgba-util/sfo.h b/include/mgba-util/sfo.h new file mode 100644 index 000000000..89404ceca --- /dev/null +++ b/include/mgba-util/sfo.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2013-2022 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 SFO_H +#define SFO_H + +#include + +CXX_GUARD_START + +#include + +void SfoInit(struct Table* sfo); + +static inline void SfoDeinit(struct Table* sfo) { + HashTableDeinit(sfo); +} + +struct VFile; +bool SfoWrite(struct Table* sfo, struct VFile* vf); + +bool SfoAddU32Value(struct Table* sfo, const char* name, uint32_t value); +bool SfoAddStrValue(struct Table* sfo, const char* name, const char* value); +bool SfoSetTitle(struct Table* sfo, const char* title); + +CXX_GUARD_END + +#endif diff --git a/include/mgba-util/vector.h b/include/mgba-util/vector.h index a4ec71b65..8c0f11e45 100644 --- a/include/mgba-util/vector.h +++ b/include/mgba-util/vector.h @@ -34,6 +34,16 @@ CXX_GUARD_START size_t NAME ## Index(const struct NAME* vector, const TYPE* member); \ void NAME ## Copy(struct NAME* dest, const struct NAME* src); +#ifdef NDEBUG +#define VECTOR_BOUNDS_CHECK(NAME, V, L) +#else +#define VECTOR_BOUNDS_CHECK(NAME, V, L) \ + if ((L) >= (V)->size) { \ + fprintf(stderr, "Vector type %s invalid access of index %" PRIuPTR " into vector of size %" PRIuPTR "\n", #NAME, (L), (V)->size); \ + abort(); \ + } +#endif + #define DEFINE_VECTOR(NAME, TYPE) \ void NAME ## Init(struct NAME* vector, size_t capacity) { \ vector->size = 0; \ @@ -50,9 +60,11 @@ CXX_GUARD_START vector->size = 0; \ } \ TYPE* NAME ## GetPointer(struct NAME* vector, size_t location) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ return &vector->vector[location]; \ } \ TYPE const* NAME ## GetConstPointer(const struct NAME* vector, size_t location) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ return &vector->vector[location]; \ } \ TYPE* NAME ## Append(struct NAME* vector) { \ @@ -78,10 +90,12 @@ CXX_GUARD_START vector->vector = realloc(vector->vector, vector->capacity * sizeof(TYPE)); \ } \ void NAME ## Shift(struct NAME* vector, size_t location, size_t difference) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ memmove(&vector->vector[location], &vector->vector[location + difference], (vector->size - location - difference) * sizeof(TYPE)); \ vector->size -= difference; \ } \ void NAME ## Unshift(struct NAME* vector, size_t location, size_t difference) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ NAME ## Resize(vector, difference); \ memmove(&vector->vector[location + difference], &vector->vector[location], (vector->size - location - difference) * sizeof(TYPE)); \ } \ @@ -97,8 +111,16 @@ CXX_GUARD_START dest->size = src->size; \ } \ -DECLARE_VECTOR(StringList, char*); DECLARE_VECTOR(IntList, int); +DECLARE_VECTOR(SInt8List, int8_t); +DECLARE_VECTOR(SInt16List, int16_t); +DECLARE_VECTOR(SInt32List, int32_t); +DECLARE_VECTOR(SIntPtrList, intptr_t); +DECLARE_VECTOR(UInt8List, uint8_t); +DECLARE_VECTOR(UInt16List, uint16_t); +DECLARE_VECTOR(UInt32List, uint32_t); +DECLARE_VECTOR(UIntPtrList, uintptr_t); +DECLARE_VECTOR(StringList, char*); CXX_GUARD_END diff --git a/include/mgba/core/input.h b/include/mgba/core/input.h index f7ef212a5..f5d8002ed 100644 --- a/include/mgba/core/input.h +++ b/include/mgba/core/input.h @@ -55,6 +55,7 @@ int mInputMapKeyBits(const struct mInputMap* map, uint32_t type, uint32_t bits, void mInputBindKey(struct mInputMap*, uint32_t type, int key, int input); int mInputQueryBinding(const struct mInputMap*, uint32_t type, int input); void mInputUnbindKey(struct mInputMap*, uint32_t type, int input); +void mInputUnbindAllKeys(struct mInputMap*, uint32_t type); int mInputMapAxis(const struct mInputMap*, uint32_t type, int axis, int value); int mInputClearAxis(const struct mInputMap*, uint32_t type, int axis, int keys); @@ -69,6 +70,7 @@ void mInputBindHat(struct mInputMap*, uint32_t type, int id, const struct mInput bool mInputQueryHat(const struct mInputMap*, uint32_t type, int id, struct mInputHatBindings* bindings); void mInputUnbindHat(struct mInputMap*, uint32_t type, int id); void mInputUnbindAllHats(struct mInputMap*, uint32_t type); +void mInputEnumerateHats(const struct mInputMap*, uint32_t type, void (handler(int hat, const struct mInputHatBindings* bindings, void* user)), void* user); bool mInputMapLoad(struct mInputMap*, uint32_t type, const struct Configuration*); void mInputMapSave(const struct mInputMap*, uint32_t type, struct Configuration*); diff --git a/include/mgba/core/serialize.h b/include/mgba/core/serialize.h index 59ca1c19f..ee9de69a1 100644 --- a/include/mgba/core/serialize.h +++ b/include/mgba/core/serialize.h @@ -16,6 +16,7 @@ enum mStateExtdataTag { EXTDATA_SAVEDATA = 2, EXTDATA_CHEATS = 3, EXTDATA_RTC = 4, + EXTDATA_SCREENSHOT_DIMENSIONS = 5, EXTDATA_META_TIME = 0x101, EXTDATA_META_CREATOR = 0x102, EXTDATA_MAX diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h index fc9ac0f65..b460623e3 100644 --- a/include/mgba/debugger/debugger.h +++ b/include/mgba/debugger/debugger.h @@ -88,8 +88,9 @@ struct mBreakpoint { struct mWatchpoint { ssize_t id; - uint32_t address; int segment; + uint32_t minAddress; + uint32_t maxAddress; enum mWatchpointType type; struct ParseTree* condition; }; diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index 6b7e1adac..0ec54559f 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -41,9 +41,13 @@ enum GBMemoryBankControllerType { GB_MBC5_RUMBLE = 0x105, GB_UNL_WISDOM_TREE = 0x200, GB_UNL_PKJD = 0x203, + GB_UNL_NT_OLD_1 = 0x210, + GB_UNL_NT_OLD_2 = 0x211, GB_UNL_NT_NEW = 0x212, - GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior + GB_UNL_BBD = 0x220, GB_UNL_HITEK = 0x221, + GB_UNL_LI_CHENG = 0x222, + GB_UNL_GGB81 = 0x223, GB_UNL_SACHEN_MMC1 = 0x230, GB_UNL_SACHEN_MMC2 = 0x231, }; diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 6e02ffa4d..d3a42a229 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -191,7 +191,6 @@ struct GBMBC1State { }; struct GBMBC6State { - bool sramAccess; bool flashBank0; bool flashBank1; }; @@ -238,6 +237,13 @@ struct GBPKJDState { uint8_t reg[2]; }; +struct GBNTOldState { + bool swapped; + uint8_t baseBank; + uint8_t bankCount; + bool rumble; +}; + struct GBNTNewState { bool splitMode; }; @@ -263,6 +269,7 @@ union GBMBCState { struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; struct GBHuC3State huc3; + struct GBNTOldState ntOld; struct GBNTNewState ntNew; struct GBPKJDState pkjd; struct GBBBDState bbd; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 3087e6b02..f4433c495 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -266,6 +266,10 @@ DECL_BITS(GBSerializedVideoFlags, Mode, 2, 2); DECL_BIT(GBSerializedVideoFlags, NotModeEventScheduled, 4); DECL_BIT(GBSerializedVideoFlags, NotFrameEventScheduled, 5); +DECL_BITFIELD(GBSerializedMBC6Flags, uint8_t); +DECL_BIT(GBSerializedMBC6Flags, FlashBank0, 0); +DECL_BIT(GBSerializedMBC6Flags, FlashBank1, 1); + DECL_BITFIELD(GBSerializedMBC7Flags, uint8_t); DECL_BITS(GBSerializedMBC7Flags, Command, 0, 2); DECL_BIT(GBSerializedMBC7Flags, Writable, 2); @@ -274,6 +278,10 @@ DECL_BITFIELD(GBSerializedSachenFlags, uint8_t); DECL_BITS(GBSerializedSachenFlags, Transition, 0, 6); DECL_BITS(GBSerializedSachenFlags, Locked, 6, 2); +DECL_BITFIELD(GBSerializedNTOldFlags, uint8_t); +DECL_BIT(GBSerializedNTOldFlags, Swapped, 0); +DECL_BIT(GBSerializedNTOldFlags, Rumble, 1); + DECL_BITFIELD(GBSerializedMemoryFlags, uint16_t); DECL_BIT(GBSerializedMemoryFlags, SramAccess, 0); DECL_BIT(GBSerializedMemoryFlags, RtcAccess, 1); @@ -392,6 +400,11 @@ struct GBSerializedState { struct { uint64_t lastLatch; } rtc; + struct { + GBSerializedMBC6Flags flags; + uint8_t bank1; + uint8_t sramBank1; + } mbc6; struct { uint8_t state; GBMBC7Field eeprom; @@ -416,6 +429,15 @@ struct GBSerializedState { uint8_t value; uint8_t mode; } huc3; + struct { + GBSerializedNTOldFlags flags; + uint8_t baseBank; + uint8_t bankCount; + } ntOld; + struct { + uint8_t splitMode; + uint8_t bank1; + } ntNew; struct { uint8_t dataSwapMode; uint8_t bankSwapMode; diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index 206aedf70..020dd2ab5 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -154,6 +154,12 @@ void GBAHalt(struct GBA* gba); void GBAStop(struct GBA* gba); void GBADebug(struct GBA* gba, uint16_t value); +#ifdef USE_ELF +struct ELF; + +bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target); +#endif + #ifdef USE_DEBUGGERS struct mDebugger; void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger); diff --git a/include/mgba/internal/gba/memory.h b/include/mgba/internal/gba/memory.h index 93d565a16..e455e1cc1 100644 --- a/include/mgba/internal/gba/memory.h +++ b/include/mgba/internal/gba/memory.h @@ -21,60 +21,60 @@ CXX_GUARD_START #include enum GBAMemoryRegion { - REGION_BIOS = 0x0, - REGION_WORKING_RAM = 0x2, - REGION_WORKING_IRAM = 0x3, - REGION_IO = 0x4, - REGION_PALETTE_RAM = 0x5, - REGION_VRAM = 0x6, - REGION_OAM = 0x7, - REGION_CART0 = 0x8, - REGION_CART0_EX = 0x9, - REGION_CART1 = 0xA, - REGION_CART1_EX = 0xB, - REGION_CART2 = 0xC, - REGION_CART2_EX = 0xD, - REGION_CART_SRAM = 0xE, - REGION_CART_SRAM_MIRROR = 0xF + GBA_REGION_BIOS = 0x0, + GBA_REGION_EWRAM = 0x2, + GBA_REGION_IWRAM = 0x3, + GBA_REGION_IO = 0x4, + GBA_REGION_PALETTE_RAM = 0x5, + GBA_REGION_VRAM = 0x6, + GBA_REGION_OAM = 0x7, + GBA_REGION_ROM0 = 0x8, + GBA_REGION_ROM0_EX = 0x9, + GBA_REGION_ROM1 = 0xA, + GBA_REGION_ROM1_EX = 0xB, + GBA_REGION_ROM2 = 0xC, + GBA_REGION_ROM2_EX = 0xD, + GBA_REGION_SRAM = 0xE, + GBA_REGION_SRAM_MIRROR = 0xF }; enum GBAMemoryBase { - BASE_BIOS = 0x00000000, - BASE_WORKING_RAM = 0x02000000, - BASE_WORKING_IRAM = 0x03000000, - BASE_IO = 0x04000000, - BASE_PALETTE_RAM = 0x05000000, - BASE_VRAM = 0x06000000, - BASE_OAM = 0x07000000, - BASE_CART0 = 0x08000000, - BASE_CART0_EX = 0x09000000, - BASE_CART1 = 0x0A000000, - BASE_CART1_EX = 0x0B000000, - BASE_CART2 = 0x0C000000, - BASE_CART2_EX = 0x0D000000, - BASE_CART_SRAM = 0x0E000000, - BASE_CART_SRAM_MIRROR = 0x0F000000 + GBA_BASE_BIOS = 0x00000000, + GBA_BASE_EWRAM = 0x02000000, + GBA_BASE_IWRAM = 0x03000000, + GBA_BASE_IO = 0x04000000, + GBA_BASE_PALETTE_RAM = 0x05000000, + GBA_BASE_VRAM = 0x06000000, + GBA_BASE_OAM = 0x07000000, + GBA_BASE_ROM0 = 0x08000000, + GBA_BASE_ROM0_EX = 0x09000000, + GBA_BASE_ROM1 = 0x0A000000, + GBA_BASE_ROM1_EX = 0x0B000000, + GBA_BASE_ROM2 = 0x0C000000, + GBA_BASE_ROM2_EX = 0x0D000000, + GBA_BASE_SRAM = 0x0E000000, + GBA_BASE_SRAM_MIRROR = 0x0F000000 }; enum { - SIZE_BIOS = 0x00004000, - SIZE_WORKING_RAM = 0x00040000, - SIZE_WORKING_IRAM = 0x00008000, - SIZE_IO = 0x00000400, - SIZE_PALETTE_RAM = 0x00000400, - SIZE_VRAM = 0x00018000, - SIZE_OAM = 0x00000400, - SIZE_CART0 = 0x02000000, - SIZE_CART1 = 0x02000000, - SIZE_CART2 = 0x02000000, - SIZE_CART_SRAM = 0x00008000, - SIZE_CART_SRAM512 = 0x00010000, - SIZE_CART_FLASH512 = 0x00010000, - SIZE_CART_FLASH1M = 0x00020000, - SIZE_CART_EEPROM = 0x00002000, - SIZE_CART_EEPROM512 = 0x00000200, + GBA_SIZE_BIOS = 0x00004000, + GBA_SIZE_EWRAM = 0x00040000, + GBA_SIZE_IWRAM = 0x00008000, + GBA_SIZE_IO = 0x00000400, + GBA_SIZE_PALETTE_RAM = 0x00000400, + GBA_SIZE_VRAM = 0x00018000, + GBA_SIZE_OAM = 0x00000400, + GBA_SIZE_ROM0 = 0x02000000, + GBA_SIZE_ROM1 = 0x02000000, + GBA_SIZE_ROM2 = 0x02000000, + GBA_SIZE_SRAM = 0x00008000, + GBA_SIZE_SRAM512 = 0x00010000, + GBA_SIZE_FLASH512 = 0x00010000, + GBA_SIZE_FLASH1M = 0x00020000, + GBA_SIZE_EEPROM = 0x00002000, + GBA_SIZE_EEPROM512 = 0x00000200, - SIZE_AGB_PRINT = 0x10000 + GBA_SIZE_AGB_PRINT = 0x10000 }; enum { diff --git a/include/mgba/internal/gba/renderers/gl.h b/include/mgba/internal/gba/renderers/gl.h index ccf682614..14cf11678 100644 --- a/include/mgba/internal/gba/renderers/gl.h +++ b/include/mgba/internal/gba/renderers/gl.h @@ -20,14 +20,12 @@ CXX_GUARD_START #ifdef USE_EPOXY #include -#elif defined(BUILD_GL) -#ifdef __APPLE__ +#elif defined(__APPLE__) #include -#else +#elif defined(BUILD_GL) #define GL_GLEXT_PROTOTYPES #include #include -#endif #else #include #endif diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index ec44dc8fd..25bd170d6 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -407,12 +407,12 @@ struct GBASerializedState { uint32_t reserved[12]; - uint16_t io[SIZE_IO >> 1]; - uint16_t pram[SIZE_PALETTE_RAM >> 1]; - uint16_t oam[SIZE_OAM >> 1]; - uint16_t vram[SIZE_VRAM >> 1]; - uint8_t iwram[SIZE_WORKING_IRAM]; - uint8_t wram[SIZE_WORKING_RAM]; + uint16_t io[GBA_SIZE_IO >> 1]; + uint16_t pram[GBA_SIZE_PALETTE_RAM >> 1]; + uint16_t oam[GBA_SIZE_OAM >> 1]; + uint16_t vram[GBA_SIZE_VRAM >> 1]; + uint8_t iwram[GBA_SIZE_IWRAM]; + uint8_t wram[GBA_SIZE_EWRAM]; }; static_assert(sizeof(struct GBASerializedState) == 0x61000, "GBA savestate struct sized wrong"); diff --git a/include/mgba/internal/script/socket.h b/include/mgba/internal/script/socket.h index 014a765bb..98953fb80 100644 --- a/include/mgba/internal/script/socket.h +++ b/include/mgba/internal/script/socket.h @@ -6,6 +6,10 @@ #ifndef M_SCRIPT_SOCKET_H #define M_SCRIPT_SOCKET_H +#include + +CXX_GUARD_START + enum mSocketErrorCode { mSCRIPT_SOCKERR_UNKNOWN_ERROR = -1, mSCRIPT_SOCKERR_OK = 0, @@ -22,4 +26,6 @@ enum mSocketErrorCode { mSCRIPT_SOCKERR_UNSUPPORTED, }; +CXX_GUARD_END + #endif diff --git a/include/mgba/internal/script/types.h b/include/mgba/internal/script/types.h new file mode 100644 index 000000000..fde5ce0ae --- /dev/null +++ b/include/mgba/internal/script/types.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2013-2023 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_SCRIPT_TYPES_INTERNAL_H +#define M_SCRIPT_TYPES_INTERNAL_H + +#include + +CXX_GUARD_START + +struct Table; +void mScriptContextGetInputTypes(struct Table*); + +void mScriptTypeAdd(struct Table*, const struct mScriptType* type); + +CXX_GUARD_END + +#endif diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 99eefe069..dcec138e1 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -89,13 +89,14 @@ uint32_t mScriptContextSetWeakref(struct mScriptContext*, struct mScriptValue* v struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mScriptValue* value); struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value); void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref); +void mScriptContextDisownWeakref(struct mScriptContext*, uint32_t weakref); void mScriptContextAttachStdlib(struct mScriptContext* context); void mScriptContextAttachSocket(struct mScriptContext* context); void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants); void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value); -void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback); +void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback, struct mScriptList* args); uint32_t mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value); void mScriptContextRemoveCallback(struct mScriptContext*, uint32_t cbid); diff --git a/include/mgba/script/input.h b/include/mgba/script/input.h new file mode 100644 index 000000000..ae1260ad1 --- /dev/null +++ b/include/mgba/script/input.h @@ -0,0 +1,271 @@ +/* Copyright (c) 2013-2022 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_SCRIPT_INPUT_H +#define M_SCRIPT_INPUT_H + +#include + +#include +#include + +CXX_GUARD_START + +enum mScriptEventType { + mSCRIPT_EV_TYPE_NONE = 0, + mSCRIPT_EV_TYPE_KEY, + mSCRIPT_EV_TYPE_MOUSE_BUTTON, + mSCRIPT_EV_TYPE_MOUSE_MOVE, + mSCRIPT_EV_TYPE_MOUSE_WHEEL, + mSCRIPT_EV_TYPE_GAMEPAD_BUTTON, + mSCRIPT_EV_TYPE_GAMEPAD_HAT, + mSCRIPT_EV_TYPE_TRIGGER, + mSCRIPT_EV_TYPE_MAX +}; + +enum mScriptInputState { + mSCRIPT_INPUT_STATE_UP = 0, + mSCRIPT_INPUT_STATE_DOWN = 1, + mSCRIPT_INPUT_STATE_HELD = 2, +}; + +enum mScriptInputDirection { + mSCRIPT_INPUT_DIR_NONE = 0, + + mSCRIPT_INPUT_DIR_NORTH = 1, + mSCRIPT_INPUT_DIR_EAST = 2, + mSCRIPT_INPUT_DIR_SOUTH = 4, + mSCRIPT_INPUT_DIR_WEST = 8, + + mSCRIPT_INPUT_DIR_UP = mSCRIPT_INPUT_DIR_NORTH, + mSCRIPT_INPUT_DIR_RIGHT = mSCRIPT_INPUT_DIR_EAST, + mSCRIPT_INPUT_DIR_DOWN = mSCRIPT_INPUT_DIR_SOUTH, + mSCRIPT_INPUT_DIR_LEFT = mSCRIPT_INPUT_DIR_WEST, + + mSCRIPT_INPUT_DIR_NORTHEAST = mSCRIPT_INPUT_DIR_NORTH | mSCRIPT_INPUT_DIR_EAST, + mSCRIPT_INPUT_DIR_NORTHWEST = mSCRIPT_INPUT_DIR_NORTH | mSCRIPT_INPUT_DIR_WEST, + mSCRIPT_INPUT_DIR_SOUTHEAST = mSCRIPT_INPUT_DIR_SOUTH | mSCRIPT_INPUT_DIR_EAST, + mSCRIPT_INPUT_DIR_SOUTHWEST = mSCRIPT_INPUT_DIR_SOUTH | mSCRIPT_INPUT_DIR_WEST, +}; + +enum mScriptKeyModifier { + mSCRIPT_KMOD_NONE = 0, + + mSCRIPT_KMOD_LSHIFT = 0x1, + mSCRIPT_KMOD_RSHIFT = 0x2, + mSCRIPT_KMOD_SHIFT = 0x3, + + mSCRIPT_KMOD_LCONTROL = 0x4, + mSCRIPT_KMOD_RCONTROL = 0x8, + mSCRIPT_KMOD_CONTROL = 0xC, + + mSCRIPT_KMOD_LALT = 0x10, + mSCRIPT_KMOD_RALT = 0x20, + mSCRIPT_KMOD_ALT = 0x30, + + mSCRIPT_KMOD_LSUPER = 0x40, + mSCRIPT_KMOD_RSUPER = 0x80, + mSCRIPT_KMOD_SUPER = 0xC0, + + mSCRIPT_KMOD_CAPS_LOCK = 0x100, + mSCRIPT_KMOD_NUM_LOCK = 0x200, + mSCRIPT_KMOD_SCROLL_LOCK = 0x400, +}; + +#define mSCRIPT_KEYBASE 0x800000 + +enum mScriptKey { + mSCRIPT_KEY_NONE = 0, + + mSCRIPT_KEY_BACKSPACE = 0x000008, + mSCRIPT_KEY_TAB = 0x000009, + mSCRIPT_KEY_ENTER = 0x00000A, + mSCRIPT_KEY_ESCAPE = 0x00001B, + mSCRIPT_KEY_DELETE = 0x00007F, + + mSCRIPT_KEY_F1 = mSCRIPT_KEYBASE | 1, + mSCRIPT_KEY_F2, + mSCRIPT_KEY_F3, + mSCRIPT_KEY_F4, + mSCRIPT_KEY_F5, + mSCRIPT_KEY_F6, + mSCRIPT_KEY_F7, + mSCRIPT_KEY_F8, + mSCRIPT_KEY_F9, + mSCRIPT_KEY_F10, + mSCRIPT_KEY_F11, + mSCRIPT_KEY_F12, + mSCRIPT_KEY_F13, + mSCRIPT_KEY_F14, + mSCRIPT_KEY_F15, + mSCRIPT_KEY_F16, + mSCRIPT_KEY_F17, + mSCRIPT_KEY_F18, + mSCRIPT_KEY_F19, + mSCRIPT_KEY_F20, + mSCRIPT_KEY_F21, + mSCRIPT_KEY_F22, + mSCRIPT_KEY_F23, + mSCRIPT_KEY_F24, + + mSCRIPT_KEY_UP = mSCRIPT_KEYBASE | 0x20, + mSCRIPT_KEY_RIGHT, + mSCRIPT_KEY_DOWN, + mSCRIPT_KEY_LEFT, + mSCRIPT_KEY_PAGE_UP, + mSCRIPT_KEY_PAGE_DOWN, + mSCRIPT_KEY_HOME, + mSCRIPT_KEY_END, + mSCRIPT_KEY_INSERT, + mSCRIPT_KEY_BREAK, + mSCRIPT_KEY_CLEAR, + mSCRIPT_KEY_PRINT_SCREEN, + mSCRIPT_KEY_SYSRQ, + mSCRIPT_KEY_MENU, + mSCRIPT_KEY_HELP, + + mSCRIPT_KEY_LSHIFT = mSCRIPT_KEYBASE | 0x30, + mSCRIPT_KEY_RSHIFT, + mSCRIPT_KEY_SHIFT, + mSCRIPT_KEY_LCONTROL, + mSCRIPT_KEY_RCONTROL, + mSCRIPT_KEY_CONTROL, + mSCRIPT_KEY_LALT, + mSCRIPT_KEY_RALT, + mSCRIPT_KEY_ALT, + mSCRIPT_KEY_LSUPER, + mSCRIPT_KEY_RSUPER, + mSCRIPT_KEY_SUPER, + mSCRIPT_KEY_CAPS_LOCK, + mSCRIPT_KEY_NUM_LOCK, + mSCRIPT_KEY_SCROLL_LOCK, + + mSCRIPT_KEY_KP_0 = mSCRIPT_KEYBASE | 0x40, + mSCRIPT_KEY_KP_1, + mSCRIPT_KEY_KP_2, + mSCRIPT_KEY_KP_3, + mSCRIPT_KEY_KP_4, + mSCRIPT_KEY_KP_5, + mSCRIPT_KEY_KP_6, + mSCRIPT_KEY_KP_7, + mSCRIPT_KEY_KP_8, + mSCRIPT_KEY_KP_9, + mSCRIPT_KEY_KP_PLUS, + mSCRIPT_KEY_KP_MINUS, + mSCRIPT_KEY_KP_MULTIPLY, + mSCRIPT_KEY_KP_DIVIDE, + mSCRIPT_KEY_KP_COMMA, + mSCRIPT_KEY_KP_POINT, + mSCRIPT_KEY_KP_ENTER, +}; + +enum mScriptMouseButton { + mSCRIPT_MOUSE_BUTTON_PRIMARY = 0, + mSCRIPT_MOUSE_BUTTON_SECONDARY = 1, + mSCRIPT_MOUSE_BUTTON_MIDDLE = 2, +}; + +struct mScriptEvent { + int32_t type; + int32_t reserved; + uint64_t seq; +}; + +struct mScriptKeyEvent { + struct mScriptEvent d; + uint8_t state; + uint16_t modifiers; + uint32_t key; +}; + +struct mScriptMouseButtonEvent { + struct mScriptEvent d; + uint8_t mouse; + uint8_t context; + uint8_t state; + uint8_t button; +}; + +struct mScriptMouseMoveEvent { + struct mScriptEvent d; + uint8_t mouse; + uint8_t context; + int32_t x; + int32_t y; +}; + +struct mScriptMouseWheelEvent { + struct mScriptEvent d; + uint8_t mouse; + int16_t x; + int16_t y; +}; + +struct mScriptGamepadButtonEvent { + struct mScriptEvent d; + uint8_t state; + uint8_t pad; + uint16_t button; +}; + +struct mScriptGamepadHatEvent { + struct mScriptEvent d; + uint8_t pad; + uint8_t hat; + uint8_t direction; +}; + +struct mScriptTriggerEvent { + struct mScriptEvent d; + uint8_t trigger; + bool state; +}; + +struct mScriptGamepad { + unsigned pad; + + char visibleName[128]; + char internalName[64]; + + struct mScriptList axes; + struct mScriptList buttons; + struct mScriptList hats; +}; + +mSCRIPT_DECLARE_STRUCT(mScriptEvent); +mSCRIPT_DECLARE_STRUCT(mScriptKeyEvent); +mSCRIPT_DECLARE_STRUCT(mScriptMouseButtonEvent); +mSCRIPT_DECLARE_STRUCT(mScriptMouseMoveEvent); +mSCRIPT_DECLARE_STRUCT(mScriptMouseWheelEvent); +mSCRIPT_DECLARE_STRUCT(mScriptGamepadButtonEvent); +mSCRIPT_DECLARE_STRUCT(mScriptGamepadHatEvent); +mSCRIPT_DECLARE_STRUCT(mScriptSensorEvent); +mSCRIPT_DECLARE_STRUCT(mScriptTriggerEvent); + +mSCRIPT_DECLARE_STRUCT(mScriptGamepad); + +void mScriptContextAttachInput(struct mScriptContext* context); + +void mScriptContextFireEvent(struct mScriptContext*, struct mScriptEvent*); + +int mScriptContextGamepadAttach(struct mScriptContext*, struct mScriptGamepad*); +bool mScriptContextGamepadDetach(struct mScriptContext*, int pad); +struct mScriptGamepad* mScriptContextGamepadLookup(struct mScriptContext*, int pad); + +void mScriptGamepadInit(struct mScriptGamepad*); +void mScriptGamepadDeinit(struct mScriptGamepad*); +void mScriptGamepadSetAxisCount(struct mScriptGamepad*, unsigned); +void mScriptGamepadSetButtonCount(struct mScriptGamepad*, unsigned); +void mScriptGamepadSetHatCount(struct mScriptGamepad*, unsigned); +void mScriptGamepadSetAxis(struct mScriptGamepad*, unsigned, int16_t value); +void mScriptGamepadSetButton(struct mScriptGamepad*, unsigned, bool down); +void mScriptGamepadSetHat(struct mScriptGamepad*, unsigned, int direction); +int16_t mScriptGamepadGetAxis(struct mScriptGamepad*, unsigned); +bool mScriptGamepadGetButton(struct mScriptGamepad*, unsigned); +int mScriptGamepadGetHat(struct mScriptGamepad*, unsigned); + +CXX_GUARD_END + +#endif diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 470ed40ce..ed8ae02e3 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -76,6 +76,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_W(TYPE) opaque #define mSCRIPT_TYPE_FIELD_CW(TYPE) opaque +#define mSCRIPT_TYPE_MS_VOID (&mSTVoid) #define mSCRIPT_TYPE_MS_S8 (&mSTSInt8) #define mSCRIPT_TYPE_MS_U8 (&mSTUInt8) #define mSCRIPT_TYPE_MS_S16 (&mSTSInt16) @@ -97,7 +98,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT) #define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME) #define mSCRIPT_TYPE_MS_PS(STRUCT) (&mSTStructPtr_ ## STRUCT) -#define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructConstPtr_ ## STRUCT) +#define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructPtrConst_ ## STRUCT) #define mSCRIPT_TYPE_MS_WSTR (&mSTStringWrapper) #define mSCRIPT_TYPE_MS_WLIST (&mSTListWrapper) #define mSCRIPT_TYPE_MS_W(TYPE) (&mSTWrapper_ ## TYPE) @@ -303,6 +304,8 @@ void mScriptValueWrap(struct mScriptValue* val, struct mScriptValue* out); struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val); const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* val); +void mScriptValueFollowPointer(struct mScriptValue* ptr, struct mScriptValue* out); + struct mScriptValue* mScriptStringCreateEmpty(size_t size); struct mScriptValue* mScriptStringCreateFromBytes(const void* string, size_t size); struct mScriptValue* mScriptStringCreateFromUTF8(const char* string); diff --git a/res/info.plist.in b/res/info.plist.in index f37904dee..eb625299c 100644 --- a/res/info.plist.in +++ b/res/info.plist.in @@ -56,5 +56,7 @@ Viewer + LSApplicationCategoryType + public.app-category.games diff --git a/res/scripts/gamepad-demo.lua b/res/scripts/gamepad-demo.lua new file mode 100644 index 000000000..90327c11c --- /dev/null +++ b/res/scripts/gamepad-demo.lua @@ -0,0 +1,33 @@ +inputBuffer = console:createBuffer("Input") + +function readPad() + inputBuffer:clear() + + if not input.activeGamepad then + inputBuffer:print("No gamepad detected\n") + return + end + + local gamepad = input.activeGamepad + local axes = gamepad.axes + local buttons = gamepad.buttons + local hats = gamepad.hats + + inputBuffer:print(gamepad.visibleName .. "\n") + inputBuffer:print(string.format("%i buttons, %i axes, %i hats\n", #buttons, #axes, #hats)) + + local sbuttons = {} + for k, v in ipairs(buttons) do + if v then + sbuttons[k] = "down" + else + sbuttons[k] = " up" + end + end + + inputBuffer:print(string.format("Buttons: %s\n", table.concat(sbuttons, ", "))) + inputBuffer:print(string.format("Axes: %s\n", table.concat(axes, ", "))) + inputBuffer:print(string.format("Hats: %s\n", table.concat(hats, ", "))) +end + +callbacks:add("frame", readPad) diff --git a/res/scripts/pokemon.lua b/res/scripts/pokemon.lua index 7ebe54bb8..d909396d0 100644 --- a/res/scripts/pokemon.lua +++ b/res/scripts/pokemon.lua @@ -412,6 +412,13 @@ local gameRubyEn = Generation3En:new{ _speciesNameTable=0x1f716c, } +local gameRubyEnR1 = Generation3En:new{ + name="Ruby (USA)", + _party=0x3004360, + _partyCount=0x3004350, + _speciesNameTable=0x1f7184, +} + local gameSapphireEn = Generation3En:new{ name="Sapphire (USA)", _party=0x3004360, @@ -419,6 +426,13 @@ local gameSapphireEn = Generation3En:new{ _speciesNameTable=0x1f70fc, } +local gameSapphireEnR1 = Generation3En:new{ + name="Sapphire (USA)", + _party=0x3004360, + _partyCount=0x3004350, + _speciesNameTable=0x1f7114, +} + local gameEmeraldEn = Generation3En:new{ name="Emerald (USA)", _party=0x20244ec, @@ -471,6 +485,10 @@ gameCrc32 = { [0x7d527d62] = gameYellowEn, [0x84ee4776] = gameFireRedEnR1, [0xdaffecec] = gameLeafGreenEnR1, + [0x61641576] = gameRubyEnR1, -- Rev 1 + [0xaeac73e6] = gameRubyEnR1, -- Rev 2 + [0xbafedae5] = gameSapphireEnR1, -- Rev 1 + [0x9cc4410e] = gameSapphireEnR1, -- Rev 2 } function printPartyStatus(game, buffer) diff --git a/src/arm/debugger/memory-debugger.c b/src/arm/debugger/memory-debugger.c index 68b4b4175..85c9995ae 100644 --- a/src/arm/debugger/memory-debugger.c +++ b/src/arm/debugger/memory-debugger.c @@ -92,12 +92,13 @@ CREATE_MULTIPLE_WATCHPOINT_SHIM(storeMultiple, WATCHPOINT_WRITE) CREATE_SHIM(setActiveRegion, void, (struct ARMCore* cpu, uint32_t address), address) static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint32_t newValue, int width) { - --width; struct mWatchpoint* watchpoint; size_t i; + uint32_t minAddress = address & ~(width - 1); + uint32_t maxAddress = minAddress + width; for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) { watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i); - if (!((watchpoint->address ^ address) & ~width) && watchpoint->type & type) { + if (watchpoint->type & type && watchpoint->minAddress < maxAddress && minAddress < watchpoint->maxAddress) { if (watchpoint->condition) { int32_t value; int segment; @@ -107,7 +108,7 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st } uint32_t oldValue; - switch (width + 1) { + switch (width) { case 1: oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0); break; diff --git a/src/core/input.c b/src/core/input.c index 76296a373..787e31f86 100644 --- a/src/core/input.c +++ b/src/core/input.c @@ -38,6 +38,11 @@ struct mInputAxisEnumerate { void* user; }; +struct mInputHatEnumerate { + void (*handler)(int axis, const struct mInputHatBindings* bindings, void* user); + void* user; +}; + static void _makeSectionName(const char* platform, char* sectionName, size_t len, uint32_t type) { snprintf(sectionName, len, "%s.input.%c%c%c%c", platform, type >> 24, type >> 16, type >> 8, type); sectionName[len - 1] = '\0'; @@ -304,6 +309,12 @@ void _unbindAxis(uint32_t axis, void* dp, void* user) { } } +void _enumerateHat(uint32_t axis, void* dp, void* ep) { + struct mInputHatEnumerate* enumUser = ep; + const struct mInputHatBindings* description = dp; + enumUser->handler(axis, description, enumUser->user); +} + static bool _loadAll(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) { if (!ConfigurationHasSection(config, sectionName)) { return false; @@ -415,6 +426,16 @@ void mInputUnbindKey(struct mInputMap* map, uint32_t type, int input) { } } +void mInputUnbindAllKeys(struct mInputMap* map, uint32_t type) { + struct mInputMapImpl* impl = _lookupMap(map, type); + if (impl) { + size_t i; + for (i = 0; i < map->info->nKeys; ++i) { + impl->map[i] = -1; + } + } +} + int mInputQueryBinding(const struct mInputMap* map, uint32_t type, int input) { if (input < 0 || (size_t) input >= map->info->nKeys) { return -1; @@ -578,6 +599,18 @@ void mInputUnbindAllHats(struct mInputMap* map, uint32_t type) { } } +void mInputEnumerateHats(const struct mInputMap* map, uint32_t type, void (handler(int hat, const struct mInputHatBindings* bindings, void* user)), void* user) { + const struct mInputMapImpl* impl = _lookupMapConst(map, type); + if (!impl) { + return; + } + struct mInputHatEnumerate enumUser = { + handler, + user + }; + TableEnumerate(&impl->axes, _enumerateHat, &enumUser); +} + bool mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) { char sectionName[SECTION_NAME_MAX]; _makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type); diff --git a/src/core/scripting.c b/src/core/scripting.c index 05433b747..a67297598 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -651,7 +651,7 @@ static struct mScriptValue* _mScriptCoreAdapterGet(struct mScriptCoreAdapter* ad static void _mScriptCoreAdapterReset(struct mScriptCoreAdapter* adapter) { adapter->core->reset(adapter->core); - mScriptContextTriggerCallback(adapter->context, "reset"); + mScriptContextTriggerCallback(adapter->context, "reset", NULL); } mSCRIPT_DECLARE_STRUCT(mScriptCoreAdapter); @@ -761,14 +761,16 @@ mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptConsole, createBuffer) mSCRIPT_DEFINE_DEFAULTS_END; static struct mScriptConsole* _ensureConsole(struct mScriptContext* context) { - struct mScriptValue* value = mScriptContextEnsureGlobal(context, "console", mSCRIPT_TYPE_MS_S(mScriptConsole)); - struct mScriptConsole* console = value->value.opaque; - if (!console) { - console = calloc(1, sizeof(*console)); - value->value.opaque = console; - value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; - mScriptContextSetDocstring(context, "console", "Singleton instance of struct::mScriptConsole"); + struct mScriptValue* value = mScriptContextGetGlobal(context, "console"); + if (value) { + return value->value.opaque; } + struct mScriptConsole* console = calloc(1, sizeof(*console)); + value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptConsole)); + value->value.opaque = console; + value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + mScriptContextSetGlobal(context, "console", value); + mScriptContextSetDocstring(context, "console", "Singleton instance of struct::mScriptConsole"); return console; } diff --git a/src/core/serialize.c b/src/core/serialize.c index c562d97e0..53e0aa168 100644 --- a/src/core/serialize.c +++ b/src/core/serialize.c @@ -262,8 +262,18 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx PNGReadClose(png, info, end); return false; } - unsigned width, height; - core->desiredVideoDimensions(core, &width, &height); + + if (!PNGReadHeader(png, info)) { + PNGReadClose(png, info, end); + return false; + } + unsigned width = png_get_image_width(png, info); + unsigned height = png_get_image_height(png, info); + if (width > 0x4000 || height > 0x4000) { + // These images are ridiculously large...let's assume a DOS attempt and reject + PNGReadClose(png, info, end); + return false; + } uint32_t* pixels = malloc(width * height * 4); if (!pixels) { PNGReadClose(png, info, end); @@ -278,8 +288,8 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx .extdata = extdata }; + bool success = true; PNGInstallChunkHandler(png, &bundle, _loadPNGChunkHandler, "gbAs gbAx"); - bool success = PNGReadHeader(png, info); success = success && PNGReadPixels(png, info, pixels, width, height, width); success = success && PNGReadFooter(png, end); PNGReadClose(png, info, end); @@ -295,6 +305,12 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx .clean = free }; mStateExtdataPut(extdata, EXTDATA_SCREENSHOT, &item); + + uint16_t dims[2] = { width, height }; + item.size = sizeof(dims); + item.data = malloc(item.size); + memcpy(item.data, dims, item.size); + mStateExtdataPut(extdata, EXTDATA_SCREENSHOT_DIMENSIONS, &item); } else { free(pixels); } diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index ddcb448c5..b9cfa03e8 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -12,10 +12,12 @@ #include #include +#include "script/test.h" + #ifdef M_CORE_GBA #include #define TEST_PLATFORM mPLATFORM_GBA -#define RAM_BASE BASE_WORKING_IRAM +#define RAM_BASE GBA_BASE_IWRAM #elif defined(M_CORE_GB) #include #define TEST_PLATFORM mPLATFORM_GB @@ -66,22 +68,6 @@ static const uint8_t _fakeGBROM[0x4000] = { mCoreConfigDeinit(&core->config); \ core->deinit(core) -#define LOAD_PROGRAM(PROG) \ - do { \ - struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ - assert_true(lua->load(lua, NULL, vf)); \ - vf->close(vf); \ - } while(0) - -#define TEST_VALUE(TYPE, NAME, VALUE) \ - do { \ - struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \ - struct mScriptValue* global = lua->getGlobal(lua, NAME); \ - assert_non_null(global); \ - assert_true(global->type->equal(global, &val)); \ - mScriptValueDeref(global); \ - } while(0) - static void _mScriptTestLog(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args) { UNUSED(category); struct mScriptTestLogger* logger = (struct mScriptTestLogger*) log; diff --git a/src/core/thread.c b/src/core/thread.c index 7101b5a54..9f4be082b 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -58,8 +58,19 @@ static void _waitOnInterrupt(struct mCoreThreadInternal* threadContext) { } static void _pokeRequest(struct mCoreThreadInternal* threadContext) { - if (threadContext->state == mTHREAD_RUNNING || threadContext->state == mTHREAD_PAUSED) { + switch (threadContext->state) { + case mTHREAD_RUNNING: + case mTHREAD_PAUSED: + case mTHREAD_CRASHED: threadContext->state = mTHREAD_REQUEST; + break; + case mTHREAD_INITIALIZED: + case mTHREAD_REQUEST: + case mTHREAD_INTERRUPTED: + case mTHREAD_INTERRUPTING: + case mTHREAD_EXITING: + case mTHREAD_SHUTDOWN: + break; } } @@ -194,7 +205,7 @@ void _script_ ## NAME(void* context) { \ if (!threadContext->scriptContext) { \ return; \ } \ - mScriptContextTriggerCallback(threadContext->scriptContext, #NAME); \ + mScriptContextTriggerCallback(threadContext->scriptContext, #NAME, NULL); \ } ADD_CALLBACK(frame) @@ -283,7 +294,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } } if (scriptContext) { - mScriptContextTriggerCallback(scriptContext, "start"); + mScriptContextTriggerCallback(scriptContext, "start", NULL); } #endif @@ -304,7 +315,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } } if (scriptContext) { - mScriptContextTriggerCallback(scriptContext, "reset"); + mScriptContextTriggerCallback(scriptContext, "reset", NULL); } #endif @@ -404,7 +415,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } #ifdef ENABLE_SCRIPTING if (scriptContext) { - mScriptContextTriggerCallback(scriptContext, "reset"); + mScriptContextTriggerCallback(scriptContext, "reset", NULL); } #endif } @@ -428,7 +439,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } #ifdef ENABLE_SCRIPTING if (scriptContext) { - mScriptContextTriggerCallback(scriptContext, "shutdown"); + mScriptContextTriggerCallback(scriptContext, "shutdown", NULL); mScriptContextDetachCore(scriptContext); } #endif diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 7c827905a..12cd7af18 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -60,6 +60,10 @@ static void _setReadWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*) static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); static void _setWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); static void _setWriteChangedWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); +static void _setReadWriteRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); +static void _setReadRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); +static void _setWriteRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); +static void _setWriteChangedRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); static void _listWatchpoints(struct CLIDebugger*, struct CLIDebugVector*); static void _trace(struct CLIDebugger*, struct CLIDebugVector*); static void _writeByte(struct CLIDebugger*, struct CLIDebugVector*); @@ -114,6 +118,10 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = { { "watch/c", _setWriteChangedWatchpoint, "Is", "Set a change watchpoint" }, { "watch/r", _setReadWatchpoint, "Is", "Set a read watchpoint" }, { "watch/w", _setWriteWatchpoint, "Is", "Set a write watchpoint" }, + { "watch-range", _setReadWriteRangeWatchpoint, "IIs", "Set a range watchpoint" }, + { "watch-range/c", _setWriteChangedRangeWatchpoint, "IIs", "Set a change range watchpoint" }, + { "watch-range/r", _setReadRangeWatchpoint, "IIs", "Set a read range watchpoint" }, + { "watch-range/w", _setWriteRangeWatchpoint, "IIs", "Set a write range watchpoint" }, { "x/1", _dumpByte, "Ii", "Examine bytes at a specified offset" }, { "x/2", _dumpHalfword, "Ii", "Examine halfwords at a specified offset" }, { "x/4", _dumpWord, "Ii", "Examine words at a specified offset" }, @@ -146,6 +154,14 @@ static struct CLIDebuggerCommandAlias _debuggerCommandAliases[] = { { "p/x", "print/x" }, { "q", "quit" }, { "w", "watch" }, + { "watchr", "watch-range" }, + { "wr", "watch-range" }, + { "watchr/c", "watch-range/c" }, + { "wr/c", "watch-range/c" }, + { "watchr/r", "watch-range/r" }, + { "wr/r", "watch-range/r" }, + { "watchr/w", "watch-range/w" }, + { "wr/w", "watch-range/w" }, { ".", "source" }, { 0, 0 } }; @@ -647,8 +663,9 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* return; } struct mWatchpoint watchpoint = { - .address = dv->intValue, .segment = dv->segmentValue, + .minAddress = dv->intValue, + .maxAddress = dv->intValue + 1, .type = type }; if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { @@ -666,6 +683,48 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* } } +static void _setRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum mWatchpointType type) { + if (!dv || dv->type != CLIDV_INT_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); + return; + } + if (!dv->next || dv->next->type != CLIDV_INT_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); + return; + } + if (!debugger->d.platform->setWatchpoint) { + debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n"); + return; + } + if (dv->intValue >= dv->next->intValue) { + debugger->backend->printf(debugger->backend, "Range watchpoint end is before start. Note that the end of the range is not included.\n"); + return; + } + if (dv->segmentValue != dv->next->segmentValue) { + debugger->backend->printf(debugger->backend, "Range watchpoint does not start and end in the same segment.\n"); + return; + } + struct mWatchpoint watchpoint = { + .segment = dv->segmentValue, + .minAddress = dv->intValue, + .maxAddress = dv->next->intValue, + .type = type + }; + if (dv->next->next && dv->next->next->type == CLIDV_CHAR_TYPE) { + struct ParseTree* tree = _parseTree((const char*[]) { dv->next->next->charValue, NULL }); + if (tree) { + watchpoint.condition = tree; + } else { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + return; + } + } + ssize_t id = debugger->d.platform->setWatchpoint(debugger->d.platform, &watchpoint); + if (id > 0) { + debugger->backend->printf(debugger->backend, INFO_WATCHPOINT_ADDED, id); + } +} + static void _setReadWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { _setWatchpoint(debugger, dv, WATCHPOINT_RW); } @@ -682,6 +741,22 @@ static void _setWriteChangedWatchpoint(struct CLIDebugger* debugger, struct CLID _setWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE); } +static void _setReadWriteRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + _setRangeWatchpoint(debugger, dv, WATCHPOINT_RW); +} + +static void _setReadRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + _setRangeWatchpoint(debugger, dv, WATCHPOINT_READ); +} + +static void _setWriteRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + _setRangeWatchpoint(debugger, dv, WATCHPOINT_WRITE); +} + +static void _setWriteChangedRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + _setRangeWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE); +} + static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { if (!dv || dv->type != CLIDV_INT_TYPE) { debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); @@ -717,9 +792,17 @@ static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector for (i = 0; i < mWatchpointListSize(&watchpoints); ++i) { struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, i); if (watchpoint->segment >= 0) { - debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->address); + if (watchpoint->maxAddress == watchpoint->minAddress + 1) { + debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->minAddress); + } else { + debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X-%X\n", watchpoint->id, watchpoint->segment, watchpoint->minAddress, watchpoint->maxAddress); + } } else { - debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->address); + if (watchpoint->maxAddress == watchpoint->minAddress + 1) { + debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->minAddress); + } else { + debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X-0x%X\n", watchpoint->id, watchpoint->minAddress, watchpoint->maxAddress); + } } } mWatchpointListDeinit(&watchpoints); diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index dfc655054..ca8c0d1ec 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -532,7 +532,7 @@ static void _processQReadCommand(struct GDBStub* stub, const char* message) { } else if (!strncmp("Xfer:memory-map:read::", message, 22)) { if (strlen(stub->memoryMapXml) == 0) { _generateMemoryMapXml(stub, stub->memoryMapXml); - } + } _processQXferCommand(stub, message + 22, stub->memoryMapXml); } else if (!strncmp("Supported:", message, 10)) { _processQSupportedCommand(stub, message + 10); @@ -575,7 +575,8 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) { .type = BREAKPOINT_HARDWARE }; struct mWatchpoint watchpoint = { - .address = address + .minAddress = address, + .maxAddress = address + 1 }; switch (message[0]) { @@ -631,10 +632,11 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) { mWatchpointListInit(&watchpoints, 0); stub->d.platform->listWatchpoints(stub->d.platform, &watchpoints); for (index = 0; index < mWatchpointListSize(&watchpoints); ++index) { - if (mWatchpointListGetPointer(&watchpoints, index)->address != address) { + struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, index); + if (address >= watchpoint->minAddress && address < watchpoint->maxAddress) { continue; } - stub->d.platform->clearBreakpoint(stub->d.platform, mWatchpointListGetPointer(&watchpoints, index)->id); + stub->d.platform->clearBreakpoint(stub->d.platform, watchpoint->id); } mWatchpointListDeinit(&watchpoints); break; diff --git a/src/debugger/parser.c b/src/debugger/parser.c index 0d4924ea9..3c3b037fa 100644 --- a/src/debugger/parser.c +++ b/src/debugger/parser.c @@ -11,8 +11,6 @@ DEFINE_VECTOR(LexVector, struct Token); -DEFINE_VECTOR(IntList, int32_t); - enum LexState { LEX_ERROR = -1, LEX_ROOT = 0, diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index 64488dcb8..33146ef88 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -19,6 +19,12 @@ #include #include +#ifdef PSP2 +#include +#elif defined(__3DS__) +#include +#endif + #include mLOG_DECLARE_CATEGORY(GUI_RUNNER); @@ -117,34 +123,39 @@ static void _drawState(struct GUIBackground* background, void* id) { struct mGUIBackground* gbaBackground = (struct mGUIBackground*) background; unsigned stateId = ((uint32_t) id) >> 16; if (gbaBackground->p->drawScreenshot) { - unsigned w, h; - gbaBackground->p->core->desiredVideoDimensions(gbaBackground->p->core, &w, &h); - size_t size = w * h * BYTES_PER_PIXEL; - if (size != gbaBackground->imageSize) { - mappedMemoryFree(gbaBackground->image, gbaBackground->imageSize); - gbaBackground->image = NULL; - } - if (gbaBackground->image && gbaBackground->screenshotId == (stateId | SCREENSHOT_VALID)) { - gbaBackground->p->drawScreenshot(gbaBackground->p, gbaBackground->image, w, h, true); + color_t* pixels = gbaBackground->image; + if (pixels && gbaBackground->screenshotId == (stateId | SCREENSHOT_VALID)) { + gbaBackground->p->drawScreenshot(gbaBackground->p, pixels, gbaBackground->w, gbaBackground->h, true); return; } else if (gbaBackground->screenshotId != (stateId | SCREENSHOT_INVALID)) { struct VFile* vf = mCoreGetState(gbaBackground->p->core, stateId, false); - color_t* pixels = gbaBackground->image; - if (!pixels) { - pixels = anonymousMemoryMap(size); - gbaBackground->image = pixels; - gbaBackground->imageSize = size; - } bool success = false; - if (vf && isPNG(vf) && pixels) { + unsigned w, h; + if (vf && isPNG(vf)) { png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES); png_infop info = png_create_info_struct(png); png_infop end = png_create_info_struct(png); - if (png && info && end) { - success = PNGReadHeader(png, info); - success = success && PNGReadPixels(png, info, pixels, w, h, w); - success = success && PNGReadFooter(png, end); + success = png && info && end; + success = success && PNGReadHeader(png, info); + w = png_get_image_width(png, info); + h = png_get_image_height(png, info); + size_t size = w * h * BYTES_PER_PIXEL; + success = success && (w < 0x4000) && (h < 0x4000); + if (success) { + if (size != gbaBackground->imageSize) { + mappedMemoryFree(pixels, gbaBackground->imageSize); + pixels = anonymousMemoryMap(size); + gbaBackground->image = pixels; + gbaBackground->imageSize = size; + } + success = pixels; } + success = success && PNGReadPixels(png, info, pixels, w, h, w); + if (success) { + gbaBackground->w = w; + gbaBackground->h = h; + } + success = success && PNGReadFooter(png, end); PNGReadClose(png, info, end); } if (vf) { @@ -450,11 +461,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { runner->setup(runner); } if (runner->config.port && runner->keySources) { - mLOG(GUI_RUNNER, DEBUG, "Loading key sources for %s...", runner->config.port); - size_t i; - for (i = 0; runner->keySources[i].id; ++i) { - mInputMapLoad(&runner->core->inputMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config)); - } + mGUILoadInputMaps(runner); } mLOG(GUI_RUNNER, DEBUG, "Reseting..."); runner->core->reset(runner->core); @@ -745,11 +752,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { void mGUIRunloop(struct mGUIRunner* runner) { if (runner->keySources) { - mLOG(GUI_RUNNER, DEBUG, "Loading key sources for %s...", runner->config.port); - size_t i; - for (i = 0; runner->keySources[i].id; ++i) { - mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config)); - } + mGUILoadInputMaps(runner); } while (!runner->running || runner->running(runner)) { char path[PATH_MAX]; @@ -770,6 +773,52 @@ void mGUIRunloop(struct mGUIRunner* runner) { } } +void mGUILoadInputMaps(struct mGUIRunner* runner) { + mLOG(GUI_RUNNER, DEBUG, "Loading key sources for %s...", runner->config.port); + size_t i; + for (i = 0; runner->keySources[i].id; ++i) { + mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config)); + } +} + +#if defined(__3DS__) || defined(PSP2) +bool mGUIGetRom(struct mGUIRunner* runner, char* out, size_t outLength) { +#ifdef PSP2 + int fd = open("app0:/filename", O_RDONLY); + strcpy(out, "app0:/"); +#elif defined(__3DS__) + int fd = open("romfs:/filename", O_RDONLY); + strcpy(out, "romfs:/"); +#endif + if (fd < 0) { + return false; + } + size_t len = strlen(out); + ssize_t size = read(fd, out + len, outLength - len); + if (size > 0 && out[len + size - 1] == '\n') { + out[len + size - 1] = '\0'; + } + close(fd); + if (size <= 0) { + return false; + } + char basedir[64]; + mCoreConfigDirectory(basedir, sizeof(basedir)); + strlcat(basedir, "/forwarders", sizeof(basedir)); +#ifdef PSP2 + sceIoMkdir(basedir, 0777); +#elif defined(__3DS__) + FSUSER_CreateDirectory(sdmcArchive, fsMakePath(PATH_ASCII, basedir), 0); +#endif + + mCoreConfigSetValue(&runner->config, "savegamePath", basedir); + mCoreConfigSetValue(&runner->config, "savestatePath", basedir); + mCoreConfigSetValue(&runner->config, "screenshotPath", basedir); + mCoreConfigSetValue(&runner->config, "cheatsPath", basedir); + return true; +} +#endif + #ifndef DISABLE_THREADING THREAD_ENTRY mGUIAutosaveThread(void* context) { struct mGUIAutosaveContext* autosave = context; diff --git a/src/feature/gui/gui-runner.h b/src/feature/gui/gui-runner.h index ea18cc5b8..0dc412838 100644 --- a/src/feature/gui/gui-runner.h +++ b/src/feature/gui/gui-runner.h @@ -33,6 +33,8 @@ struct mGUIBackground { color_t* image; size_t imageSize; + uint16_t w; + uint16_t h; unsigned screenshotId; }; @@ -95,9 +97,14 @@ struct mGUIRunner { void mGUIInit(struct mGUIRunner*, const char* port); void mGUIDeinit(struct mGUIRunner*); +void mGUILoadInputMaps(struct mGUIRunner* runner); void mGUIRun(struct mGUIRunner*, const char* path); void mGUIRunloop(struct mGUIRunner*); +#if defined(__3DS__) || defined(PSP2) +bool mGUIGetRom(struct mGUIRunner* runner, char* out, size_t outLength); +#endif + #ifndef DISABLE_THREADING THREAD_ENTRY mGUIAutosaveThread(void* context); #endif diff --git a/src/feature/updater-main.c b/src/feature/updater-main.c index 4b1f216ff..bc29e5cd4 100644 --- a/src/feature/updater-main.c +++ b/src/feature/updater-main.c @@ -29,6 +29,8 @@ #define W_OK 02 #endif +FILE* logfile; + bool extractArchive(struct VDir* archive, const char* root, bool prefix) { char path[PATH_MAX] = {0}; struct VDirEntry* vde; @@ -53,7 +55,7 @@ bool extractArchive(struct VDir* archive, const char* root, bool prefix) { } switch (vde->type(vde)) { case VFS_DIRECTORY: - printf("mkdir %s\n", fname); + fprintf(logfile, "mkdir %s\n", fname); if (mkdir(path, 0755) < 0 && errno != EEXIST) { return false; } @@ -70,7 +72,7 @@ bool extractArchive(struct VDir* archive, const char* root, bool prefix) { } break; case VFS_FILE: - printf("extract %s\n", fname); + fprintf(logfile, "extract %s\n", fname); vfIn = archive->openFile(archive, vde->name(vde), O_RDONLY); errno = 0; vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); @@ -111,13 +113,17 @@ int main(int argc, char* argv[]) { const char* root; int ok = 1; + mCoreConfigDirectory(bin, sizeof(bin)); + strncat(bin, "/updater.log", sizeof(bin)); + logfile = fopen(bin, "w"); + mCoreConfigInit(&config, "updater"); if (!mCoreConfigLoad(&config)) { - puts("Failed to load config"); + fputs("Failed to load config\n", logfile); } else if (!mUpdateGetArchivePath(&config, updateArchive, sizeof(updateArchive)) || !(root = mUpdateGetRoot(&config))) { - puts("No pending update found"); + fputs("No pending update found\n", logfile); } else if (access(root, W_OK)) { - puts("Cannot write to update path"); + fputs("Cannot write to update path\n", logfile); } else { #ifdef __APPLE__ char subdir[PATH_MAX]; @@ -160,7 +166,6 @@ int main(int argc, char* argv[]) { } off_t diff = devend - devinfo - 1; memcpy(devpath, &devinfo[1], diff); - puts(devpath); break; } int retstat; @@ -177,11 +182,11 @@ int main(int argc, char* argv[]) { archive = VDirOpenArchive(updateArchive); } if (archive) { - puts("Extracting update"); + fputs("Extracting update\n", logfile); if (extractArchive(archive, root, prefix)) { ok = 0; } else { - puts("An error occurred"); + fputs("An error occurred\n", logfile); } archive->close(archive); unlink(updateArchive); @@ -218,10 +223,10 @@ int main(int argc, char* argv[]) { close(infd); } if (ok == 2) { - puts("Cannot move update over old file"); + fputs("Cannot move update over old file\n", logfile); } } else { - puts("Cannot move update over old file"); + fputs("Cannot move update over old file\n", logfile); } } else { ok = 0; @@ -232,10 +237,10 @@ int main(int argc, char* argv[]) { } #endif else { - puts("Cannot open update archive"); + fputs("Cannot open update archive\n", logfile); } if (ok == 0) { - puts("Complete"); + fputs("Complete", logfile); const char* command = mUpdateGetCommand(&config); strlcpy(bin, command, sizeof(bin)); mUpdateDeregister(&config); @@ -260,6 +265,7 @@ int main(int argc, char* argv[]) { } } mCoreConfigDeinit(&config); + fclose(logfile); if (ok == 0) { #ifdef _WIN32 char qbin[PATH_MAX + 2] = {0}; diff --git a/src/feature/updater.c b/src/feature/updater.c index 442a71648..e9f47f440 100644 --- a/src/feature/updater.c +++ b/src/feature/updater.c @@ -78,14 +78,7 @@ static void _updateMatch(const char* key, const char* value, void* user) { return; } const char* item = &key[dotLoc + 1]; - - struct Table* out = user; - struct mUpdate* update = HashTableLookup(out, match->channel); - if (!update) { - update = calloc(1, sizeof(*update)); - HashTableInsert(out, match->channel, update); - } - _updateUpdate(update, item, value); + _updateUpdate(match->out, item, value); } bool mUpdaterInit(struct mUpdaterContext* context, const char* manifest) { diff --git a/src/gb/CMakeLists.txt b/src/gb/CMakeLists.txt index 74e7800de..a02ab370a 100644 --- a/src/gb/CMakeLists.txt +++ b/src/gb/CMakeLists.txt @@ -7,6 +7,12 @@ set(SOURCE_FILES input.c io.c mbc.c + mbc/huc-3.c + mbc/licensed.c + mbc/mbc.c + mbc/pocket-cam.c + mbc/tama5.c + mbc/unlicensed.c memory.c overrides.c serialize.c diff --git a/src/gb/audio.c b/src/gb/audio.c index 0b6a03ecb..d2d6a9260 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -269,6 +269,29 @@ void GBAudioWriteNR31(struct GBAudio* audio, uint8_t value) { void GBAudioWriteNR32(struct GBAudio* audio, uint8_t value) { GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x4); audio->ch3.volume = GBAudioRegisterBankVolumeGetVolumeGB(value); + + audio->ch3.sample = audio->ch3.wavedata8[audio->ch3.window >> 1]; + if (!(audio->ch3.window & 1)) { + audio->ch3.sample >>= 4; + } + audio->ch3.sample &= 0xF; + int volume; + switch (audio->ch3.volume) { + case 0: + volume = 4; + break; + case 1: + volume = 0; + break; + case 2: + volume = 1; + break; + default: + case 3: + volume = 2; + break; + } + audio->ch3.sample >>= volume; } void GBAudioWriteNR33(struct GBAudio* audio, uint8_t value) { @@ -377,13 +400,13 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) { } void GBAudioWriteNR50(struct GBAudio* audio, uint8_t value) { - GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2); + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF); audio->volumeRight = GBRegisterNR50GetVolumeRight(value); audio->volumeLeft = GBRegisterNR50GetVolumeLeft(value); } void GBAudioWriteNR51(struct GBAudio* audio, uint8_t value) { - GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2); + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF); audio->ch1Right = GBRegisterNR51GetCh1Right(value); audio->ch2Right = GBRegisterNR51GetCh2Right(value); audio->ch3Right = GBRegisterNR51GetCh3Right(value); @@ -476,11 +499,11 @@ void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) { if (!audio->enable) { return; } - if (audio->p && channels != 0xF && timestamp - audio->lastSample > (int) (SAMPLE_INTERVAL * audio->timingFactor)) { + if (audio->p && channels != 0x1F && timestamp - audio->lastSample > (int) (SAMPLE_INTERVAL * audio->timingFactor)) { GBAudioSample(audio, timestamp); } - if (audio->playingCh1 && (channels & 0x1)) { + if (audio->playingCh1 && (channels & 0x1) && audio->ch1.envelope.dead != 2) { int period = 4 * (2048 - audio->ch1.control.frequency) * audio->timingFactor; int32_t diff = timestamp - audio->ch1.lastUpdate; if (diff >= period) { @@ -490,7 +513,7 @@ void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) { _updateSquareSample(&audio->ch1); } } - if (audio->playingCh2 && (channels & 0x2)) { + if (audio->playingCh2 && (channels & 0x2) && audio->ch2.envelope.dead != 2) { int period = 4 * (2048 - audio->ch2.control.frequency) * audio->timingFactor; int32_t diff = timestamp - audio->ch2.lastUpdate; if (diff >= period) { @@ -756,7 +779,7 @@ void GBAudioSample(struct GBAudio* audio, int32_t timestamp) { for (sample = audio->sampleIndex; timestamp >= interval && sample < GB_MAX_SAMPLES; ++sample, timestamp -= interval) { int16_t sampleLeft = 0; int16_t sampleRight = 0; - GBAudioRun(audio, sample * interval + audio->lastSample, 0xF); + GBAudioRun(audio, sample * interval + audio->lastSample, 0x1F); GBAudioSamplePSG(audio, &sampleLeft, &sampleRight); sampleLeft = (sampleLeft * audio->masterVolume * 6) >> 7; sampleRight = (sampleRight * audio->masterVolume * 6) >> 7; @@ -863,7 +886,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudi envelope->currentVolume &= 0xF; } _updateEnvelopeDead(envelope); - return (envelope->initialVolume || envelope->direction) && envelope->dead != 2; + return envelope->initialVolume || envelope->direction; } static void _updateSquareSample(struct GBAudioSquareChannel* ch) { diff --git a/src/gb/gb.c b/src/gb/gb.c index dc3a37191..a3a870f28 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -596,13 +596,13 @@ void GBReset(struct SM83Core* cpu) { GBVideoReset(&gb->video); GBTimerReset(&gb->timer); GBIOReset(gb); + GBAudioReset(&gb->audio); if (!gb->biosVf && gb->memory.rom) { GBSkipBIOS(gb); } else { mTimingSchedule(&gb->timing, &gb->timer.event, 0); } - GBAudioReset(&gb->audio); GBSIOReset(&gb->sio); cpu->memory.setActiveRegion(cpu, cpu->pc); @@ -744,6 +744,25 @@ void GBSkipBIOS(struct GB* gb) { GBUnmapBIOS(gb); } + GBIOWrite(gb, GB_REG_NR52, 0xF1); + GBIOWrite(gb, GB_REG_NR14, 0x3F); + GBIOWrite(gb, GB_REG_NR10, 0x80); + GBIOWrite(gb, GB_REG_NR11, 0xBF); + GBIOWrite(gb, GB_REG_NR12, 0xF3); + GBIOWrite(gb, GB_REG_NR13, 0xF3); + GBIOWrite(gb, GB_REG_NR24, 0x3F); + GBIOWrite(gb, GB_REG_NR21, 0x3F); + GBIOWrite(gb, GB_REG_NR22, 0x00); + GBIOWrite(gb, GB_REG_NR34, 0x3F); + GBIOWrite(gb, GB_REG_NR30, 0x7F); + GBIOWrite(gb, GB_REG_NR31, 0xFF); + GBIOWrite(gb, GB_REG_NR32, 0x9F); + GBIOWrite(gb, GB_REG_NR44, 0x3F); + GBIOWrite(gb, GB_REG_NR41, 0xFF); + GBIOWrite(gb, GB_REG_NR42, 0x00); + GBIOWrite(gb, GB_REG_NR43, 0x00); + GBIOWrite(gb, GB_REG_NR50, 0x77); + GBIOWrite(gb, GB_REG_NR51, 0xF3); GBIOWrite(gb, GB_REG_LCDC, 0x91); gb->memory.io[GB_REG_BANK] = 0x1; GBVideoSkipBIOS(&gb->video); diff --git a/src/gb/io.c b/src/gb/io.c index c6ad66c36..ba4473550 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -162,36 +162,7 @@ void GBIOReset(struct GB* gb) { GBIOWrite(gb, GB_REG_TMA, 0); GBIOWrite(gb, GB_REG_TAC, 0); GBIOWrite(gb, GB_REG_IF, 1); - gb->audio.playingCh1 = false; - gb->audio.playingCh2 = false; - gb->audio.playingCh3 = false; - gb->audio.playingCh4 = false; - GBIOWrite(gb, GB_REG_NR52, 0xF1); - GBIOWrite(gb, GB_REG_NR14, 0x3F); - GBIOWrite(gb, GB_REG_NR10, 0x80); - GBIOWrite(gb, GB_REG_NR11, 0xBF); - GBIOWrite(gb, GB_REG_NR12, 0xF3); - GBIOWrite(gb, GB_REG_NR13, 0xF3); - GBIOWrite(gb, GB_REG_NR24, 0x3F); - GBIOWrite(gb, GB_REG_NR21, 0x3F); - GBIOWrite(gb, GB_REG_NR22, 0x00); - GBIOWrite(gb, GB_REG_NR34, 0x3F); - GBIOWrite(gb, GB_REG_NR30, 0x7F); - GBIOWrite(gb, GB_REG_NR31, 0xFF); - GBIOWrite(gb, GB_REG_NR32, 0x9F); - GBIOWrite(gb, GB_REG_NR44, 0x3F); - GBIOWrite(gb, GB_REG_NR41, 0xFF); - GBIOWrite(gb, GB_REG_NR42, 0x00); - GBIOWrite(gb, GB_REG_NR43, 0x00); - GBIOWrite(gb, GB_REG_NR50, 0x77); - GBIOWrite(gb, GB_REG_NR51, 0xF3); - if (!gb->biosVf) { - GBIOWrite(gb, GB_REG_LCDC, 0x91); - gb->memory.io[GB_REG_BANK] = 1; - } else { - GBIOWrite(gb, GB_REG_LCDC, 0x00); - gb->memory.io[GB_REG_BANK] = 0xFF; - } + GBIOWrite(gb, GB_REG_LCDC, 0x00); GBIOWrite(gb, GB_REG_SCY, 0x00); GBIOWrite(gb, GB_REG_SCX, 0x00); GBIOWrite(gb, GB_REG_LYC, 0x00); @@ -203,6 +174,7 @@ void GBIOReset(struct GB* gb) { } GBIOWrite(gb, GB_REG_WY, 0x00); GBIOWrite(gb, GB_REG_WX, 0x00); + gb->memory.io[GB_REG_BANK] = 0xFF; if (gb->model & GB_MODEL_CGB) { GBIOWrite(gb, GB_REG_KEY0, 0); GBIOWrite(gb, GB_REG_JOYP, 0xFF); @@ -754,7 +726,7 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) { gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_SCX, state->io[GB_REG_SCX]); gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_WY, state->io[GB_REG_WY]); gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_WX, state->io[GB_REG_WX]); - if (gb->model & GB_MODEL_SGB) { + if (gb->model == GB_MODEL_SGB) { gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_BGP, state->io[GB_REG_BGP]); gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_OBP0, state->io[GB_REG_OBP0]); gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_OBP1, state->io[GB_REG_OBP1]); diff --git a/src/gb/mbc.c b/src/gb/mbc.c index d6c39035c..27ccfe784 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -3,12 +3,9 @@ * 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 "gb/mbc/mbc-private.h" -#include -#include #include -#include #include #include #include @@ -17,12 +14,6 @@ const uint32_t GB_LOGO_HASH = 0x46195417; mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC", "gb.mbc"); -static const uint8_t _tama6RTCMask[32] = { - //0 1 2 3 4 5 6 7 8 9 A B C D E F - 0xF, 0x7, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0xF, 0x1, 0xF, 0xF, 0x0, 0x0, 0x0, - 0x0, 0x0, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0x0, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0, -}; - static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) { UNUSED(address); UNUSED(value); @@ -32,42 +23,6 @@ static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) { } } -static void _GBMBC1(struct GB*, uint16_t address, uint8_t value); -static void _GBMBC2(struct GB*, uint16_t address, uint8_t value); -static void _GBMBC3(struct GB*, uint16_t address, uint8_t value); -static void _GBMBC5(struct GB*, uint16_t address, uint8_t value); -static void _GBMBC6(struct GB*, uint16_t address, uint8_t value); -static void _GBMBC7(struct GB*, uint16_t address, uint8_t value); -static void _GBMMM01(struct GB*, uint16_t address, uint8_t value); -static void _GBHuC1(struct GB*, uint16_t address, uint8_t value); -static void _GBHuC3(struct GB*, uint16_t address, uint8_t value); -static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value); -static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value); -static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value); -static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value); -static void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); -static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); -static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); -static void _GBSachen(struct GB* gb, uint16_t address, uint8_t value); - -static uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address); -static uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address); -static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address); -static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value); - -static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address); -static uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address); -static uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address); -static uint8_t _GBBBDRead(struct GBMemory*, uint16_t address); -static uint8_t _GBHitekRead(struct GBMemory*, uint16_t address); -static uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address); -static uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address); - -static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address); -static void _GBPocketCamCapture(struct GBMemory*); - -static void _GBMBC6MapChip(struct GB*, int half, uint8_t value); - void GBMBCSwitchBank(struct GB* gb, int bank) { size_t bankStart = bank * GB_SIZE_CART_BANK0; if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) { @@ -159,10 +114,10 @@ static struct { {"BBD", GB_UNL_BBD}, {"HITK", GB_UNL_HITEK}, {"SNTX", GB_MBC_AUTODETECT}, // TODO - {"NTO1", GB_MBC_AUTODETECT}, // TODO - {"NTO2", GB_MBC_AUTODETECT}, // TODO + {"NTO1", GB_UNL_NT_OLD_1}, + {"NTO2", GB_UNL_NT_OLD_2}, {"NTN", GB_UNL_NT_NEW}, - {"LICH", GB_MBC_AUTODETECT}, // TODO + {"LICH", GB_UNL_LI_CHENG}, {"LBMC", GB_MBC_AUTODETECT}, // TODO {"LIBA", GB_MBC_AUTODETECT}, // TODO {"PKJD", GB_UNL_PKJD}, @@ -171,7 +126,7 @@ static struct { {"SAM2", GB_UNL_SACHEN_MMC2}, {"ROCK", GB_MBC_AUTODETECT}, // TODO {"NGHK", GB_MBC_AUTODETECT}, // TODO - {"GB81", GB_MBC_AUTODETECT}, // TODO + {"GB81", GB_UNL_GGB81}, {"TPP1", GB_MBC_AUTODETECT}, // TODO {NULL, GB_MBC_AUTODETECT}, @@ -255,6 +210,19 @@ static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t if (mem[0x7FFF] != 0x01) { // Make sure we're not using a "fixed" version return GB_UNL_BBD; } + break; + case 0x79f34594: // DATA. + case 0x7e8c539b: // TD-SOFT + return GB_UNL_GGB81; + case 0x20d092e2: + case 0xd2b57657: + if (cart->type == 0x01) { // Make sure we're not using a "fixed" version + return GB_UNL_LI_CHENG; + } + if ((0x8000 << cart->romSize) != size) { + return GB_UNL_LI_CHENG; + } + break; } if (mem[0x104] == 0xCE && mem[0x144] == 0xED && mem[0x114] == 0x66) { @@ -486,6 +454,12 @@ void GBMBCInit(struct GB* gb) { case GB_UNL_WISDOM_TREE: gb->memory.mbcWrite = _GBWisdomTree; break; + case GB_UNL_NT_OLD_1: + gb->memory.mbcWrite = _GBNTOld1; + break; + case GB_UNL_NT_OLD_2: + gb->memory.mbcWrite = _GBNTOld2; + break; case GB_UNL_NT_NEW: gb->memory.mbcWrite = _GBNTNew; break; @@ -505,6 +479,14 @@ void GBMBCInit(struct GB* gb) { gb->memory.mbcState.bbd.bankSwapMode = 7; gb->memory.mbcReadBank1 = true; break; + case GB_UNL_LI_CHENG: + gb->memory.mbcWrite = _GBLiCheng; + break; + case GB_UNL_GGB81: + gb->memory.mbcWrite = _GBGGB81; + gb->memory.mbcRead = _GBGGB81Read; + gb->memory.mbcReadBank1 = true; + break; case GB_UNL_SACHEN_MMC1: gb->memory.mbcWrite = _GBSachen; gb->memory.mbcRead = _GBSachenMMC1Read; @@ -569,7 +551,6 @@ void GBMBCReset(struct GB* gb) { case GB_MBC6: GBMBCSwitchHalfBank(gb, 0, 2); GBMBCSwitchHalfBank(gb, 1, 3); - gb->memory.mbcState.mbc6.sramAccess = false; GBMBCSwitchSramHalfBank(gb, 0, 0); GBMBCSwitchSramHalfBank(gb, 0, 1); break; @@ -583,1626 +564,7 @@ void GBMBCReset(struct GB* gb) { gb->memory.sramBank = gb->memory.sram; } -static void _latchRtc(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch) { - time_t t; - if (rtc) { - if (rtc->sample) { - rtc->sample(rtc); - } - t = rtc->unixTime(rtc); - } else { - t = time(0); - } - time_t currentLatch = t; - t -= *rtcLastLatch; - *rtcLastLatch = currentLatch; - - int64_t diff; - diff = rtcRegs[0] + t % 60; - if (diff < 0) { - diff += 60; - t -= 60; - } - rtcRegs[0] = diff % 60; - t /= 60; - t += diff / 60; - - diff = rtcRegs[1] + t % 60; - if (diff < 0) { - diff += 60; - t -= 60; - } - rtcRegs[1] = diff % 60; - t /= 60; - t += diff / 60; - - diff = rtcRegs[2] + t % 24; - if (diff < 0) { - diff += 24; - t -= 24; - } - rtcRegs[2] = diff % 24; - t /= 24; - t += diff / 24; - - diff = rtcRegs[3] + ((rtcRegs[4] & 1) << 8) + (t & 0x1FF); - rtcRegs[3] = diff; - rtcRegs[4] &= 0xFE; - rtcRegs[4] |= (diff >> 8) & 1; - if (diff & 0x200) { - rtcRegs[4] |= 0x80; - } -} - -static void _GBMBC1Update(struct GB* gb) { - struct GBMBC1State* state = &gb->memory.mbcState.mbc1; - int bank = state->bankLo; - bank &= (1 << state->multicartStride) - 1; - bank |= state->bankHi << state->multicartStride; - if (state->mode) { - GBMBCSwitchBank0(gb, state->bankHi << state->multicartStride); - GBMBCSwitchSramBank(gb, state->bankHi & 3); - } else { - GBMBCSwitchBank0(gb, 0); - GBMBCSwitchSramBank(gb, 0); - } - if (!(state->bankLo & 0x1F)) { - ++state->bankLo; - ++bank; - } - GBMBCSwitchBank(gb, bank); -} - -void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank = value & 0x1F; - switch (address >> 13) { - case 0x0: - switch (value & 0xF) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value); - break; - } - break; - case 0x1: - memory->mbcState.mbc1.bankLo = bank; - _GBMBC1Update(gb); - break; - case 0x2: - bank &= 3; - memory->mbcState.mbc1.bankHi = bank; - _GBMBC1Update(gb); - break; - case 0x3: - memory->mbcState.mbc1.mode = value & 1; - _GBMBC1Update(gb); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value); - break; - } -} - -void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int shift = (address & 1) * 4; - int bank = value & 0xF; - switch ((address & 0xC100) >> 8) { - case 0x0: - switch (value & 0x0F) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC2 unknown value %02X", value); - break; - } - break; - case 0x1: - if (!bank) { - ++bank; - } - GBMBCSwitchBank(gb, bank); - break; - case 0x80: - case 0x81: - case 0x82: - case 0x83: - if (!memory->sramAccess) { - return; - } - address &= 0x1FF; - memory->sramBank[(address >> 1)] &= 0xF0 >> shift; - memory->sramBank[(address >> 1)] |= (value & 0xF) << shift; - gb->sramDirty |= mSAVEDATA_DIRT_NEW; - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value); - break; - } -} - -static uint8_t _GBMBC2Read(struct GBMemory* memory, uint16_t address) { - if (!memory->sramAccess) { - return 0xFF; - } - address &= 0x1FF; - int shift = (address & 1) * 4; - return (memory->sramBank[(address >> 1)] >> shift) | 0xF0; -} - -void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank = value; - switch (address >> 13) { - case 0x0: - switch (value & 0xF) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value); - break; - } - break; - case 0x1: - if (gb->memory.romSize < GB_SIZE_CART_BANK0 * 0x80) { - bank &= 0x7F; - } - if (!bank) { - ++bank; - } - GBMBCSwitchBank(gb, bank); - break; - case 0x2: - bank &= 0xF; - if (bank < 8) { - GBMBCSwitchSramBank(gb, value); - memory->rtcAccess = false; - } else if (bank <= 0xC) { - memory->activeRtcReg = bank - 8; - memory->rtcAccess = true; - } - break; - case 0x3: - if (memory->rtcLatched && value == 0) { - memory->rtcLatched = false; - } else if (!memory->rtcLatched && value == 1) { - _latchRtc(gb->memory.rtc, gb->memory.rtcRegs, &gb->memory.rtcLastLatch); - memory->rtcLatched = true; - } - break; - } -} - -void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank; - switch (address >> 12) { - case 0x0: - case 0x1: - switch (value) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value); - break; - } - break; - case 0x2: - bank = (memory->currentBank & 0x100) | value; - GBMBCSwitchBank(gb, bank); - break; - case 0x3: - bank = (memory->currentBank & 0xFF) | ((value & 1) << 8); - GBMBCSwitchBank(gb, bank); - break; - case 0x4: - case 0x5: - if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) { - memory->rumble->setRumble(memory->rumble, (value >> 3) & 1); - value &= ~8; - } - GBMBCSwitchSramBank(gb, value & 0xF); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value); - break; - } -} - -void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank = value; - switch (address >> 10) { - case 0: - switch (value) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC6 unknown value %02X", value); - break; - } - break; - case 0x1: - GBMBCSwitchSramHalfBank(gb, 0, bank); - break; - case 0x2: - GBMBCSwitchSramHalfBank(gb, 1, bank); - break; - case 0x3: - mLOG(GB_MBC, STUB, "MBC6 unimplemented flash OE write: %04X:%02X", address, value); - break; - case 0x4: - mLOG(GB_MBC, STUB, "MBC6 unimplemented flash WE write: %04X:%02X", address, value); - break; - case 0x8: - case 0x9: - GBMBCSwitchHalfBank(gb, 0, bank); - break; - case 0xA: - case 0xB: - _GBMBC6MapChip(gb, 0, value); - break; - case 0xC: - case 0xD: - GBMBCSwitchHalfBank(gb, 1, bank); - break; - case 0xE: - case 0xF: - _GBMBC6MapChip(gb, 1, value); - break; - case 0x28: - case 0x29: - case 0x2A: - case 0x2B: - if (memory->sramAccess) { - memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; - gb->sramDirty |= mSAVEDATA_DIRT_NEW; - } - break; - case 0x2C: - case 0x2D: - case 0x2E: - case 0x2F: - if (memory->sramAccess) { - memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; - } - break; - default: - mLOG(GB_MBC, STUB, "MBC6 unknown address: %04X:%02X", address, value); - break; - } -} - -uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) { - if (!memory->sramAccess) { - return 0xFF; - } - switch (address >> 12) { - case 0xA: - return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; - case 0xB: - return memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; - } - return 0xFF; -} - -static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) { - if (!half) { - gb->memory.mbcState.mbc6.flashBank0 = !!(value & 0x08); - GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank); - } else { - gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08); - GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank1); - } -} - -void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) { - int bank = value & 0x7F; - switch (address >> 13) { - case 0x0: - switch (value) { - default: - case 0: - gb->memory.mbcState.mbc7.access = 0; - break; - case 0xA: - gb->memory.mbcState.mbc7.access |= 1; - break; - } - break; - case 0x1: - GBMBCSwitchBank(gb, bank); - break; - case 0x2: - if (value == 0x40) { - gb->memory.mbcState.mbc7.access |= 2; - } else { - gb->memory.mbcState.mbc7.access &= ~2; - } - break; - case 0x5: - _GBMBC7Write(&gb->memory, address, value); - gb->sramDirty |= mSAVEDATA_DIRT_NEW; - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value); - break; - } -} - -uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) { - struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; - if (mbc7->access != 3) { - return 0xFF; - } - switch (address & 0xF0) { - case 0x20: - if (memory->rotation && memory->rotation->readTiltX) { - int32_t x = -memory->rotation->readTiltX(memory->rotation); - x >>= 21; - x += 0x81D0; - return x; - } - return 0xFF; - case 0x30: - if (memory->rotation && memory->rotation->readTiltX) { - int32_t x = -memory->rotation->readTiltX(memory->rotation); - x >>= 21; - x += 0x81D0; - return x >> 8; - } - return 7; - case 0x40: - if (memory->rotation && memory->rotation->readTiltY) { - int32_t y = -memory->rotation->readTiltY(memory->rotation); - y >>= 21; - y += 0x81D0; - return y; - } - return 0xFF; - case 0x50: - if (memory->rotation && memory->rotation->readTiltY) { - int32_t y = -memory->rotation->readTiltY(memory->rotation); - y >>= 21; - y += 0x81D0; - return y >> 8; - } - return 7; - case 0x60: - return 0; - case 0x80: - return mbc7->eeprom; - default: - return 0xFF; - } -} - -static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) { - struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; - if (mbc7->access != 3) { - return; - } - switch (address & 0xF0) { - case 0x00: - mbc7->latch = (value & 0x55) == 0x55; - return; - case 0x10: - mbc7->latch |= (value & 0xAA); - if (mbc7->latch == 0xAB && memory->rotation && memory->rotation->sample) { - memory->rotation->sample(memory->rotation); - } - mbc7->latch = 0; - return; - default: - mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value); - return; - case 0x80: - break; - } - GBMBC7Field old = memory->mbcState.mbc7.eeprom; - value = GBMBC7FieldFillDO(value); // Hi-Z - if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) { - mbc7->state = GBMBC7_STATE_IDLE; - } - if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) { - if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) { - mbc7->sr <<= 1; - mbc7->sr |= GBMBC7FieldGetDI(value); - ++mbc7->srBits; - } - switch (mbc7->state) { - case GBMBC7_STATE_IDLE: - if (GBMBC7FieldIsDI(value)) { - mbc7->state = GBMBC7_STATE_READ_COMMAND; - mbc7->srBits = 0; - mbc7->sr = 0; - } - break; - case GBMBC7_STATE_READ_COMMAND: - if (mbc7->srBits == 10) { - mbc7->state = 0x10 | (mbc7->sr >> 6); - if (mbc7->state & 0xC) { - mbc7->state &= ~0x3; - } - mbc7->srBits = 0; - mbc7->address = mbc7->sr & 0x7F; - } - break; - case GBMBC7_STATE_DO: - value = GBMBC7FieldSetDO(value, mbc7->sr >> 15); - mbc7->sr <<= 1; - --mbc7->srBits; - if (!mbc7->srBits) { - mbc7->state = GBMBC7_STATE_IDLE; - } - break; - default: - break; - } - switch (mbc7->state) { - case GBMBC7_STATE_EEPROM_EWEN: - mbc7->writable = true; - mbc7->state = GBMBC7_STATE_IDLE; - break; - case GBMBC7_STATE_EEPROM_EWDS: - mbc7->writable = false; - mbc7->state = GBMBC7_STATE_IDLE; - break; - case GBMBC7_STATE_EEPROM_WRITE: - if (mbc7->srBits == 16) { - if (mbc7->writable) { - memory->sram[mbc7->address * 2] = mbc7->sr >> 8; - memory->sram[mbc7->address * 2 + 1] = mbc7->sr; - } - mbc7->state = GBMBC7_STATE_IDLE; - } - break; - case GBMBC7_STATE_EEPROM_ERASE: - if (mbc7->writable) { - memory->sram[mbc7->address * 2] = 0xFF; - memory->sram[mbc7->address * 2 + 1] = 0xFF; - } - mbc7->state = GBMBC7_STATE_IDLE; - break; - case GBMBC7_STATE_EEPROM_READ: - mbc7->srBits = 16; - mbc7->sr = memory->sram[mbc7->address * 2] << 8; - mbc7->sr |= memory->sram[mbc7->address * 2 + 1]; - mbc7->state = GBMBC7_STATE_DO; - value = GBMBC7FieldClearDO(value); - break; - case GBMBC7_STATE_EEPROM_WRAL: - if (mbc7->srBits == 16) { - if (mbc7->writable) { - int i; - for (i = 0; i < 128; ++i) { - memory->sram[i * 2] = mbc7->sr >> 8; - memory->sram[i * 2 + 1] = mbc7->sr; - } - } - mbc7->state = GBMBC7_STATE_IDLE; - } - break; - case GBMBC7_STATE_EEPROM_ERAL: - if (mbc7->writable) { - int i; - for (i = 0; i < 128; ++i) { - memory->sram[i * 2] = 0xFF; - memory->sram[i * 2 + 1] = 0xFF; - } - } - mbc7->state = GBMBC7_STATE_IDLE; - break; - default: - break; - } - } else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) { - value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old)); - } - mbc7->eeprom = value; -} - -void _GBMMM01(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - if (!memory->mbcState.mmm01.locked) { - switch (address >> 13) { - case 0x0: - memory->mbcState.mmm01.locked = true; - GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0); - break; - case 0x1: - memory->mbcState.mmm01.currentBank0 &= ~0x7F; - memory->mbcState.mmm01.currentBank0 |= value & 0x7F; - break; - case 0x2: - memory->mbcState.mmm01.currentBank0 &= ~0x180; - memory->mbcState.mmm01.currentBank0 |= (value & 0x30) << 3; - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); - break; - } - return; - } - switch (address >> 13) { - case 0x0: - switch (value) { - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - memory->sramAccess = false; - break; - } - break; - case 0x1: - GBMBCSwitchBank(gb, value + memory->mbcState.mmm01.currentBank0); - break; - case 0x2: - GBMBCSwitchSramBank(gb, value); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); - break; - } -} - -void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank = value & 0x3F; - switch (address >> 13) { - case 0x0: - switch (value) { - case 0xE: - memory->sramAccess = false; - break; - default: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - } - break; - case 0x1: - GBMBCSwitchBank(gb, bank); - break; - case 0x2: - GBMBCSwitchSramBank(gb, value); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "HuC-1 unknown address: %04X:%02X", address, value); - break; - } -} - -static void _latchHuC3Rtc(struct mRTCSource* rtc, uint8_t* huc3Regs, time_t* rtcLastLatch) { - time_t t; - if (rtc) { - if (rtc->sample) { - rtc->sample(rtc); - } - t = rtc->unixTime(rtc); - } else { - t = time(0); - } - t -= *rtcLastLatch; - t /= 60; - - if (!t) { - return; - } - *rtcLastLatch += t * 60; - - int minutes = huc3Regs[GBHUC3_RTC_MINUTES_HI] << 8; - minutes |= huc3Regs[GBHUC3_RTC_MINUTES_MI] << 4; - minutes |= huc3Regs[GBHUC3_RTC_MINUTES_LO]; - minutes += t % 1440; - t /= 1440; - if (minutes >= 1440) { - minutes -= 1440; - ++t; - } else if (minutes < 0) { - minutes += 1440; - --t; - } - huc3Regs[GBHUC3_RTC_MINUTES_LO] = minutes & 0xF; - huc3Regs[GBHUC3_RTC_MINUTES_MI] = (minutes >> 4) & 0xF; - huc3Regs[GBHUC3_RTC_MINUTES_HI] = (minutes >> 8) & 0xF; - - int days = huc3Regs[GBHUC3_RTC_DAYS_LO]; - days |= huc3Regs[GBHUC3_RTC_DAYS_MI] << 4; - days |= huc3Regs[GBHUC3_RTC_DAYS_HI] << 8; - - days += t; - - huc3Regs[GBHUC3_RTC_DAYS_LO] = days & 0xF; - huc3Regs[GBHUC3_RTC_DAYS_MI] = (days >> 4) & 0xF; - huc3Regs[GBHUC3_RTC_DAYS_HI] = (days >> 8) & 0xF; -} - -static void _huc3Commit(struct GB* gb, struct GBHuC3State* state) { - size_t c; - switch (state->value & 0x70) { - case 0x10: - if ((state->index & 0xF8) == 0x10) { - _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch); - } - state->value &= 0xF0; - state->value |= state->registers[state->index] & 0xF; - mLOG(GB_MBC, DEBUG, "HuC-3 read: %02X:%X", state->index, state->value & 0xF); - if (state->value & 0x10) { - ++state->index; - } - break; - case 0x30: - mLOG(GB_MBC, DEBUG, "HuC-3 write: %02X:%X", state->index, state->value & 0xF); - state->registers[state->index] = state->value & 0xF; - if (state->value & 0x10) { - ++state->index; - } - break; - case 0x40: - state->index &= 0xF0; - state->index |= (state->value) & 0xF; - mLOG(GB_MBC, DEBUG, "HuC-3 index (low): %02X", state->index); - break; - case 0x50: - state->index &= 0x0F; - state->index |= ((state->value) & 0xF) << 4; - mLOG(GB_MBC, DEBUG, "HuC-3 index (high): %02X", state->index); - break; - case 0x60: - switch (state->value & 0xF) { - case GBHUC3_CMD_LATCH: - _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch); - memcpy(state->registers, &state->registers[GBHUC3_RTC_MINUTES_LO], 6); - mLOG(GB_MBC, DEBUG, "HuC-3 RTC latch"); - break; - case GBHUC3_CMD_SET_RTC: - memcpy(&state->registers[GBHUC3_RTC_MINUTES_LO], state->registers, 6); - mLOG(GB_MBC, DEBUG, "HuC-3 set RTC"); - break; - case GBHUC3_CMD_RO: - mLOG(GB_MBC, STUB, "HuC-3 unimplemented read-only mode"); - break; - case GBHUC3_CMD_TONE: - if (state->registers[GBHUC3_SPEAKER_ENABLE] == 1) { - for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) { - struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c); - if (callbacks->alarm) { - callbacks->alarm(callbacks->context); - } - } - mLOG(GB_MBC, DEBUG, "HuC-3 tone %i", state->registers[GBHUC3_SPEAKER_TONE] & 3); - } - break; - default: - mLOG(GB_MBC, STUB, "HuC-3 unknown command: %X", state->value & 0xF); - break; - } - state->value = 0xE1; - break; - default: - mLOG(GB_MBC, STUB, "HuC-3 unknown mode commit: %02X:%02X", state->index, state->value); - break; - } -} - -void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - struct GBHuC3State* state = &memory->mbcState.huc3; - int bank = value & 0x7F; - if (address & 0x1FFF) { - mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value); - } - - switch (address >> 13) { - case 0x0: - switch (value) { - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - memory->sramAccess = false; - break; - } - state->mode = value; - break; - case 0x1: - GBMBCSwitchBank(gb, bank); - break; - case 0x2: - GBMBCSwitchSramBank(gb, bank); - break; - case 0x5: - switch (state->mode) { - case GBHUC3_MODE_IN: - state->value = 0x80 | value; - break; - case GBHUC3_MODE_COMMIT: - _huc3Commit(gb, state); - break; - default: - mLOG(GB_MBC, STUB, "HuC-3 unknown mode write: %02X:%02X", state->mode, value); - } - break; - default: - // TODO - mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value); - break; - } -} - -uint8_t _GBHuC3Read(struct GBMemory* memory, uint16_t address) { - struct GBHuC3State* state = &memory->mbcState.huc3; - switch (state->mode) { - case GBHUC3_MODE_SRAM_RO: - case GBHUC3_MODE_SRAM_RW: - return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; - case GBHUC3_MODE_IN: - case GBHUC3_MODE_OUT: - return 0x80 | state->value; - default: - return 0xFF; - } -} - -void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank = value & 0x3F; - switch (address >> 13) { - case 0x0: - switch (value) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "Pocket Cam unknown value %02X", value); - break; - } - break; - case 0x1: - GBMBCSwitchBank(gb, bank); - break; - case 0x2: - if (value < 0x10) { - GBMBCSwitchSramBank(gb, value); - memory->mbcState.pocketCam.registersActive = false; - memory->directSramAccess = true; - } else { - memory->mbcState.pocketCam.registersActive = true; - memory->directSramAccess = false; - } - break; - case 0x5: - if (!memory->mbcState.pocketCam.registersActive) { - break; - } - address &= 0x7F; - if (address == 0 && value & 1) { - value &= 6; // TODO: Timing - gb->sramDirty |= mSAVEDATA_DIRT_NEW; - _GBPocketCamCapture(memory); - } - if (address < sizeof(memory->mbcState.pocketCam.registers)) { - memory->mbcState.pocketCam.registers[address] = value; - } - break; - default: - mLOG(GB_MBC, STUB, "Pocket Cam unknown address: %04X:%02X", address, value); - break; - } -} - -uint8_t _GBPocketCamRead(struct GBMemory* memory, uint16_t address) { - if (memory->mbcState.pocketCam.registersActive) { - if ((address & 0x7F) == 0) { - return memory->mbcState.pocketCam.registers[0]; - } - return 0; - } - return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; -} - -void _GBPocketCamCapture(struct GBMemory* memory) { - if (!memory->cam) { - return; - } - const void* image = NULL; - size_t stride; - enum mColorFormat format; - memory->cam->requestImage(memory->cam, &image, &stride, &format); - if (!image) { - return; - } - memset(&memory->sram[0x100], 0, GBCAM_HEIGHT * GBCAM_WIDTH / 4); - struct GBPocketCamState* pocketCam = &memory->mbcState.pocketCam; - size_t x, y; - for (y = 0; y < GBCAM_HEIGHT; ++y) { - for (x = 0; x < GBCAM_WIDTH; ++x) { - uint32_t gray; - uint32_t color; - switch (format) { - case mCOLOR_XBGR8: - case mCOLOR_XRGB8: - case mCOLOR_ARGB8: - case mCOLOR_ABGR8: - color = ((const uint32_t*) image)[y * stride + x]; - gray = (color & 0xFF) + ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF); - break; - case mCOLOR_BGRX8: - case mCOLOR_RGBX8: - case mCOLOR_RGBA8: - case mCOLOR_BGRA8: - color = ((const uint32_t*) image)[y * stride + x]; - gray = ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF) + ((color >> 24) & 0xFF); - break; - case mCOLOR_BGR5: - case mCOLOR_RGB5: - case mCOLOR_ARGB5: - case mCOLOR_ABGR5: - color = ((const uint16_t*) image)[y * stride + x]; - gray = ((color << 3) & 0xF8) + ((color >> 2) & 0xF8) + ((color >> 7) & 0xF8); - break; - case mCOLOR_BGR565: - case mCOLOR_RGB565: - color = ((const uint16_t*) image)[y * stride + x]; - gray = ((color << 3) & 0xF8) + ((color >> 3) & 0xFC) + ((color >> 8) & 0xF8); - break; - case mCOLOR_BGRA5: - case mCOLOR_RGBA5: - color = ((const uint16_t*) image)[y * stride + x]; - gray = ((color << 2) & 0xF8) + ((color >> 3) & 0xF8) + ((color >> 8) & 0xF8); - break; - default: - mLOG(GB_MBC, WARN, "Unsupported pixel format: %X", format); - return; - } - uint16_t exposure = (pocketCam->registers[2] << 8) | (pocketCam->registers[3]); - gray = (gray + 1) * exposure / 0x300; - // TODO: Additional processing - int matrixEntry = 3 * ((x & 3) + 4 * (y & 3)); - if (gray < pocketCam->registers[matrixEntry + 6]) { - gray = 0x101; - } else if (gray < pocketCam->registers[matrixEntry + 7]) { - gray = 0x100; - } else if (gray < pocketCam->registers[matrixEntry + 8]) { - gray = 0x001; - } else { - gray = 0; - } - int coord = (((x >> 3) & 0xF) * 8 + (y & 0x7)) * 2 + (y & ~0x7) * 0x20; - uint16_t existing; - LOAD_16LE(existing, coord + 0x100, memory->sram); - existing |= gray << (7 - (x & 7)); - STORE_16LE(existing, coord + 0x100, memory->sram); - } - } -} - -static const int _daysToMonth[] = { - [ 1] = 0, - [ 2] = 31, - [ 3] = 31 + 28, - [ 4] = 31 + 28 + 31, - [ 5] = 31 + 28 + 31 + 30, - [ 6] = 31 + 28 + 31 + 30 + 31, - [ 7] = 31 + 28 + 31 + 30 + 31 + 30, - [ 8] = 31 + 28 + 31 + 30 + 31 + 30 + 31, - [ 9] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, - [10] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, - [11] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, - [12] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, -}; - -static int _tama6DMYToDayOfYear(int day, int month, int year) { - if (month < 1 || month > 12) { - return -1; - } - day += _daysToMonth[month]; - if (month > 2 && (year & 3) == 0) { - ++day; - } - return day; -} - -static int _tama6DayOfYearToMonth(int day, int year) { - int month; - for (month = 1; month < 12; ++month) { - if (day <= _daysToMonth[month + 1]) { - return month; - } - if (month == 2 && (year & 3) == 0) { - if (day == 60) { - return 2; - } - --day; - } - } - return 12; -} - -static int _tama6DayOfYearToDayOfMonth(int day, int year) { - int month; - for (month = 1; month < 12; ++month) { - if (day <= _daysToMonth[month + 1]) { - return day - _daysToMonth[month]; - } - if (month == 2 && (year & 3) == 0) { - if (day == 60) { - return 29; - } - --day; - } - } - return day - _daysToMonth[12]; -} - -static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, time_t* rtcLastLatch) { - time_t t; - if (rtc) { - if (rtc->sample) { - rtc->sample(rtc); - } - t = rtc->unixTime(rtc); - } else { - t = time(0); - } - time_t currentLatch = t; - t -= *rtcLastLatch; - *rtcLastLatch = currentLatch; - if (!t || tama5->disabled) { - return; - } - - uint8_t* timerRegs = tama5->rtcTimerPage; - bool is24hour = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_24_HOUR]; - int64_t diff; - diff = timerRegs[GBTAMA6_RTC_PA0_SECOND_1] + timerRegs[GBTAMA6_RTC_PA0_SECOND_10] * 10 + t % 60; - if (diff < 0) { - diff += 60; - t -= 60; - } - timerRegs[GBTAMA6_RTC_PA0_SECOND_1] = diff % 10; - timerRegs[GBTAMA6_RTC_PA0_SECOND_10] = (diff % 60) / 10; - t /= 60; - t += diff / 60; - - diff = timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] + timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] * 10 + t % 60; - if (diff < 0) { - diff += 60; - t -= 60; - } - timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] = diff % 10; - timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] = (diff % 60) / 10; - t /= 60; - t += diff / 60; - - diff = timerRegs[GBTAMA6_RTC_PA0_HOUR_1]; - if (is24hour) { - diff += timerRegs[GBTAMA6_RTC_PA0_HOUR_10] * 10; - } else { - int hour10 = timerRegs[GBTAMA6_RTC_PA0_HOUR_10]; - diff += (hour10 & 1) * 10; - diff += (hour10 & 2) * 12; - } - diff += t % 24; - if (diff < 0) { - diff += 24; - t -= 24; - } - if (is24hour) { - timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 24) % 10; - timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10; - } else { - timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10; - timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2; - } - t /= 24; - t += diff / 24; - - int day = timerRegs[GBTAMA6_RTC_PA0_DAY_1] + timerRegs[GBTAMA6_RTC_PA0_DAY_10] * 10; - int month = timerRegs[GBTAMA6_RTC_PA0_MONTH_1] + timerRegs[GBTAMA6_RTC_PA0_MONTH_10] * 10; - int year = timerRegs[GBTAMA6_RTC_PA0_YEAR_1] + timerRegs[GBTAMA6_RTC_PA0_YEAR_10] * 10; - int leapYear = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR]; - int dayOfWeek = timerRegs[GBTAMA6_RTC_PA0_WEEK]; - int dayInYear = _tama6DMYToDayOfYear(day, month, leapYear); - diff = dayInYear + t; - while (diff <= 0) { - // Previous year - if (leapYear & 3) { - diff += 365; - } else { - diff += 366; - } - --year; - --leapYear; - } - while (diff > (leapYear & 3 ? 365 : 366)) { - // Future year - if (year % 4) { - diff -= 365; - } else { - diff -= 366; - } - ++year; - ++leapYear; - } - dayOfWeek = (dayOfWeek + diff) % 7; - year %= 100; - leapYear &= 3; - - day = _tama6DayOfYearToDayOfMonth(diff, leapYear); - month = _tama6DayOfYearToMonth(diff, leapYear); - - timerRegs[GBTAMA6_RTC_PA0_WEEK] = dayOfWeek; - tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR] = leapYear; - - timerRegs[GBTAMA6_RTC_PA0_DAY_1] = day % 10; - timerRegs[GBTAMA6_RTC_PA0_DAY_10] = day / 10; - - timerRegs[GBTAMA6_RTC_PA0_MONTH_1] = month % 10; - timerRegs[GBTAMA6_RTC_PA0_MONTH_10] = month / 10; - - timerRegs[GBTAMA6_RTC_PA0_YEAR_1] = year % 10; - timerRegs[GBTAMA6_RTC_PA0_YEAR_10] = year / 10; -} - -void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - struct GBTAMA5State* tama5 = &memory->mbcState.tama5; - switch (address >> 13) { - case 0x5: - if (address & 1) { - tama5->reg = value; - } else { - value &= 0xF; - if (tama5->reg < GBTAMA5_MAX) { - mLOG(GB_MBC, DEBUG, "TAMA5 write: %02X:%X", tama5->reg, value); - tama5->registers[tama5->reg] = value; - uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO]; - uint8_t out = (tama5->registers[GBTAMA5_WRITE_HI] << 4) | tama5->registers[GBTAMA5_WRITE_LO]; - switch (tama5->reg) { - case GBTAMA5_BANK_LO: - case GBTAMA5_BANK_HI: - GBMBCSwitchBank(gb, tama5->registers[GBTAMA5_BANK_LO] | (tama5->registers[GBTAMA5_BANK_HI] << 4)); - break; - case GBTAMA5_WRITE_LO: - case GBTAMA5_WRITE_HI: - case GBTAMA5_ADDR_HI: - break; - case GBTAMA5_ADDR_LO: - switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) { - case 0x0: // RAM write - memory->sram[address] = out; - gb->sramDirty |= mSAVEDATA_DIRT_NEW; - break; - case 0x1: // RAM read - break; - case 0x2: // Other commands - switch (address) { - case GBTAMA6_DISABLE_TIMER: - tama5->disabled = true; - tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0x7; - tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0x7; - tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0x7; - tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0x7; - break; - case GBTAMA6_ENABLE_TIMER: - tama5->disabled = false; - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_1] = 0; - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_10] = 0; - tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x8; - tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x8; - tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x8; - tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x8; - break; - case GBTAMA6_MINUTE_WRITE: - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1] = out & 0xF; - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] = out >> 4; - break; - case GBTAMA6_HOUR_WRITE: - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1] = out & 0xF; - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] = out >> 4; - break; - case GBTAMA6_DISABLE_ALARM: - tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xB; - tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xB; - tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xB; - tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xB; - break; - case GBTAMA6_ENABLE_ALARM: - tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x4; - tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x4; - tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x4; - tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x4; - break; - } - break; - case 0x4: // RTC access - address = tama5->registers[GBTAMA5_WRITE_LO]; - if (address >= GBTAMA6_RTC_PAGE) { - break; - } - out = tama5->registers[GBTAMA5_WRITE_HI]; - switch (tama5->registers[GBTAMA5_ADDR_LO]) { - case 0: - out &= _tama6RTCMask[address]; - tama5->rtcTimerPage[address] = out; - break; - case 2: - out &= _tama6RTCMask[address | 0x10]; - tama5->rtcAlarmPage[address] = out; - break; - case 4: - tama5->rtcFreePage0[address] = out; - break; - case 6: - tama5->rtcFreePage1[address] = out; - break; - } - break; - default: - mLOG(GB_MBC, STUB, "TAMA5 unknown address: %02X:%02X", address, out); - break; - } - break; - default: - mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X:%X", tama5->reg, value); - break; - } - } else { - mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X", tama5->reg); - } - } - break; - default: - mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X:%02X", address, value); - } -} - -uint8_t _GBTAMA5Read(struct GBMemory* memory, uint16_t address) { - struct GBTAMA5State* tama5 = &memory->mbcState.tama5; - if ((address & 0x1FFF) > 1) { - mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X", address); - } - if (address & 1) { - return 0xFF; - } else { - uint8_t value = 0xF0; - uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO]; - switch (tama5->reg) { - case GBTAMA5_ACTIVE: - return 0xF1; - case GBTAMA5_READ_LO: - case GBTAMA5_READ_HI: - switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) { - case 0x1: - value = memory->sram[address]; - break; - case 0x2: - mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address); - _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch); - switch (address) { - case GBTAMA6_MINUTE_READ: - value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1]; - break; - case GBTAMA6_HOUR_READ: - value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1]; - break; - default: - value = address; - break; - } - break; - case 0x4: - if (tama5->reg == GBTAMA5_READ_HI) { - mLOG(GB_MBC, GAME_ERROR, "TAMA5 reading RTC incorrectly"); - break; - } - _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch); - address = tama5->registers[GBTAMA5_WRITE_LO]; - if (address > GBTAMA6_RTC_PAGE) { - value = 0; - break; - } - switch (tama5->registers[GBTAMA5_ADDR_LO]) { - case 1: - value = tama5->rtcTimerPage[address]; - break; - case 3: - value = tama5->rtcTimerPage[address]; - break; - case 5: - value = tama5->rtcTimerPage[address]; - break; - case 7: - value = tama5->rtcTimerPage[address]; - break; - } - break; - default: - mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address); - break; - } - if (tama5->reg == GBTAMA5_READ_HI) { - value >>= 4; - } - value |= 0xF0; - return value; - default: - mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg); - return 0xF1; - } - } -} - -void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value) { - UNUSED(value); - int bank = address & 0x3F; - switch (address >> 14) { - case 0x0: - GBMBCSwitchBank0(gb, bank * 2); - GBMBCSwitchBank(gb, bank * 2 + 1); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "Wisdom Tree unknown address: %04X:%02X", address, value); - break; - } -} - -void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - switch (address >> 13) { - case 0x2: - if (value < 8) { - memory->directSramAccess = true; - memory->activeRtcReg = 0; - } else if (value >= 0xD && value <= 0xF) { - memory->directSramAccess = false; - memory->rtcAccess = false; - memory->activeRtcReg = value - 8; - } - break; - case 0x5: - if (!memory->sramAccess) { - return; - } - switch (memory->activeRtcReg) { - case 0: - memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value; - break; - case 5: - case 6: - memory->mbcState.pkjd.reg[memory->activeRtcReg - 5] = value; - break; - case 7: - switch (value) { - case 0x11: - memory->mbcState.pkjd.reg[0]--; - break; - case 0x12: - memory->mbcState.pkjd.reg[1]--; - break; - case 0x41: - memory->mbcState.pkjd.reg[0] += memory->mbcState.pkjd.reg[1]; - break; - case 0x42: - memory->mbcState.pkjd.reg[1] += memory->mbcState.pkjd.reg[0]; - break; - case 0x51: - memory->mbcState.pkjd.reg[0]++; - break; - case 0x52: - memory->mbcState.pkjd.reg[1]--; - break; - } - break; - } - return; - } - _GBMBC3(gb, address, value); -} - -static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) { - if (!memory->sramAccess) { - return 0xFF; - } - switch (memory->activeRtcReg) { - case 0: - return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; - case 5: - case 6: - return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5]; - default: - return 0; - } -} - -void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - if (address >> 8 == 0x14) { - memory->mbcState.ntNew.splitMode = true; - return; - } - if (memory->mbcState.ntNew.splitMode) { - int bank = value; - if (bank < 2) { - bank = 2; - } - switch (address >> 10) { - case 8: - GBMBCSwitchHalfBank(gb, 0, bank); - return; - case 9: - GBMBCSwitchHalfBank(gb, 1, bank); - return; - } - } - _GBMBC5(gb, address, value); -} - -static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) { - uint8_t newbyte = 0; - int i; - for(i = 0; i < 8; ++i) { - int oldbit = reorder[i]; - int newbit = i; - newbyte += ((input >> oldbit) & 1) << newbit; - } - - return newbyte; -} - -static const uint8_t _bbdDataReordering[8][8] = { - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 03 - NOT KNOWN YET - { 0, 5, 1, 3, 4, 2, 6, 7 }, // 04 - Garou - { 0, 4, 2, 3, 1, 5, 6, 7 }, // 05 - Harry - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET - { 0, 1, 5, 3, 4, 2, 6, 7 }, // 07 - Digimon -}; - -static const uint8_t _bbdBankReordering[8][8] = { - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET - { 3, 4, 2, 0, 1, 5, 6, 7 }, // 03 - 0,1 unconfirmed. Digimon/Garou - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 04 - NOT KNOWN YET - { 1, 2, 3, 4, 0, 5, 6, 7 }, // 05 - 0,1 unconfirmed. Harry - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 07 - NOT KNOWN YET -}; - -void _GBBBD(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - switch (address & 0xF0FF) { - case 0x2000: - value = _reorderBits(value, _bbdBankReordering[memory->mbcState.bbd.bankSwapMode]); - break; - case 0x2001: - memory->mbcState.bbd.dataSwapMode = value & 0x07; - if (!(memory->mbcState.bbd.dataSwapMode == 0x07 || memory->mbcState.bbd.dataSwapMode == 0x05 || memory->mbcState.bbd.dataSwapMode == 0x04 || memory->mbcState.bbd.dataSwapMode == 0x00)) { - mLOG(GB_MBC, STUB, "Bitswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode); - } - break; - case 0x2080: - memory->mbcState.bbd.bankSwapMode = value & 0x07; - if (!(memory->mbcState.bbd.bankSwapMode == 0x03 || memory->mbcState.bbd.bankSwapMode == 0x05 || memory->mbcState.bbd.bankSwapMode == 0x00)) { - mLOG(GB_MBC, STUB, "Bankswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode); - } - break; - } - _GBMBC5(gb, address, value); -} - -uint8_t _GBBBDRead(struct GBMemory* memory, uint16_t address) { - switch (address >> 14) { - case 0: - default: - return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; - case 1: - return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _bbdDataReordering[memory->mbcState.bbd.dataSwapMode]); - } -} - -static const uint8_t _hitekDataReordering[8][8] = { - { 0, 1, 2, 3, 4, 5, 6, 7 }, - { 0, 6, 5, 3, 4, 1, 2, 7 }, - { 0, 5, 6, 3, 4, 2, 1, 7 }, - { 0, 6, 2, 3, 4, 5, 1, 7 }, - { 0, 6, 1, 3, 4, 5, 2, 7 }, - { 0, 1, 6, 3, 4, 5, 2, 7 }, - { 0, 2, 6, 3, 4, 1, 5, 7 }, - { 0, 6, 2, 3, 4, 1, 5, 7 }, -}; - -static const uint8_t _hitekBankReordering[8][8] = { - { 0, 1, 2, 3, 4, 5, 6, 7 }, - { 3, 2, 1, 0, 4, 5, 6, 7 }, - { 2, 1, 0, 3, 4, 5, 6, 7 }, - { 1, 0, 3, 2, 4, 5, 6, 7 }, - { 0, 3, 2, 1, 4, 5, 6, 7 }, - { 2, 3, 0, 1, 4, 5, 6, 7 }, - { 3, 0, 1, 2, 4, 5, 6, 7 }, - { 2, 0, 3, 1, 4, 5, 6, 7 }, -}; - -void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - switch (address & 0xF0FF) { - case 0x2000: - value = _reorderBits(value, _hitekBankReordering[memory->mbcState.bbd.bankSwapMode]); - break; - case 0x2001: - memory->mbcState.bbd.dataSwapMode = value & 0x07; - break; - case 0x2080: - memory->mbcState.bbd.bankSwapMode = value & 0x07; - break; - case 0x300: - // See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return - return; - } - _GBMBC5(gb, address, value); -} - -uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) { - switch (address >> 14) { - case 0: - default: - return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; - case 1: - return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _hitekDataReordering[memory->mbcState.bbd.dataSwapMode]); - } -} - -void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) { - struct GBSachenState* state = &gb->memory.mbcState.sachen; - uint8_t bank = value; - switch (address >> 13) { - case 0: - if ((state->unmaskedBank & 0x30) == 0x30) { - state->baseBank = bank; - GBMBCSwitchBank0(gb, state->baseBank & state->mask); - } - break; - case 1: - if (!bank) { - bank = 1; - } - state->unmaskedBank = bank; - bank = (bank & ~state->mask) | (state->baseBank & state->mask); - GBMBCSwitchBank(gb, bank); - break; - case 2: - if ((state->unmaskedBank & 0x30) == 0x30) { - state->mask = value; - bank = (state->unmaskedBank & ~state->mask) | (state->baseBank & state->mask); - GBMBCSwitchBank(gb, bank); - GBMBCSwitchBank0(gb, state->baseBank & state->mask); - } - break; - case 6: - if (gb->memory.mbcType == GB_UNL_SACHEN_MMC2 && state->locked == GB_SACHEN_LOCKED_DMG) { - state->locked = GB_SACHEN_LOCKED_CGB; - state->transition = 0; - } - break; - } -} - -static uint16_t _unscrambleSachen(uint16_t address) { - uint16_t unscrambled = address & 0xFFAC; - unscrambled |= (address & 0x40) >> 6; - unscrambled |= (address & 0x10) >> 3; - unscrambled |= (address & 0x02) << 3; - unscrambled |= (address & 0x01) << 6; - return unscrambled; -} - -uint8_t _GBSachenMMC1Read(struct GBMemory* memory, uint16_t address) { - struct GBSachenState* state = &memory->mbcState.sachen; - if (state->locked != GB_SACHEN_UNLOCKED && (address & 0xFF00) == 0x100) { - ++state->transition; - if (state->transition == 0x31) { - state->locked = GB_SACHEN_UNLOCKED; - } else { - address |= 0x80; - } - } - - if ((address & 0xFF00) == 0x0100) { - address = _unscrambleSachen(address); - } - - if (address < GB_BASE_CART_BANK1) { - return memory->romBase[address]; - } else if (address < GB_BASE_VRAM) { - return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; - } else { - return 0xFF; - } -} - -uint8_t _GBSachenMMC2Read(struct GBMemory* memory, uint16_t address) { - struct GBSachenState* state = &memory->mbcState.sachen; - if (address >= 0xC000 && state->locked == GB_SACHEN_LOCKED_DMG) { - state->transition = 0; - state->locked = GB_SACHEN_LOCKED_CGB; - } - - if (state->locked != GB_SACHEN_UNLOCKED && (address & 0x8700) == 0x0100) { - ++state->transition; - if (state->transition == 0x31) { - ++state->locked; - state->transition = 0; - } - } - - if ((address & 0xFF00) == 0x0100) { - if (state->locked == GB_SACHEN_LOCKED_CGB) { - address |= 0x80; - } - address = _unscrambleSachen(address); - } - - if (address < GB_BASE_CART_BANK1) { - return memory->romBase[address]; - } else if (address < GB_BASE_VRAM) { - return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; - } else { - return 0xFF; - } -} - -static void _appendSaveSuffix(struct GB* gb, const void* buffer, size_t size) { +void _GBMBCAppendSaveSuffix(struct GB* gb, const void* buffer, size_t size) { struct VFile* vf = gb->sramVf; if ((size_t) vf->size(vf) < gb->sramSize + size) { // Writing past the end of the file can invalidate the file mapping @@ -2245,7 +607,7 @@ void GBMBCRTCWrite(struct GB* gb) { uint8_t rtcRegs[5]; memcpy(rtcRegs, gb->memory.rtcRegs, sizeof(rtcRegs)); time_t rtcLastLatch = gb->memory.rtcLastLatch; - _latchRtc(gb->memory.rtc, rtcRegs, &rtcLastLatch); + _GBMBCLatchRTC(gb->memory.rtc, rtcRegs, &rtcLastLatch); struct GBMBCRTCSaveBuffer rtcBuffer; STORE_32LE(rtcRegs[0], 0, &rtcBuffer.sec); @@ -2260,100 +622,5 @@ void GBMBCRTCWrite(struct GB* gb) { STORE_32LE(gb->memory.rtcRegs[4], 0, &rtcBuffer.latchedDaysHi); STORE_64LE(gb->memory.rtcLastLatch, 0, &rtcBuffer.unixTime); - _appendSaveSuffix(gb, &rtcBuffer, sizeof(rtcBuffer)); -} - -void GBMBCHuC3Read(struct GB* gb) { - struct GBMBCHuC3SaveBuffer buffer; - struct VFile* vf = gb->sramVf; - if (!vf) { - return; - } - vf->seek(vf, gb->sramSize, SEEK_SET); - if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) { - return; - } - - size_t i; - for (i = 0; i < 0x80; ++i) { - gb->memory.mbcState.huc3.registers[i * 2] = buffer.regs[i] & 0xF; - gb->memory.mbcState.huc3.registers[i * 2 + 1] = buffer.regs[i] >> 4; - } - LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); -} - -void GBMBCHuC3Write(struct GB* gb) { - struct VFile* vf = gb->sramVf; - if (!vf) { - return; - } - - struct GBMBCHuC3SaveBuffer buffer; - size_t i; - for (i = 0; i < 0x80; ++i) { - buffer.regs[i] = gb->memory.mbcState.huc3.registers[i * 2] & 0xF; - buffer.regs[i] |= gb->memory.mbcState.huc3.registers[i * 2 + 1] << 4; - } - STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); - - _appendSaveSuffix(gb, &buffer, sizeof(buffer)); -} - -void GBMBCTAMA5Read(struct GB* gb) { - struct GBMBCTAMA5SaveBuffer buffer; - struct VFile* vf = gb->sramVf; - if (!vf) { - return; - } - vf->seek(vf, gb->sramSize, SEEK_SET); - if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) { - gb->memory.mbcState.tama5.disabled = false; - return; - } - - size_t i; - for (i = 0; i < 0x8; ++i) { - gb->memory.mbcState.tama5.rtcTimerPage[i * 2] = buffer.rtcTimerPage[i] & 0xF; - gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] = buffer.rtcTimerPage[i] >> 4; - gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] = buffer.rtcAlarmPage[i] & 0xF; - gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] = buffer.rtcAlarmPage[i] >> 4; - gb->memory.mbcState.tama5.rtcFreePage0[i * 2] = buffer.rtcFreePage0[i] & 0xF; - gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] = buffer.rtcFreePage0[i] >> 4; - gb->memory.mbcState.tama5.rtcFreePage1[i * 2] = buffer.rtcFreePage1[i] & 0xF; - gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] = buffer.rtcFreePage1[i] >> 4; - } - LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); - - gb->memory.mbcState.tama5.disabled = !(gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] & 0x8); - - gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xC; - gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xC; - gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 1; - gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xC; - gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] |= 2; - gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xC; - gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] |= 3; -} - -void GBMBCTAMA5Write(struct GB* gb) { - struct VFile* vf = gb->sramVf; - if (!vf) { - return; - } - - struct GBMBCTAMA5SaveBuffer buffer = {0}; - size_t i; - for (i = 0; i < 8; ++i) { - buffer.rtcTimerPage[i] = gb->memory.mbcState.tama5.rtcTimerPage[i * 2] & 0xF; - buffer.rtcTimerPage[i] |= gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] << 4; - buffer.rtcAlarmPage[i] = gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] & 0xF; - buffer.rtcAlarmPage[i] |= gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] << 4; - buffer.rtcFreePage0[i] = gb->memory.mbcState.tama5.rtcFreePage0[i * 2] & 0xF; - buffer.rtcFreePage0[i] |= gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] << 4; - buffer.rtcFreePage1[i] = gb->memory.mbcState.tama5.rtcFreePage1[i * 2] & 0xF; - buffer.rtcFreePage1[i] |= gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] << 4; - } - STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); - - _appendSaveSuffix(gb, &buffer, sizeof(buffer)); + _GBMBCAppendSaveSuffix(gb, &rtcBuffer, sizeof(rtcBuffer)); } diff --git a/src/gb/mbc/huc-3.c b/src/gb/mbc/huc-3.c new file mode 100644 index 000000000..522897517 --- /dev/null +++ b/src/gb/mbc/huc-3.c @@ -0,0 +1,218 @@ +/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h" + +#include +#include + +static void _latchHuC3Rtc(struct mRTCSource* rtc, uint8_t* huc3Regs, time_t* rtcLastLatch) { + time_t t; + if (rtc) { + if (rtc->sample) { + rtc->sample(rtc); + } + t = rtc->unixTime(rtc); + } else { + t = time(0); + } + t -= *rtcLastLatch; + t /= 60; + + if (!t) { + return; + } + *rtcLastLatch += t * 60; + + int minutes = huc3Regs[GBHUC3_RTC_MINUTES_HI] << 8; + minutes |= huc3Regs[GBHUC3_RTC_MINUTES_MI] << 4; + minutes |= huc3Regs[GBHUC3_RTC_MINUTES_LO]; + minutes += t % 1440; + t /= 1440; + if (minutes >= 1440) { + minutes -= 1440; + ++t; + } else if (minutes < 0) { + minutes += 1440; + --t; + } + huc3Regs[GBHUC3_RTC_MINUTES_LO] = minutes & 0xF; + huc3Regs[GBHUC3_RTC_MINUTES_MI] = (minutes >> 4) & 0xF; + huc3Regs[GBHUC3_RTC_MINUTES_HI] = (minutes >> 8) & 0xF; + + int days = huc3Regs[GBHUC3_RTC_DAYS_LO]; + days |= huc3Regs[GBHUC3_RTC_DAYS_MI] << 4; + days |= huc3Regs[GBHUC3_RTC_DAYS_HI] << 8; + + days += t; + + huc3Regs[GBHUC3_RTC_DAYS_LO] = days & 0xF; + huc3Regs[GBHUC3_RTC_DAYS_MI] = (days >> 4) & 0xF; + huc3Regs[GBHUC3_RTC_DAYS_HI] = (days >> 8) & 0xF; +} + +static void _huc3Commit(struct GB* gb, struct GBHuC3State* state) { + size_t c; + switch (state->value & 0x70) { + case 0x10: + if ((state->index & 0xF8) == 0x10) { + _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch); + } + state->value &= 0xF0; + state->value |= state->registers[state->index] & 0xF; + mLOG(GB_MBC, DEBUG, "HuC-3 read: %02X:%X", state->index, state->value & 0xF); + if (state->value & 0x10) { + ++state->index; + } + break; + case 0x30: + mLOG(GB_MBC, DEBUG, "HuC-3 write: %02X:%X", state->index, state->value & 0xF); + state->registers[state->index] = state->value & 0xF; + if (state->value & 0x10) { + ++state->index; + } + break; + case 0x40: + state->index &= 0xF0; + state->index |= (state->value) & 0xF; + mLOG(GB_MBC, DEBUG, "HuC-3 index (low): %02X", state->index); + break; + case 0x50: + state->index &= 0x0F; + state->index |= ((state->value) & 0xF) << 4; + mLOG(GB_MBC, DEBUG, "HuC-3 index (high): %02X", state->index); + break; + case 0x60: + switch (state->value & 0xF) { + case GBHUC3_CMD_LATCH: + _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch); + memcpy(state->registers, &state->registers[GBHUC3_RTC_MINUTES_LO], 6); + mLOG(GB_MBC, DEBUG, "HuC-3 RTC latch"); + break; + case GBHUC3_CMD_SET_RTC: + memcpy(&state->registers[GBHUC3_RTC_MINUTES_LO], state->registers, 6); + mLOG(GB_MBC, DEBUG, "HuC-3 set RTC"); + break; + case GBHUC3_CMD_RO: + mLOG(GB_MBC, STUB, "HuC-3 unimplemented read-only mode"); + break; + case GBHUC3_CMD_TONE: + if (state->registers[GBHUC3_SPEAKER_ENABLE] == 1) { + for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c); + if (callbacks->alarm) { + callbacks->alarm(callbacks->context); + } + } + mLOG(GB_MBC, DEBUG, "HuC-3 tone %i", state->registers[GBHUC3_SPEAKER_TONE] & 3); + } + break; + default: + mLOG(GB_MBC, STUB, "HuC-3 unknown command: %X", state->value & 0xF); + break; + } + state->value = 0xE1; + break; + default: + mLOG(GB_MBC, STUB, "HuC-3 unknown mode commit: %02X:%02X", state->index, state->value); + break; + } +} + +void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBHuC3State* state = &memory->mbcState.huc3; + int bank = value & 0x7F; + if (address & 0x1FFF) { + mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value); + } + + switch (address >> 13) { + case 0x0: + switch (value) { + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + memory->sramAccess = false; + break; + } + state->mode = value; + break; + case 0x1: + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + GBMBCSwitchSramBank(gb, bank); + break; + case 0x5: + switch (state->mode) { + case GBHUC3_MODE_IN: + state->value = 0x80 | value; + break; + case GBHUC3_MODE_COMMIT: + _huc3Commit(gb, state); + break; + default: + mLOG(GB_MBC, STUB, "HuC-3 unknown mode write: %02X:%02X", state->mode, value); + } + break; + default: + // TODO + mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBHuC3Read(struct GBMemory* memory, uint16_t address) { + struct GBHuC3State* state = &memory->mbcState.huc3; + switch (state->mode) { + case GBHUC3_MODE_SRAM_RO: + case GBHUC3_MODE_SRAM_RW: + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; + case GBHUC3_MODE_IN: + case GBHUC3_MODE_OUT: + return 0x80 | state->value; + default: + return 0xFF; + } +} + +void GBMBCHuC3Read(struct GB* gb) { + struct GBMBCHuC3SaveBuffer buffer; + struct VFile* vf = gb->sramVf; + if (!vf) { + return; + } + vf->seek(vf, gb->sramSize, SEEK_SET); + if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) { + return; + } + + size_t i; + for (i = 0; i < 0x80; ++i) { + gb->memory.mbcState.huc3.registers[i * 2] = buffer.regs[i] & 0xF; + gb->memory.mbcState.huc3.registers[i * 2 + 1] = buffer.regs[i] >> 4; + } + LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); +} + +void GBMBCHuC3Write(struct GB* gb) { + struct VFile* vf = gb->sramVf; + if (!vf) { + return; + } + + struct GBMBCHuC3SaveBuffer buffer; + size_t i; + for (i = 0; i < 0x80; ++i) { + buffer.regs[i] = gb->memory.mbcState.huc3.registers[i * 2] & 0xF; + buffer.regs[i] |= gb->memory.mbcState.huc3.registers[i * 2 + 1] << 4; + } + STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); + + _GBMBCAppendSaveSuffix(gb, &buffer, sizeof(buffer)); +} diff --git a/src/gb/mbc/licensed.c b/src/gb/mbc/licensed.c new file mode 100644 index 000000000..22f0ae524 --- /dev/null +++ b/src/gb/mbc/licensed.c @@ -0,0 +1,84 @@ +/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h" + +#include + +void _GBMMM01(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + if (!memory->mbcState.mmm01.locked) { + switch (address >> 13) { + case 0x0: + memory->mbcState.mmm01.locked = true; + GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0); + break; + case 0x1: + memory->mbcState.mmm01.currentBank0 &= ~0x7F; + memory->mbcState.mmm01.currentBank0 |= value & 0x7F; + break; + case 0x2: + memory->mbcState.mmm01.currentBank0 &= ~0x180; + memory->mbcState.mmm01.currentBank0 |= (value & 0x30) << 3; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); + break; + } + return; + } + switch (address >> 13) { + case 0x0: + switch (value) { + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + memory->sramAccess = false; + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, value + memory->mbcState.mmm01.currentBank0); + break; + case 0x2: + GBMBCSwitchSramBank(gb, value); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x3F; + switch (address >> 13) { + case 0x0: + switch (value) { + case 0xE: + memory->sramAccess = false; + break; + default: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + GBMBCSwitchSramBank(gb, value); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "HuC-1 unknown address: %04X:%02X", address, value); + break; + } +} diff --git a/src/gb/mbc/mbc-private.h b/src/gb/mbc/mbc-private.h new file mode 100644 index 000000000..4a07ab716 --- /dev/null +++ b/src/gb/mbc/mbc-private.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2013-2016 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 GB_MBC_PRIVATE_H +#define GB_MBC_PRIVATE_H + +#include + +CXX_GUARD_START + +struct GB; +struct GBMemory; +struct mRTCSource; + +void _GBMBC1(struct GB*, uint16_t address, uint8_t value); +void _GBMBC2(struct GB*, uint16_t address, uint8_t value); +void _GBMBC3(struct GB*, uint16_t address, uint8_t value); +void _GBMBC5(struct GB*, uint16_t address, uint8_t value); +void _GBMBC6(struct GB*, uint16_t address, uint8_t value); +void _GBMBC7(struct GB*, uint16_t address, uint8_t value); + +void _GBMMM01(struct GB*, uint16_t address, uint8_t value); +void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value); +void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value); + +void _GBHuC1(struct GB*, uint16_t address, uint8_t value); +void _GBHuC3(struct GB*, uint16_t address, uint8_t value); + +void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value); +void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value); +void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value); +void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value); +void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); +void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); +void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); +void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value); +void _GBGGB81(struct GB* gb, uint16_t address, uint8_t value); +void _GBSachen(struct GB* gb, uint16_t address, uint8_t value); + +uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address); +uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address); +uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address); +void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value); + +uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address); +uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address); +uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address); + +uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address); +uint8_t _GBBBDRead(struct GBMemory*, uint16_t address); +uint8_t _GBHitekRead(struct GBMemory*, uint16_t address); +uint8_t _GBGGB81Read(struct GBMemory*, uint16_t address); +uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address); +uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address); + +void _GBMBCLatchRTC(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch); +void _GBMBCAppendSaveSuffix(struct GB* gb, const void* buffer, size_t size); + +CXX_GUARD_END + +#endif diff --git a/src/gb/mbc/mbc.c b/src/gb/mbc/mbc.c new file mode 100644 index 000000000..864b6e9f8 --- /dev/null +++ b/src/gb/mbc/mbc.c @@ -0,0 +1,565 @@ +/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h" + +#include +#include +#include + +static void _GBMBC6MapChip(struct GB*, int half, uint8_t value); + +void _GBMBCLatchRTC(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch) { + time_t t; + if (rtc) { + if (rtc->sample) { + rtc->sample(rtc); + } + t = rtc->unixTime(rtc); + } else { + t = time(0); + } + time_t currentLatch = t; + t -= *rtcLastLatch; + *rtcLastLatch = currentLatch; + + int64_t diff; + diff = rtcRegs[0] + t % 60; + if (diff < 0) { + diff += 60; + t -= 60; + } + rtcRegs[0] = diff % 60; + t /= 60; + t += diff / 60; + + diff = rtcRegs[1] + t % 60; + if (diff < 0) { + diff += 60; + t -= 60; + } + rtcRegs[1] = diff % 60; + t /= 60; + t += diff / 60; + + diff = rtcRegs[2] + t % 24; + if (diff < 0) { + diff += 24; + t -= 24; + } + rtcRegs[2] = diff % 24; + t /= 24; + t += diff / 24; + + diff = rtcRegs[3] + ((rtcRegs[4] & 1) << 8) + (t & 0x1FF); + rtcRegs[3] = diff; + rtcRegs[4] &= 0xFE; + rtcRegs[4] |= (diff >> 8) & 1; + if (diff & 0x200) { + rtcRegs[4] |= 0x80; + } +} + +static void _GBMBC1Update(struct GB* gb) { + struct GBMBC1State* state = &gb->memory.mbcState.mbc1; + int bank = state->bankLo; + bank &= (1 << state->multicartStride) - 1; + bank |= state->bankHi << state->multicartStride; + if (state->mode) { + GBMBCSwitchBank0(gb, state->bankHi << state->multicartStride); + GBMBCSwitchSramBank(gb, state->bankHi & 3); + } else { + GBMBCSwitchBank0(gb, 0); + GBMBCSwitchSramBank(gb, 0); + } + if (!(state->bankLo & 0x1F)) { + ++state->bankLo; + ++bank; + } + GBMBCSwitchBank(gb, bank); +} + +void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x1F; + switch (address >> 13) { + case 0x0: + switch (value & 0xF) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value); + break; + } + break; + case 0x1: + memory->mbcState.mbc1.bankLo = bank; + _GBMBC1Update(gb); + break; + case 0x2: + bank &= 3; + memory->mbcState.mbc1.bankHi = bank; + _GBMBC1Update(gb); + break; + case 0x3: + memory->mbcState.mbc1.mode = value & 1; + _GBMBC1Update(gb); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int shift = (address & 1) * 4; + int bank = value & 0xF; + switch ((address & 0xC100) >> 8) { + case 0x0: + switch (value & 0x0F) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC2 unknown value %02X", value); + break; + } + break; + case 0x1: + if (!bank) { + ++bank; + } + GBMBCSwitchBank(gb, bank); + break; + case 0x80: + case 0x81: + case 0x82: + case 0x83: + if (!memory->sramAccess) { + return; + } + address &= 0x1FF; + memory->sramBank[(address >> 1)] &= 0xF0 >> shift; + memory->sramBank[(address >> 1)] |= (value & 0xF) << shift; + gb->sramDirty |= mSAVEDATA_DIRT_NEW; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBMBC2Read(struct GBMemory* memory, uint16_t address) { + if (!memory->sramAccess) { + return 0xFF; + } + address &= 0x1FF; + int shift = (address & 1) * 4; + return (memory->sramBank[(address >> 1)] >> shift) | 0xF0; +} + +void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value; + switch (address >> 13) { + case 0x0: + switch (value & 0xF) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value); + break; + } + break; + case 0x1: + if (gb->memory.romSize < GB_SIZE_CART_BANK0 * 0x80) { + bank &= 0x7F; + } + if (!bank) { + ++bank; + } + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + bank &= 0xF; + if (bank < 8) { + GBMBCSwitchSramBank(gb, value); + memory->rtcAccess = false; + } else if (bank <= 0xC) { + memory->activeRtcReg = bank - 8; + memory->rtcAccess = true; + } + break; + case 0x3: + if (memory->rtcLatched && value == 0) { + memory->rtcLatched = false; + } else if (!memory->rtcLatched && value == 1) { + _GBMBCLatchRTC(gb->memory.rtc, gb->memory.rtcRegs, &gb->memory.rtcLastLatch); + memory->rtcLatched = true; + } + break; + } +} + +void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank; + switch (address >> 12) { + case 0x0: + case 0x1: + switch (value) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value); + break; + } + break; + case 0x2: + bank = (memory->currentBank & 0x100) | value; + GBMBCSwitchBank(gb, bank); + break; + case 0x3: + bank = (memory->currentBank & 0xFF) | ((value & 1) << 8); + GBMBCSwitchBank(gb, bank); + break; + case 0x4: + case 0x5: + if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) { + memory->rumble->setRumble(memory->rumble, (value >> 3) & 1); + value &= ~8; + } + GBMBCSwitchSramBank(gb, value & 0xF); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value; + switch (address >> 10) { + case 0: + switch (value) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC6 unknown value %02X", value); + break; + } + break; + case 0x1: + GBMBCSwitchSramHalfBank(gb, 0, bank); + break; + case 0x2: + GBMBCSwitchSramHalfBank(gb, 1, bank); + break; + case 0x3: + mLOG(GB_MBC, STUB, "MBC6 unimplemented flash OE write: %04X:%02X", address, value); + break; + case 0x4: + mLOG(GB_MBC, STUB, "MBC6 unimplemented flash WE write: %04X:%02X", address, value); + break; + case 0x8: + case 0x9: + GBMBCSwitchHalfBank(gb, 0, bank); + break; + case 0xA: + case 0xB: + _GBMBC6MapChip(gb, 0, value); + break; + case 0xC: + case 0xD: + GBMBCSwitchHalfBank(gb, 1, bank); + break; + case 0xE: + case 0xF: + _GBMBC6MapChip(gb, 1, value); + break; + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + if (memory->sramAccess) { + memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; + gb->sramDirty |= mSAVEDATA_DIRT_NEW; + } + break; + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + if (memory->sramAccess) { + memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; + } + break; + default: + mLOG(GB_MBC, STUB, "MBC6 unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) { + if (!memory->sramAccess) { + return 0xFF; + } + switch (address >> 12) { + case 0xA: + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; + case 0xB: + return memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; + } + return 0xFF; +} + +static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) { + if (!half) { + gb->memory.mbcState.mbc6.flashBank0 = !!(value & 0x08); + GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank); + } else { + gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08); + GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank1); + } +} + +void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) { + int bank = value & 0x7F; + switch (address >> 13) { + case 0x0: + switch (value) { + default: + case 0: + gb->memory.mbcState.mbc7.access = 0; + break; + case 0xA: + gb->memory.mbcState.mbc7.access |= 1; + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + if (value == 0x40) { + gb->memory.mbcState.mbc7.access |= 2; + } else { + gb->memory.mbcState.mbc7.access &= ~2; + } + break; + case 0x5: + _GBMBC7Write(&gb->memory, address, value); + gb->sramDirty |= mSAVEDATA_DIRT_NEW; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) { + struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; + if (mbc7->access != 3) { + return 0xFF; + } + switch (address & 0xF0) { + case 0x20: + if (memory->rotation && memory->rotation->readTiltX) { + int32_t x = -memory->rotation->readTiltX(memory->rotation); + x >>= 21; + x += 0x81D0; + return x; + } + return 0xFF; + case 0x30: + if (memory->rotation && memory->rotation->readTiltX) { + int32_t x = -memory->rotation->readTiltX(memory->rotation); + x >>= 21; + x += 0x81D0; + return x >> 8; + } + return 7; + case 0x40: + if (memory->rotation && memory->rotation->readTiltY) { + int32_t y = -memory->rotation->readTiltY(memory->rotation); + y >>= 21; + y += 0x81D0; + return y; + } + return 0xFF; + case 0x50: + if (memory->rotation && memory->rotation->readTiltY) { + int32_t y = -memory->rotation->readTiltY(memory->rotation); + y >>= 21; + y += 0x81D0; + return y >> 8; + } + return 7; + case 0x60: + return 0; + case 0x80: + return mbc7->eeprom; + default: + return 0xFF; + } +} + +void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) { + struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; + if (mbc7->access != 3) { + return; + } + switch (address & 0xF0) { + case 0x00: + mbc7->latch = (value & 0x55) == 0x55; + return; + case 0x10: + mbc7->latch |= (value & 0xAA); + if (mbc7->latch == 0xAB && memory->rotation && memory->rotation->sample) { + memory->rotation->sample(memory->rotation); + } + mbc7->latch = 0; + return; + default: + mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value); + return; + case 0x80: + break; + } + GBMBC7Field old = memory->mbcState.mbc7.eeprom; + value = GBMBC7FieldFillDO(value); // Hi-Z + if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) { + mbc7->state = GBMBC7_STATE_IDLE; + } + if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) { + if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) { + mbc7->sr <<= 1; + mbc7->sr |= GBMBC7FieldGetDI(value); + ++mbc7->srBits; + } + switch (mbc7->state) { + case GBMBC7_STATE_IDLE: + if (GBMBC7FieldIsDI(value)) { + mbc7->state = GBMBC7_STATE_READ_COMMAND; + mbc7->srBits = 0; + mbc7->sr = 0; + } + break; + case GBMBC7_STATE_READ_COMMAND: + if (mbc7->srBits == 10) { + mbc7->state = 0x10 | (mbc7->sr >> 6); + if (mbc7->state & 0xC) { + mbc7->state &= ~0x3; + } + mbc7->srBits = 0; + mbc7->address = mbc7->sr & 0x7F; + } + break; + case GBMBC7_STATE_DO: + value = GBMBC7FieldSetDO(value, mbc7->sr >> 15); + mbc7->sr <<= 1; + --mbc7->srBits; + if (!mbc7->srBits) { + mbc7->state = GBMBC7_STATE_IDLE; + } + break; + default: + break; + } + switch (mbc7->state) { + case GBMBC7_STATE_EEPROM_EWEN: + mbc7->writable = true; + mbc7->state = GBMBC7_STATE_IDLE; + break; + case GBMBC7_STATE_EEPROM_EWDS: + mbc7->writable = false; + mbc7->state = GBMBC7_STATE_IDLE; + break; + case GBMBC7_STATE_EEPROM_WRITE: + if (mbc7->srBits == 16) { + if (mbc7->writable) { + memory->sram[mbc7->address * 2] = mbc7->sr >> 8; + memory->sram[mbc7->address * 2 + 1] = mbc7->sr; + } + mbc7->state = GBMBC7_STATE_IDLE; + } + break; + case GBMBC7_STATE_EEPROM_ERASE: + if (mbc7->writable) { + memory->sram[mbc7->address * 2] = 0xFF; + memory->sram[mbc7->address * 2 + 1] = 0xFF; + } + mbc7->state = GBMBC7_STATE_IDLE; + break; + case GBMBC7_STATE_EEPROM_READ: + mbc7->srBits = 16; + mbc7->sr = memory->sram[mbc7->address * 2] << 8; + mbc7->sr |= memory->sram[mbc7->address * 2 + 1]; + mbc7->state = GBMBC7_STATE_DO; + value = GBMBC7FieldClearDO(value); + break; + case GBMBC7_STATE_EEPROM_WRAL: + if (mbc7->srBits == 16) { + if (mbc7->writable) { + int i; + for (i = 0; i < 128; ++i) { + memory->sram[i * 2] = mbc7->sr >> 8; + memory->sram[i * 2 + 1] = mbc7->sr; + } + } + mbc7->state = GBMBC7_STATE_IDLE; + } + break; + case GBMBC7_STATE_EEPROM_ERAL: + if (mbc7->writable) { + int i; + for (i = 0; i < 128; ++i) { + memory->sram[i * 2] = 0xFF; + memory->sram[i * 2 + 1] = 0xFF; + } + } + mbc7->state = GBMBC7_STATE_IDLE; + break; + default: + break; + } + } else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) { + value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old)); + } + mbc7->eeprom = value; +} diff --git a/src/gb/mbc/pocket-cam.c b/src/gb/mbc/pocket-cam.c new file mode 100644 index 000000000..c587dfa6a --- /dev/null +++ b/src/gb/mbc/pocket-cam.c @@ -0,0 +1,149 @@ +/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h" + +#include +#include + +static void _GBPocketCamCapture(struct GBMemory*); + +void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x3F; + switch (address >> 13) { + case 0x0: + switch (value) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "Pocket Cam unknown value %02X", value); + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + if (value < 0x10) { + GBMBCSwitchSramBank(gb, value); + memory->mbcState.pocketCam.registersActive = false; + memory->directSramAccess = true; + } else { + memory->mbcState.pocketCam.registersActive = true; + memory->directSramAccess = false; + } + break; + case 0x5: + if (!memory->mbcState.pocketCam.registersActive) { + break; + } + address &= 0x7F; + if (address == 0 && value & 1) { + value &= 6; // TODO: Timing + gb->sramDirty |= mSAVEDATA_DIRT_NEW; + _GBPocketCamCapture(memory); + } + if (address < sizeof(memory->mbcState.pocketCam.registers)) { + memory->mbcState.pocketCam.registers[address] = value; + } + break; + default: + mLOG(GB_MBC, STUB, "Pocket Cam unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBPocketCamRead(struct GBMemory* memory, uint16_t address) { + if (memory->mbcState.pocketCam.registersActive) { + if ((address & 0x7F) == 0) { + return memory->mbcState.pocketCam.registers[0]; + } + return 0; + } + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; +} + +void _GBPocketCamCapture(struct GBMemory* memory) { + if (!memory->cam) { + return; + } + const void* image = NULL; + size_t stride; + enum mColorFormat format; + memory->cam->requestImage(memory->cam, &image, &stride, &format); + if (!image) { + return; + } + memset(&memory->sram[0x100], 0, GBCAM_HEIGHT * GBCAM_WIDTH / 4); + struct GBPocketCamState* pocketCam = &memory->mbcState.pocketCam; + size_t x, y; + for (y = 0; y < GBCAM_HEIGHT; ++y) { + for (x = 0; x < GBCAM_WIDTH; ++x) { + uint32_t gray; + uint32_t color; + switch (format) { + case mCOLOR_XBGR8: + case mCOLOR_XRGB8: + case mCOLOR_ARGB8: + case mCOLOR_ABGR8: + color = ((const uint32_t*) image)[y * stride + x]; + gray = (color & 0xFF) + ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF); + break; + case mCOLOR_BGRX8: + case mCOLOR_RGBX8: + case mCOLOR_RGBA8: + case mCOLOR_BGRA8: + color = ((const uint32_t*) image)[y * stride + x]; + gray = ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF) + ((color >> 24) & 0xFF); + break; + case mCOLOR_BGR5: + case mCOLOR_RGB5: + case mCOLOR_ARGB5: + case mCOLOR_ABGR5: + color = ((const uint16_t*) image)[y * stride + x]; + gray = ((color << 3) & 0xF8) + ((color >> 2) & 0xF8) + ((color >> 7) & 0xF8); + break; + case mCOLOR_BGR565: + case mCOLOR_RGB565: + color = ((const uint16_t*) image)[y * stride + x]; + gray = ((color << 3) & 0xF8) + ((color >> 3) & 0xFC) + ((color >> 8) & 0xF8); + break; + case mCOLOR_BGRA5: + case mCOLOR_RGBA5: + color = ((const uint16_t*) image)[y * stride + x]; + gray = ((color << 2) & 0xF8) + ((color >> 3) & 0xF8) + ((color >> 8) & 0xF8); + break; + default: + mLOG(GB_MBC, WARN, "Unsupported pixel format: %X", format); + return; + } + uint16_t exposure = (pocketCam->registers[2] << 8) | (pocketCam->registers[3]); + gray = (gray + 1) * exposure / 0x300; + // TODO: Additional processing + int matrixEntry = 3 * ((x & 3) + 4 * (y & 3)); + if (gray < pocketCam->registers[matrixEntry + 6]) { + gray = 0x101; + } else if (gray < pocketCam->registers[matrixEntry + 7]) { + gray = 0x100; + } else if (gray < pocketCam->registers[matrixEntry + 8]) { + gray = 0x001; + } else { + gray = 0; + } + int coord = (((x >> 3) & 0xF) * 8 + (y & 0x7)) * 2 + (y & ~0x7) * 0x20; + uint16_t existing; + LOAD_16LE(existing, coord + 0x100, memory->sram); + existing |= gray << (7 - (x & 7)); + STORE_16LE(existing, coord + 0x100, memory->sram); + } + } +} diff --git a/src/gb/mbc/tama5.c b/src/gb/mbc/tama5.c new file mode 100644 index 000000000..505a35ed8 --- /dev/null +++ b/src/gb/mbc/tama5.c @@ -0,0 +1,433 @@ +/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h" + +#include +#include +#include + +static const uint8_t _tama6RTCMask[32] = { + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 0xF, 0x7, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0xF, 0x1, 0xF, 0xF, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0x0, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0, +}; + +static const int _daysToMonth[] = { + [ 1] = 0, + [ 2] = 31, + [ 3] = 31 + 28, + [ 4] = 31 + 28 + 31, + [ 5] = 31 + 28 + 31 + 30, + [ 6] = 31 + 28 + 31 + 30 + 31, + [ 7] = 31 + 28 + 31 + 30 + 31 + 30, + [ 8] = 31 + 28 + 31 + 30 + 31 + 30 + 31, + [ 9] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + [10] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + [11] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + [12] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, +}; + +static int _tama6DMYToDayOfYear(int day, int month, int year) { + if (month < 1 || month > 12) { + return -1; + } + day += _daysToMonth[month]; + if (month > 2 && (year & 3) == 0) { + ++day; + } + return day; +} + +static int _tama6DayOfYearToMonth(int day, int year) { + int month; + for (month = 1; month < 12; ++month) { + if (day <= _daysToMonth[month + 1]) { + return month; + } + if (month == 2 && (year & 3) == 0) { + if (day == 60) { + return 2; + } + --day; + } + } + return 12; +} + +static int _tama6DayOfYearToDayOfMonth(int day, int year) { + int month; + for (month = 1; month < 12; ++month) { + if (day <= _daysToMonth[month + 1]) { + return day - _daysToMonth[month]; + } + if (month == 2 && (year & 3) == 0) { + if (day == 60) { + return 29; + } + --day; + } + } + return day - _daysToMonth[12]; +} + +static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, time_t* rtcLastLatch) { + time_t t; + if (rtc) { + if (rtc->sample) { + rtc->sample(rtc); + } + t = rtc->unixTime(rtc); + } else { + t = time(0); + } + time_t currentLatch = t; + t -= *rtcLastLatch; + *rtcLastLatch = currentLatch; + if (!t || tama5->disabled) { + return; + } + + uint8_t* timerRegs = tama5->rtcTimerPage; + bool is24hour = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_24_HOUR]; + int64_t diff; + diff = timerRegs[GBTAMA6_RTC_PA0_SECOND_1] + timerRegs[GBTAMA6_RTC_PA0_SECOND_10] * 10 + t % 60; + if (diff < 0) { + diff += 60; + t -= 60; + } + timerRegs[GBTAMA6_RTC_PA0_SECOND_1] = diff % 10; + timerRegs[GBTAMA6_RTC_PA0_SECOND_10] = (diff % 60) / 10; + t /= 60; + t += diff / 60; + + diff = timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] + timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] * 10 + t % 60; + if (diff < 0) { + diff += 60; + t -= 60; + } + timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] = diff % 10; + timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] = (diff % 60) / 10; + t /= 60; + t += diff / 60; + + diff = timerRegs[GBTAMA6_RTC_PA0_HOUR_1]; + if (is24hour) { + diff += timerRegs[GBTAMA6_RTC_PA0_HOUR_10] * 10; + } else { + int hour10 = timerRegs[GBTAMA6_RTC_PA0_HOUR_10]; + diff += (hour10 & 1) * 10; + diff += (hour10 & 2) * 12; + } + diff += t % 24; + if (diff < 0) { + diff += 24; + t -= 24; + } + if (is24hour) { + timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 24) % 10; + timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10; + } else { + timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10; + timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2; + } + t /= 24; + t += diff / 24; + + int day = timerRegs[GBTAMA6_RTC_PA0_DAY_1] + timerRegs[GBTAMA6_RTC_PA0_DAY_10] * 10; + int month = timerRegs[GBTAMA6_RTC_PA0_MONTH_1] + timerRegs[GBTAMA6_RTC_PA0_MONTH_10] * 10; + int year = timerRegs[GBTAMA6_RTC_PA0_YEAR_1] + timerRegs[GBTAMA6_RTC_PA0_YEAR_10] * 10; + int leapYear = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR]; + int dayOfWeek = timerRegs[GBTAMA6_RTC_PA0_WEEK]; + int dayInYear = _tama6DMYToDayOfYear(day, month, leapYear); + diff = dayInYear + t; + while (diff <= 0) { + // Previous year + if (leapYear & 3) { + diff += 365; + } else { + diff += 366; + } + --year; + --leapYear; + } + while (diff > (leapYear & 3 ? 365 : 366)) { + // Future year + if (year % 4) { + diff -= 365; + } else { + diff -= 366; + } + ++year; + ++leapYear; + } + dayOfWeek = (dayOfWeek + diff) % 7; + year %= 100; + leapYear &= 3; + + day = _tama6DayOfYearToDayOfMonth(diff, leapYear); + month = _tama6DayOfYearToMonth(diff, leapYear); + + timerRegs[GBTAMA6_RTC_PA0_WEEK] = dayOfWeek; + tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR] = leapYear; + + timerRegs[GBTAMA6_RTC_PA0_DAY_1] = day % 10; + timerRegs[GBTAMA6_RTC_PA0_DAY_10] = day / 10; + + timerRegs[GBTAMA6_RTC_PA0_MONTH_1] = month % 10; + timerRegs[GBTAMA6_RTC_PA0_MONTH_10] = month / 10; + + timerRegs[GBTAMA6_RTC_PA0_YEAR_1] = year % 10; + timerRegs[GBTAMA6_RTC_PA0_YEAR_10] = year / 10; +} + +void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBTAMA5State* tama5 = &memory->mbcState.tama5; + switch (address >> 13) { + case 0x5: + if (address & 1) { + tama5->reg = value; + } else { + value &= 0xF; + if (tama5->reg < GBTAMA5_MAX) { + mLOG(GB_MBC, DEBUG, "TAMA5 write: %02X:%X", tama5->reg, value); + tama5->registers[tama5->reg] = value; + uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO]; + uint8_t out = (tama5->registers[GBTAMA5_WRITE_HI] << 4) | tama5->registers[GBTAMA5_WRITE_LO]; + switch (tama5->reg) { + case GBTAMA5_BANK_LO: + case GBTAMA5_BANK_HI: + GBMBCSwitchBank(gb, tama5->registers[GBTAMA5_BANK_LO] | (tama5->registers[GBTAMA5_BANK_HI] << 4)); + break; + case GBTAMA5_WRITE_LO: + case GBTAMA5_WRITE_HI: + case GBTAMA5_ADDR_HI: + break; + case GBTAMA5_ADDR_LO: + switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) { + case 0x0: // RAM write + memory->sram[address] = out; + gb->sramDirty |= mSAVEDATA_DIRT_NEW; + break; + case 0x1: // RAM read + break; + case 0x2: // Other commands + switch (address) { + case GBTAMA6_DISABLE_TIMER: + tama5->disabled = true; + tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0x7; + tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0x7; + tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0x7; + tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0x7; + break; + case GBTAMA6_ENABLE_TIMER: + tama5->disabled = false; + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_1] = 0; + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_10] = 0; + tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x8; + tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x8; + tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x8; + tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x8; + break; + case GBTAMA6_MINUTE_WRITE: + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1] = out & 0xF; + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] = out >> 4; + break; + case GBTAMA6_HOUR_WRITE: + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1] = out & 0xF; + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] = out >> 4; + break; + case GBTAMA6_DISABLE_ALARM: + tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xB; + tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xB; + tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xB; + tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xB; + break; + case GBTAMA6_ENABLE_ALARM: + tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x4; + tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x4; + tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x4; + tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x4; + break; + } + break; + case 0x4: // RTC access + address = tama5->registers[GBTAMA5_WRITE_LO]; + if (address >= GBTAMA6_RTC_PAGE) { + break; + } + out = tama5->registers[GBTAMA5_WRITE_HI]; + switch (tama5->registers[GBTAMA5_ADDR_LO]) { + case 0: + out &= _tama6RTCMask[address]; + tama5->rtcTimerPage[address] = out; + break; + case 2: + out &= _tama6RTCMask[address | 0x10]; + tama5->rtcAlarmPage[address] = out; + break; + case 4: + tama5->rtcFreePage0[address] = out; + break; + case 6: + tama5->rtcFreePage1[address] = out; + break; + } + break; + default: + mLOG(GB_MBC, STUB, "TAMA5 unknown address: %02X:%02X", address, out); + break; + } + break; + default: + mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X:%X", tama5->reg, value); + break; + } + } else { + mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X", tama5->reg); + } + } + break; + default: + mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X:%02X", address, value); + } +} + +uint8_t _GBTAMA5Read(struct GBMemory* memory, uint16_t address) { + struct GBTAMA5State* tama5 = &memory->mbcState.tama5; + if ((address & 0x1FFF) > 1) { + mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X", address); + } + if (address & 1) { + return 0xFF; + } else { + uint8_t value = 0xF0; + uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO]; + switch (tama5->reg) { + case GBTAMA5_ACTIVE: + return 0xF1; + case GBTAMA5_READ_LO: + case GBTAMA5_READ_HI: + switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) { + case 0x1: + value = memory->sram[address]; + break; + case 0x2: + mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address); + _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch); + switch (address) { + case GBTAMA6_MINUTE_READ: + value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1]; + break; + case GBTAMA6_HOUR_READ: + value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1]; + break; + default: + value = address; + break; + } + break; + case 0x4: + if (tama5->reg == GBTAMA5_READ_HI) { + mLOG(GB_MBC, GAME_ERROR, "TAMA5 reading RTC incorrectly"); + break; + } + _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch); + address = tama5->registers[GBTAMA5_WRITE_LO]; + if (address > GBTAMA6_RTC_PAGE) { + value = 0; + break; + } + switch (tama5->registers[GBTAMA5_ADDR_LO]) { + case 1: + value = tama5->rtcTimerPage[address]; + break; + case 3: + value = tama5->rtcTimerPage[address]; + break; + case 5: + value = tama5->rtcTimerPage[address]; + break; + case 7: + value = tama5->rtcTimerPage[address]; + break; + } + break; + default: + mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address); + break; + } + if (tama5->reg == GBTAMA5_READ_HI) { + value >>= 4; + } + value |= 0xF0; + return value; + default: + mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg); + return 0xF1; + } + } +} + + +void GBMBCTAMA5Read(struct GB* gb) { + struct GBMBCTAMA5SaveBuffer buffer; + struct VFile* vf = gb->sramVf; + if (!vf) { + return; + } + vf->seek(vf, gb->sramSize, SEEK_SET); + if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) { + gb->memory.mbcState.tama5.disabled = false; + return; + } + + size_t i; + for (i = 0; i < 0x8; ++i) { + gb->memory.mbcState.tama5.rtcTimerPage[i * 2] = buffer.rtcTimerPage[i] & 0xF; + gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] = buffer.rtcTimerPage[i] >> 4; + gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] = buffer.rtcAlarmPage[i] & 0xF; + gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] = buffer.rtcAlarmPage[i] >> 4; + gb->memory.mbcState.tama5.rtcFreePage0[i * 2] = buffer.rtcFreePage0[i] & 0xF; + gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] = buffer.rtcFreePage0[i] >> 4; + gb->memory.mbcState.tama5.rtcFreePage1[i * 2] = buffer.rtcFreePage1[i] & 0xF; + gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] = buffer.rtcFreePage1[i] >> 4; + } + LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); + + gb->memory.mbcState.tama5.disabled = !(gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] & 0x8); + + gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xC; + gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xC; + gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 1; + gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xC; + gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] |= 2; + gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xC; + gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] |= 3; +} + +void GBMBCTAMA5Write(struct GB* gb) { + struct VFile* vf = gb->sramVf; + if (!vf) { + return; + } + + struct GBMBCTAMA5SaveBuffer buffer = {0}; + size_t i; + for (i = 0; i < 8; ++i) { + buffer.rtcTimerPage[i] = gb->memory.mbcState.tama5.rtcTimerPage[i * 2] & 0xF; + buffer.rtcTimerPage[i] |= gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] << 4; + buffer.rtcAlarmPage[i] = gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] & 0xF; + buffer.rtcAlarmPage[i] |= gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] << 4; + buffer.rtcFreePage0[i] = gb->memory.mbcState.tama5.rtcFreePage0[i * 2] & 0xF; + buffer.rtcFreePage0[i] |= gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] << 4; + buffer.rtcFreePage1[i] = gb->memory.mbcState.tama5.rtcFreePage1[i * 2] & 0xF; + buffer.rtcFreePage1[i] |= gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] << 4; + } + STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); + + _GBMBCAppendSaveSuffix(gb, &buffer, sizeof(buffer)); +} diff --git a/src/gb/mbc/unlicensed.c b/src/gb/mbc/unlicensed.c new file mode 100644 index 000000000..58725fa98 --- /dev/null +++ b/src/gb/mbc/unlicensed.c @@ -0,0 +1,500 @@ +/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h" + +#include + +void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value) { + UNUSED(value); + int bank = address & 0x3F; + switch (address >> 14) { + case 0x0: + GBMBCSwitchBank0(gb, bank * 2); + GBMBCSwitchBank(gb, bank * 2 + 1); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "Wisdom Tree unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + switch (address >> 13) { + case 0x2: + if (value < 8) { + memory->directSramAccess = true; + memory->activeRtcReg = 0; + } else if (value >= 0xD && value <= 0xF) { + memory->directSramAccess = false; + memory->rtcAccess = false; + memory->activeRtcReg = value - 8; + } + break; + case 0x5: + if (!memory->sramAccess) { + return; + } + switch (memory->activeRtcReg) { + case 0: + memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value; + break; + case 5: + case 6: + memory->mbcState.pkjd.reg[memory->activeRtcReg - 5] = value; + break; + case 7: + switch (value) { + case 0x11: + memory->mbcState.pkjd.reg[0]--; + break; + case 0x12: + memory->mbcState.pkjd.reg[1]--; + break; + case 0x41: + memory->mbcState.pkjd.reg[0] += memory->mbcState.pkjd.reg[1]; + break; + case 0x42: + memory->mbcState.pkjd.reg[1] += memory->mbcState.pkjd.reg[0]; + break; + case 0x51: + memory->mbcState.pkjd.reg[0]++; + break; + case 0x52: + memory->mbcState.pkjd.reg[1]--; + break; + } + break; + } + return; + } + _GBMBC3(gb, address, value); +} + +uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) { + if (!memory->sramAccess) { + return 0xFF; + } + switch (memory->activeRtcReg) { + case 0: + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; + case 5: + case 6: + return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5]; + default: + return 0; + } +} + + +static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) { + uint8_t newbyte = 0; + int i; + for(i = 0; i < 8; ++i) { + int oldbit = reorder[i]; + int newbit = i; + newbyte += ((input >> oldbit) & 1) << newbit; + } + + return newbyte; +} + +static const uint8_t _ntOld1Reorder[8] = { + 0, 2, 1, 4, 3, 5, 6, 7 +}; + +void _ntOldMulticart(struct GB* gb, uint16_t address, uint8_t value, const uint8_t reorder[8]) { + struct GBMemory* memory = &gb->memory; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; + int bank = value; + + switch (address & 3) { + case 0: + mLOG(GB_MBC, STUB, "Unimplemented NT Old 1 address 0"); + break; + case 1: + value &= 0x3F; + mbcState->baseBank = value * 2; + if (mbcState->baseBank) { + GBMBCSwitchBank0(gb, mbcState->baseBank); + GBMBCSwitchBank(gb, mbcState->baseBank + 1); + } + break; + case 2: + if ((value & 0xF0) == 0xE0) { + gb->sramSize = 0x2000; + GBResizeSram(gb, gb->sramSize); + } + switch (value & 0xF) { + case 0x00: + mbcState->bankCount = 32; + break; + case 0x08: + mbcState->bankCount = 16; + break; + case 0xC: + mbcState->bankCount = 8; + break; + case 0xE: + mbcState->bankCount = 4; + break; + case 0xF: + mbcState->bankCount = 2; + break; + default: + mbcState->bankCount = 32; + break; + } + break; + case 3: + mbcState->swapped = !!(value & 0x10); + + bank = memory->currentBank; + if (mbcState->swapped) { + bank = _reorderBits(bank, reorder); + } + GBMBCSwitchBank(gb, bank); + break; + } +} + +void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; + int bank = value; + + switch (address >> 12) { + case 0x0: + case 0x1: + _GBMBC3(gb, address, value); + break; + case 0x2: + case 0x3: + bank &= 0x1F; + if (!bank) { + bank = 1; + } + if (mbcState->swapped) { + bank = _reorderBits(bank, _ntOld1Reorder); + } + if (mbcState->bankCount) { + bank &= mbcState->bankCount - 1; + } + GBMBCSwitchBank(gb, bank + mbcState->baseBank); + break; + case 0x5: + _ntOldMulticart(gb, address, value, _ntOld1Reorder); + break; + } +} + +static const uint8_t _ntOld2Reorder[8] = { + 1, 2, 0, 3, 4, 5, 6, 7 +}; + +void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; + int bank = value; + + switch (address >> 12) { + case 0x0: + case 0x1: + _GBMBC3(gb, address, value); + break; + case 0x2: + case 0x3: + if (!bank) { + bank = 1; + } + if (mbcState->swapped) { + bank = _reorderBits(bank, _ntOld2Reorder); + } + if (mbcState->bankCount) { + bank &= mbcState->bankCount - 1; + } + GBMBCSwitchBank(gb, bank + mbcState->baseBank); + break; + case 0x5: + _ntOldMulticart(gb, address, value, _ntOld2Reorder); + // Fall through + case 0x4: + if (address == 0x5001) { + mbcState->rumble = !!(value & 0x80); + } + + if (mbcState->rumble) { + memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02)); + } + break; + } +} + +void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + if (address >> 8 == 0x14) { + memory->mbcState.ntNew.splitMode = true; + return; + } + if (memory->mbcState.ntNew.splitMode) { + int bank = value; + if (bank < 2) { + bank = 2; + } + switch (address >> 10) { + case 8: + GBMBCSwitchHalfBank(gb, 0, bank); + return; + case 9: + GBMBCSwitchHalfBank(gb, 1, bank); + return; + } + } + _GBMBC5(gb, address, value); +} + +static const uint8_t _bbdDataReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 03 - NOT KNOWN YET + { 0, 5, 1, 3, 4, 2, 6, 7 }, // 04 - Garou + { 0, 4, 2, 3, 1, 5, 6, 7 }, // 05 - Harry + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET + { 0, 1, 5, 3, 4, 2, 6, 7 }, // 07 - Digimon +}; + +static const uint8_t _bbdBankReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET + { 3, 4, 2, 0, 1, 5, 6, 7 }, // 03 - 0,1 unconfirmed. Digimon/Garou + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 04 - NOT KNOWN YET + { 1, 2, 3, 4, 0, 5, 6, 7 }, // 05 - 0,1 unconfirmed. Harry + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 07 - NOT KNOWN YET +}; + +void _GBBBD(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + switch (address & 0xF0FF) { + case 0x2000: + value = _reorderBits(value, _bbdBankReordering[memory->mbcState.bbd.bankSwapMode]); + break; + case 0x2001: + memory->mbcState.bbd.dataSwapMode = value & 0x07; + if (!(memory->mbcState.bbd.dataSwapMode == 0x07 || memory->mbcState.bbd.dataSwapMode == 0x05 || memory->mbcState.bbd.dataSwapMode == 0x04 || memory->mbcState.bbd.dataSwapMode == 0x00)) { + mLOG(GB_MBC, STUB, "Bitswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode); + } + break; + case 0x2080: + memory->mbcState.bbd.bankSwapMode = value & 0x07; + if (!(memory->mbcState.bbd.bankSwapMode == 0x03 || memory->mbcState.bbd.bankSwapMode == 0x05 || memory->mbcState.bbd.bankSwapMode == 0x00)) { + mLOG(GB_MBC, STUB, "Bankswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode); + } + break; + } + _GBMBC5(gb, address, value); +} + +uint8_t _GBBBDRead(struct GBMemory* memory, uint16_t address) { + switch (address >> 14) { + case 0: + default: + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + case 1: + return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _bbdDataReordering[memory->mbcState.bbd.dataSwapMode]); + } +} + +static const uint8_t _hitekDataReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 0, 6, 5, 3, 4, 1, 2, 7 }, + { 0, 5, 6, 3, 4, 2, 1, 7 }, + { 0, 6, 2, 3, 4, 5, 1, 7 }, + { 0, 6, 1, 3, 4, 5, 2, 7 }, + { 0, 1, 6, 3, 4, 5, 2, 7 }, + { 0, 2, 6, 3, 4, 1, 5, 7 }, + { 0, 6, 2, 3, 4, 1, 5, 7 }, +}; + +static const uint8_t _hitekBankReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 3, 2, 1, 0, 4, 5, 6, 7 }, + { 2, 1, 0, 3, 4, 5, 6, 7 }, + { 1, 0, 3, 2, 4, 5, 6, 7 }, + { 0, 3, 2, 1, 4, 5, 6, 7 }, + { 2, 3, 0, 1, 4, 5, 6, 7 }, + { 3, 0, 1, 2, 4, 5, 6, 7 }, + { 2, 0, 3, 1, 4, 5, 6, 7 }, +}; + +void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + switch (address & 0xF0FF) { + case 0x2000: + value = _reorderBits(value, _hitekBankReordering[memory->mbcState.bbd.bankSwapMode]); + break; + case 0x2001: + memory->mbcState.bbd.dataSwapMode = value & 0x07; + break; + case 0x2080: + memory->mbcState.bbd.bankSwapMode = value & 0x07; + break; + case 0x300: + // See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return + return; + } + _GBMBC5(gb, address, value); +} + +uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) { + switch (address >> 14) { + case 0: + default: + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + case 1: + return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _hitekDataReordering[memory->mbcState.bbd.dataSwapMode]); + } +} + +static const uint8_t _ggb81DataReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 0, 2, 1, 3, 4, 6, 5, 7 }, + { 0, 6, 5, 3, 4, 2, 1, 7 }, + { 0, 5, 1, 3, 4, 2, 6, 7 }, + { 0, 5, 2, 3, 4, 1, 6, 7 }, + { 0, 2, 6, 3, 4, 5, 1, 7 }, + { 0, 1, 6, 3, 4, 2, 5, 7 }, + { 0, 2, 5, 3, 4, 6, 1, 7 }, +}; + +void _GBGGB81(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + switch (address & 0xF0FF) { + case 0x2001: + memory->mbcState.bbd.dataSwapMode = value & 0x07; + break; + } + _GBMBC5(gb, address, value); +} + +uint8_t _GBGGB81Read(struct GBMemory* memory, uint16_t address) { + switch (address >> 14) { + case 0: + default: + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + case 1: + return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _ggb81DataReordering[memory->mbcState.bbd.dataSwapMode]); + } +} + +void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) { + if (address > 0x2100 && address < 0x3000) { + return; + } + _GBMBC5(gb, address, value); +} + +void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) { + struct GBSachenState* state = &gb->memory.mbcState.sachen; + uint8_t bank = value; + switch (address >> 13) { + case 0: + if ((state->unmaskedBank & 0x30) == 0x30) { + state->baseBank = bank; + GBMBCSwitchBank0(gb, state->baseBank & state->mask); + } + break; + case 1: + if (!bank) { + bank = 1; + } + state->unmaskedBank = bank; + bank = (bank & ~state->mask) | (state->baseBank & state->mask); + GBMBCSwitchBank(gb, bank); + break; + case 2: + if ((state->unmaskedBank & 0x30) == 0x30) { + state->mask = value; + bank = (state->unmaskedBank & ~state->mask) | (state->baseBank & state->mask); + GBMBCSwitchBank(gb, bank); + GBMBCSwitchBank0(gb, state->baseBank & state->mask); + } + break; + case 6: + if (gb->memory.mbcType == GB_UNL_SACHEN_MMC2 && state->locked == GB_SACHEN_LOCKED_DMG) { + state->locked = GB_SACHEN_LOCKED_CGB; + state->transition = 0; + } + break; + } +} + +static uint16_t _unscrambleSachen(uint16_t address) { + uint16_t unscrambled = address & 0xFFAC; + unscrambled |= (address & 0x40) >> 6; + unscrambled |= (address & 0x10) >> 3; + unscrambled |= (address & 0x02) << 3; + unscrambled |= (address & 0x01) << 6; + return unscrambled; +} + +uint8_t _GBSachenMMC1Read(struct GBMemory* memory, uint16_t address) { + struct GBSachenState* state = &memory->mbcState.sachen; + if (state->locked != GB_SACHEN_UNLOCKED && (address & 0xFF00) == 0x100) { + ++state->transition; + if (state->transition == 0x31) { + state->locked = GB_SACHEN_UNLOCKED; + } else { + address |= 0x80; + } + } + + if ((address & 0xFF00) == 0x0100) { + address = _unscrambleSachen(address); + } + + if (address < GB_BASE_CART_BANK1) { + return memory->romBase[address]; + } else if (address < GB_BASE_VRAM) { + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + } else { + return 0xFF; + } +} + +uint8_t _GBSachenMMC2Read(struct GBMemory* memory, uint16_t address) { + struct GBSachenState* state = &memory->mbcState.sachen; + if (address >= 0xC000 && state->locked == GB_SACHEN_LOCKED_DMG) { + state->transition = 0; + state->locked = GB_SACHEN_LOCKED_CGB; + } + + if (state->locked != GB_SACHEN_UNLOCKED && (address & 0x8700) == 0x0100) { + ++state->transition; + if (state->transition == 0x31) { + ++state->locked; + state->transition = 0; + } + } + + if ((address & 0xFF00) == 0x0100) { + if (state->locked == GB_SACHEN_LOCKED_CGB) { + address |= 0x80; + } + address = _unscrambleSachen(address); + } + + if (address < GB_BASE_CART_BANK1) { + return memory->romBase[address]; + } else if (address < GB_BASE_VRAM) { + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + } else { + return 0xFF; + } +} diff --git a/src/gb/memory.c b/src/gb/memory.c index 0c11308fe..5c4d97876 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -558,7 +558,7 @@ uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { gb->memory.hdmaDest |= 0x8000; bool wasHdma = gb->memory.isHdma; gb->memory.isHdma = value & 0x80; - if ((!wasHdma && !gb->memory.isHdma) || (GBRegisterLCDCIsEnable(gb->memory.io[GB_REG_LCDC]) && gb->video.mode == 0)) { + if ((!wasHdma && !gb->memory.isHdma) || gb->video.mode == 0) { if (gb->memory.isHdma) { gb->memory.hdmaRemaining = 0x10; } else { @@ -566,8 +566,6 @@ uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { } gb->cpuBlocked = true; mTimingSchedule(&gb->timing, &gb->memory.hdmaEvent, 0); - } else if (gb->memory.isHdma && !GBRegisterLCDCIsEnable(gb->memory.io[GB_REG_LCDC])) { - return 0x80 | ((value + 1) & 0x7F); } return value & 0x7F; } @@ -755,6 +753,12 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { case GB_MBC3_RTC: STORE_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch); break; + case GB_MBC6: + state->memory.mbc6.flags = GBSerializedMBC6FlagsSetFlashBank0(0, memory->mbcState.mbc6.flashBank0); + state->memory.mbc6.flags = GBSerializedMBC6FlagsSetFlashBank1(state->memory.mbc6.flags, memory->mbcState.mbc6.flashBank1); + state->memory.mbc6.bank1 = memory->currentBank1; + state->memory.mbc6.sramBank1 = memory->currentSramBank1; + break; case GB_MBC7: state->memory.mbc7.state = memory->mbcState.mbc7.state; state->memory.mbc7.eeprom = memory->mbcState.mbc7.eeprom; @@ -797,8 +801,20 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { state->memory.mmm01.locked = memory->mbcState.mmm01.locked; state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0; break; + case GB_UNL_NT_OLD_1: + case GB_UNL_NT_OLD_2: + state->memory.ntOld.flags = GBSerializedNTOldFlagsSetSwapped(0, memory->mbcState.ntOld.swapped); + state->memory.ntOld.flags = GBSerializedNTOldFlagsSetRumble(state->memory.ntOld.flags, memory->mbcState.ntOld.rumble); + state->memory.ntOld.baseBank = memory->mbcState.ntOld.baseBank; + state->memory.ntOld.bankCount = memory->mbcState.ntOld.bankCount; + break; + case GB_UNL_NT_NEW: + state->memory.ntNew.splitMode = memory->mbcState.ntNew.splitMode; + state->memory.ntNew.bank1 = memory->currentBank1; + break; case GB_UNL_BBD: case GB_UNL_HITEK: + case GB_UNL_GGB81: state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode; state->memory.bbd.bankSwapMode = memory->mbcState.bbd.bankSwapMode; break; @@ -823,9 +839,11 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { memory->wramCurrentBank = state->memory.wramCurrentBank; memory->sramCurrentBank = state->memory.sramCurrentBank; - GBMBCSwitchBank(gb, memory->currentBank); GBMemorySwitchWramBank(memory, memory->wramCurrentBank); - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + if (memory->mbcType != GB_MBC6 && memory->mbcType != GB_UNL_NT_NEW) { + GBMBCSwitchBank(gb, memory->currentBank); + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + } LOAD_16LE(memory->dmaSource, 0, &state->memory.dmaSource); LOAD_16LE(memory->dmaDest, 0, &state->memory.dmaDest); @@ -882,6 +900,16 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { case GB_MBC3_RTC: LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch); break; + case GB_MBC6: + memory->mbcState.mbc6.flashBank0 = GBSerializedMBC6FlagsGetFlashBank0(state->memory.mbc6.flags); + memory->mbcState.mbc6.flashBank1 = GBSerializedMBC6FlagsGetFlashBank1(state->memory.mbc6.flags); + memory->currentBank1 = state->memory.mbc6.bank1; + memory->currentSramBank1 = state->memory.mbc6.sramBank1; + GBMBCSwitchHalfBank(gb, 0, memory->currentBank); + GBMBCSwitchHalfBank(gb, 1, memory->currentBank1); + GBMBCSwitchSramHalfBank(gb, 0, memory->sramCurrentBank); + GBMBCSwitchSramHalfBank(gb, 1, memory->currentSramBank1); + break; case GB_MBC7: memory->mbcState.mbc7.state = state->memory.mbc7.state; memory->mbcState.mbc7.eeprom = state->memory.mbc7.eeprom; @@ -929,8 +957,27 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2); } break; + case GB_UNL_NT_OLD_1: + case GB_UNL_NT_OLD_2: + memory->mbcState.ntOld.swapped = GBSerializedNTOldFlagsGetSwapped(state->memory.ntOld.flags); + memory->mbcState.ntOld.rumble = GBSerializedNTOldFlagsGetRumble(state->memory.ntOld.flags); + memory->mbcState.ntOld.baseBank = state->memory.ntOld.baseBank; + memory->mbcState.ntOld.bankCount = state->memory.ntOld.bankCount; + GBMBCSwitchBank0(gb, memory->mbcState.ntOld.baseBank); + break; + case GB_UNL_NT_NEW: + memory->mbcState.ntNew.splitMode = state->memory.ntNew.splitMode; + memory->currentBank1 = state->memory.ntNew.bank1; + if (memory->mbcState.ntNew.splitMode) { + GBMBCSwitchHalfBank(gb, 0, memory->currentBank); + GBMBCSwitchHalfBank(gb, 1, memory->currentBank1); + } else { + GBMBCSwitchBank(gb, memory->currentBank); + } + break; case GB_UNL_BBD: case GB_UNL_HITEK: + case GB_UNL_GGB81: memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7; memory->mbcState.bbd.bankSwapMode = state->memory.bbd.bankSwapMode & 0x7; break; diff --git a/src/gb/overrides.c b/src/gb/overrides.c index 5bfc4f3cf..20e489b9a 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -672,7 +672,19 @@ static const struct GBCartridgeOverride _overrides[] = { { 0x630ED957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug) { 0x5AFF0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug) { 0xA61856BD, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug) + // Unlicensed bootlegs { 0x30F8F86C, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) + { 0xE1147E75, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // Rockman 8 + { 0xEFF88FAA, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // True Color 25 in 1 (NT-9920) + { 0x811925D9, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // 23 in 1 (CR2011) + { 0x62A8016A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // 29 in 1 (CR2020) + { 0x5758D6D9, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Caise Gedou 24 in 1 Diannao Huamian Xuan Game (CY2060) + { 0x62A8016A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Caise Gedou 29 in 1 Diannao Huamian Xuan Game (CY2061) + { 0x80265A64, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Rockman X4 (Megaman X4) + { 0x805459DE, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Sonic Adventure 8 + { 0x0B1B808A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Donkey Kong 5 + { 0x0B1B808A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Donkey Kong 5 (Alt) + { 0x4650EB9A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Mario Special 3 { 0xB289D95A, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Capcom vs SNK - Millennium Fight 2001 { 0x688D6713, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 02 4 { 0x8931A272, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 2 @@ -683,6 +695,7 @@ static const struct GBCartridgeOverride _overrides[] = { { 0xBC75D7B8, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon - Mewtwo Strikes Back { 0xFF0B60CC, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Shuma Baolong 02 4 { 0x14A992A6, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // /Street Fighter Zero 4 + { 0x3EF5AFB2, GB_MODEL_AUTODETECT, GB_UNL_LI_CHENG, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) { 0, 0, 0, { 0 } } }; diff --git a/src/gb/sio/lockstep.c b/src/gb/sio/lockstep.c index 4d1f1f549..b8dccf891 100644 --- a/src/gb/sio/lockstep.c +++ b/src/gb/sio/lockstep.c @@ -157,7 +157,7 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) { } } // Tell the other GBs they can continue up to where we were - node->p->d.addCycles(&node->p->d, node->id, node->eventDiff); + node->p->d.addCycles(&node->p->d, 0, node->eventDiff); #ifndef NDEBUG node->phase = node->p->d.transferActive; #endif @@ -169,26 +169,28 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) { static uint32_t _slaveUpdate(struct GBSIOLockstepNode* node) { enum mLockstepPhase transferActive; + int id; ATOMIC_LOAD(transferActive, node->p->d.transferActive); + ATOMIC_LOAD(id, node->id); bool signal = false; switch (transferActive) { case TRANSFER_IDLE: - node->p->d.addCycles(&node->p->d, node->id, LOCKSTEP_INCREMENT); + node->p->d.addCycles(&node->p->d, id, LOCKSTEP_INCREMENT); break; case TRANSFER_STARTING: case TRANSFER_FINISHING: break; case TRANSFER_STARTED: - if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) { + if (node->p->d.unusedCycles(&node->p->d, id) > node->eventDiff) { break; } node->transferFinished = false; signal = true; break; case TRANSFER_FINISHED: - if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) { + if (node->p->d.unusedCycles(&node->p->d, id) > node->eventDiff) { break; } _finishTransfer(node); @@ -199,7 +201,7 @@ static uint32_t _slaveUpdate(struct GBSIOLockstepNode* node) { node->phase = node->p->d.transferActive; #endif if (signal) { - node->p->d.signal(&node->p->d, 1 << node->id); + node->p->d.signal(&node->p->d, 1 << id); } return 0; } @@ -215,11 +217,13 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, int32_t cycles = 0; node->nextEvent -= cyclesLate; if (node->nextEvent <= 0) { - if (!node->id) { + int id; + ATOMIC_LOAD(id, node->id); + if (!id) { cycles = _masterUpdate(node); } else { cycles = _slaveUpdate(node); - cycles += node->p->d.useCycles(&node->p->d, node->id, node->eventDiff); + cycles += node->p->d.useCycles(&node->p->d, id, node->eventDiff); } node->eventDiff = 0; } else { @@ -240,7 +244,9 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, static void GBSIOLockstepNodeWriteSB(struct GBSIODriver* driver, uint8_t value) { struct GBSIOLockstepNode* node = (struct GBSIOLockstepNode*) driver; - node->p->pendingSB[node->id] = value; + int id; + ATOMIC_LOAD(id, node->id); + node->p->pendingSB[id] = value; } static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t value) { @@ -252,11 +258,17 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu mLockstepLock(&node->p->d); bool claimed = false; if (ATOMIC_CMPXCHG(node->p->masterClaimed, claimed, true)) { - if (node->id != 0) { + int id; + ATOMIC_LOAD(id, node->id); + if (id != 0) { + unsigned sb; node->p->players[0]->id = 1; - node->p->players[1] = node->p->players[0]; - node->p->players[0] = node->p->players[1]; node->id = 0; + node->p->players[1] = node->p->players[0]; + node->p->players[0] = node; + sb = node->p->pendingSB[0]; + node->p->pendingSB[0] = node->p->pendingSB[1]; + node->p->pendingSB[1] = sb; } ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); ATOMIC_STORE(node->p->d.transferCycles, GBSIOCyclesPerTransfer[(value >> 1) & 1]); diff --git a/src/gba/audio.c b/src/gba/audio.c index 5d7dd347b..f957ef191 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -118,10 +118,10 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED); info->reg = GBADMARegisterSetWidth(info->reg, 1); switch (info->dest) { - case BASE_IO | REG_FIFO_A_LO: + case GBA_BASE_IO | REG_FIFO_A_LO: audio->chA.dmaSource = number; break; - case BASE_IO | REG_FIFO_B_LO: + case GBA_BASE_IO | REG_FIFO_B_LO: audio->chB.dmaSource = number; break; default: diff --git a/src/gba/bios.c b/src/gba/bios.c index d824830aa..ad610a287 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -49,11 +49,11 @@ static void _SoftReset(struct GBA* gba) { cpu->gprs[ARM_LR] = 0; cpu->gprs[ARM_SP] = GBA_SP_BASE_SYSTEM; int8_t flag = ((int8_t*) gba->memory.iwram)[0x7FFA]; - memset(((int8_t*) gba->memory.iwram) + SIZE_WORKING_IRAM - 0x200, 0, 0x200); + memset(((int8_t*) gba->memory.iwram) + GBA_SIZE_IWRAM - 0x200, 0, 0x200); if (flag) { - cpu->gprs[ARM_PC] = BASE_WORKING_RAM; + cpu->gprs[ARM_PC] = GBA_BASE_EWRAM; } else { - cpu->gprs[ARM_PC] = BASE_CART0; + cpu->gprs[ARM_PC] = GBA_BASE_ROM0; } _ARMSetMode(cpu, MODE_ARM); ARMWritePC(cpu); @@ -62,120 +62,120 @@ static void _SoftReset(struct GBA* gba) { static void _RegisterRamReset(struct GBA* gba) { uint32_t registers = gba->cpu->gprs[0]; struct ARMCore* cpu = gba->cpu; - cpu->memory.store16(cpu, BASE_IO | REG_DISPCNT, 0x0080, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DISPCNT, 0x0080, 0); if (registers & 0x01) { - memset(gba->memory.wram, 0, SIZE_WORKING_RAM); + memset(gba->memory.wram, 0, GBA_SIZE_EWRAM); } if (registers & 0x02) { - memset(gba->memory.iwram, 0, SIZE_WORKING_IRAM - 0x200); + memset(gba->memory.iwram, 0, GBA_SIZE_IWRAM - 0x200); } if (registers & 0x04) { - memset(gba->video.palette, 0, SIZE_PALETTE_RAM); + memset(gba->video.palette, 0, GBA_SIZE_PALETTE_RAM); } if (registers & 0x08) { - memset(gba->video.vram, 0, SIZE_VRAM); + memset(gba->video.vram, 0, GBA_SIZE_VRAM); } if (registers & 0x10) { - memset(gba->video.oam.raw, 0, SIZE_OAM); + memset(gba->video.oam.raw, 0, GBA_SIZE_OAM); } if (registers & 0x20) { - cpu->memory.store16(cpu, BASE_IO | REG_SIOCNT, 0x0000, 0); - cpu->memory.store16(cpu, BASE_IO | REG_RCNT, RCNT_INITIAL, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SIOMLT_SEND, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_JOYCNT, 0, 0); - cpu->memory.store32(cpu, BASE_IO | REG_JOY_RECV_LO, 0, 0); - cpu->memory.store32(cpu, BASE_IO | REG_JOY_TRANS_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SIOCNT, 0x0000, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_RCNT, RCNT_INITIAL, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SIOMLT_SEND, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_JOYCNT, 0, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_JOY_RECV_LO, 0, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_JOY_TRANS_LO, 0, 0); } if (registers & 0x40) { - cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_X, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND2CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND2CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_X, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND4CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND4CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_X, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUNDBIAS, 0x200, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND1CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND1CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND1CNT_X, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND2CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND2CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND3CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND3CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND3CNT_X, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND4CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND4CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDCNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDCNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDCNT_X, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDBIAS, 0x200, 0); memset(gba->audio.psg.ch3.wavedata32, 0, sizeof(gba->audio.psg.ch3.wavedata32)); } if (registers & 0x80) { - cpu->memory.store16(cpu, BASE_IO | REG_DISPSTAT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_VCOUNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG0CNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG1CNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2CNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3CNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG0HOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG0VOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG1HOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG1VOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2HOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2VOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3HOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3VOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2PA, 0x100, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2PB, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2PC, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2PD, 0x100, 0); - cpu->memory.store32(cpu, BASE_IO | REG_BG2X_LO, 0, 0); - cpu->memory.store32(cpu, BASE_IO | REG_BG2Y_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3PA, 0x100, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3PB, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3PC, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3PD, 0x100, 0); - cpu->memory.store32(cpu, BASE_IO | REG_BG3X_LO, 0, 0); - cpu->memory.store32(cpu, BASE_IO | REG_BG3Y_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WIN0H, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WIN1H, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WIN0V, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WIN1V, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WININ, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WINOUT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_MOSAIC, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BLDCNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BLDALPHA, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BLDY, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0SAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0SAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0DAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0DAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1SAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1SAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1DAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1DAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2SAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2SAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2DAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2DAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3SAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3SAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3DAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3DAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM0CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM0CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM1CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM1CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM2CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM2CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM3CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM3CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_IE, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_IF, 0xFFFF, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WAITCNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_IME, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DISPSTAT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_VCOUNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG0CNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG1CNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2CNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3CNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG0HOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG0VOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG1HOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG1VOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2HOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2VOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3HOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3VOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PA, 0x100, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PB, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PC, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PD, 0x100, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG2X_LO, 0, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG2Y_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PA, 0x100, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PB, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PC, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PD, 0x100, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG3X_LO, 0, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG3Y_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN0H, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN1H, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN0V, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN1V, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WININ, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WINOUT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_MOSAIC, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BLDCNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BLDALPHA, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BLDY, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0SAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0SAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0DAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0DAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1SAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1SAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1DAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1DAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2SAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2SAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2DAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2DAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3SAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3SAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3DAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3DAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM0CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM0CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM1CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM1CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM2CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM2CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM3CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM3CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_IE, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_IF, 0xFFFF, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WAITCNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_IME, 0, 0); } if (registers & 0x9C) { gba->video.renderer->reset(gba->video.renderer); @@ -267,7 +267,7 @@ static void _MidiKey2Freq(struct GBA* gba) { struct ARMCore* cpu = gba->cpu; int oldRegion = gba->memory.activeRegion; - gba->memory.activeRegion = REGION_BIOS; + gba->memory.activeRegion = GBA_REGION_BIOS; uint32_t key = cpu->memory.load32(cpu, cpu->gprs[0] + 4, 0); gba->memory.activeRegion = oldRegion; @@ -336,12 +336,14 @@ static int16_t _ArcTan(int32_t i, int32_t* r1, int32_t* r3, uint32_t* cycles) { static int16_t _ArcTan2(int32_t x, int32_t y, int32_t* r1, uint32_t* cycles) { if (!y) { + *cycles = 11; if (x >= 0) { return 0; } return 0x8000; } if (!x) { + *cycles = 11; if (y >= 0) { return 0x4000; } @@ -484,7 +486,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { break; case GBA_SWI_CPU_SET: case GBA_SWI_CPU_FAST_SET: - if (cpu->gprs[0] >> BASE_OFFSET < REGION_WORKING_RAM) { + if (cpu->gprs[0] >> BASE_OFFSET < GBA_REGION_EWRAM) { mLOG(GBA_BIOS, GAME_ERROR, "Cannot CpuSet from BIOS"); break; } @@ -499,7 +501,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { case GBA_SWI_GET_BIOS_CHECKSUM: cpu->gprs[0] = GBA_BIOS_CHECKSUM; cpu->gprs[1] = 1; - cpu->gprs[3] = SIZE_BIOS; + cpu->gprs[3] = GBA_SIZE_BIOS; break; case GBA_SWI_BG_AFFINE_SET: _BgAffineSet(gba); @@ -508,7 +510,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { _ObjAffineSet(gba); break; case GBA_SWI_BIT_UNPACK: - if (cpu->gprs[0] < BASE_WORKING_RAM) { + if (cpu->gprs[0] < GBA_BASE_EWRAM) { mLOG(GBA_BIOS, GAME_ERROR, "Bad BitUnPack source"); break; } @@ -516,9 +518,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { default: mLOG(GBA_BIOS, GAME_ERROR, "Bad BitUnPack destination"); // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_VRAM: _unBitPack(gba); break; } @@ -533,9 +535,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { default: mLOG(GBA_BIOS, GAME_ERROR, "Bad LZ77 destination"); // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_VRAM: useStall = true; _unLz77(gba, immediate == GBA_SWI_LZ77_UNCOMP_WRAM ? 1 : 2); break; @@ -550,9 +552,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { default: mLOG(GBA_BIOS, GAME_ERROR, "Bad Huffman destination"); // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_VRAM: _unHuffman(gba); break; } @@ -567,9 +569,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { default: mLOG(GBA_BIOS, GAME_ERROR, "Bad RL destination"); // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_VRAM: _unRl(gba, immediate == GBA_SWI_RL_UNCOMP_WRAM ? 1 : 2); break; } @@ -585,9 +587,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { default: mLOG(GBA_BIOS, GAME_ERROR, "Bad UnFilter destination"); // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_VRAM: _unFilter(gba, immediate == GBA_SWI_DIFF_16BIT_UNFILTER ? 2 : 1, immediate == GBA_SWI_DIFF_8BIT_UNFILTER_WRAM ? 1 : 2); break; } diff --git a/src/gba/cart/vfame.c b/src/gba/cart/vfame.c index 57b6d903b..aedcf5d3e 100644 --- a/src/gba/cart/vfame.c +++ b/src/gba/cart/vfame.c @@ -246,7 +246,7 @@ void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t valu // if mode has been set - the address and value of the SRAM write will be modified address = _modifySramAddress(cart->cartType, address, cart->sramMode); value = _modifySramValue(cart->cartType, value, cart->sramMode); - address &= (SIZE_CART_SRAM - 1); + address &= (GBA_SIZE_SRAM - 1); sramData[address] = value; } diff --git a/src/gba/cheats.c b/src/gba/cheats.c index ec92bc3c2..d055194be 100644 --- a/src/gba/cheats.c +++ b/src/gba/cheats.c @@ -105,36 +105,36 @@ static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_ char line[18] = "XXXXXXXX XXXXXXXX"; snprintf(line, sizeof(line), "%08X %08X", op1, op2); - int gsaP, rgsaP, parP, rparP; + int nextProbability; int maxProbability = INT_MIN; switch (set->gsaVersion) { case 0: // Try to detect GameShark version GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds); - gsaP = GBACheatGameSharkProbability(o1, o2); + nextProbability = GBACheatGameSharkProbability(o1, o2); o1 = op1; o2 = op2; - if (gsaP > maxProbability) { - maxProbability = gsaP; + if (nextProbability > maxProbability) { + maxProbability = nextProbability; GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1); } GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds); - parP = GBACheatProActionReplayProbability(o1, o2); - if (parP > maxProbability) { - maxProbability = parP; + nextProbability = GBACheatProActionReplayProbability(o1, o2); + if (nextProbability > maxProbability) { + maxProbability = nextProbability; GBACheatSetGameSharkVersion(set, GBA_GS_PARV3); } - rgsaP = GBACheatGameSharkProbability(op1, op1); - if (rgsaP > maxProbability) { - maxProbability = rgsaP; + nextProbability = GBACheatGameSharkProbability(op1, op2); + if (nextProbability > maxProbability) { + maxProbability = nextProbability; GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1_RAW); } - rparP = GBACheatProActionReplayProbability(op1, op1); - if (rparP > maxProbability) { - maxProbability = rparP; + nextProbability = GBACheatProActionReplayProbability(op1, op2); + if (nextProbability > maxProbability) { + maxProbability = nextProbability; GBACheatSetGameSharkVersion(set, GBA_GS_PARV3_RAW); } @@ -323,49 +323,49 @@ static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* dir int GBACheatAddressIsReal(uint32_t address) { switch (address >> BASE_OFFSET) { - case REGION_BIOS: + case GBA_REGION_BIOS: return -0x80; break; - case REGION_WORKING_RAM: - if ((address & OFFSET_MASK) > SIZE_WORKING_RAM) { + case GBA_REGION_EWRAM: + if ((address & OFFSET_MASK) > GBA_SIZE_EWRAM) { return -0x40; } return 0x20; - case REGION_WORKING_IRAM: - if ((address & OFFSET_MASK) > SIZE_WORKING_IRAM) { + case GBA_REGION_IWRAM: + if ((address & OFFSET_MASK) > GBA_SIZE_IWRAM) { return -0x40; } return 0x20; - case REGION_IO: - if ((address & OFFSET_MASK) > SIZE_IO) { + case GBA_REGION_IO: + if ((address & OFFSET_MASK) > GBA_SIZE_IO) { return -0x80; } return 0x10; - case REGION_OAM: - if ((address & OFFSET_MASK) > SIZE_OAM) { + case GBA_REGION_OAM: + if ((address & OFFSET_MASK) > GBA_SIZE_OAM) { return -0x80; } return -0x8; - case REGION_VRAM: - if ((address & OFFSET_MASK) > SIZE_VRAM) { + case GBA_REGION_VRAM: + if ((address & OFFSET_MASK) > GBA_SIZE_VRAM) { return -0x80; } return -0x8; - case REGION_PALETTE_RAM: - if ((address & OFFSET_MASK) > SIZE_PALETTE_RAM) { + case GBA_REGION_PALETTE_RAM: + if ((address & OFFSET_MASK) > GBA_SIZE_PALETTE_RAM) { return -0x80; } return -0x8; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: return -0x8; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: - if ((address & OFFSET_MASK) > SIZE_CART_FLASH512) { + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: + if ((address & OFFSET_MASK) > GBA_SIZE_FLASH512) { return -0x80; } return -0x8; diff --git a/src/gba/cheats/codebreaker.c b/src/gba/cheats/codebreaker.c index 0ba5c80ea..611222425 100644 --- a/src/gba/cheats/codebreaker.c +++ b/src/gba/cheats/codebreaker.c @@ -215,7 +215,7 @@ bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t o return false; } cheats->hook = malloc(sizeof(*cheats->hook)); - cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1)); + cheats->hook->address = GBA_BASE_ROM0 | (op1 & (GBA_SIZE_ROM0 - 1)); cheats->hook->mode = MODE_THUMB; cheats->hook->refs = 1; cheats->hook->reentries = 0; @@ -278,7 +278,7 @@ bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t o cheat = mCheatListAppend(&cheats->d.list); cheat->type = CHEAT_IF_NAND; cheat->width = 2; - cheat->address = BASE_IO | REG_KEYINPUT; + cheat->address = GBA_BASE_IO | REG_KEYINPUT; cheat->operand = op2; cheat->repeat = 1; return true; diff --git a/src/gba/cheats/gameshark.c b/src/gba/cheats/gameshark.c index 88f48ab49..72071d962 100644 --- a/src/gba/cheats/gameshark.c +++ b/src/gba/cheats/gameshark.c @@ -150,7 +150,7 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t break; case GSA_PATCH: romPatch = mCheatPatchListAppend(&cheats->d.romPatches); - romPatch->address = BASE_CART0 | ((op1 & 0xFFFFFF) << 1); + romPatch->address = GBA_BASE_ROM0 | ((op1 & 0xFFFFFF) << 1); romPatch->value = op2; romPatch->applied = false; romPatch->width = 2; @@ -207,7 +207,7 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t return false; } cheats->hook = malloc(sizeof(*cheats->hook)); - cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1)); + cheats->hook->address = GBA_BASE_ROM0 | (op1 & (GBA_SIZE_ROM0 - 1)); cheats->hook->mode = MODE_THUMB; cheats->hook->refs = 1; cheats->hook->reentries = 0; diff --git a/src/gba/cheats/parv3.c b/src/gba/cheats/parv3.c index 30cc5515b..e254ac94d 100644 --- a/src/gba/cheats/parv3.c +++ b/src/gba/cheats/parv3.c @@ -233,7 +233,7 @@ static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) { } if (romPatch >= 0) { struct mCheatPatch* patch = mCheatPatchListAppend(&cheats->d.romPatches); - patch->address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1); + patch->address = GBA_BASE_ROM0 | ((op2 & 0xFFFFFF) << 1); patch->applied = false; patch->check = false; patch->width = 2; @@ -282,7 +282,7 @@ bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uin return false; } cheats->hook = malloc(sizeof(*cheats->hook)); - cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 2)); + cheats->hook->address = GBA_BASE_ROM0 | (op1 & (GBA_SIZE_ROM0 - 2)); cheats->hook->mode = MODE_THUMB; cheats->hook->refs = 1; cheats->hook->reentries = 0; @@ -320,7 +320,7 @@ bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uin case PAR3_BASE_OTHER: width = ((op1 >> 24) & 1) + 1; cheat->type = CHEAT_ASSIGN; - cheat->address = BASE_IO | (op1 & OFFSET_MASK); + cheat->address = GBA_BASE_IO | (op1 & OFFSET_MASK); break; } if (op1 & 0x01000000 && (op1 & 0xFE000000) != 0xC6000000) { diff --git a/src/gba/core.c b/src/gba/core.c index 5b134338f..c5f1946d6 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -57,76 +57,76 @@ static const struct mCoreChannelInfo _GBAAudioChannels[] = { static const struct mCoreMemoryBlock _GBAMemoryBlocks[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, - { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, }; static const struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, - { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART_SRAM, "sram", "SRAM", "Static RAM (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_SRAM, SIZE_CART_SRAM, true }, + { GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_SRAM, "sram", "SRAM", "Static RAM (64kiB)", GBA_BASE_SRAM, GBA_BASE_SRAM + GBA_SIZE_SRAM, GBA_SIZE_SRAM, true }, }; static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash512[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, - { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_SRAM, "sram", "Flash", "Flash Memory (64kiB)", GBA_BASE_SRAM, GBA_BASE_SRAM + GBA_SIZE_FLASH512, GBA_SIZE_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, }; static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, - { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 }, + { GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_SRAM, "sram", "Flash", "Flash Memory (64kiB)", GBA_BASE_SRAM, GBA_BASE_SRAM + GBA_SIZE_FLASH512, GBA_SIZE_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 }, }; static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, - { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, SIZE_CART_EEPROM, SIZE_CART_EEPROM, mCORE_MEMORY_RW }, + { GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, GBA_SIZE_EEPROM, GBA_SIZE_EEPROM, mCORE_MEMORY_RW }, }; static const struct mCoreRegisterInfo _GBARegisters[] = { @@ -237,7 +237,7 @@ static bool _GBACoreInit(struct mCore* core) { #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 mDirectorySetInit(&core->dirs); #endif - + return true; } @@ -512,7 +512,7 @@ static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { - if (ELFEntry(elf) == BASE_CART0) { + if (GBAVerifyELFEntry(elf, GBA_BASE_ROM0)) { GBALoadNull(core->board); } bool success = mCoreLoadELF(core, elf); @@ -700,8 +700,8 @@ static void _GBACoreReset(struct mCore* core) { #endif ARMReset(core->cpu); - bool forceSkip = gba->romVf && GBAIsMB(gba->romVf); - if (!(forceSkip || core->opts.skipBios) && (gba->romVf || gba->memory.rom) && gba->pristineRomSize >= 0xA0 && gba->biosVf) { + bool forceSkip = gba->mbVf || core->opts.skipBios; + if (!forceSkip && (gba->romVf || gba->memory.rom) && gba->pristineRomSize >= 0xA0 && gba->biosVf) { uint32_t crc = doCrc32(&gba->memory.rom[1], 0x9C); if (crc != LOGO_CRC32) { mLOG(STATUS, WARN, "Invalid logo, skipping BIOS"); @@ -709,7 +709,7 @@ static void _GBACoreReset(struct mCore* core) { } } - if (forceSkip || (core->opts.skipBios && (gba->romVf || gba->memory.rom))) { + if (forceSkip) { GBASkipBIOS(core->board); } @@ -907,36 +907,36 @@ void* _GBACoreGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) { switch (id) { default: return NULL; - case REGION_BIOS: - *sizeOut = SIZE_BIOS; + case GBA_REGION_BIOS: + *sizeOut = GBA_SIZE_BIOS; return gba->memory.bios; - case REGION_WORKING_RAM: - *sizeOut = SIZE_WORKING_RAM; + case GBA_REGION_EWRAM: + *sizeOut = GBA_SIZE_EWRAM; return gba->memory.wram; - case REGION_WORKING_IRAM: - *sizeOut = SIZE_WORKING_IRAM; + case GBA_REGION_IWRAM: + *sizeOut = GBA_SIZE_IWRAM; return gba->memory.iwram; - case REGION_PALETTE_RAM: - *sizeOut = SIZE_PALETTE_RAM; + case GBA_REGION_PALETTE_RAM: + *sizeOut = GBA_SIZE_PALETTE_RAM; return gba->video.palette; - case REGION_VRAM: - *sizeOut = SIZE_VRAM; + case GBA_REGION_VRAM: + *sizeOut = GBA_SIZE_VRAM; return gba->video.vram; - case REGION_OAM: - *sizeOut = SIZE_OAM; + case GBA_REGION_OAM: + *sizeOut = GBA_SIZE_OAM; return gba->video.oam.raw; - case REGION_CART0: - case REGION_CART1: - case REGION_CART2: + case GBA_REGION_ROM0: + case GBA_REGION_ROM1: + case GBA_REGION_ROM2: *sizeOut = gba->memory.romSize; return gba->memory.rom; - case REGION_CART_SRAM: + case GBA_REGION_SRAM: if (gba->memory.savedata.type == SAVEDATA_FLASH1M) { - *sizeOut = SIZE_CART_FLASH1M; + *sizeOut = GBA_SIZE_FLASH1M; return gba->memory.savedata.currentBank; } // Fall through - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM_MIRROR: *sizeOut = GBASavedataSize(&gba->memory.savedata); return gba->memory.savedata.data; } @@ -1153,7 +1153,7 @@ static bool _GBACoreLookupIdentifier(struct mCore* core, const char* name, int32 for (i = 0; i < REG_MAX; i += 2) { const char* reg = GBAIORegisterNames[i >> 1]; if (reg && strcasecmp(reg, name) == 0) { - *value = BASE_IO | i; + *value = GBA_BASE_IO | i; return true; } } @@ -1321,7 +1321,7 @@ static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* c struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL); state->id = 0; - state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM; + state->cpu.gprs[ARM_PC] = GBA_BASE_EWRAM; int channelId = mVideoLoggerAddChannel(context); gbacore->vlProxy.logger = malloc(sizeof(struct mVideoLogger)); @@ -1490,8 +1490,8 @@ static void _GBAVLPReset(struct mCore* core) { // Make sure CPU loop never spins GBAHalt(gba); - gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL); - gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); + gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IME, 0, NULL); + gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IE, 0, NULL); } static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) { @@ -1510,13 +1510,13 @@ static bool _GBAVLPLoadState(struct mCore* core, const void* state) { struct GBA* gba = (struct GBA*) core->board; gba->timing.root = NULL; - gba->cpu->gprs[ARM_PC] = BASE_WORKING_RAM; + gba->cpu->gprs[ARM_PC] = GBA_BASE_EWRAM; gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); // Make sure CPU loop never spins GBAHalt(gba); - gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL); - gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); + gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IME, 0, NULL); + gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IE, 0, NULL); GBAVideoDeserialize(&gba->video, state); GBAIODeserialize(gba, state); GBAAudioReset(&gba->audio); diff --git a/src/gba/dma.c b/src/gba/dma.c index 14b9fb777..a5d94dd26 100644 --- a/src/gba/dma.c +++ b/src/gba/dma.c @@ -33,14 +33,14 @@ void GBADMAReset(struct GBA* gba) { gba->memory.activeDMA = -1; } static bool _isValidDMASAD(int dma, uint32_t address) { - if (dma == 0 && address >= BASE_CART0 && address < BASE_CART_SRAM) { + if (dma == 0 && address >= GBA_BASE_ROM0 && address < GBA_BASE_SRAM) { return false; } - return address >= BASE_WORKING_RAM; + return address >= GBA_BASE_EWRAM; } static bool _isValidDMADAD(int dma, uint32_t address) { - return dma == 3 || address < BASE_CART0; + return dma == 3 || address < GBA_BASE_ROM0; } uint32_t GBADMAWriteSAD(struct GBA* gba, int dma, uint32_t address) { @@ -244,7 +244,6 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { struct GBAMemory* memory = &gba->memory; struct ARMCore* cpu = gba->cpu; uint32_t width = 2 << GBADMARegisterGetWidth(info->reg); - int32_t wordsRemaining = info->nextCount; uint32_t source = info->nextSource; uint32_t dest = info->nextDest; uint32_t sourceRegion = source >> BASE_OFFSET; @@ -252,6 +251,8 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { int32_t cycles = 2; gba->cpuBlocked = true; + gba->performingDMA = 1 | (number << 1); + if (info->count == info->nextCount) { if (width == 4) { cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion]; @@ -267,53 +268,47 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { } info->when += cycles; - gba->performingDMA = 1 | (number << 1); if (width == 4) { if (source) { memory->dmaTransferRegister = cpu->memory.load32(cpu, source, 0); } - gba->bus = memory->dmaTransferRegister; cpu->memory.store32(cpu, dest, memory->dmaTransferRegister, 0); } else { - if (sourceRegion == REGION_CART2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) { + if (sourceRegion == GBA_REGION_ROM2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) { memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata); memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; } else if (source) { memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0); memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; } - if (destRegion == REGION_CART2_EX) { + if (destRegion == GBA_REGION_ROM2_EX) { if (memory->savedata.type == SAVEDATA_AUTODETECT) { mLOG(GBA_MEM, INFO, "Detected EEPROM savegame"); GBASavedataInitEEPROM(&memory->savedata); } if (memory->savedata.type == SAVEDATA_EEPROM512 || memory->savedata.type == SAVEDATA_EEPROM) { - GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, wordsRemaining); + GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, info->nextCount); } } else { cpu->memory.store16(cpu, dest, memory->dmaTransferRegister, 0); - } - gba->bus = memory->dmaTransferRegister; } + gba->bus = memory->dmaTransferRegister; int sourceOffset; - if (info->nextSource >= BASE_CART0 && info->nextSource < BASE_CART_SRAM && GBADMARegisterGetSrcControl(info->reg) < 3) { + if (info->nextSource >= GBA_BASE_ROM0 && info->nextSource < GBA_BASE_SRAM && GBADMARegisterGetSrcControl(info->reg) < 3) { sourceOffset = width; } else { sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width; } int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width; if (source) { - source += sourceOffset; + info->nextSource += sourceOffset; } - dest += destOffset; - --wordsRemaining; - gba->performingDMA = 0; + info->nextDest += destOffset; + --info->nextCount; - info->nextCount = wordsRemaining; - info->nextSource = source; - info->nextDest = dest; + gba->performingDMA = 0; int i; for (i = 0; i < 4; ++i) { @@ -324,9 +319,9 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { } } - if (!wordsRemaining) { + if (!info->nextCount) { info->nextCount |= 0x80000000; - if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) { + if (sourceRegion < GBA_REGION_ROM0 || destRegion < GBA_REGION_ROM0) { info->when += 2; } } diff --git a/src/gba/extra/audio-mixer.c b/src/gba/extra/audio-mixer.c index 503fdc8ad..0bb9b5b35 100644 --- a/src/gba/extra/audio-mixer.c +++ b/src/gba/extra/audio-mixer.c @@ -245,7 +245,7 @@ static void _mp2kReload(struct GBAAudioMixer* mixer) { } bool _mp2kEngage(struct GBAAudioMixer* mixer, uint32_t address) { - if (address < BASE_WORKING_RAM) { + if (address < GBA_BASE_EWRAM) { return false; } if (address != mixer->contextAddress) { diff --git a/src/gba/extra/proxy.c b/src/gba/extra/proxy.c index a7f28d153..82bbd3ca9 100644 --- a/src/gba/extra/proxy.c +++ b/src/gba/extra/proxy.c @@ -63,9 +63,9 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct renderer->logger->parsePacket = _parsePacket; renderer->logger->handleEvent = _handleEvent; renderer->logger->vramBlock = _vramBlock; - renderer->logger->paletteSize = SIZE_PALETTE_RAM; - renderer->logger->vramSize = SIZE_VRAM; - renderer->logger->oamSize = SIZE_OAM; + renderer->logger->paletteSize = GBA_SIZE_PALETTE_RAM; + renderer->logger->vramSize = GBA_SIZE_VRAM; + renderer->logger->oamSize = GBA_SIZE_OAM; renderer->backend = backend; } @@ -82,9 +82,9 @@ static void _init(struct GBAVideoProxyRenderer* proxyRenderer) { } static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) { - memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, SIZE_OAM); - memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM); - memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, SIZE_VRAM); + memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GBA_SIZE_OAM); + memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, GBA_SIZE_PALETTE_RAM); + memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GBA_SIZE_VRAM); mVideoLoggerRendererReset(proxyRenderer->logger); } @@ -199,19 +199,19 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); break; case DIRTY_PALETTE: - if (item->address < SIZE_PALETTE_RAM) { + if (item->address < GBA_SIZE_PALETTE_RAM) { STORE_16LE(item->value, item->address, logger->palette); proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); } break; case DIRTY_OAM: - if (item->address < SIZE_OAM) { + if (item->address < GBA_SIZE_OAM) { STORE_16LE(item->value, item->address << 1, logger->oam); proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); } break; case DIRTY_VRAM: - if (item->address <= SIZE_VRAM - 0x1000) { + if (item->address <= GBA_SIZE_VRAM - 0x1000) { logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); } else { diff --git a/src/gba/gba.c b/src/gba/gba.c index bd0353d0c..588a138f3 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -108,7 +108,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { gba->keyCallback = NULL; mCoreCallbacksListInit(&gba->coreCallbacks, 0); - gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); + gba->biosChecksum = GBAChecksum(gba->memory.bios, GBA_SIZE_BIOS); gba->idleOptimization = IDLE_LOOP_REMOVE; gba->idleLoop = IDLE_LOOP_NONE; @@ -137,7 +137,7 @@ void GBAUnloadROM(struct GBA* gba) { gba->yankedRomSize = 0; } #ifndef FIXED_ROM_BUFFER - mappedMemoryFree(gba->memory.rom, SIZE_CART0); + mappedMemoryFree(gba->memory.rom, GBA_SIZE_ROM0); #endif } @@ -172,7 +172,7 @@ void GBADestroy(struct GBA* gba) { GBAUnloadMB(gba); if (gba->biosVf) { - gba->biosVf->unmap(gba->biosVf, gba->memory.bios, SIZE_BIOS); + gba->biosVf->unmap(gba->biosVf, gba->memory.bios, GBA_SIZE_BIOS); gba->biosVf->close(gba->biosVf); gba->biosVf = 0; } @@ -213,6 +213,7 @@ void GBAReset(struct ARMCore* cpu) { gba->earlyExit = false; gba->dmaPC = 0; gba->biosStall = 0; + gba->keysLast = 0x400; if (gba->yankedRomSize) { gba->memory.romSize = gba->yankedRomSize; gba->memory.romMask = toPow2(gba->memory.romSize) - 1; @@ -246,7 +247,7 @@ void GBAReset(struct ARMCore* cpu) { if (GBAIsMB(gba->mbVf) && !isELF) { gba->mbVf->seek(gba->mbVf, 0, SEEK_SET); - gba->mbVf->read(gba->mbVf, gba->memory.wram, SIZE_WORKING_RAM); + gba->mbVf->read(gba->mbVf, gba->memory.wram, GBA_SIZE_EWRAM); } gba->lastJump = 0; @@ -258,7 +259,7 @@ void GBAReset(struct ARMCore* cpu) { memset(gba->debugString, 0, sizeof(gba->debugString)); - if (gba->romVf && gba->romVf->size(gba->romVf) > SIZE_CART0) { + if (gba->romVf && gba->romVf->size(gba->romVf) > GBA_SIZE_ROM0) { char ident; gba->romVf->seek(gba->romVf, 0xAC, SEEK_SET); gba->romVf->read(gba->romVf, &ident, 1); @@ -273,9 +274,11 @@ void GBASkipBIOS(struct GBA* gba) { struct ARMCore* cpu = gba->cpu; if (cpu->gprs[ARM_PC] == BASE_RESET + WORD_SIZE_ARM) { if (gba->memory.rom) { - cpu->gprs[ARM_PC] = BASE_CART0; + cpu->gprs[ARM_PC] = GBA_BASE_ROM0; + } else if (gba->memory.wram[0x30]) { + cpu->gprs[ARM_PC] = GBA_BASE_EWRAM + 0xC0; } else { - cpu->gprs[ARM_PC] = BASE_WORKING_RAM + 0xC0; + cpu->gprs[ARM_PC] = GBA_BASE_EWRAM; } gba->video.vcount = 0x7E; gba->memory.io[REG_VCOUNT >> 1] = 0x7E; @@ -357,14 +360,14 @@ bool GBALoadNull(struct GBA* gba) { gba->romVf = NULL; gba->pristineRomSize = 0; #ifndef FIXED_ROM_BUFFER - gba->memory.rom = anonymousMemoryMap(SIZE_CART0); + gba->memory.rom = anonymousMemoryMap(GBA_SIZE_ROM0); #else gba->memory.rom = romBuffer; #endif gba->isPristine = false; gba->yankedRomSize = 0; - gba->memory.romSize = SIZE_CART0; - gba->memory.romMask = SIZE_CART0 - 1; + gba->memory.romSize = GBA_SIZE_ROM0; + gba->memory.romMask = GBA_SIZE_ROM0 - 1; gba->romCrc32 = 0; if (gba->cpu) { @@ -378,9 +381,9 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) { GBAUnloadMB(gba); gba->mbVf = vf; vf->seek(vf, 0, SEEK_SET); - memset(gba->memory.wram, 0, SIZE_WORKING_RAM); - vf->read(vf, gba->memory.wram, SIZE_WORKING_RAM); - if (gba->cpu && gba->memory.activeRegion == REGION_WORKING_RAM) { + memset(gba->memory.wram, 0, GBA_SIZE_EWRAM); + vf->read(vf, gba->memory.wram, GBA_SIZE_EWRAM); + if (gba->cpu && gba->memory.activeRegion == GBA_REGION_IWRAM) { gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); } return true; @@ -402,7 +405,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { gba->isPristine = true; gba->pristineRomSize = vf->size(vf); vf->seek(vf, 0, SEEK_SET); - if (gba->pristineRomSize > SIZE_CART0) { + if (gba->pristineRomSize > GBA_SIZE_ROM0) { char ident; vf->seek(vf, 0xAC, SEEK_SET); vf->read(vf, &ident, 1); @@ -412,13 +415,13 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { #ifdef FIXED_ROM_BUFFER gba->memory.rom = romBuffer; #else - gba->memory.rom = anonymousMemoryMap(SIZE_CART0); + gba->memory.rom = anonymousMemoryMap(GBA_SIZE_ROM0); #endif } else { - gba->memory.rom = vf->map(vf, SIZE_CART0, MAP_READ); - gba->memory.romSize = SIZE_CART0; + gba->memory.rom = vf->map(vf, GBA_SIZE_ROM0, MAP_READ); + gba->memory.romSize = GBA_SIZE_ROM0; } - gba->pristineRomSize = SIZE_CART0; + gba->pristineRomSize = GBA_SIZE_ROM0; } else if (gba->pristineRomSize == 0x00100000) { // 1 MiB ROMs (e.g. Classic NES) all appear as 4x mirrored, but not more gba->isPristine = false; @@ -426,7 +429,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { #ifdef FIXED_ROM_BUFFER gba->memory.rom = romBuffer; #else - gba->memory.rom = anonymousMemoryMap(SIZE_CART0); + gba->memory.rom = anonymousMemoryMap(GBA_SIZE_ROM0); #endif vf->read(vf, gba->memory.rom, gba->pristineRomSize); memcpy(&gba->memory.rom[0x40000], gba->memory.rom, 0x00100000); @@ -447,15 +450,15 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { if (popcount32(gba->memory.romSize) != 1) { // This ROM is either a bad dump or homebrew. Emulate flash cart behavior. #ifndef FIXED_ROM_BUFFER - void* newRom = anonymousMemoryMap(SIZE_CART0); + void* newRom = anonymousMemoryMap(GBA_SIZE_ROM0); memcpy(newRom, gba->memory.rom, gba->pristineRomSize); gba->memory.rom = newRom; #endif - gba->memory.romSize = SIZE_CART0; - gba->memory.romMask = SIZE_CART0 - 1; + gba->memory.romSize = GBA_SIZE_ROM0; + gba->memory.romMask = GBA_SIZE_ROM0 - 1; gba->isPristine = false; } - if (gba->cpu && gba->memory.activeRegion >= REGION_CART0) { + if (gba->cpu && gba->memory.activeRegion >= GBA_REGION_ROM0) { gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); } GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]); @@ -482,23 +485,23 @@ void GBAYankROM(struct GBA* gba) { } void GBALoadBIOS(struct GBA* gba, struct VFile* vf) { - if (vf->size(vf) != SIZE_BIOS) { + if (vf->size(vf) != GBA_SIZE_BIOS) { mLOG(GBA, WARN, "Incorrect BIOS size"); return; } - uint32_t* bios = vf->map(vf, SIZE_BIOS, MAP_READ); + uint32_t* bios = vf->map(vf, GBA_SIZE_BIOS, MAP_READ); if (!bios) { mLOG(GBA, WARN, "Couldn't map BIOS"); return; } if (gba->biosVf) { - gba->biosVf->unmap(gba->biosVf, gba->memory.bios, SIZE_BIOS); + gba->biosVf->unmap(gba->biosVf, gba->memory.bios, GBA_SIZE_BIOS); gba->biosVf->close(gba->biosVf); } gba->biosVf = vf; gba->memory.bios = bios; gba->memory.fullBios = 1; - uint32_t checksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); + uint32_t checksum = GBAChecksum(gba->memory.bios, GBA_SIZE_BIOS); mLOG(GBA, DEBUG, "BIOS Checksum: 0x%X", checksum); if (checksum == GBA_BIOS_CHECKSUM) { mLOG(GBA, INFO, "Official GBA BIOS detected"); @@ -508,7 +511,7 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf) { mLOG(GBA, WARN, "BIOS checksum incorrect"); } gba->biosChecksum = checksum; - if (gba->memory.activeRegion == REGION_BIOS) { + if (gba->memory.activeRegion == GBA_REGION_BIOS) { gba->cpu->memory.activeRegion = gba->memory.bios; } // TODO: error check @@ -516,18 +519,18 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf) { void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { size_t patchedSize = patch->outputSize(patch, gba->memory.romSize); - if (!patchedSize || patchedSize > SIZE_CART0) { + if (!patchedSize || patchedSize > GBA_SIZE_ROM0) { return; } - void* newRom = anonymousMemoryMap(SIZE_CART0); + void* newRom = anonymousMemoryMap(GBA_SIZE_ROM0); if (!patch->applyPatch(patch, gba->memory.rom, gba->pristineRomSize, newRom, patchedSize)) { - mappedMemoryFree(newRom, SIZE_CART0); + mappedMemoryFree(newRom, GBA_SIZE_ROM0); return; } if (gba->romVf) { #ifndef FIXED_ROM_BUFFER if (!gba->isPristine) { - mappedMemoryFree(gba->memory.rom, SIZE_CART0); + mappedMemoryFree(gba->memory.rom, GBA_SIZE_ROM0); } else { gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); } @@ -595,6 +598,63 @@ void GBADebug(struct GBA* gba, uint16_t flags) { gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags); } +#ifdef USE_ELF +bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target) { + if (ELFEntry(elf) == target) { + return true; + } + + struct ELFProgramHeaders ph; + ELFProgramHeadersInit(&ph, 0); + ELFGetProgramHeaders(elf, &ph); + size_t i; + for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) { + Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i); + if (!phdr->p_filesz) { + continue; + } + + size_t phdrS = phdr->p_paddr; + size_t phdrE = phdrS + phdr->p_filesz; + + // Does the segment contain our target address? + if (target < phdrS || target + 4 > phdrE) { + continue; + } + + // File offset to what should be the rom entry instruction + size_t off = phdr->p_offset + target - phdrS; + + size_t eSize; + const char* bytes = ELFBytes(elf, &eSize); + + // Bounds and alignment check + if (off >= eSize || off & 3) { + continue; + } + + uint32_t opcode; + LOAD_32(opcode, off, bytes); + struct ARMInstructionInfo info; + ARMDecodeARM(opcode, &info); + + if (info.branchType != ARM_BRANCH && info.branchType != ARM_BRANCH_LINKED) { + continue; + } + + uint32_t bTarget = target + info.op1.immediate + 8; + + if (ELFEntry(elf) == bTarget) { + ELFProgramHeadersDeinit(&ph); + return true; + } + } + + ELFProgramHeadersDeinit(&ph); + return false; +} +#endif + bool GBAIsROM(struct VFile* vf) { if (!vf) { return false; @@ -606,7 +666,7 @@ bool GBAIsROM(struct VFile* vf) { uint32_t entry = ELFEntry(elf); bool isGBA = true; isGBA = isGBA && ELFMachine(elf) == EM_ARM; - isGBA = isGBA && (entry == BASE_CART0 || entry == BASE_WORKING_RAM + 0xC0); + isGBA = isGBA && (GBAVerifyELFEntry(elf, GBA_BASE_ROM0) || GBAVerifyELFEntry(elf, GBA_BASE_EWRAM + 0xC0)); ELFClose(elf); return isGBA; } @@ -662,12 +722,12 @@ bool GBAIsMB(struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { - bool isMB = ELFEntry(elf) == BASE_WORKING_RAM + 0xC0; + bool isMB = GBAVerifyELFEntry(elf, GBA_BASE_EWRAM + 0xC0); ELFClose(elf); return isMB; } #endif - if (vf->size(vf) > SIZE_WORKING_RAM) { + if (vf->size(vf) > GBA_SIZE_EWRAM) { return false; } if (vf->seek(vf, GBA_MB_MAGIC_OFFSET, SEEK_SET) < 0) { @@ -704,10 +764,10 @@ bool GBAIsMB(struct VFile* vf) { } pc += 4; LOAD_32(opcode, 0, &signature); - if ((opcode & ~0x1FFFF) == BASE_WORKING_RAM) { + if ((opcode & ~0x1FFFF) == GBA_BASE_EWRAM) { ++wramAddrs; } - if ((opcode & ~0x1FFFF) == BASE_CART0) { + if ((opcode & ~0x1FFFF) == GBA_BASE_ROM0) { ++romAddrs; } ARMDecodeARM(opcode, &info); @@ -730,10 +790,10 @@ bool GBAIsMB(struct VFile* vf) { if (vf->seek(vf, pc, SEEK_SET) < 0) { break; } - if ((immediate & ~0x1FFFF) == BASE_WORKING_RAM) { + if ((immediate & ~0x1FFFF) == GBA_BASE_EWRAM) { ++wramLoads; } - if ((immediate & ~0x1FFFF) == BASE_CART0) { + if ((immediate & ~0x1FFFF) == GBA_BASE_ROM0) { ++romLoads; } } diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index b68d063a3..84737afd4 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -2,7 +2,7 @@ #include -const uint8_t hleBios[SIZE_BIOS] = { +const uint8_t hleBios[GBA_SIZE_BIOS] = { 0xd3, 0x00, 0x00, 0xea, 0x66, 0x00, 0x00, 0xea, 0x0c, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, 0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0x00, 0x00, diff --git a/src/gba/hle-bios.make b/src/gba/hle-bios.make index b890b43ef..5ac7f1e9b 100644 --- a/src/gba/hle-bios.make +++ b/src/gba/hle-bios.make @@ -15,4 +15,4 @@ hle-bios.c: hle-bios.bin echo >> $@ echo '#include ' >> $@ echo >> $@ - xxd -i $< | sed -e 's/unsigned char hle_bios_bin\[\]/const uint8_t hleBios[SIZE_BIOS]/' -e 's/^ \+/\t/' | grep -v hle_bios_bin_len >> $@ + xxd -i $< | sed -e 's/unsigned char hle_bios_bin\[\]/const uint8_t hleBios[GBA_SIZE_BIOS]/' -e 's/^ \+/\t/' | grep -v hle_bios_bin_len >> $@ diff --git a/src/gba/io.c b/src/gba/io.c index ca0cbe5b5..4e3763536 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -625,18 +625,18 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) { return; } if (address == REG_POSTFLG) { - gba->memory.io[(address & (SIZE_IO - 1)) >> 1] = value; + gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1] = value; return; } if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) { gba->debugString[address - REG_DEBUG_STRING] = value; return; } - if (address > SIZE_IO) { + if (address > GBA_SIZE_IO) { return; } uint16_t value16 = value << (8 * (address & 1)); - value16 |= (gba->memory.io[(address & (SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1))); + value16 |= (gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1))); GBAIOWrite(gba, address & 0xFFFFFFFE, value16); } diff --git a/src/gba/memory.c b/src/gba/memory.c index c9a1f59dd..4f3063cfb 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -89,8 +89,8 @@ void GBAMemoryInit(struct GBA* gba) { gba->memory.agbPrintBuffer = NULL; gba->memory.agbPrintBufferBackup = NULL; - gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM + SIZE_WORKING_IRAM); - gba->memory.iwram = &gba->memory.wram[SIZE_WORKING_RAM >> 2]; + gba->memory.wram = anonymousMemoryMap(GBA_SIZE_EWRAM + GBA_SIZE_IWRAM); + gba->memory.iwram = &gba->memory.wram[GBA_SIZE_EWRAM >> 2]; GBADMAInit(gba); GBAVFameInit(&gba->memory.vfame); @@ -101,15 +101,15 @@ void GBAMemoryInit(struct GBA* gba) { } void GBAMemoryDeinit(struct GBA* gba) { - mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM + SIZE_WORKING_IRAM); + mappedMemoryFree(gba->memory.wram, GBA_SIZE_EWRAM + GBA_SIZE_IWRAM); if (gba->memory.rom) { mappedMemoryFree(gba->memory.rom, gba->memory.romSize); } if (gba->memory.agbPrintBuffer) { - mappedMemoryFree(gba->memory.agbPrintBuffer, SIZE_AGB_PRINT); + mappedMemoryFree(gba->memory.agbPrintBuffer, GBA_SIZE_AGB_PRINT); } if (gba->memory.agbPrintBufferBackup) { - mappedMemoryFree(gba->memory.agbPrintBufferBackup, SIZE_AGB_PRINT); + mappedMemoryFree(gba->memory.agbPrintBufferBackup, GBA_SIZE_AGB_PRINT); } GBACartEReaderDeinit(&gba->memory.ereader); @@ -117,11 +117,11 @@ void GBAMemoryDeinit(struct GBA* gba) { void GBAMemoryReset(struct GBA* gba) { if (gba->memory.wram && gba->memory.rom) { - memset(gba->memory.wram, 0, SIZE_WORKING_RAM); + memset(gba->memory.wram, 0, GBA_SIZE_EWRAM); } if (gba->memory.iwram) { - memset(gba->memory.iwram, 0, SIZE_WORKING_IRAM); + memset(gba->memory.iwram, 0, GBA_SIZE_IWRAM); } memset(gba->memory.io, 0, sizeof(gba->memory.io)); @@ -133,11 +133,11 @@ void GBAMemoryReset(struct GBA* gba) { gba->memory.agbPrintBase = 0; memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx)); if (gba->memory.agbPrintBuffer) { - mappedMemoryFree(gba->memory.agbPrintBuffer, SIZE_AGB_PRINT); + mappedMemoryFree(gba->memory.agbPrintBuffer, GBA_SIZE_AGB_PRINT); gba->memory.agbPrintBuffer = NULL; } if (gba->memory.agbPrintBufferBackup) { - mappedMemoryFree(gba->memory.agbPrintBufferBackup, SIZE_AGB_PRINT); + mappedMemoryFree(gba->memory.agbPrintBufferBackup, GBA_SIZE_AGB_PRINT); gba->memory.agbPrintBufferBackup = NULL; } @@ -186,11 +186,11 @@ static void _analyzeForIdleLoop(struct GBA* gba, struct ARMCore* cpu, uint32_t a } else { loadAddress += offset; } - if ((loadAddress >> BASE_OFFSET) == REGION_IO && !GBAIOIsReadConstant(loadAddress)) { + if ((loadAddress >> BASE_OFFSET) == GBA_REGION_IO && !GBAIOIsReadConstant(loadAddress)) { gba->idleDetectionStep = -1; return; } - if ((loadAddress >> BASE_OFFSET) < REGION_CART0 || (loadAddress >> BASE_OFFSET) > REGION_CART2_EX) { + if ((loadAddress >> BASE_OFFSET) < GBA_REGION_ROM0 || (loadAddress >> BASE_OFFSET) > GBA_REGION_ROM2_EX) { gba->taintedRegisters[info.op1.reg] = true; } else { switch (info.memory.width) { @@ -232,7 +232,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { struct GBAMemory* memory = &gba->memory; int newRegion = address >> BASE_OFFSET; - if (gba->idleOptimization >= IDLE_LOOP_REMOVE && memory->activeRegion != REGION_BIOS) { + if (gba->idleOptimization >= IDLE_LOOP_REMOVE && memory->activeRegion != GBA_REGION_BIOS) { if (address == gba->idleLoop) { if (gba->haltPending) { gba->haltPending = false; @@ -273,33 +273,33 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { } else { cpu->memory.activeMask &= -WORD_SIZE_ARM; } - if (newRegion < REGION_CART0 || (address & (SIZE_CART0 - 1)) < memory->romSize) { + if (newRegion < GBA_REGION_ROM0 || (address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { return; } } - if (memory->activeRegion == REGION_BIOS) { + if (memory->activeRegion == GBA_REGION_BIOS) { memory->biosPrefetch = cpu->prefetch[1]; } memory->activeRegion = newRegion; switch (newRegion) { - case REGION_BIOS: + case GBA_REGION_BIOS: cpu->memory.activeRegion = memory->bios; - cpu->memory.activeMask = SIZE_BIOS - 1; + cpu->memory.activeMask = GBA_SIZE_BIOS - 1; break; - case REGION_WORKING_RAM: + case GBA_REGION_EWRAM: cpu->memory.activeRegion = memory->wram; - cpu->memory.activeMask = SIZE_WORKING_RAM - 1; + cpu->memory.activeMask = GBA_SIZE_EWRAM - 1; break; - case REGION_WORKING_IRAM: + case GBA_REGION_IWRAM: cpu->memory.activeRegion = memory->iwram; - cpu->memory.activeMask = SIZE_WORKING_IRAM - 1; + cpu->memory.activeMask = GBA_SIZE_IWRAM - 1; break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: cpu->memory.activeRegion = (uint32_t*) gba->video.palette; - cpu->memory.activeMask = SIZE_PALETTE_RAM - 1; + cpu->memory.activeMask = GBA_SIZE_PALETTE_RAM - 1; break; - case REGION_VRAM: + case GBA_REGION_VRAM: if (address & 0x10000) { cpu->memory.activeRegion = (uint32_t*) &gba->video.vram[0x8000]; cpu->memory.activeMask = 0x00007FFF; @@ -308,19 +308,19 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { cpu->memory.activeMask = 0x0000FFFF; } break; - case REGION_OAM: + case GBA_REGION_OAM: cpu->memory.activeRegion = (uint32_t*) gba->video.oam.raw; - cpu->memory.activeMask = SIZE_OAM - 1; + cpu->memory.activeMask = GBA_SIZE_OAM - 1; break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: cpu->memory.activeRegion = memory->rom; cpu->memory.activeMask = memory->romMask; - if ((address & (SIZE_CART0 - 1)) < memory->romSize) { + if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { break; } if ((address & 0x00FFFFFE) == AGB_PRINT_FLUSH_ADDR && memory->agbPrintProtect == 0x20) { @@ -362,8 +362,8 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { value = GBALoadBad(cpu); #define LOAD_BIOS \ - if (address < SIZE_BIOS) { \ - if (memory->activeRegion == REGION_BIOS) { \ + if (address < GBA_SIZE_BIOS) { \ + if (memory->activeRegion == GBA_REGION_BIOS) { \ LOAD_32(value, address & -4, memory->bios); \ } else { \ mLOG(GBA_MEM, GAME_ERROR, "Bad BIOS Load32: 0x%08X", address); \ @@ -374,20 +374,20 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { value = GBALoadBad(cpu); \ } -#define LOAD_WORKING_RAM \ - LOAD_32(value, address & (SIZE_WORKING_RAM - 4), memory->wram); \ - wait += waitstatesRegion[REGION_WORKING_RAM]; +#define LOAD_EWRAM \ + LOAD_32(value, address & (GBA_SIZE_EWRAM - 4), memory->wram); \ + wait += waitstatesRegion[GBA_REGION_EWRAM]; -#define LOAD_WORKING_IRAM LOAD_32(value, address & (SIZE_WORKING_IRAM - 4), memory->iwram); +#define LOAD_IWRAM LOAD_32(value, address & (GBA_SIZE_IWRAM - 4), memory->iwram); #define LOAD_IO value = GBAIORead(gba, address & OFFSET_MASK & ~3) | (GBAIORead(gba, (address & OFFSET_MASK & ~1) | 2) << 16); #define LOAD_PALETTE_RAM \ - LOAD_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ - wait += waitstatesRegion[REGION_PALETTE_RAM]; + LOAD_32(value, address & (GBA_SIZE_PALETTE_RAM - 4), gba->video.palette); \ + wait += waitstatesRegion[GBA_REGION_PALETTE_RAM]; #define LOAD_VRAM \ - if ((address & 0x0001FFFF) >= SIZE_VRAM) { \ - if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \ + if ((address & 0x0001FFFF) >= GBA_SIZE_VRAM) { \ + if ((address & (GBA_SIZE_VRAM | 0x00014000)) == GBA_SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \ mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load32: 0x%08X", address); \ value = 0; \ } else { \ @@ -397,16 +397,16 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \ } \ ++wait; \ - if (gba->video.shouldStall) { \ + if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { \ wait += GBAMemoryStallVRAM(gba, wait, 1); \ } -#define LOAD_OAM LOAD_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); +#define LOAD_OAM LOAD_32(value, address & (GBA_SIZE_OAM - 4), gba->video.oam.raw); #define LOAD_CART \ wait += waitstatesRegion[address >> BASE_OFFSET]; \ - if ((address & (SIZE_CART0 - 1)) < memory->romSize) { \ - LOAD_32(value, address & (SIZE_CART0 - 4), memory->rom); \ + if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { \ + LOAD_32(value, address & (GBA_SIZE_ROM0 - 4), memory->rom); \ } else if (memory->vfame.cartType) { \ value = GBAVFameGetPatternValue(address, 32); \ } else { \ @@ -431,13 +431,13 @@ uint32_t GBALoadBad(struct ARMCore* cpu) { if (cpu->executionMode == MODE_THUMB) { /* http://ngemu.com/threads/gba-open-bus.170809/ */ switch (cpu->gprs[ARM_PC] >> BASE_OFFSET) { - case REGION_BIOS: - case REGION_OAM: + case GBA_REGION_BIOS: + case GBA_REGION_OAM: /* This isn't right half the time, but we don't have $+6 handy */ value <<= 16; value |= cpu->prefetch[0]; break; - case REGION_WORKING_IRAM: + case GBA_REGION_IWRAM: /* This doesn't handle prefetch clobbering */ if (cpu->gprs[ARM_PC] & 2) { value <<= 16; @@ -462,37 +462,37 @@ uint32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { char* waitstatesRegion = memory->waitstatesNonseq32; switch (address >> BASE_OFFSET) { - case REGION_BIOS: + case GBA_REGION_BIOS: LOAD_BIOS; break; - case REGION_WORKING_RAM: - LOAD_WORKING_RAM; + case GBA_REGION_EWRAM: + LOAD_EWRAM; break; - case REGION_WORKING_IRAM: - LOAD_WORKING_IRAM; + case GBA_REGION_IWRAM: + LOAD_IWRAM; break; - case REGION_IO: + case GBA_REGION_IO: LOAD_IO; break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: LOAD_PALETTE_RAM; break; - case REGION_VRAM: + case GBA_REGION_VRAM: LOAD_VRAM; break; - case REGION_OAM: + case GBA_REGION_OAM: LOAD_OAM; break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: LOAD_CART; break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: LOAD_SRAM; break; default: @@ -503,7 +503,7 @@ uint32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { if (cycleCounter) { wait += 2; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -520,9 +520,9 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { int wait = 0; switch (address >> BASE_OFFSET) { - case REGION_BIOS: - if (address < SIZE_BIOS) { - if (memory->activeRegion == REGION_BIOS) { + case GBA_REGION_BIOS: + if (address < GBA_SIZE_BIOS) { + if (memory->activeRegion == GBA_REGION_BIOS) { LOAD_16(value, address & -2, memory->bios); } else { mLOG(GBA_MEM, GAME_ERROR, "Bad BIOS Load16: 0x%08X", address); @@ -533,22 +533,22 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = (GBALoadBad(cpu) >> ((address & 2) * 8)) & 0xFFFF; } break; - case REGION_WORKING_RAM: - LOAD_16(value, address & (SIZE_WORKING_RAM - 2), memory->wram); - wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; + case GBA_REGION_EWRAM: + LOAD_16(value, address & (GBA_SIZE_EWRAM - 2), memory->wram); + wait = memory->waitstatesNonseq16[GBA_REGION_EWRAM]; break; - case REGION_WORKING_IRAM: - LOAD_16(value, address & (SIZE_WORKING_IRAM - 2), memory->iwram); + case GBA_REGION_IWRAM: + LOAD_16(value, address & (GBA_SIZE_IWRAM - 2), memory->iwram); break; - case REGION_IO: + case GBA_REGION_IO: value = GBAIORead(gba, address & (OFFSET_MASK - 1)); break; - case REGION_PALETTE_RAM: - LOAD_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); + case GBA_REGION_PALETTE_RAM: + LOAD_16(value, address & (GBA_SIZE_PALETTE_RAM - 2), gba->video.palette); break; - case REGION_VRAM: - if ((address & 0x0001FFFF) >= SIZE_VRAM) { - if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { + case GBA_REGION_VRAM: + if ((address & 0x0001FFFF) >= GBA_SIZE_VRAM) { + if ((address & (GBA_SIZE_VRAM | 0x00014000)) == GBA_SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load16: 0x%08X", address); value = 0; break; @@ -557,24 +557,24 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } else { LOAD_16(value, address & 0x0001FFFE, gba->video.vram); } - if (gba->video.shouldStall) { + if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { wait += GBAMemoryStallVRAM(gba, wait, 0); } break; - case REGION_OAM: - LOAD_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); + case GBA_REGION_OAM: + LOAD_16(value, address & (GBA_SIZE_OAM - 2), gba->video.oam.raw); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; - if ((address & (SIZE_CART0 - 1)) < memory->romSize) { - LOAD_16(value, address & (SIZE_CART0 - 2), memory->rom); + if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { + LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom); } else if (memory->vfame.cartType) { value = GBAVFameGetPatternValue(address, 16); - } else if ((address & (SIZE_CART0 - 1)) >= AGB_PRINT_BASE) { + } else if ((address & (GBA_SIZE_ROM0 - 1)) >= AGB_PRINT_BASE) { uint32_t agbPrintAddr = address & 0x00FFFFFF; if (agbPrintAddr == AGB_PRINT_PROTECT) { value = memory->agbPrintProtect; @@ -589,14 +589,14 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = (address >> 1) & 0xFFFF; } break; - case REGION_CART2_EX: + case GBA_REGION_ROM2_EX: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; if (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512) { value = GBASavedataReadEEPROM(&memory->savedata); } else if ((address & 0x0DFC0000) >= 0x0DF80000 && memory->hw.devices & HW_EREADER) { value = GBACartEReaderRead(&memory->ereader, address); - } else if ((address & (SIZE_CART0 - 1)) < memory->romSize) { - LOAD_16(value, address & (SIZE_CART0 - 2), memory->rom); + } else if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { + LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom); } else if (memory->vfame.cartType) { value = GBAVFameGetPatternValue(address, 16); } else { @@ -604,8 +604,8 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = (address >> 1) & 0xFFFF; } break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; value = GBALoad8(cpu, address, 0); value |= value << 8; @@ -618,7 +618,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { if (cycleCounter) { wait += 2; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -635,9 +635,9 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { int wait = 0; switch (address >> BASE_OFFSET) { - case REGION_BIOS: - if (address < SIZE_BIOS) { - if (memory->activeRegion == REGION_BIOS) { + case GBA_REGION_BIOS: + if (address < GBA_SIZE_BIOS) { + if (memory->activeRegion == GBA_REGION_BIOS) { value = ((uint8_t*) memory->bios)[address]; } else { mLOG(GBA_MEM, GAME_ERROR, "Bad BIOS Load8: 0x%08X", address); @@ -648,22 +648,22 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = (GBALoadBad(cpu) >> ((address & 3) * 8)) & 0xFF; } break; - case REGION_WORKING_RAM: - value = ((uint8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)]; - wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; + case GBA_REGION_EWRAM: + value = ((uint8_t*) memory->wram)[address & (GBA_SIZE_EWRAM - 1)]; + wait = memory->waitstatesNonseq16[GBA_REGION_EWRAM]; break; - case REGION_WORKING_IRAM: - value = ((uint8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)]; + case GBA_REGION_IWRAM: + value = ((uint8_t*) memory->iwram)[address & (GBA_SIZE_IWRAM - 1)]; break; - case REGION_IO: + case GBA_REGION_IO: value = (GBAIORead(gba, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF; break; - case REGION_PALETTE_RAM: - value = ((uint8_t*) gba->video.palette)[address & (SIZE_PALETTE_RAM - 1)]; + case GBA_REGION_PALETTE_RAM: + value = ((uint8_t*) gba->video.palette)[address & (GBA_SIZE_PALETTE_RAM - 1)]; break; - case REGION_VRAM: - if ((address & 0x0001FFFF) >= SIZE_VRAM) { - if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { + case GBA_REGION_VRAM: + if ((address & 0x0001FFFF) >= GBA_SIZE_VRAM) { + if ((address & (GBA_SIZE_VRAM | 0x00014000)) == GBA_SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load8: 0x%08X", address); value = 0; break; @@ -676,18 +676,18 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { wait += GBAMemoryStallVRAM(gba, wait, 0); } break; - case REGION_OAM: - value = ((uint8_t*) gba->video.oam.raw)[address & (SIZE_OAM - 1)]; + case GBA_REGION_OAM: + value = ((uint8_t*) gba->video.oam.raw)[address & (GBA_SIZE_OAM - 1)]; break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; - if ((address & (SIZE_CART0 - 1)) < memory->romSize) { - value = ((uint8_t*) memory->rom)[address & (SIZE_CART0 - 1)]; + if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { + value = ((uint8_t*) memory->rom)[address & (GBA_SIZE_ROM0 - 1)]; } else if (memory->vfame.cartType) { value = GBAVFameGetPatternValue(address, 8); } else { @@ -695,8 +695,8 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = ((address >> 1) >> ((address & 1) * 8)) & 0xFF; } break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; if (memory->savedata.type == SAVEDATA_AUTODETECT) { mLOG(GBA_MEM, INFO, "Detected SRAM savegame"); @@ -708,13 +708,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) { - value = memory->savedata.data[address & (SIZE_CART_SRAM - 1)]; + value = memory->savedata.data[address & (GBA_SIZE_SRAM - 1)]; } else if (memory->savedata.type == SAVEDATA_FLASH512 || memory->savedata.type == 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) { - value = memory->savedata.data[address & (SIZE_CART_SRAM512 - 1)]; + value = memory->savedata.data[address & (GBA_SIZE_SRAM512 - 1)]; } else { mLOG(GBA_MEM, GAME_ERROR, "Reading from non-existent SRAM: 0x%08X", address); value = 0xFF; @@ -729,7 +729,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { if (cycleCounter) { wait += 2; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -737,28 +737,28 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { return value; } -#define STORE_WORKING_RAM \ - STORE_32(value, address & (SIZE_WORKING_RAM - 4), memory->wram); \ - wait += waitstatesRegion[REGION_WORKING_RAM]; +#define STORE_EWRAM \ + STORE_32(value, address & (GBA_SIZE_EWRAM - 4), memory->wram); \ + wait += waitstatesRegion[GBA_REGION_EWRAM]; -#define STORE_WORKING_IRAM \ - STORE_32(value, address & (SIZE_WORKING_IRAM - 4), memory->iwram); +#define STORE_IWRAM \ + STORE_32(value, address & (GBA_SIZE_IWRAM - 4), memory->iwram); #define STORE_IO \ GBAIOWrite32(gba, address & (OFFSET_MASK - 3), value); #define STORE_PALETTE_RAM \ - LOAD_32(oldValue, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ + LOAD_32(oldValue, address & (GBA_SIZE_PALETTE_RAM - 4), gba->video.palette); \ if (oldValue != value) { \ - STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ - gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \ - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value); \ + STORE_32(value, address & (GBA_SIZE_PALETTE_RAM - 4), gba->video.palette); \ + gba->video.renderer->writePalette(gba->video.renderer, (address & (GBA_SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \ + gba->video.renderer->writePalette(gba->video.renderer, address & (GBA_SIZE_PALETTE_RAM - 4), value); \ } \ - wait += waitstatesRegion[REGION_PALETTE_RAM]; + wait += waitstatesRegion[GBA_REGION_PALETTE_RAM]; #define STORE_VRAM \ - if ((address & 0x0001FFFF) >= SIZE_VRAM) { \ - if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \ + if ((address & 0x0001FFFF) >= GBA_SIZE_VRAM) { \ + if ((address & (GBA_SIZE_VRAM | 0x00014000)) == GBA_SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \ mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Store32: 0x%08X", address); \ } else { \ LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); \ @@ -777,16 +777,16 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } \ } \ ++wait; \ - if (gba->video.shouldStall) { \ + if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { \ wait += GBAMemoryStallVRAM(gba, wait, 1); \ } #define STORE_OAM \ - LOAD_32(oldValue, address & (SIZE_OAM - 4), gba->video.oam.raw); \ + LOAD_32(oldValue, address & (GBA_SIZE_OAM - 4), gba->video.oam.raw); \ if (oldValue != value) { \ - STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); \ - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); \ - gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1); \ + STORE_32(value, address & (GBA_SIZE_OAM - 4), gba->video.oam.raw); \ + gba->video.renderer->writeOAM(gba->video.renderer, (address & (GBA_SIZE_OAM - 4)) >> 1); \ + gba->video.renderer->writeOAM(gba->video.renderer, ((address & (GBA_SIZE_OAM - 4)) >> 1) + 1); \ } #define STORE_CART \ @@ -818,34 +818,34 @@ void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle char* waitstatesRegion = memory->waitstatesNonseq32; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - STORE_WORKING_RAM; + case GBA_REGION_EWRAM: + STORE_EWRAM; break; - case REGION_WORKING_IRAM: - STORE_WORKING_IRAM + case GBA_REGION_IWRAM: + STORE_IWRAM break; - case REGION_IO: + case GBA_REGION_IO: STORE_IO; break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: STORE_PALETTE_RAM; break; - case REGION_VRAM: + case GBA_REGION_VRAM: STORE_VRAM; break; - case REGION_OAM: + case GBA_REGION_OAM: STORE_OAM; break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: STORE_CART; break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: STORE_SRAM; break; default: @@ -855,7 +855,7 @@ void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle if (cycleCounter) { ++wait; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -869,26 +869,26 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle int16_t oldValue; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - STORE_16(value, address & (SIZE_WORKING_RAM - 2), memory->wram); - wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; + case GBA_REGION_EWRAM: + STORE_16(value, address & (GBA_SIZE_EWRAM - 2), memory->wram); + wait = memory->waitstatesNonseq16[GBA_REGION_EWRAM]; break; - case REGION_WORKING_IRAM: - STORE_16(value, address & (SIZE_WORKING_IRAM - 2), memory->iwram); + case GBA_REGION_IWRAM: + STORE_16(value, address & (GBA_SIZE_IWRAM - 2), memory->iwram); break; - case REGION_IO: + case GBA_REGION_IO: GBAIOWrite(gba, address & (OFFSET_MASK - 1), value); break; - case REGION_PALETTE_RAM: - LOAD_16(oldValue, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); + case GBA_REGION_PALETTE_RAM: + LOAD_16(oldValue, address & (GBA_SIZE_PALETTE_RAM - 2), gba->video.palette); if (oldValue != value) { - STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value); + STORE_16(value, address & (GBA_SIZE_PALETTE_RAM - 2), gba->video.palette); + gba->video.renderer->writePalette(gba->video.renderer, address & (GBA_SIZE_PALETTE_RAM - 2), value); } break; - case REGION_VRAM: - if ((address & 0x0001FFFF) >= SIZE_VRAM) { - if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { + case GBA_REGION_VRAM: + if ((address & 0x0001FFFF) >= GBA_SIZE_VRAM) { + if ((address & (GBA_SIZE_VRAM | 0x00014000)) == GBA_SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Store16: 0x%08X", address); break; } @@ -904,18 +904,18 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); } } - if (gba->video.shouldStall) { + if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { wait += GBAMemoryStallVRAM(gba, wait, 0); } break; - case REGION_OAM: - LOAD_16(oldValue, address & (SIZE_OAM - 2), gba->video.oam.raw); + case GBA_REGION_OAM: + LOAD_16(oldValue, address & (GBA_SIZE_OAM - 2), gba->video.oam.raw); if (value != oldValue) { - STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1); + STORE_16(value, address & (GBA_SIZE_OAM - 2), gba->video.oam.raw); + gba->video.renderer->writeOAM(gba->video.renderer, (address & (GBA_SIZE_OAM - 2)) >> 1); } break; - case REGION_CART0: + case GBA_REGION_ROM0: if (IS_GPIO_REGISTER(address & 0xFFFFFE)) { if (memory->hw.devices == HW_NONE) { mLOG(GBA_HW, WARN, "Write to GPIO address %08X on cartridge without GPIO", address); @@ -930,22 +930,22 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle break; } // Fall through - case REGION_CART0_EX: + case GBA_REGION_ROM0_EX: if ((address & 0x00FFFFFF) >= AGB_PRINT_BASE) { uint32_t agbPrintAddr = address & 0x00FFFFFF; if (agbPrintAddr == AGB_PRINT_PROTECT) { memory->agbPrintProtect = value; if (!memory->agbPrintBuffer) { - memory->agbPrintBuffer = anonymousMemoryMap(SIZE_AGB_PRINT); - if (memory->romSize >= SIZE_CART0 / 2) { + memory->agbPrintBuffer = anonymousMemoryMap(GBA_SIZE_AGB_PRINT); + if (memory->romSize >= GBA_SIZE_ROM0 / 2) { int base = 0; - if (memory->romSize == SIZE_CART0) { + if (memory->romSize == GBA_SIZE_ROM0) { base = address & 0x01000000; } memory->agbPrintBase = base; - memory->agbPrintBufferBackup = anonymousMemoryMap(SIZE_AGB_PRINT); - memcpy(memory->agbPrintBufferBackup, &memory->rom[(AGB_PRINT_TOP | base) >> 2], SIZE_AGB_PRINT); + memory->agbPrintBufferBackup = anonymousMemoryMap(GBA_SIZE_AGB_PRINT); + memcpy(memory->agbPrintBufferBackup, &memory->rom[(AGB_PRINT_TOP | base) >> 2], GBA_SIZE_AGB_PRINT); LOAD_16(memory->agbPrintProtectBackup, AGB_PRINT_PROTECT | base, memory->rom); LOAD_16(memory->agbPrintCtxBackup.request, AGB_PRINT_STRUCT | base, memory->rom); LOAD_16(memory->agbPrintCtxBackup.bank, (AGB_PRINT_STRUCT | base) + 2, memory->rom); @@ -967,7 +967,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle } mLOG(GBA_MEM, GAME_ERROR, "Bad cartridge Store16: 0x%08X", address); break; - case REGION_CART2_EX: + case GBA_REGION_ROM2_EX: if ((address & 0x0DFC0000) >= 0x0DF80000 && memory->hw.devices & HW_EREADER) { GBACartEReaderWrite(&memory->ereader, address, value); break; @@ -981,8 +981,8 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle } mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store16: 0x%08X", address); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: if (address & 1) { mLOG(GBA_MEM, GAME_ERROR, "Unaligned SRAM Store16: 0x%08X", address); break; @@ -997,7 +997,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle if (cycleCounter) { ++wait; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -1011,20 +1011,20 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo uint16_t oldValue; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)] = value; - wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; + case GBA_REGION_EWRAM: + ((int8_t*) memory->wram)[address & (GBA_SIZE_EWRAM - 1)] = value; + wait = memory->waitstatesNonseq16[GBA_REGION_EWRAM]; break; - case REGION_WORKING_IRAM: - ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)] = value; + case GBA_REGION_IWRAM: + ((int8_t*) memory->iwram)[address & (GBA_SIZE_IWRAM - 1)] = value; break; - case REGION_IO: + case GBA_REGION_IO: GBAIOWrite8(gba, address & OFFSET_MASK, value); break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: GBAStore16(cpu, address & ~1, ((uint8_t) value) | ((uint8_t) value << 8), cycleCounter); break; - case REGION_VRAM: + case GBA_REGION_VRAM: if ((address & 0x0001FFFF) >= ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address); break; @@ -1038,14 +1038,14 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo wait += GBAMemoryStallVRAM(gba, wait, 0); } break; - case REGION_OAM: + case GBA_REGION_OAM: mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address); break; - case REGION_CART0: + case GBA_REGION_ROM0: mLOG(GBA_MEM, STUB, "Unimplemented memory Store8: 0x%08X", address); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: if (memory->savedata.type == SAVEDATA_AUTODETECT) { if (address == SAVEDATA_FLASH_BASE) { mLOG(GBA_MEM, INFO, "Detected Flash savegame"); @@ -1063,18 +1063,18 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo if (memory->vfame.cartType) { GBAVFameSramWrite(&memory->vfame, address, value, memory->savedata.data); } else { - memory->savedata.data[address & (SIZE_CART_SRAM - 1)] = value; + memory->savedata.data[address & (GBA_SIZE_SRAM - 1)] = value; } 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) { - memory->savedata.data[address & (SIZE_CART_SRAM512 - 1)] = value; + memory->savedata.data[address & (GBA_SIZE_SRAM512 - 1)] = value; memory->savedata.dirty |= mSAVEDATA_DIRT_NEW; } else { mLOG(GBA_MEM, GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address); } - wait = memory->waitstatesNonseq16[REGION_CART_SRAM]; + wait = memory->waitstatesNonseq16[GBA_REGION_SRAM]; break; default: mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store8: 0x%08X", address); @@ -1083,7 +1083,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo if (cycleCounter) { ++wait; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -1095,31 +1095,31 @@ uint32_t GBAView32(struct ARMCore* cpu, uint32_t address) { uint32_t value = 0; address &= ~3; switch (address >> BASE_OFFSET) { - case REGION_BIOS: - if (address < SIZE_BIOS) { + case GBA_REGION_BIOS: + if (address < GBA_SIZE_BIOS) { LOAD_32(value, address, gba->memory.bios); } break; - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_PALETTE_RAM: - case REGION_VRAM: - case REGION_OAM: - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_PALETTE_RAM: + case GBA_REGION_VRAM: + case GBA_REGION_OAM: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: value = GBALoad32(cpu, address, 0); break; - case REGION_IO: + case GBA_REGION_IO: if ((address & OFFSET_MASK) < REG_MAX) { value = gba->memory.io[(address & OFFSET_MASK) >> 1]; value |= gba->memory.io[((address & OFFSET_MASK) >> 1) + 1] << 16; } break; - case REGION_CART_SRAM: + case GBA_REGION_SRAM: value = GBALoad8(cpu, address, 0); value |= GBALoad8(cpu, address + 1, 0) << 8; value |= GBALoad8(cpu, address + 2, 0) << 16; @@ -1136,30 +1136,30 @@ uint16_t GBAView16(struct ARMCore* cpu, uint32_t address) { uint16_t value = 0; address &= ~1; switch (address >> BASE_OFFSET) { - case REGION_BIOS: - if (address < SIZE_BIOS) { + case GBA_REGION_BIOS: + if (address < GBA_SIZE_BIOS) { LOAD_16(value, address, gba->memory.bios); } break; - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_PALETTE_RAM: - case REGION_VRAM: - case REGION_OAM: - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_PALETTE_RAM: + case GBA_REGION_VRAM: + case GBA_REGION_OAM: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: value = GBALoad16(cpu, address, 0); break; - case REGION_IO: + case GBA_REGION_IO: if ((address & OFFSET_MASK) < REG_MAX) { value = gba->memory.io[(address & OFFSET_MASK) >> 1]; } break; - case REGION_CART_SRAM: + case GBA_REGION_SRAM: value = GBALoad8(cpu, address, 0); value |= GBALoad8(cpu, address + 1, 0) << 8; break; @@ -1173,26 +1173,26 @@ uint8_t GBAView8(struct ARMCore* cpu, uint32_t address) { struct GBA* gba = (struct GBA*) cpu->master; uint8_t value = 0; switch (address >> BASE_OFFSET) { - case REGION_BIOS: - if (address < SIZE_BIOS) { + case GBA_REGION_BIOS: + if (address < GBA_SIZE_BIOS) { value = ((uint8_t*) gba->memory.bios)[address]; } break; - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: - case REGION_CART_SRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: + case GBA_REGION_SRAM: value = GBALoad8(cpu, address, 0); break; - case REGION_IO: - case REGION_PALETTE_RAM: - case REGION_VRAM: - case REGION_OAM: + case GBA_REGION_IO: + case GBA_REGION_PALETTE_RAM: + case GBA_REGION_VRAM: + case GBA_REGION_OAM: value = GBAView16(cpu, address) >> ((address & 1) * 8); break; default: @@ -1207,25 +1207,25 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o int32_t oldValue = -1; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - LOAD_32(oldValue, address & (SIZE_WORKING_RAM - 4), memory->wram); - STORE_32(value, address & (SIZE_WORKING_RAM - 4), memory->wram); + case GBA_REGION_EWRAM: + LOAD_32(oldValue, address & (GBA_SIZE_EWRAM - 4), memory->wram); + STORE_32(value, address & (GBA_SIZE_EWRAM - 4), memory->wram); break; - case REGION_WORKING_IRAM: - LOAD_32(oldValue, address & (SIZE_WORKING_IRAM - 4), memory->iwram); - STORE_32(value, address & (SIZE_WORKING_IRAM - 4), memory->iwram); + case GBA_REGION_IWRAM: + LOAD_32(oldValue, address & (GBA_SIZE_IWRAM - 4), memory->iwram); + STORE_32(value, address & (GBA_SIZE_IWRAM - 4), memory->iwram); break; - case REGION_IO: + case GBA_REGION_IO: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch32: 0x%08X", address); break; - case REGION_PALETTE_RAM: - LOAD_32(oldValue, address & (SIZE_PALETTE_RAM - 1), gba->video.palette); - STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value); - gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); + case GBA_REGION_PALETTE_RAM: + LOAD_32(oldValue, address & (GBA_SIZE_PALETTE_RAM - 1), gba->video.palette); + STORE_32(value, address & (GBA_SIZE_PALETTE_RAM - 4), gba->video.palette); + gba->video.renderer->writePalette(gba->video.renderer, address & (GBA_SIZE_PALETTE_RAM - 4), value); + gba->video.renderer->writePalette(gba->video.renderer, (address & (GBA_SIZE_PALETTE_RAM - 4)) + 2, value >> 16); break; - case REGION_VRAM: - if ((address & 0x0001FFFF) < SIZE_VRAM) { + case GBA_REGION_VRAM: + if ((address & 0x0001FFFF) < GBA_SIZE_VRAM) { LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); STORE_32(value, address & 0x0001FFFC, gba->video.vram); gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFC); @@ -1237,31 +1237,31 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) | 2); } break; - case REGION_OAM: - LOAD_32(oldValue, address & (SIZE_OAM - 4), gba->video.oam.raw); - STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); - gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) + 2) >> 1); + case GBA_REGION_OAM: + LOAD_32(oldValue, address & (GBA_SIZE_OAM - 4), gba->video.oam.raw); + STORE_32(value, address & (GBA_SIZE_OAM - 4), gba->video.oam.raw); + gba->video.renderer->writeOAM(gba->video.renderer, (address & (GBA_SIZE_OAM - 4)) >> 1); + gba->video.renderer->writeOAM(gba->video.renderer, ((address & (GBA_SIZE_OAM - 4)) + 2) >> 1); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: _pristineCow(gba); - if ((address & (SIZE_CART0 - 4)) >= gba->memory.romSize) { - gba->memory.romSize = (address & (SIZE_CART0 - 4)) + 4; + if ((address & (GBA_SIZE_ROM0 - 4)) >= gba->memory.romSize) { + gba->memory.romSize = (address & (GBA_SIZE_ROM0 - 4)) + 4; gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } - LOAD_32(oldValue, address & (SIZE_CART0 - 4), gba->memory.rom); - STORE_32(value, address & (SIZE_CART0 - 4), gba->memory.rom); + LOAD_32(oldValue, address & (GBA_SIZE_ROM0 - 4), gba->memory.rom); + STORE_32(value, address & (GBA_SIZE_ROM0 - 4), gba->memory.rom); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: if (memory->savedata.type == SAVEDATA_SRAM) { - LOAD_32(oldValue, address & (SIZE_CART_SRAM - 4), memory->savedata.data); - STORE_32(value, address & (SIZE_CART_SRAM - 4), memory->savedata.data); + LOAD_32(oldValue, address & (GBA_SIZE_SRAM - 4), memory->savedata.data); + STORE_32(value, address & (GBA_SIZE_SRAM - 4), memory->savedata.data); } else { mLOG(GBA_MEM, GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address); } @@ -1281,24 +1281,24 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o int16_t oldValue = -1; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - LOAD_16(oldValue, address & (SIZE_WORKING_RAM - 2), memory->wram); - STORE_16(value, address & (SIZE_WORKING_RAM - 2), memory->wram); + case GBA_REGION_EWRAM: + LOAD_16(oldValue, address & (GBA_SIZE_EWRAM - 2), memory->wram); + STORE_16(value, address & (GBA_SIZE_EWRAM - 2), memory->wram); break; - case REGION_WORKING_IRAM: - LOAD_16(oldValue, address & (SIZE_WORKING_IRAM - 2), memory->iwram); - STORE_16(value, address & (SIZE_WORKING_IRAM - 2), memory->iwram); + case GBA_REGION_IWRAM: + LOAD_16(oldValue, address & (GBA_SIZE_IWRAM - 2), memory->iwram); + STORE_16(value, address & (GBA_SIZE_IWRAM - 2), memory->iwram); break; - case REGION_IO: + case GBA_REGION_IO: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch16: 0x%08X", address); break; - case REGION_PALETTE_RAM: - LOAD_16(oldValue, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); - STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value); + case GBA_REGION_PALETTE_RAM: + LOAD_16(oldValue, address & (GBA_SIZE_PALETTE_RAM - 2), gba->video.palette); + STORE_16(value, address & (GBA_SIZE_PALETTE_RAM - 2), gba->video.palette); + gba->video.renderer->writePalette(gba->video.renderer, address & (GBA_SIZE_PALETTE_RAM - 2), value); break; - case REGION_VRAM: - if ((address & 0x0001FFFF) < SIZE_VRAM) { + case GBA_REGION_VRAM: + if ((address & 0x0001FFFF) < GBA_SIZE_VRAM) { LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram); STORE_16(value, address & 0x0001FFFE, gba->video.vram); gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); @@ -1308,30 +1308,30 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); } break; - case REGION_OAM: - LOAD_16(oldValue, address & (SIZE_OAM - 2), gba->video.oam.raw); - STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1); + case GBA_REGION_OAM: + LOAD_16(oldValue, address & (GBA_SIZE_OAM - 2), gba->video.oam.raw); + STORE_16(value, address & (GBA_SIZE_OAM - 2), gba->video.oam.raw); + gba->video.renderer->writeOAM(gba->video.renderer, (address & (GBA_SIZE_OAM - 2)) >> 1); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: _pristineCow(gba); - if ((address & (SIZE_CART0 - 1)) >= gba->memory.romSize) { - gba->memory.romSize = (address & (SIZE_CART0 - 2)) + 2; + if ((address & (GBA_SIZE_ROM0 - 1)) >= gba->memory.romSize) { + gba->memory.romSize = (address & (GBA_SIZE_ROM0 - 2)) + 2; gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } - LOAD_16(oldValue, address & (SIZE_CART0 - 2), gba->memory.rom); - STORE_16(value, address & (SIZE_CART0 - 2), gba->memory.rom); + LOAD_16(oldValue, address & (GBA_SIZE_ROM0 - 2), gba->memory.rom); + STORE_16(value, address & (GBA_SIZE_ROM0 - 2), gba->memory.rom); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: if (memory->savedata.type == SAVEDATA_SRAM) { - LOAD_16(oldValue, address & (SIZE_CART_SRAM - 2), memory->savedata.data); - STORE_16(value, address & (SIZE_CART_SRAM - 2), memory->savedata.data); + LOAD_16(oldValue, address & (GBA_SIZE_SRAM - 2), memory->savedata.data); + STORE_16(value, address & (GBA_SIZE_SRAM - 2), memory->savedata.data); } else { mLOG(GBA_MEM, GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address); } @@ -1351,45 +1351,45 @@ void GBAPatch8(struct ARMCore* cpu, uint32_t address, int8_t value, int8_t* old) int8_t oldValue = -1; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - oldValue = ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)]; - ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)] = value; + case GBA_REGION_EWRAM: + oldValue = ((int8_t*) memory->wram)[address & (GBA_SIZE_EWRAM - 1)]; + ((int8_t*) memory->wram)[address & (GBA_SIZE_EWRAM - 1)] = value; break; - case REGION_WORKING_IRAM: - oldValue = ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)]; - ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)] = value; + case GBA_REGION_IWRAM: + oldValue = ((int8_t*) memory->iwram)[address & (GBA_SIZE_IWRAM - 1)]; + ((int8_t*) memory->iwram)[address & (GBA_SIZE_IWRAM - 1)] = value; break; - case REGION_IO: + case GBA_REGION_IO: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address); break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address); break; - case REGION_VRAM: + case GBA_REGION_VRAM: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address); break; - case REGION_OAM: + case GBA_REGION_OAM: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: _pristineCow(gba); - if ((address & (SIZE_CART0 - 1)) >= gba->memory.romSize) { - gba->memory.romSize = (address & (SIZE_CART0 - 2)) + 2; + if ((address & (GBA_SIZE_ROM0 - 1)) >= gba->memory.romSize) { + gba->memory.romSize = (address & (GBA_SIZE_ROM0 - 2)) + 2; gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } - oldValue = ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)]; - ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)] = value; + oldValue = ((int8_t*) memory->rom)[address & (GBA_SIZE_ROM0 - 1)]; + ((int8_t*) memory->rom)[address & (GBA_SIZE_ROM0 - 1)] = value; break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: if (memory->savedata.type == SAVEDATA_SRAM) { - oldValue = ((int8_t*) memory->savedata.data)[address & (SIZE_CART_SRAM - 1)]; - ((int8_t*) memory->savedata.data)[address & (SIZE_CART_SRAM - 1)] = value; + oldValue = ((int8_t*) memory->savedata.data)[address & (GBA_SIZE_SRAM - 1)]; + ((int8_t*) memory->savedata.data)[address & (GBA_SIZE_SRAM - 1)] = value; } else { mLOG(GBA_MEM, GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address); } @@ -1458,43 +1458,43 @@ uint32_t GBALoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum L uint32_t addressMisalign = address & 0x3; int region = address >> BASE_OFFSET; - if (region < REGION_CART_SRAM) { + if (region < GBA_REGION_SRAM) { address &= 0xFFFFFFFC; } int wait = memory->waitstatesSeq32[region] - memory->waitstatesNonseq32[region]; switch (region) { - case REGION_BIOS: + case GBA_REGION_BIOS: LDM_LOOP(LOAD_BIOS); break; - case REGION_WORKING_RAM: - LDM_LOOP(LOAD_WORKING_RAM); + case GBA_REGION_EWRAM: + LDM_LOOP(LOAD_EWRAM); break; - case REGION_WORKING_IRAM: - LDM_LOOP(LOAD_WORKING_IRAM); + case GBA_REGION_IWRAM: + LDM_LOOP(LOAD_IWRAM); break; - case REGION_IO: + case GBA_REGION_IO: LDM_LOOP(LOAD_IO); break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: LDM_LOOP(LOAD_PALETTE_RAM); break; - case REGION_VRAM: + case GBA_REGION_VRAM: LDM_LOOP(LOAD_VRAM); break; - case REGION_OAM: + case GBA_REGION_OAM: LDM_LOOP(LOAD_OAM); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: LDM_LOOP(LOAD_CART); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: LDM_LOOP(LOAD_SRAM); break; default: @@ -1504,7 +1504,7 @@ uint32_t GBALoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum L if (cycleCounter) { ++wait; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -1580,40 +1580,40 @@ uint32_t GBAStoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum uint32_t addressMisalign = address & 0x3; int region = address >> BASE_OFFSET; - if (region < REGION_CART_SRAM) { + if (region < GBA_REGION_SRAM) { address &= 0xFFFFFFFC; } int wait = memory->waitstatesSeq32[region] - memory->waitstatesNonseq32[region]; switch (region) { - case REGION_WORKING_RAM: - STM_LOOP(STORE_WORKING_RAM); + case GBA_REGION_EWRAM: + STM_LOOP(STORE_EWRAM); break; - case REGION_WORKING_IRAM: - STM_LOOP(STORE_WORKING_IRAM); + case GBA_REGION_IWRAM: + STM_LOOP(STORE_IWRAM); break; - case REGION_IO: + case GBA_REGION_IO: STM_LOOP(STORE_IO); break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: STM_LOOP(STORE_PALETTE_RAM); break; - case REGION_VRAM: + case GBA_REGION_VRAM: STM_LOOP(STORE_VRAM); break; - case REGION_OAM: + case GBA_REGION_OAM: STM_LOOP(STORE_OAM); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: STM_LOOP(STORE_CART); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: STM_LOOP(STORE_SRAM); break; default: @@ -1622,7 +1622,7 @@ uint32_t GBAStoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum } if (cycleCounter) { - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -1651,26 +1651,26 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { int ws2seq = (parameters & 0x0400) >> 10; int prefetch = parameters & 0x4000; - memory->waitstatesNonseq16[REGION_CART_SRAM] = memory->waitstatesNonseq16[REGION_CART_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; - memory->waitstatesSeq16[REGION_CART_SRAM] = memory->waitstatesSeq16[REGION_CART_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; - memory->waitstatesNonseq32[REGION_CART_SRAM] = memory->waitstatesNonseq32[REGION_CART_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; - memory->waitstatesSeq32[REGION_CART_SRAM] = memory->waitstatesSeq32[REGION_CART_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; + memory->waitstatesNonseq16[GBA_REGION_SRAM] = memory->waitstatesNonseq16[GBA_REGION_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; + memory->waitstatesSeq16[GBA_REGION_SRAM] = memory->waitstatesSeq16[GBA_REGION_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; + memory->waitstatesNonseq32[GBA_REGION_SRAM] = memory->waitstatesNonseq32[GBA_REGION_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; + memory->waitstatesSeq32[GBA_REGION_SRAM] = memory->waitstatesSeq32[GBA_REGION_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; - memory->waitstatesNonseq16[REGION_CART0] = memory->waitstatesNonseq16[REGION_CART0_EX] = GBA_ROM_WAITSTATES[ws0]; - memory->waitstatesNonseq16[REGION_CART1] = memory->waitstatesNonseq16[REGION_CART1_EX] = GBA_ROM_WAITSTATES[ws1]; - memory->waitstatesNonseq16[REGION_CART2] = memory->waitstatesNonseq16[REGION_CART2_EX] = GBA_ROM_WAITSTATES[ws2]; + memory->waitstatesNonseq16[GBA_REGION_ROM0] = memory->waitstatesNonseq16[GBA_REGION_ROM0_EX] = GBA_ROM_WAITSTATES[ws0]; + memory->waitstatesNonseq16[GBA_REGION_ROM1] = memory->waitstatesNonseq16[GBA_REGION_ROM1_EX] = GBA_ROM_WAITSTATES[ws1]; + memory->waitstatesNonseq16[GBA_REGION_ROM2] = memory->waitstatesNonseq16[GBA_REGION_ROM2_EX] = GBA_ROM_WAITSTATES[ws2]; - memory->waitstatesSeq16[REGION_CART0] = memory->waitstatesSeq16[REGION_CART0_EX] = GBA_ROM_WAITSTATES_SEQ[ws0seq]; - memory->waitstatesSeq16[REGION_CART1] = memory->waitstatesSeq16[REGION_CART1_EX] = GBA_ROM_WAITSTATES_SEQ[ws1seq + 2]; - memory->waitstatesSeq16[REGION_CART2] = memory->waitstatesSeq16[REGION_CART2_EX] = GBA_ROM_WAITSTATES_SEQ[ws2seq + 4]; + memory->waitstatesSeq16[GBA_REGION_ROM0] = memory->waitstatesSeq16[GBA_REGION_ROM0_EX] = GBA_ROM_WAITSTATES_SEQ[ws0seq]; + memory->waitstatesSeq16[GBA_REGION_ROM1] = memory->waitstatesSeq16[GBA_REGION_ROM1_EX] = GBA_ROM_WAITSTATES_SEQ[ws1seq + 2]; + memory->waitstatesSeq16[GBA_REGION_ROM2] = memory->waitstatesSeq16[GBA_REGION_ROM2_EX] = GBA_ROM_WAITSTATES_SEQ[ws2seq + 4]; - memory->waitstatesNonseq32[REGION_CART0] = memory->waitstatesNonseq32[REGION_CART0_EX] = memory->waitstatesNonseq16[REGION_CART0] + 1 + memory->waitstatesSeq16[REGION_CART0]; - memory->waitstatesNonseq32[REGION_CART1] = memory->waitstatesNonseq32[REGION_CART1_EX] = memory->waitstatesNonseq16[REGION_CART1] + 1 + memory->waitstatesSeq16[REGION_CART1]; - memory->waitstatesNonseq32[REGION_CART2] = memory->waitstatesNonseq32[REGION_CART2_EX] = memory->waitstatesNonseq16[REGION_CART2] + 1 + memory->waitstatesSeq16[REGION_CART2]; + memory->waitstatesNonseq32[GBA_REGION_ROM0] = memory->waitstatesNonseq32[GBA_REGION_ROM0_EX] = memory->waitstatesNonseq16[GBA_REGION_ROM0] + 1 + memory->waitstatesSeq16[GBA_REGION_ROM0]; + memory->waitstatesNonseq32[GBA_REGION_ROM1] = memory->waitstatesNonseq32[GBA_REGION_ROM1_EX] = memory->waitstatesNonseq16[GBA_REGION_ROM1] + 1 + memory->waitstatesSeq16[GBA_REGION_ROM1]; + memory->waitstatesNonseq32[GBA_REGION_ROM2] = memory->waitstatesNonseq32[GBA_REGION_ROM2_EX] = memory->waitstatesNonseq16[GBA_REGION_ROM2] + 1 + memory->waitstatesSeq16[GBA_REGION_ROM2]; - memory->waitstatesSeq32[REGION_CART0] = memory->waitstatesSeq32[REGION_CART0_EX] = 2 * memory->waitstatesSeq16[REGION_CART0] + 1; - memory->waitstatesSeq32[REGION_CART1] = memory->waitstatesSeq32[REGION_CART1_EX] = 2 * memory->waitstatesSeq16[REGION_CART1] + 1; - memory->waitstatesSeq32[REGION_CART2] = memory->waitstatesSeq32[REGION_CART2_EX] = 2 * memory->waitstatesSeq16[REGION_CART2] + 1; + memory->waitstatesSeq32[GBA_REGION_ROM0] = memory->waitstatesSeq32[GBA_REGION_ROM0_EX] = 2 * memory->waitstatesSeq16[GBA_REGION_ROM0] + 1; + memory->waitstatesSeq32[GBA_REGION_ROM1] = memory->waitstatesSeq32[GBA_REGION_ROM1_EX] = 2 * memory->waitstatesSeq16[GBA_REGION_ROM1] + 1; + memory->waitstatesSeq32[GBA_REGION_ROM2] = memory->waitstatesSeq32[GBA_REGION_ROM2_EX] = 2 * memory->waitstatesSeq16[GBA_REGION_ROM2] + 1; memory->prefetch = prefetch; @@ -1684,7 +1684,7 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { int phi = (parameters >> 11) & 3; uint32_t base = memory->agbPrintBase; if (phi == 3) { - memcpy(&memory->rom[(AGB_PRINT_TOP | base) >> 2], memory->agbPrintBuffer, SIZE_AGB_PRINT); + memcpy(&memory->rom[(AGB_PRINT_TOP | base) >> 2], memory->agbPrintBuffer, GBA_SIZE_AGB_PRINT); STORE_16(memory->agbPrintProtect, AGB_PRINT_PROTECT | base, memory->rom); STORE_16(memory->agbPrintCtx.request, AGB_PRINT_STRUCT | base, memory->rom); STORE_16(memory->agbPrintCtx.bank, (AGB_PRINT_STRUCT | base) + 2, memory->rom); @@ -1692,7 +1692,7 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { STORE_16(memory->agbPrintCtx.put, (AGB_PRINT_STRUCT | base) + 6, memory->rom); STORE_32(_agbPrintFunc, AGB_PRINT_FLUSH_ADDR | base, memory->rom); } else { - memcpy(&memory->rom[(AGB_PRINT_TOP | base) >> 2], memory->agbPrintBufferBackup, SIZE_AGB_PRINT); + memcpy(&memory->rom[(AGB_PRINT_TOP | base) >> 2], memory->agbPrintBufferBackup, GBA_SIZE_AGB_PRINT); STORE_16(memory->agbPrintProtectBackup, AGB_PRINT_PROTECT | base, memory->rom); STORE_16(memory->agbPrintCtxBackup.request, AGB_PRINT_STRUCT | base, memory->rom); STORE_16(memory->agbPrintCtxBackup.bank, (AGB_PRINT_STRUCT | base) + 2, memory->rom); @@ -1709,10 +1709,10 @@ void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters) { int wait = 15 - ((parameters >> 8) & 0xF); if (wait) { - memory->waitstatesNonseq16[REGION_WORKING_RAM] = wait; - memory->waitstatesSeq16[REGION_WORKING_RAM] = wait; - memory->waitstatesNonseq32[REGION_WORKING_RAM] = 2 * wait + 1; - memory->waitstatesSeq32[REGION_WORKING_RAM] = 2 * wait + 1; + memory->waitstatesNonseq16[GBA_REGION_EWRAM] = wait; + memory->waitstatesSeq16[GBA_REGION_EWRAM] = wait; + memory->waitstatesNonseq32[GBA_REGION_EWRAM] = 2 * wait + 1; + memory->waitstatesSeq32[GBA_REGION_EWRAM] = 2 * wait + 1; cpu->memory.activeSeqCycles32 = memory->waitstatesSeq32[memory->activeRegion]; cpu->memory.activeSeqCycles16 = memory->waitstatesSeq16[memory->activeRegion]; @@ -1732,7 +1732,7 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; - if (memory->activeRegion < REGION_CART0 || !memory->prefetch) { + if (memory->activeRegion < GBA_REGION_ROM0 || !memory->prefetch) { // The wait is the stall return wait; } @@ -1797,13 +1797,13 @@ int32_t GBAMemoryStallVRAM(struct GBA* gba, int32_t wait, int extra) { } void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state) { - memcpy(state->wram, memory->wram, SIZE_WORKING_RAM); - memcpy(state->iwram, memory->iwram, SIZE_WORKING_IRAM); + memcpy(state->wram, memory->wram, GBA_SIZE_EWRAM); + memcpy(state->iwram, memory->iwram, GBA_SIZE_IWRAM); } void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state) { - memcpy(memory->wram, state->wram, SIZE_WORKING_RAM); - memcpy(memory->iwram, state->iwram, SIZE_WORKING_IRAM); + memcpy(memory->wram, state->wram, GBA_SIZE_EWRAM); + memcpy(memory->iwram, state->iwram, GBA_SIZE_IWRAM); } void _pristineCow(struct GBA* gba) { @@ -1811,9 +1811,9 @@ void _pristineCow(struct GBA* gba) { return; } #if !defined(FIXED_ROM_BUFFER) && !defined(__wii__) - void* newRom = anonymousMemoryMap(SIZE_CART0); + void* newRom = anonymousMemoryMap(GBA_SIZE_ROM0); memcpy(newRom, gba->memory.rom, gba->memory.romSize); - memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize); + memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, GBA_SIZE_ROM0 - gba->memory.romSize); if (gba->cpu->memory.activeRegion == gba->memory.rom) { gba->cpu->memory.activeRegion = newRom; } @@ -1855,16 +1855,16 @@ void GBAPrintFlush(struct GBA* gba) { static void _agbPrintStore(struct GBA* gba, uint32_t address, int16_t value) { struct GBAMemory* memory = &gba->memory; if ((address & 0x00FFFFFF) < AGB_PRINT_TOP) { - STORE_16(value, address & (SIZE_AGB_PRINT - 2), memory->agbPrintBuffer); + STORE_16(value, address & (GBA_SIZE_AGB_PRINT - 2), memory->agbPrintBuffer); } else if ((address & 0x00FFFFF8) == AGB_PRINT_STRUCT) { (&memory->agbPrintCtx.request)[(address & 7) >> 1] = value; } - if (memory->romSize == SIZE_CART0) { + if (memory->romSize == GBA_SIZE_ROM0) { _pristineCow(gba); - STORE_16(value, address & (SIZE_CART0 - 2), memory->rom); - } else if (memory->agbPrintCtx.bank == 0xFD && memory->romSize >= SIZE_CART0 / 2) { + STORE_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom); + } else if (memory->agbPrintCtx.bank == 0xFD && memory->romSize >= GBA_SIZE_ROM0 / 2) { _pristineCow(gba); - STORE_16(value, address & (SIZE_CART0 / 2 - 2), memory->rom); + STORE_16(value, address & (GBA_SIZE_ROM0 / 2 - 2), memory->rom); } } @@ -1872,7 +1872,7 @@ static int16_t _agbPrintLoad(struct GBA* gba, uint32_t address) { struct GBAMemory* memory = &gba->memory; int16_t value = address >> 1; if (address < AGB_PRINT_TOP && memory->agbPrintBuffer) { - LOAD_16(value, address & (SIZE_AGB_PRINT - 1), memory->agbPrintBuffer); + LOAD_16(value, address & (GBA_SIZE_AGB_PRINT - 1), memory->agbPrintBuffer); } else if ((address & 0x00FFFFF8) == AGB_PRINT_STRUCT) { value = (&memory->agbPrintCtx.request)[(address & 7) >> 1]; } diff --git a/src/gba/renderers/cache-set.c b/src/gba/renderers/cache-set.c index 51512721d..40272bedd 100644 --- a/src/gba/renderers/cache-set.c +++ b/src/gba/renderers/cache-set.c @@ -61,7 +61,7 @@ void GBAVideoCacheAssociate(struct mCacheSet* cache, struct GBAVideo* video) { mCacheSetAssignVRAM(cache, video->vram); video->renderer->cache = cache; size_t i; - for (i = 0; i < SIZE_PALETTE_RAM / 2; ++i) { + for (i = 0; i < GBA_SIZE_PALETTE_RAM / 2; ++i) { mCacheSetWritePalette(cache, i, mColorFrom555(video->palette[i])); } GBAVideoCacheWriteVideoRegister(cache, REG_DISPCNT, video->p->memory.io[REG_DISPCNT >> 1]); diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index 9d7ccafa7..b6ffa2aac 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -664,10 +664,12 @@ static const char* const _finalize = " if (((topFlags.y & 13) == 5 || topFlags.w > 0) && (bottomFlags.y & 2) == 2) {\n" " topPixel.rgb *= float(topFlags.z) / 16.;\n" " topPixel.rgb += bottomPixel.rgb * float(windowFlags.y) / 16.;\n" - " } else if ((topFlags.y & 13) == 9) {\n" - " topPixel.rgb += (1. - topPixel.rgb) * float(windowFlags.z) / 16.;\n" - " } else if ((topFlags.y & 13) == 13) {\n" - " topPixel.rgb -= topPixel.rgb * float(windowFlags.z) / 16.;\n" + " } else if (topFlags.w == 0) { \n" + " if ((topFlags.y & 13) == 9) {\n" + " topPixel.rgb += (1. - topPixel.rgb) * float(windowFlags.z) / 16.;\n" + " } else if ((topFlags.y & 13) == 13) {\n" + " topPixel.rgb -= topPixel.rgb * float(windowFlags.z) / 16.;\n" + " }\n" " }\n" " color = topPixel;\n" "}"; diff --git a/src/gba/renderers/software-mode0.c b/src/gba/renderers/software-mode0.c index 209032388..aa1d63924 100644 --- a/src/gba/renderers/software-mode0.c +++ b/src/gba/renderers/software-mode0.c @@ -17,7 +17,6 @@ #define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ - palette = &mainPalette[paletteData]; \ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ LOAD_32(tileData, charBase, vram); \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ @@ -36,7 +35,6 @@ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ LOAD_32(tileData, charBase, vram); \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ - palette = &mainPalette[paletteData]; \ pixel = &renderer->row[outX]; \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ if (outX < renderer->start) { \ @@ -93,7 +91,6 @@ carryData = 0; \ } else { \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ - palette = &mainPalette[paletteData]; \ LOAD_32(tileData, charBase, vram); \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ tileData >>= 4 * baseX; \ @@ -123,7 +120,6 @@ carryData = 0; \ } else { \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ - palette = &mainPalette[paletteData]; \ LOAD_32(tileData, charBase, vram); \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ tileData >>= x * 4; \ @@ -154,33 +150,26 @@ localY = 7 - localY; \ } \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ - palette = &mainPalette[paletteData]; \ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ if (UNLIKELY(charBase >= 0x10000)) { \ pixel += 8; \ continue; \ } \ - LOAD_32(tileData, charBase, vram); \ + if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ + LOAD_32(tileData, charBase, vram); \ + } else { \ + LOAD_32BE(tileData, charBase, vram); \ + tileData = ((tileData & 0xF0F0F0F0) >> 4) | ((tileData & 0x0F0F0F0F) << 4); \ + } \ if (tileData) { \ - if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \ - } else { \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \ - } \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \ } \ pixel += 8; \ } @@ -523,17 +512,16 @@ void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer uint32_t screenBase; uint32_t charBase; - color_t* mainPalette = renderer->normalPalette; + color_t* palette = renderer->normalPalette; if (renderer->d.highlightAmount && background->highlight) { - mainPalette = renderer->highlightPalette; + palette = renderer->highlightPalette; } if (variant) { - mainPalette = renderer->variantPalette; + palette = renderer->variantPalette; if (renderer->d.highlightAmount && background->highlight) { - mainPalette = renderer->highlightVariantPalette; + palette = renderer->highlightVariantPalette; } } - color_t* palette = mainPalette; PREPARE_OBJWIN; int outX = renderer->start; diff --git a/src/gba/renderers/software-obj.c b/src/gba/renderers/software-obj.c index 8d67199ec..3b44d86a5 100644 --- a/src/gba/renderers/software-obj.c +++ b/src/gba/renderers/software-obj.c @@ -169,10 +169,10 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT || (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || objwinSlowPath) { int target2 = renderer->target2Bd; - target2 |= renderer->bg[0].target2; - target2 |= renderer->bg[1].target2; - target2 |= renderer->bg[2].target2; - target2 |= renderer->bg[3].target2; + target2 |= renderer->bg[0].target2 && renderer->bg[0].enabled; + target2 |= renderer->bg[1].target2 && renderer->bg[1].enabled; + target2 |= renderer->bg[2].target2 && renderer->bg[2].enabled; + target2 |= renderer->bg[3].target2 && renderer->bg[3].enabled; if (target2) { renderer->forceTarget1 = true; flags |= FLAG_REBLEND; diff --git a/src/gba/renderers/software-private.h b/src/gba/renderers/software-private.h index 2ace684f1..ec8ef4175 100644 --- a/src/gba/renderers/software-private.h +++ b/src/gba/renderers/software-private.h @@ -86,28 +86,55 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re #define COMPOSITE_16_OBJWIN(BLEND, IDX) \ if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \ - unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \ + unsigned color; \ unsigned mergedFlags = flags; \ if (current & FLAG_OBJWIN) { \ mergedFlags = objwinFlags; \ + color = objwinPalette[paletteData | pixelData]; \ + } else if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \ + color = renderer->normalPalette[paletteData | pixelData]; \ + } else { \ + color = palette[paletteData | pixelData]; \ } \ _composite ## BLEND ## Objwin(renderer, &pixel[IDX], color | mergedFlags, current); \ } #define COMPOSITE_16_NO_OBJWIN(BLEND, IDX) \ - _composite ## BLEND ## NoObjwin(renderer, &pixel[IDX], palette[pixelData] | flags, current); + { \ + unsigned color; \ + if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \ + color = renderer->normalPalette[paletteData | pixelData]; \ + } else { \ + color = palette[paletteData | pixelData]; \ + } \ + _composite ## BLEND ## NoObjwin(renderer, &pixel[IDX], color | flags, current); \ + } #define COMPOSITE_256_OBJWIN(BLEND, IDX) \ if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \ - unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \ + unsigned color; \ unsigned mergedFlags = flags; \ if (current & FLAG_OBJWIN) { \ mergedFlags = objwinFlags; \ + color = objwinPalette[pixelData]; \ + } else if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \ + color = renderer->normalPalette[pixelData]; \ + } else { \ + color = palette[pixelData]; \ } \ _composite ## BLEND ## Objwin(renderer, &pixel[IDX], color | mergedFlags, current); \ } -#define COMPOSITE_256_NO_OBJWIN COMPOSITE_16_NO_OBJWIN +#define COMPOSITE_256_NO_OBJWIN(BLEND, IDX) \ + { \ + unsigned color; \ + if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \ + color = renderer->normalPalette[pixelData]; \ + } else { \ + color = palette[pixelData]; \ + } \ + _composite ## BLEND ## NoObjwin(renderer, &pixel[IDX], color | flags, current); \ + } #define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, IDX) \ pixelData = tileData & 0xF; \ diff --git a/src/gba/savedata.c b/src/gba/savedata.c index 443e8015d..455ee0802 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -69,22 +69,22 @@ void GBASavedataDeinit(struct GBASavedata* savedata) { } else { switch (savedata->type) { case SAVEDATA_SRAM: - mappedMemoryFree(savedata->data, SIZE_CART_SRAM); + mappedMemoryFree(savedata->data, GBA_SIZE_SRAM); break; case SAVEDATA_SRAM512: - mappedMemoryFree(savedata->data, SIZE_CART_SRAM512); + mappedMemoryFree(savedata->data, GBA_SIZE_SRAM512); break; case SAVEDATA_FLASH512: - mappedMemoryFree(savedata->data, SIZE_CART_FLASH512); + mappedMemoryFree(savedata->data, GBA_SIZE_FLASH512); break; case SAVEDATA_FLASH1M: - mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M); + mappedMemoryFree(savedata->data, GBA_SIZE_FLASH1M); break; case SAVEDATA_EEPROM: - mappedMemoryFree(savedata->data, SIZE_CART_EEPROM); + mappedMemoryFree(savedata->data, GBA_SIZE_EEPROM); break; case SAVEDATA_EEPROM512: - mappedMemoryFree(savedata->data, SIZE_CART_EEPROM512); + mappedMemoryFree(savedata->data, GBA_SIZE_EEPROM512); break; case SAVEDATA_FORCE_NONE: case SAVEDATA_AUTODETECT: @@ -129,17 +129,17 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) { if (savedata->data) { switch (savedata->type) { case SAVEDATA_SRAM: - return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM; + return out->write(out, savedata->data, GBA_SIZE_SRAM) == GBA_SIZE_SRAM; case SAVEDATA_SRAM512: - return out->write(out, savedata->data, SIZE_CART_SRAM512) == SIZE_CART_SRAM512; + return out->write(out, savedata->data, GBA_SIZE_SRAM512) == GBA_SIZE_SRAM512; case SAVEDATA_FLASH512: - return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512; + return out->write(out, savedata->data, GBA_SIZE_FLASH512) == GBA_SIZE_FLASH512; case SAVEDATA_FLASH1M: - return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M; + return out->write(out, savedata->data, GBA_SIZE_FLASH1M) == GBA_SIZE_FLASH1M; case SAVEDATA_EEPROM: - return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM; + return out->write(out, savedata->data, GBA_SIZE_EEPROM) == GBA_SIZE_EEPROM; case SAVEDATA_EEPROM512: - return out->write(out, savedata->data, SIZE_CART_EEPROM512) == SIZE_CART_EEPROM512; + return out->write(out, savedata->data, GBA_SIZE_EEPROM512) == GBA_SIZE_EEPROM512; case SAVEDATA_AUTODETECT: case SAVEDATA_FORCE_NONE: return true; @@ -160,17 +160,17 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) { size_t GBASavedataSize(const struct GBASavedata* savedata) { switch (savedata->type) { case SAVEDATA_SRAM: - return SIZE_CART_SRAM; + return GBA_SIZE_SRAM; case SAVEDATA_SRAM512: - return SIZE_CART_SRAM512; + return GBA_SIZE_SRAM512; case SAVEDATA_FLASH512: - return SIZE_CART_FLASH512; + return GBA_SIZE_FLASH512; case SAVEDATA_FLASH1M: - return SIZE_CART_FLASH1M; + return GBA_SIZE_FLASH1M; case SAVEDATA_EEPROM: - return SIZE_CART_EEPROM; + return GBA_SIZE_EEPROM; case SAVEDATA_EEPROM512: - return SIZE_CART_EEPROM512; + return GBA_SIZE_EEPROM512; case SAVEDATA_FORCE_NONE: return 0; case SAVEDATA_AUTODETECT: @@ -262,14 +262,14 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) { mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata"); return; } - int32_t flashSize = SIZE_CART_FLASH512; + int32_t flashSize = GBA_SIZE_FLASH512; if (savedata->type == SAVEDATA_FLASH1M) { - flashSize = SIZE_CART_FLASH1M; + flashSize = GBA_SIZE_FLASH1M; } off_t end; if (!savedata->vf) { end = 0; - savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M); + savedata->data = anonymousMemoryMap(GBA_SIZE_FLASH1M); } else { end = savedata->vf->size(savedata->vf); if (end < flashSize) { @@ -279,7 +279,7 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) { } savedata->currentBank = savedata->data; - if (end < SIZE_CART_FLASH512) { + if (end < GBA_SIZE_FLASH512) { memset(&savedata->data[end], 0xFF, flashSize - end); } } @@ -291,14 +291,14 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) { mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata"); return; } - int32_t eepromSize = SIZE_CART_EEPROM512; + int32_t eepromSize = GBA_SIZE_EEPROM512; if (savedata->type == SAVEDATA_EEPROM) { - eepromSize = SIZE_CART_EEPROM; + eepromSize = GBA_SIZE_EEPROM; } off_t end; if (!savedata->vf) { end = 0; - savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM); + savedata->data = anonymousMemoryMap(GBA_SIZE_EEPROM); } else { end = savedata->vf->size(savedata->vf); if (end < eepromSize) { @@ -306,8 +306,8 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) { } savedata->data = savedata->vf->map(savedata->vf, eepromSize, savedata->mapMode); } - if (end < SIZE_CART_EEPROM512) { - memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM512 - end); + if (end < GBA_SIZE_EEPROM512) { + memset(&savedata->data[end], 0xFF, GBA_SIZE_EEPROM512 - end); } } @@ -321,17 +321,17 @@ void GBASavedataInitSRAM(struct GBASavedata* savedata) { off_t end; if (!savedata->vf) { end = 0; - savedata->data = anonymousMemoryMap(SIZE_CART_SRAM); + savedata->data = anonymousMemoryMap(GBA_SIZE_SRAM); } else { end = savedata->vf->size(savedata->vf); - if (end < SIZE_CART_SRAM) { - savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM); + if (end < GBA_SIZE_SRAM) { + savedata->vf->truncate(savedata->vf, GBA_SIZE_SRAM); } - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, savedata->mapMode); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_SRAM, savedata->mapMode); } - if (end < SIZE_CART_SRAM) { - memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end); + if (end < GBA_SIZE_SRAM) { + memset(&savedata->data[end], 0xFF, GBA_SIZE_SRAM - end); } } @@ -345,17 +345,17 @@ void GBASavedataInitSRAM512(struct GBASavedata* savedata) { off_t end; if (!savedata->vf) { end = 0; - savedata->data = anonymousMemoryMap(SIZE_CART_SRAM512); + savedata->data = anonymousMemoryMap(GBA_SIZE_SRAM512); } else { end = savedata->vf->size(savedata->vf); - if (end < SIZE_CART_SRAM512) { - savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM512); + if (end < GBA_SIZE_SRAM512) { + savedata->vf->truncate(savedata->vf, GBA_SIZE_SRAM512); } - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM512, savedata->mapMode); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_SRAM512, savedata->mapMode); } - if (end < SIZE_CART_SRAM512) { - memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM512 - end); + if (end < GBA_SIZE_SRAM512) { + memset(&savedata->data[end], 0xFF, GBA_SIZE_SRAM512 - end); } } @@ -465,7 +465,7 @@ void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8 } static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) { - if (size < SIZE_CART_EEPROM512) { + if (size < GBA_SIZE_EEPROM512) { return; } if (savedata->type == SAVEDATA_EEPROM) { @@ -475,13 +475,13 @@ static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) { if (!savedata->vf) { return; } - savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM512); - if (savedata->vf->size(savedata->vf) < SIZE_CART_EEPROM) { - savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM); - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode); - memset(&savedata->data[SIZE_CART_EEPROM512], 0xFF, SIZE_CART_EEPROM - SIZE_CART_EEPROM512); + savedata->vf->unmap(savedata->vf, savedata->data, GBA_SIZE_EEPROM512); + if (savedata->vf->size(savedata->vf) < GBA_SIZE_EEPROM) { + savedata->vf->truncate(savedata->vf, GBA_SIZE_EEPROM); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_EEPROM, savedata->mapMode); + memset(&savedata->data[GBA_SIZE_EEPROM512], 0xFF, GBA_SIZE_EEPROM - GBA_SIZE_EEPROM512); } else { - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_EEPROM, savedata->mapMode); } } @@ -509,7 +509,7 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32 savedata->writeAddress |= (value & 0x1) << 6; } else if (writeSize == 1) { savedata->command = EEPROM_COMMAND_NULL; - } else if ((savedata->writeAddress >> 3) < SIZE_CART_EEPROM) { + } else if ((savedata->writeAddress >> 3) < GBA_SIZE_EEPROM) { _ensureEeprom(savedata, savedata->writeAddress >> 3); uint8_t current = savedata->data[savedata->writeAddress >> 3]; current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7))); @@ -551,7 +551,7 @@ uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) { int step = 63 - savedata->readBitsRemaining; uint32_t address = (savedata->readAddress + step) >> 3; _ensureEeprom(savedata, address); - if (address >= SIZE_CART_EEPROM) { + if (address >= GBA_SIZE_EEPROM) { mLOG(GBA_SAVE, GAME_ERROR, "Reading beyond end of EEPROM: %08X", address); return 0xFF; } @@ -699,13 +699,13 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) { mLOG(GBA_SAVE, INFO, "Updating flash chip from 512kb to 1Mb"); savedata->type = SAVEDATA_FLASH1M; if (savedata->vf) { - savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512); - if (savedata->vf->size(savedata->vf) < SIZE_CART_FLASH1M) { - savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M); - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE); - memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512); + savedata->vf->unmap(savedata->vf, savedata->data, GBA_SIZE_FLASH512); + if (savedata->vf->size(savedata->vf) < GBA_SIZE_FLASH1M) { + savedata->vf->truncate(savedata->vf, GBA_SIZE_FLASH1M); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_FLASH1M, MAP_WRITE); + memset(&savedata->data[GBA_SIZE_FLASH512], 0xFF, GBA_SIZE_FLASH512); } else { - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_FLASH1M, MAP_WRITE); } } } @@ -715,9 +715,9 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) { void _flashErase(struct GBASavedata* savedata) { mLOG(GBA_SAVE, DEBUG, "Performing flash chip erase"); savedata->dirty |= mSAVEDATA_DIRT_NEW; - size_t size = SIZE_CART_FLASH512; + size_t size = GBA_SIZE_FLASH512; if (savedata->type == SAVEDATA_FLASH1M) { - size = SIZE_CART_FLASH1M; + size = GBA_SIZE_FLASH1M; } memset(savedata->data, 0xFF, size); } diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 1079932be..7e6ed36f8 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -102,7 +102,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { mLOG(GBA_STATE, WARN, "Savestate created using a different version of the BIOS: expected %08X, got %08X", gba->biosChecksum, ucheck); uint32_t pc; LOAD_32(pc, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs); - if ((ucheck == GBA_BIOS_CHECKSUM || gba->biosChecksum == GBA_BIOS_CHECKSUM) && pc < SIZE_BIOS && pc >= 0x20) { + if ((ucheck == GBA_BIOS_CHECKSUM || gba->biosChecksum == GBA_BIOS_CHECKSUM) && pc < GBA_SIZE_BIOS && pc >= 0x20) { error = true; } } @@ -128,7 +128,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { } LOAD_32(check, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs); int region = (check >> BASE_OFFSET); - if ((region == REGION_CART0 || region == REGION_CART1 || region == REGION_CART2) && ((check - WORD_SIZE_ARM) & SIZE_CART0) >= gba->memory.romSize - WORD_SIZE_ARM) { + if ((region == GBA_REGION_ROM0 || region == GBA_REGION_ROM1 || region == GBA_REGION_ROM2) && ((check - WORD_SIZE_ARM) & GBA_SIZE_ROM0) >= gba->memory.romSize - WORD_SIZE_ARM) { mLOG(GBA_STATE, WARN, "Savestate created using a differently sized version of the ROM"); error = true; } diff --git a/src/gba/sharkport.c b/src/gba/sharkport.c index 9922f425f..9e1e64518 100644 --- a/src/gba/sharkport.c +++ b/src/gba/sharkport.c @@ -19,7 +19,7 @@ static bool _importSavedata(struct GBA* gba, void* payload, size_t size) { bool success = false; switch (gba->memory.savedata.type) { case SAVEDATA_FLASH512: - if (size > SIZE_CART_FLASH512) { + if (size > GBA_SIZE_FLASH512) { GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M); } // Fall through @@ -33,7 +33,7 @@ static bool _importSavedata(struct GBA* gba, void* payload, size_t size) { goto cleanup; } - if (size == SIZE_CART_EEPROM || size == SIZE_CART_EEPROM512) { + if (size == GBA_SIZE_EEPROM || size == GBA_SIZE_EEPROM512) { size_t i; for (i = 0; i < size; i += 8) { uint32_t lo, hi; @@ -119,7 +119,7 @@ int GBASavedataSharkPortPayloadSize(struct VFile* vf) { void* GBASavedataSharkPortGetPayload(struct VFile* vf, size_t* osize, uint8_t* oheader, bool testChecksum) { int32_t size = GBASavedataSharkPortPayloadSize(vf); - if (size < 0x1C || size > SIZE_CART_FLASH1M + 0x1C) { + if (size < 0x1C || size > GBA_SIZE_FLASH1M + 0x1C) { return NULL; } size -= 0x1C; @@ -336,15 +336,15 @@ int GBASavedataGSVPayloadSize(struct VFile* vf) { LOAD_32(type, 0, &header.type); switch (type) { case 2: - return SIZE_CART_SRAM; + return GBA_SIZE_SRAM; case 3: - return SIZE_CART_EEPROM512; + return GBA_SIZE_EEPROM512; case 4: - return SIZE_CART_EEPROM; + return GBA_SIZE_EEPROM; case 5: - return SIZE_CART_FLASH512; + return GBA_SIZE_FLASH512; case 6: - return SIZE_CART_FLASH1M; // Unconfirmed + return GBA_SIZE_FLASH1M; // Unconfirmed default: return vf->size(vf) - GSV_PAYLOAD_OFFSET; } @@ -352,7 +352,7 @@ int GBASavedataGSVPayloadSize(struct VFile* vf) { void* GBASavedataGSVGetPayload(struct VFile* vf, size_t* osize, uint8_t* ident, bool testChecksum) { int32_t size = GBASavedataGSVPayloadSize(vf); - if (!size || size > SIZE_CART_FLASH1M) { + if (!size || size > GBA_SIZE_FLASH1M) { return NULL; } diff --git a/src/gba/video.c b/src/gba/video.c index 7268f19de..fd22d1dd7 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -54,7 +54,7 @@ MGBA_EXPORT const int GBAVideoObjSizes[16][2] = { void GBAVideoInit(struct GBAVideo* video) { video->renderer = NULL; - video->vram = anonymousMemoryMap(SIZE_VRAM); + video->vram = anonymousMemoryMap(GBA_SIZE_VRAM); video->frameskip = 0; video->event.name = "GBA Video"; video->event.callback = NULL; @@ -93,7 +93,7 @@ void GBAVideoReset(struct GBAVideo* video) { void GBAVideoDeinit(struct GBAVideo* video) { video->renderer->deinit(video->renderer); - mappedMemoryFree(video->vram, SIZE_VRAM); + mappedMemoryFree(video->vram, GBA_SIZE_VRAM); } void GBAVideoDummyRendererCreate(struct GBAVideoRenderer* renderer) { @@ -325,9 +325,9 @@ static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, si } void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) { - memcpy(state->vram, video->vram, SIZE_VRAM); - memcpy(state->oam, video->oam.raw, SIZE_OAM); - memcpy(state->pram, video->palette, SIZE_PALETTE_RAM); + memcpy(state->vram, video->vram, GBA_SIZE_VRAM); + memcpy(state->oam, video->oam.raw, GBA_SIZE_OAM); + memcpy(state->pram, video->palette, GBA_SIZE_PALETTE_RAM); STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent); int32_t flags = 0; if (video->event.callback == _startHdraw) { @@ -340,16 +340,16 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* } void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state) { - memcpy(video->vram, state->vram, SIZE_VRAM); + memcpy(video->vram, state->vram, GBA_SIZE_VRAM); uint16_t value; int i; - for (i = 0; i < SIZE_OAM; i += 2) { + for (i = 0; i < GBA_SIZE_OAM; i += 2) { LOAD_16(value, i, state->oam); - GBAStore16(video->p->cpu, BASE_OAM | i, value, 0); + GBAStore16(video->p->cpu, GBA_BASE_OAM | i, value, 0); } - for (i = 0; i < SIZE_PALETTE_RAM; i += 2) { + for (i = 0; i < GBA_SIZE_PALETTE_RAM; i += 2) { LOAD_16(value, i, state->pram); - GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0); + GBAStore16(video->p->cpu, GBA_BASE_PALETTE_RAM | i, value, 0); } LOAD_32(video->frameCounter, 0, &state->video.frameCounter); diff --git a/src/platform/3ds/CMakeLists.txt b/src/platform/3ds/CMakeLists.txt index 7fd3e270f..893af0317 100644 --- a/src/platform/3ds/CMakeLists.txt +++ b/src/platform/3ds/CMakeLists.txt @@ -1,4 +1,4 @@ -set(USE_VFS_3DS ON CACHE BOOL "Use 3DS-specific file support") +set(USE_VFS_3DS OFF CACHE BOOL "Use 3DS-specific file support") mark_as_advanced(USE_VFS_3DS) find_program(3DSLINK 3dslink) diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index 71fcc1aa2..5356130ce 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -817,7 +817,16 @@ THREAD_ENTRY _core2Test(void* context) { UNUSED(context); } -int main() { +int main(int argc, char* argv[]) { + char initialPath[PATH_MAX] = { 0 }; + if (argc > 1) { + strncpy(initialPath, argv[1], sizeof(PATH_MAX)); + } else { + u8 hmac[0x20]; + memset(hmac, 0, sizeof(hmac)); + APT_ReceiveDeliverArg(initialPath, sizeof(initialPath), hmac, NULL, NULL); + } + rotation.d.sample = _sampleRotation; rotation.d.readTiltX = _readTiltX; rotation.d.readTiltY = _readTiltY; @@ -1046,9 +1055,29 @@ int main() { _map3DSKey(&runner.params.keyMap, KEY_CSTICK_UP, mGUI_INPUT_INCREASE_BRIGHTNESS); _map3DSKey(&runner.params.keyMap, KEY_CSTICK_DOWN, mGUI_INPUT_DECREASE_BRIGHTNESS); - mGUIRunloop(&runner); + Result res = romfsInit(); + bool useRomfs = false; + if (R_SUCCEEDED(res)) { + useRomfs = mGUIGetRom(&runner, initialPath, sizeof(initialPath)); + if (!useRomfs) { + romfsExit(); + _cleanup(); + return 1; + } + } + + if (initialPath[0] == '/' || useRomfs) { + mGUILoadInputMaps(&runner); + mGUIRun(&runner, initialPath); + } else { + mGUIRunloop(&runner); + } + mGUIDeinit(&runner); + if (useRomfs) { + romfsExit(); + } _cleanup(); return 0; } diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index 9bbd55ae6..68f54f2c7 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -617,6 +617,39 @@ void retro_run(void) { core->desiredVideoDimensions(core, &width, &height); videoCallback(outputBuffer, width, height, BYTES_PER_PIXEL * 256); +#ifdef M_CORE_GBA + if (core->platform(core) == mPLATFORM_GBA) { + blip_t *audioChannelLeft = core->getAudioChannel(core, 0); + blip_t *audioChannelRight = core->getAudioChannel(core, 1); + int samplesAvail = blip_samples_avail(audioChannelLeft); + if (samplesAvail > 0) { + /* Update 'running average' of number of + * samples per frame. + * Note that this is not a true running + * average, but just a leaky-integrator/ + * exponential moving average, used because + * it is simple and fast (i.e. requires no + * window of samples). */ + audioSamplesPerFrameAvg = (SAMPLES_PER_FRAME_MOVING_AVG_ALPHA * (float)samplesAvail) + + ((1.0f - SAMPLES_PER_FRAME_MOVING_AVG_ALPHA) * audioSamplesPerFrameAvg); + size_t samplesToRead = (size_t)(audioSamplesPerFrameAvg); + /* Resize audio output buffer, if required */ + if (audioSampleBufferSize < (samplesToRead * 2)) { + audioSampleBufferSize = (samplesToRead * 2); + audioSampleBuffer = realloc(audioSampleBuffer, audioSampleBufferSize * sizeof(int16_t)); + } + int produced = blip_read_samples(audioChannelLeft, audioSampleBuffer, samplesToRead, true); + blip_read_samples(audioChannelRight, audioSampleBuffer + 1, samplesToRead, true); + if (produced > 0) { + if (audioLowPassEnabled) { + _audioLowPassFilter(audioSampleBuffer, produced); + } + audioCallback(audioSampleBuffer, (size_t)produced); + } + } + } +#endif + if (rumbleCallback) { if (rumbleUp) { rumbleCallback(0, RETRO_RUMBLE_STRONG, rumbleUp * 0xFFFF / (rumbleUp + rumbleDown)); @@ -643,66 +676,66 @@ static void _setupMaps(struct mCore* core) { /* Map internal working RAM */ descs[0].ptr = gba->memory.iwram; - descs[0].start = BASE_WORKING_IRAM; - descs[0].len = SIZE_WORKING_IRAM; + descs[0].start = GBA_BASE_IWRAM; + descs[0].len = GBA_SIZE_IWRAM; descs[0].select = 0xFF000000; /* Map working RAM */ descs[1].ptr = gba->memory.wram; - descs[1].start = BASE_WORKING_RAM; - descs[1].len = SIZE_WORKING_RAM; + descs[1].start = GBA_BASE_EWRAM; + descs[1].len = GBA_SIZE_EWRAM; descs[1].select = 0xFF000000; /* Map save RAM */ /* TODO: if SRAM is flash, use start=0 addrspace="S" instead */ descs[2].ptr = savedataSize ? savedata : NULL; - descs[2].start = BASE_CART_SRAM; + descs[2].start = GBA_BASE_SRAM; descs[2].len = savedataSize; /* Map ROM */ descs[3].ptr = gba->memory.rom; - descs[3].start = BASE_CART0; + descs[3].start = GBA_BASE_ROM0; descs[3].len = romSize; descs[3].flags = RETRO_MEMDESC_CONST; descs[4].ptr = gba->memory.rom; - descs[4].start = BASE_CART1; + descs[4].start = GBA_BASE_ROM1; descs[4].len = romSize; descs[4].flags = RETRO_MEMDESC_CONST; descs[5].ptr = gba->memory.rom; - descs[5].start = BASE_CART2; + descs[5].start = GBA_BASE_ROM2; descs[5].len = romSize; descs[5].flags = RETRO_MEMDESC_CONST; /* Map BIOS */ descs[6].ptr = gba->memory.bios; - descs[6].start = BASE_BIOS; - descs[6].len = SIZE_BIOS; + descs[6].start = GBA_BASE_BIOS; + descs[6].len = GBA_SIZE_BIOS; descs[6].flags = RETRO_MEMDESC_CONST; /* Map VRAM */ descs[7].ptr = gba->video.vram; - descs[7].start = BASE_VRAM; - descs[7].len = SIZE_VRAM; + descs[7].start = GBA_BASE_VRAM; + descs[7].len = GBA_SIZE_VRAM; descs[7].select = 0xFF000000; /* Map palette RAM */ descs[8].ptr = gba->video.palette; - descs[8].start = BASE_PALETTE_RAM; - descs[8].len = SIZE_PALETTE_RAM; + descs[8].start = GBA_BASE_PALETTE_RAM; + descs[8].len = GBA_SIZE_PALETTE_RAM; descs[8].select = 0xFF000000; /* Map OAM */ descs[9].ptr = &gba->video.oam; /* video.oam is a structure */ - descs[9].start = BASE_OAM; - descs[9].len = SIZE_OAM; + descs[9].start = GBA_BASE_OAM; + descs[9].len = GBA_SIZE_OAM; descs[9].select = 0xFF000000; /* Map mmapped I/O */ descs[10].ptr = gba->memory.io; - descs[10].start = BASE_IO; - descs[10].len = SIZE_IO; + descs[10].start = GBA_BASE_IO; + descs[10].len = GBA_SIZE_IO; mmaps.descriptors = descs; mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]); diff --git a/src/platform/opengl/gles2.h b/src/platform/opengl/gles2.h index 31a2d3e2a..144a64a13 100644 --- a/src/platform/opengl/gles2.h +++ b/src/platform/opengl/gles2.h @@ -12,14 +12,12 @@ CXX_GUARD_START #ifdef USE_EPOXY #include -#elif defined(BUILD_GL) -#ifdef __APPLE__ +#elif defined(__APPLE__) #include -#else +#elif defined(BUILD_GL) #define GL_GLEXT_PROTOTYPES #include #include -#endif #elif defined(BUILD_GLES3) #include #else diff --git a/src/platform/psp2/main.c b/src/platform/psp2/main.c index c474d9d85..747535e25 100644 --- a/src/platform/psp2/main.c +++ b/src/platform/psp2/main.c @@ -153,6 +153,8 @@ static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) { } int main() { + char initialPath[PATH_MAX] = { 0 }; + vita2d_init(); struct GUIFont* font = GUIFontCreate(); struct mGUIRunner runner = { @@ -278,7 +280,13 @@ int main() { mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_SQUARE, mGUI_INPUT_SCREEN_MODE); scePowerSetArmClockFrequency(444); - mGUIRunloop(&runner); + + if (mGUIGetRom(&runner, initialPath, sizeof(initialPath))) { + mGUILoadInputMaps(&runner); + mGUIRun(&runner, initialPath); + } else { + mGUIRunloop(&runner); + } vita2d_fini(); mGUIDeinit(&runner); diff --git a/src/platform/python/mgba/gba.py b/src/platform/python/mgba/gba.py index 09deafc6c..a0c86ac84 100644 --- a/src/platform/python/mgba/gba.py +++ b/src/platform/python/mgba/gba.py @@ -123,19 +123,19 @@ class GBAMemory(Memory): def __init__(self, core, romSize=lib.SIZE_CART0): super(GBAMemory, self).__init__(core, 0x100000000) - self.bios = Memory(core, lib.SIZE_BIOS, lib.BASE_BIOS) - self.wram = Memory(core, lib.SIZE_WORKING_RAM, lib.BASE_WORKING_RAM) - self.iwram = Memory(core, lib.SIZE_WORKING_IRAM, lib.BASE_WORKING_IRAM) - self.io = Memory(core, lib.SIZE_IO, lib.BASE_IO) # pylint: disable=invalid-name - self.palette = Memory(core, lib.SIZE_PALETTE_RAM, lib.BASE_PALETTE_RAM) - self.vram = Memory(core, lib.SIZE_VRAM, lib.BASE_VRAM) - self.oam = Memory(core, lib.SIZE_OAM, lib.BASE_OAM) + self.bios = Memory(core, lib.GBA_SIZE_BIOS, lib.GBA_BASE_BIOS) + self.wram = Memory(core, lib.GBA_SIZE_EWRAM, lib.GBA_BASE_EWRAM) + self.iwram = Memory(core, lib.GBA_SIZE_IWRAM, lib.GBA_BASE_IWRAM) + self.io = Memory(core, lib.GBA_SIZE_IO, lib.GBA_BASE_IO) # pylint: disable=invalid-name + self.palette = Memory(core, lib.GBA_SIZE_PALETTE_RAM, lib.GBA_BASE_PALETTE_RAM) + self.vram = Memory(core, lib.GBA_SIZE_VRAM, lib.GBA_BASE_VRAM) + self.oam = Memory(core, lib.GBA_SIZE_OAM, lib.GBA_BASE_OAM) self.cart0 = Memory(core, romSize, lib.BASE_CART0) self.cart1 = Memory(core, romSize, lib.BASE_CART1) self.cart2 = Memory(core, romSize, lib.BASE_CART2) self.cart = self.cart0 self.rom = self.cart0 - self.sram = Memory(core, lib.SIZE_CART_SRAM, lib.BASE_CART_SRAM) + self.sram = Memory(core, lib.GBA_SIZE_SRAM, lib.GBA_BASE_SRAM) class GBASprite(Sprite): diff --git a/src/platform/qt/AboutScreen.cpp b/src/platform/qt/AboutScreen.cpp index 1bf672f98..dae9e8dc9 100644 --- a/src/platform/qt/AboutScreen.cpp +++ b/src/platform/qt/AboutScreen.cpp @@ -74,7 +74,7 @@ AboutScreen::AboutScreen(QWidget* parent) { QString copyright = m_ui.copyright->text(); - copyright.replace("{year}", QLatin1String("2022")); + copyright.replace("{year}", QLatin1String("2023")); m_ui.copyright->setText(copyright); } } diff --git a/src/platform/qt/AbstractUpdater.cpp b/src/platform/qt/AbstractUpdater.cpp index b878f8a2f..ae6df137b 100644 --- a/src/platform/qt/AbstractUpdater.cpp +++ b/src/platform/qt/AbstractUpdater.cpp @@ -8,16 +8,17 @@ #include #include +#include "GBAApp.h" + using namespace QGBA; AbstractUpdater::AbstractUpdater(QObject* parent) : QObject(parent) - , m_netman(new QNetworkAccessManager(this)) { } void AbstractUpdater::checkUpdate() { - QNetworkReply* reply = m_netman->get(QNetworkRequest(manifestLocation())); + QNetworkReply* reply = GBAApp::app()->httpGet(manifestLocation()); chaseRedirects(reply, &AbstractUpdater::manifestDownloaded); } @@ -36,7 +37,7 @@ void AbstractUpdater::downloadUpdate() { return; } m_isUpdating = true; - QNetworkReply* reply = m_netman->get(QNetworkRequest(url)); + QNetworkReply* reply = GBAApp::app()->httpGet(url); chaseRedirects(reply, &AbstractUpdater::updateDownloaded); } @@ -54,7 +55,7 @@ void AbstractUpdater::chaseRedirects(QNetworkReply* reply, void (AbstractUpdater connect(reply, &QNetworkReply::finished, this, [this, reply, cb]() { // TODO: check domains, etc if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 == 3) { - QNetworkReply* newReply = m_netman->get(QNetworkRequest(reply->header(QNetworkRequest::LocationHeader).toString())); + QNetworkReply* newReply = GBAApp::app()->httpGet(reply->header(QNetworkRequest::LocationHeader).toString()); chaseRedirects(newReply, cb); } else { (this->*cb)(reply); @@ -69,7 +70,7 @@ void AbstractUpdater::manifestDownloaded(QNetworkReply* reply) { if (!url.isValid()) { emit updateDone(false); } else { - QNetworkReply* reply = m_netman->get(QNetworkRequest(url)); + QNetworkReply* reply = GBAApp::app()->httpGet(url); chaseRedirects(reply, &AbstractUpdater::updateDownloaded); } } else { diff --git a/src/platform/qt/AbstractUpdater.h b/src/platform/qt/AbstractUpdater.h index 5fa385175..e12d3eabe 100644 --- a/src/platform/qt/AbstractUpdater.h +++ b/src/platform/qt/AbstractUpdater.h @@ -9,7 +9,6 @@ #include #include -class QNetworkAccessManager; class QNetworkReply; namespace QGBA { @@ -44,7 +43,6 @@ private: void updateDownloaded(QNetworkReply*); bool m_isUpdating = false; - QNetworkAccessManager* m_netman; QByteArray m_manifest; }; diff --git a/src/platform/qt/ApplicationUpdater.cpp b/src/platform/qt/ApplicationUpdater.cpp index ce752ecce..21def050c 100644 --- a/src/platform/qt/ApplicationUpdater.cpp +++ b/src/platform/qt/ApplicationUpdater.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "ApplicationUpdatePrompt.h" #include "ConfigController.h" @@ -71,9 +72,10 @@ QStringList ApplicationUpdater::listChannels() { } QString ApplicationUpdater::currentChannel() { - QLatin1String version(projectVersion); - QLatin1String branch(gitBranch); - if (branch == QLatin1String("heads/") + version) { + QString version(projectVersion); + QString branch(gitBranch); + QRegularExpression stable("^(?:(?:refs/)?(?:tags|heads)/)?[0-9]+\\.[0-9]+\\.[0-9]+$"); + if (branch.contains(stable) || (branch == "(unknown)" && version.contains(stable))) { return QLatin1String("stable"); } else { return QLatin1String("dev"); @@ -136,8 +138,16 @@ QUrl ApplicationUpdater::parseManifest(const QByteArray& manifest) { QString ApplicationUpdater::destination() const { QFileInfo path(updateInfo().url.path()); - QDir dir(ConfigController::configDir()); - return dir.filePath(QLatin1String("update.") + path.completeSuffix()); + QDir dir(ConfigController::cacheDir()); + // QFileInfo::completeSuffix will eat all .'s in the filename...including + // ones in the version string, turning mGBA-1.0.0-win32.7z into + // 0.0-win32.7z instead of the intended .7z + // As a result, so we have to split out the complete suffix manually. + QString suffix(path.suffix()); + if (path.completeBaseName().endsWith(".tar")) { + suffix = "tar." + suffix; + } + return dir.filePath(QLatin1String("update.") + suffix); } const char* ApplicationUpdater::platform() { @@ -166,7 +176,8 @@ const char* ApplicationUpdater::platform() { } ApplicationUpdater::UpdateInfo::UpdateInfo(const QString& prefix, const mUpdate* update) - : size(update->size) + : rev(-1) + , size(update->size) , url(prefix + update->path) { if (update->rev > 0) { diff --git a/src/platform/qt/AssetTile.cpp b/src/platform/qt/AssetTile.cpp index bd9f3a188..78db4ccd9 100644 --- a/src/platform/qt/AssetTile.cpp +++ b/src/platform/qt/AssetTile.cpp @@ -51,8 +51,8 @@ void AssetTile::setController(std::shared_ptr controller) { #ifdef M_CORE_GBA case mPLATFORM_GBA: m_addressWidth = 8; - m_addressBase = BASE_VRAM; - m_boundaryBase = BASE_VRAM | 0x10000; + m_addressBase = GBA_BASE_VRAM; + m_boundaryBase = GBA_BASE_VRAM | 0x10000; break; #endif #ifdef M_CORE_GB diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 3db5ebc55..7bd8e095b 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -39,7 +39,6 @@ if(NOT ${QT}Widgets_FOUND) endif() if(APPLE) - execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK) if(MACOSX_SDK VERSION_GREATER 10.14) list(APPEND QT_DEFINES USE_SHARE_WIDGET) endif() @@ -95,14 +94,24 @@ set(SOURCE_FILES Display.cpp DisplayGL.cpp DisplayQt.cpp + ForwarderController.cpp + ForwarderGenerator.cpp + ForwarderGenerator3DS.cpp + ForwarderGeneratorVita.cpp + ForwarderView.cpp FrameView.cpp GBAApp.cpp GBAKeyEditor.cpp GIFView.cpp - GamepadAxisEvent.cpp - GamepadButtonEvent.cpp - GamepadHatEvent.cpp IOViewer.cpp + input/Gamepad.cpp + input/GamepadAxisEvent.cpp + input/GamepadButtonEvent.cpp + input/GamepadHatEvent.cpp + input/InputDriver.cpp + input/InputSource.cpp + input/InputMapper.cpp + input/KeySource.cpp InputController.cpp InputProfile.cpp KeyEditor.cpp @@ -119,6 +128,7 @@ set(SOURCE_FILES MessagePainter.cpp MultiplayerController.cpp ObjView.cpp + OpenGLBug.cpp OverrideView.cpp PaletteView.cpp PlacementControl.cpp @@ -152,6 +162,7 @@ set(UI_FILES CheatsView.ui DebuggerConsole.ui DolphinConnector.ui + ForwarderView.ui FrameView.ui GIFView.ui IOViewer.ui @@ -192,6 +203,7 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5 set(AUDIO_SRC) if(BUILD_SDL) + list(APPEND SOURCE_FILES input/SDLInputDriver.cpp) list(APPEND AUDIO_SRC AudioProcessorSDL.cpp) endif() @@ -414,7 +426,7 @@ if(QT_STATIC) list(APPEND QT_LIBRARIES "-framework AVFoundation" "-framework CoreMedia" "-framework SystemConfiguration" "-framework Security") set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE}") elseif(UNIX) - list(APPEND QT_LIBRARIES ${QT}::FontDatabaseSupport ${QT}::XcbQpa) + list(APPEND QT_LIBRARIES ${QT}::FontDatabaseSupport ${QT}::XcbQpa ${QT}::QWaylandIntegrationPlugin) endif() endif() target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES}) diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index f940e4c78..1116dba79 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -382,3 +382,7 @@ const QString& ConfigController::configDir() { } return s_configDir; } + +const QString& ConfigController::cacheDir() { + return configDir(); +} diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 4b7f859fb..ecd126867 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -105,6 +105,7 @@ public: void usage(const char* arg0) const; static const QString& configDir(); + static const QString& cacheDir(); static bool isPortable(); public slots: diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 61774de71..37fc1b01f 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -914,7 +914,10 @@ void CoreController::scanCard(const QString& path) { if (!file.open(QIODevice::ReadOnly)) { return; } - m_eReaderData = file.read(2912); + QByteArray eReaderData = file.read(2912); + if (eReaderData.isEmpty()) { + return; + } file.seek(0); QStringList lines; @@ -936,6 +939,7 @@ void CoreController::scanCard(const QString& path) { } } scanCards(lines); + m_eReaderData = eReaderData; } else if (image.size() == QSize(989, 44) || image.size() == QSize(639, 44)) { const uchar* bits = image.constBits(); size_t size; diff --git a/src/platform/qt/CoreManager.cpp b/src/platform/qt/CoreManager.cpp index b5fc3e413..cac1c2560 100644 --- a/src/platform/qt/CoreManager.cpp +++ b/src/platform/qt/CoreManager.cpp @@ -31,6 +31,7 @@ void CoreManager::setMultiplayerController(MultiplayerController* multiplayer) { CoreController* CoreManager::loadGame(const QString& path) { QFileInfo info(path); if (!info.isReadable()) { + // Open specific file in archive QString fname = info.fileName(); QString base = info.path(); if (base.endsWith("/") || base.endsWith(QDir::separator())) { @@ -40,13 +41,8 @@ CoreController* CoreManager::loadGame(const QString& path) { if (dir) { VFile* vf = dir->openFile(dir, fname.toUtf8().constData(), O_RDONLY); if (vf) { - struct VFile* vfclone = VFileMemChunk(NULL, vf->size(vf)); - uint8_t buffer[2048]; - ssize_t read; - while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) { - vfclone->write(vfclone, buffer, read); - } - vf->close(vf); + struct VFile* vfclone = VFileDevice::openMemory(vf->size(vf)); + VFileDevice::copyFile(vf, vfclone); vf = vfclone; } dir->close(dir); @@ -59,15 +55,16 @@ CoreController* CoreManager::loadGame(const QString& path) { VFile* vf = nullptr; VDir* archive = VDirOpenArchive(path.toUtf8().constData()); if (archive) { + // Open first file in archive VFile* vfOriginal = VDirFindFirst(archive, [](VFile* vf) { return mCoreIsCompatible(vf) != mPLATFORM_NONE; }); if (vfOriginal) { ssize_t size = vfOriginal->size(vfOriginal); if (size > 0) { - void* mem = vfOriginal->map(vfOriginal, size, MAP_READ); - vf = VFileMemChunk(mem, size); - vfOriginal->unmap(vfOriginal, mem, size); + struct VFile* vfclone = VFileDevice::openMemory(vfOriginal->size(vfOriginal)); + VFileDevice::copyFile(vfOriginal, vfclone); + vf = vfclone; } vfOriginal->close(vfOriginal); } @@ -75,6 +72,7 @@ CoreController* CoreManager::loadGame(const QString& path) { } QDir dir(info.dir()); if (!vf) { + // Open bare file vf = VFileOpen(info.canonicalFilePath().toUtf8().constData(), O_RDONLY); } return loadGame(vf, info.fileName(), dir.canonicalPath()); diff --git a/src/platform/qt/DiscordCoordinator.cpp b/src/platform/qt/DiscordCoordinator.cpp index b090863cd..72ca4cc1b 100644 --- a/src/platform/qt/DiscordCoordinator.cpp +++ b/src/platform/qt/DiscordCoordinator.cpp @@ -22,7 +22,7 @@ namespace DiscordCoordinator { static bool s_gameRunning = false; static bool s_inited = false; -static QString s_title; +static QByteArray s_title; static void updatePresence() { if (!s_inited) { @@ -30,7 +30,7 @@ static void updatePresence() { } if (s_gameRunning) { DiscordRichPresence discordPresence{}; - discordPresence.details = s_title.toUtf8().constData(); + discordPresence.details = s_title.constData(); discordPresence.instance = 1; discordPresence.largeImageKey = "mgba"; #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) @@ -74,8 +74,10 @@ void gameStarted(std::shared_ptr controller) { s_title = controller->thread()->core->dirs.baseName; QString dbTitle = controller->dbTitle(); if (!dbTitle.isNull()) { - s_title = dbTitle; + s_title = dbTitle.toUtf8(); } + // Non-const QByteArrays are null-terminated so we don't need to append null even after truncation + s_title.truncate(128); updatePresence(); } diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index 7548e674c..704f9463a 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -10,6 +10,7 @@ #include "DisplayGL.h" #include "DisplayQt.h" #include "LogController.h" +#include "utils.h" #include @@ -169,3 +170,32 @@ void QGBA::Display::mouseMoveEvent(QMouseEvent*) { m_mouseTimer.stop(); m_mouseTimer.start(); } + +QPoint QGBA::Display::normalizedPoint(CoreController* controller, const QPoint& localRef) { + QSize screen(controller->screenDimensions()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + QSize newSize((QSizeF(size()) * devicePixelRatioF()).toSize()); +#else + QSize newSize((QSizeF(size()) * devicePixelRatio()).toSize()); +#endif + + if (m_lockAspectRatio) { + QGBA::lockAspectRatio(screen, newSize); + } + + if (m_lockIntegerScaling) { + QGBA::lockIntegerScaling(screen, newSize); + } + + QPointF newPos(localRef); + newPos -= QPointF(width() / 2.0, height() / 2.0); + newPos = QPointF(newPos.x() * screen.width(), newPos.y() * screen.height()); + newPos = QPointF(newPos.x() / newSize.width(), newPos.y() / newSize.height()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + newPos *= devicePixelRatioF(); +#else + newPos *= devicePixelRatio(); +#endif + newPos += QPointF(screen.width() / 2.0, screen.height() / 2.0); + return newPos.toPoint(); +} diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index af3cf7f00..574a0e8e9 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -44,6 +44,8 @@ public: bool isShowOSD() const { return m_showOSD; } bool isShowFrameCounter() const { return m_showFrameCounter; } + QPoint normalizedPoint(CoreController*, const QPoint& localRef); + virtual void attach(std::shared_ptr); virtual void configure(ConfigController*); virtual void startDrawing(std::shared_ptr) = 0; diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 16dece1b4..469b7e315 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -44,6 +44,8 @@ using QOpenGLFunctions_Baseline = QOpenGLFunctions_3_2_Core; #define OVERHEAD_NSEC 300000 #endif +#include "OpenGLBug.h" + using namespace QGBA; QHash DisplayGL::s_supports; @@ -55,6 +57,14 @@ uint qHash(const QSurfaceFormat& format, uint seed) { return qHash(representation, seed); } +mGLWidget::mGLWidget(QWidget* parent) + : QOpenGLWidget(parent) +{ + setUpdateBehavior(QOpenGLWidget::PartialUpdate); + + connect(&m_refresh, &QTimer::timeout, this, static_cast(&QWidget::update)); +} + void mGLWidget::initializeGL() { m_vao = std::make_unique(); m_vao->create(); @@ -83,8 +93,7 @@ void mGLWidget::initializeGL() { m_positionLocation = m_program->attributeLocation("position"); m_vaoDone = false; - - connect(&m_refresh, &QTimer::timeout, this, static_cast(&QWidget::update)); + m_tex = 0; } bool mGLWidget::finalizeVAO() { @@ -115,6 +124,10 @@ void mGLWidget::paintGL() { if (!m_vaoDone && !finalizeVAO()) { return; } + if (!m_tex) { + m_refresh.start(10); + return; + } QOpenGLFunctions_Baseline* fn = context()->versionFunctions(); m_program->bind(); m_vao->bind(); @@ -164,6 +177,11 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent) m_drawThread.setObjectName("Painter Thread"); m_painter->setThread(&m_drawThread); + m_proxyThread.setObjectName("OpenGL Proxy Thread"); + m_proxyContext = std::make_unique(); + m_proxyContext->setFormat(format); + connect(m_painter.get(), &PainterGL::created, this, &DisplayGL::setupProxyThread); + connect(&m_drawThread, &QThread::started, m_painter.get(), &PainterGL::create); connect(m_painter.get(), &PainterGL::started, this, [this] { m_hasStarted = true; @@ -178,6 +196,11 @@ DisplayGL::~DisplayGL() { QMetaObject::invokeMethod(m_painter.get(), "destroy", Qt::BlockingQueuedConnection); m_drawThread.exit(); m_drawThread.wait(); + + if (m_proxyThread.isRunning()) { + m_proxyThread.exit(); + m_proxyThread.wait(); + } } bool DisplayGL::supportsShaders() const { @@ -198,9 +221,6 @@ void DisplayGL::startDrawing(std::shared_ptr controller) { m_painter->setContext(controller); m_painter->setMessagePainter(messagePainter()); m_context = controller; - if (videoProxy()) { - videoProxy()->moveToThread(&m_drawThread); - } lockAspectRatio(isAspectRatioLocked()); lockIntegerScaling(isIntegerScalingLocked()); @@ -298,7 +318,7 @@ void DisplayGL::pauseDrawing() { if (m_hasStarted) { m_isDrawing = false; QMetaObject::invokeMethod(m_painter.get(), "pause", Qt::BlockingQueuedConnection); - if (QGuiApplication::platformName() != "xcb") { + if (!shouldDisableUpdates()) { setUpdatesEnabled(true); } } @@ -400,11 +420,34 @@ bool DisplayGL::shouldDisableUpdates() { void DisplayGL::setVideoProxy(std::shared_ptr proxy) { Display::setVideoProxy(proxy); if (proxy) { - proxy->moveToThread(&m_drawThread); + proxy->moveToThread(&m_proxyThread); } m_painter->setVideoProxy(proxy); } +void DisplayGL::setupProxyThread() { + m_proxyContext->moveToThread(&m_proxyThread); + m_proxySurface.create(); + connect(&m_proxyThread, &QThread::started, m_proxyContext.get(), [this]() { + m_proxyContext->setShareContext(m_painter->shareContext()); + m_proxyContext->create(); + m_proxyContext->makeCurrent(&m_proxySurface); +#if defined(_WIN32) && defined(USE_EPOXY) + epoxy_handle_external_wglMakeCurrent(); +#endif + }); + connect(m_painter.get(), &PainterGL::texSwapped, m_proxyContext.get(), [this]() { + if (!m_context->hardwareAccelerated()) { + return; + } + if (videoProxy()) { + videoProxy()->processData(); + } + m_painter->updateFramebufferHandle(); + }, Qt::BlockingQueuedConnection); + m_proxyThread.start(); +} + int DisplayGL::framebufferHandle() { return m_painter->glTex(); } @@ -473,6 +516,12 @@ void PainterGL::create() { gl2Backend = static_cast(malloc(sizeof(mGLES2Context))); mGLES2ContextCreate(gl2Backend); m_backend = &gl2Backend->d; + QOpenGLFunctions* fn = m_gl->functions(); + fn->glGenTextures(m_bridgeTexes.size(), m_bridgeTexes.data()); + for (auto tex : m_bridgeTexes) { + m_freeTex.enqueue(tex); + } + m_bridgeTexIn = m_freeTex.dequeue(); } #endif @@ -492,10 +541,10 @@ void PainterGL::create() { painter->makeCurrent(); #if defined(BUILD_GLES2) || defined(BUILD_GLES3) + mGLES2Context* gl2Backend = reinterpret_cast(painter->m_backend); if (painter->m_widget && painter->supportsShaders()) { - QOpenGLFunctions_Baseline* fn = painter->m_gl->versionFunctions(); + QOpenGLFunctions* fn = painter->m_gl->functions(); fn->glFinish(); - mGLES2Context* gl2Backend = reinterpret_cast(painter->m_backend); painter->m_widget->setTex(painter->m_finalTex[painter->m_finalTexIdx]); painter->m_finalTexIdx ^= 1; gl2Backend->finalShader.tex = painter->m_finalTex[painter->m_finalTexIdx]; @@ -520,7 +569,6 @@ void PainterGL::create() { m_finalTexIdx = 0; gl2Backend->finalShader.tex = m_finalTex[m_finalTexIdx]; - m_widget->setTex(m_finalTex[m_finalTexIdx]); } m_shader.preprocessShader = static_cast(&reinterpret_cast(m_backend)->initialShader); } @@ -530,6 +578,9 @@ void PainterGL::create() { m_backend->filter = false; m_backend->lockAspectRatio = false; m_backend->interframeBlending = false; + m_gl->doneCurrent(); + + emit created(); } void PainterGL::destroy() { @@ -538,9 +589,11 @@ void PainterGL::destroy() { } makeCurrent(); #if defined(BUILD_GLES2) || defined(BUILD_GLES3) + QOpenGLFunctions* fn = m_gl->functions(); if (m_shader.passes) { mGLES2ShaderFree(&m_shader); } + fn->glDeleteTextures(m_bridgeTexes.size(), m_bridgeTexes.data()); #endif m_backend->deinit(m_backend); m_gl->doneCurrent(); @@ -617,8 +670,20 @@ void PainterGL::filter(bool filter) { } } +#ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#endif + void PainterGL::start() { makeCurrent(); +#if defined(BUILD_GLES3) && !defined(Q_OS_MAC) + if (glContextHasBug(OpenGLBug::GLTHREAD_BLOCKS_SWAP)) { + // Suggested on Discord as a way to strongly hint that glthread should be disabled + // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/8035 + QOpenGLFunctions* fn = m_gl->functions(); + fn->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + } +#endif #if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_supportsShaders && m_shader.passes) { @@ -626,6 +691,7 @@ void PainterGL::start() { } #endif resizeContext(); + m_context->addFrameAction(std::bind(&PainterGL::swapTex, this)); m_buffer = nullptr; m_active = true; @@ -634,7 +700,7 @@ void PainterGL::start() { } void PainterGL::draw() { - if (!m_started || m_queue.isEmpty()) { + if (!m_started || (m_queue.isEmpty() && m_queueTex.isEmpty())) { return; } @@ -661,7 +727,7 @@ void PainterGL::draw() { return; } dequeue(); - bool forceRedraw = !m_videoProxy; + bool forceRedraw = true; if (!m_delayTimer.isValid()) { m_delayTimer.start(); } else { @@ -715,11 +781,6 @@ void PainterGL::doStop() { m_videoProxy->processData(); } } - if (m_videoProxy) { - m_videoProxy->reset(); - m_videoProxy->moveToThread(m_window->thread()); - m_videoProxy.reset(); - } m_backend->clear(m_backend); m_backend->swap(m_backend); } @@ -741,7 +802,7 @@ void PainterGL::performDraw() { m_backend->postFrame(m_backend, m_buffer); } m_backend->drawFrame(m_backend); - if (m_showOSD && m_messagePainter) { + if (m_showOSD && m_messagePainter && !glContextHasBug(OpenGLBug::IG4ICD_CRASH)) { m_painter.begin(m_paintDev.get()); m_messagePainter->paint(&m_painter); m_painter.end(); @@ -749,38 +810,60 @@ void PainterGL::performDraw() { } void PainterGL::enqueue(const uint32_t* backing) { + if (!backing) { + return; + } QMutexLocker locker(&m_mutex); uint32_t* buffer = nullptr; - if (backing) { - if (m_free.isEmpty()) { - buffer = m_queue.dequeue(); - } else { - buffer = m_free.takeLast(); - } - if (buffer) { - QSize size = m_context->screenDimensions(); - memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL); - } + if (m_free.isEmpty()) { + buffer = m_queue.dequeue(); + } else { + buffer = m_free.takeLast(); + } + if (buffer) { + QSize size = m_context->screenDimensions(); + memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL); } m_queue.enqueue(buffer); } +void PainterGL::enqueue(GLuint tex) { + QMutexLocker locker(&m_mutex); + if (m_freeTex.isEmpty()) { + m_bridgeTexIn = m_queueTex.dequeue(); + } else { + m_bridgeTexIn = m_freeTex.takeLast(); + } + m_queueTex.enqueue(tex); +} + void PainterGL::dequeue() { QMutexLocker locker(&m_mutex); - if (m_queue.isEmpty()) { - return; + if (!m_queue.isEmpty()) { + uint32_t* buffer = m_queue.dequeue(); + if (m_buffer) { + m_free.append(m_buffer); + } + m_buffer = buffer; } - uint32_t* buffer = m_queue.dequeue(); - if (m_buffer) { - m_free.append(m_buffer); - m_buffer = nullptr; + + if (!m_queueTex.isEmpty()) { + if (m_bridgeTexOut != std::numeric_limits::max()) { + m_freeTex.enqueue(m_bridgeTexOut); + } + m_bridgeTexOut = m_queueTex.dequeue(); +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) + if (supportsShaders()) { + mGLES2Context* gl2Backend = reinterpret_cast(m_backend); + gl2Backend->tex = m_bridgeTexOut; + } +#endif } - m_buffer = buffer; } void PainterGL::dequeueAll(bool keep) { QMutexLocker locker(&m_mutex); - uint32_t* buffer = 0; + uint32_t* buffer = nullptr; while (!m_queue.isEmpty()) { buffer = m_queue.dequeue(); if (keep) { @@ -796,6 +879,19 @@ void PainterGL::dequeueAll(bool keep) { m_free.append(m_buffer); m_buffer = nullptr; } + + m_queueTex.clear(); + m_freeTex.clear(); + for (auto tex : m_bridgeTexes) { + if (keep && tex == m_bridgeTexIn) { + continue; + } + m_freeTex.enqueue(tex); + } + if (!keep) { + m_bridgeTexIn = m_freeTex.dequeue(); + m_bridgeTexOut = std::numeric_limits::max(); + } } void PainterGL::setVideoProxy(std::shared_ptr proxy) { @@ -811,12 +907,20 @@ void PainterGL::setShaders(struct VDir* dir) { return; } #if defined(BUILD_GLES2) || defined(BUILD_GLES3) + if (!m_started) { + makeCurrent(); + } + if (m_shader.passes) { mGLES2ShaderDetach(reinterpret_cast(m_backend)); mGLES2ShaderFree(&m_shader); } mGLES2ShaderLoad(&m_shader, dir); mGLES2ShaderAttach(reinterpret_cast(m_backend), static_cast(m_shader.passes), m_shader.nPasses); + + if (!m_started) { + m_gl->doneCurrent(); + } #endif } @@ -825,10 +929,18 @@ void PainterGL::clearShaders() { return; } #if defined(BUILD_GLES2) || defined(BUILD_GLES3) + if (!m_started) { + makeCurrent(); + } + if (m_shader.passes) { mGLES2ShaderDetach(reinterpret_cast(m_backend)); mGLES2ShaderFree(&m_shader); } + + if (!m_started) { + m_gl->doneCurrent(); + } #endif } @@ -851,4 +963,39 @@ int PainterGL::glTex() { #endif } +QOpenGLContext* PainterGL::shareContext() { + if (m_widget) { + return m_widget->context(); + } else { + return m_gl.get(); + } +} + +void PainterGL::updateFramebufferHandle() { + QOpenGLFunctions* fn = m_gl->functions(); + // TODO: Figure out why glFlush doesn't work here on Intel/Windows + if (glContextHasBug(OpenGLBug::CROSS_THREAD_FLUSH)) { + fn->glFinish(); + } else { + fn->glFlush(); + } + + CoreController::Interrupter interrupter(m_context); + if (!m_context->hardwareAccelerated()) { + return; + } + enqueue(m_bridgeTexIn); + m_context->setFramebufferHandle(m_bridgeTexIn); +} + +void PainterGL::swapTex() { + if (!m_started) { + return; + } + + CoreController::Interrupter interrupter(m_context); + emit texSwapped(); + m_context->addFrameAction(std::bind(&PainterGL::swapTex, this)); +} + #endif diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 913a3ee96..3ed31a9d8 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -50,6 +50,8 @@ class mGLWidget : public QOpenGLWidget { Q_OBJECT public: + mGLWidget(QWidget* parent = nullptr); + void setTex(GLuint tex) { m_tex = tex; } void setVBO(GLuint vbo) { m_vbo = vbo; } bool finalizeVAO(); @@ -110,6 +112,9 @@ protected: virtual void paintEvent(QPaintEvent*) override { forceDraw(); } virtual void resizeEvent(QResizeEvent*) override; +private slots: + void setupProxyThread(); + private: void resizePainter(); bool shouldDisableUpdates(); @@ -120,8 +125,11 @@ private: bool m_hasStarted = false; std::unique_ptr m_painter; QThread m_drawThread; + QThread m_proxyThread; std::shared_ptr m_context; mGLWidget* m_gl; + QOffscreenSurface m_proxySurface; + std::unique_ptr m_proxyContext; }; class PainterGL : public QObject { @@ -135,15 +143,21 @@ public: void setContext(std::shared_ptr); void setMessagePainter(MessagePainter*); void enqueue(const uint32_t* backing); + void enqueue(GLuint tex); void stop(); bool supportsShaders() const { return m_supportsShaders; } int glTex(); + QOpenGLContext* shareContext(); + void setVideoProxy(std::shared_ptr); void interrupt(); + // Run on main thread + void swapTex(); + public slots: void create(); void destroy(); @@ -161,13 +175,16 @@ public slots: void showFrameCounter(bool enable); void filter(bool filter); void resizeContext(); + void updateFramebufferHandle(); void setShaders(struct VDir*); void clearShaders(); VideoShader* shaders(); signals: + void created(); void started(); + void texSwapped(); private slots: void doStop(); @@ -182,6 +199,14 @@ private: QList m_free; QQueue m_queue; uint32_t* m_buffer = nullptr; + + std::array m_bridgeTexes; + QQueue m_freeTex; + QQueue m_queueTex; + + GLuint m_bridgeTexIn = std::numeric_limits::max(); + GLuint m_bridgeTexOut = std::numeric_limits::max(); + QPainter m_painter; QMutex m_mutex; QWindow* m_window; diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp new file mode 100644 index 000000000..56e202019 --- /dev/null +++ b/src/platform/qt/ForwarderController.cpp @@ -0,0 +1,254 @@ +/* Copyright (c) 2013-2022 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 "ForwarderController.h" + +#include +#include +#include +#include + +#include "ConfigController.h" +#include "GBAApp.h" +#include "VFileDevice.h" + +#include +#include +#include + +using namespace QGBA; + +#ifdef Q_OS_WIN +const QChar LIST_SPLIT{';'}; +const char* SUFFIX = ".exe"; +#else +const QChar LIST_SPLIT{':'}; +const char* SUFFIX = ""; +#endif + +ForwarderController::ForwarderController(QObject* parent) + : QObject(parent) + , m_originalPath(qgetenv("PATH")) +{ + connect(this, &ForwarderController::buildFailed, this, &ForwarderController::cleanup); + connect(this, &ForwarderController::buildComplete, this, &ForwarderController::cleanup); +} + +void ForwarderController::setGenerator(std::unique_ptr&& generator) { + m_generator = std::move(generator); + connect(m_generator.get(), &ForwarderGenerator::buildFailed, this, &ForwarderController::buildFailed); + connect(m_generator.get(), &ForwarderGenerator::buildComplete, this, &ForwarderController::buildComplete); +} + +void ForwarderController::startBuild(const QString& outFilename) { + if (m_inProgress) { + return; + } + m_inProgress = true; + m_outFilename = outFilename; + +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) + // Amend the path for downloaded programs forwarder-kit + QByteArray arr = m_originalPath; + QStringList path = QString::fromUtf8(arr).split(LIST_SPLIT); + path << ConfigController::cacheDir(); + arr = path.join(LIST_SPLIT).toUtf8(); + qputenv("PATH", arr); +#endif + + QStringList neededTools = m_generator->externalTools(); + for (const auto& tool : neededTools) { + if (!toolInstalled(tool)) { + downloadForwarderKit(); + return; + } + } + if (m_baseFilename.isEmpty()) { + downloadManifest(); + } else { + m_generator->rebuild(m_baseFilename, m_outFilename); + } +} + +void ForwarderController::downloadForwarderKit() { + QString fkUrl("https://github.com/mgba-emu/forwarder-kit/releases/latest/download/forwarder-kit-%1.zip"); +#ifdef Q_OS_WIN64 + fkUrl = fkUrl.arg("win64"); +#elif defined(Q_OS_WIN32) + fkUrl = fkUrl.arg("win32"); +#elif defined(Q_OS_MAC) && (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + // Modern macOS build + fkUrl = fkUrl.arg("macos"); +#else + // TODO + emit buildFailed(); + return; +#endif + QNetworkReply* reply = GBAApp::app()->httpGet(QUrl(fkUrl)); + connectReply(reply, FORWARDER_KIT, &ForwarderController::gotForwarderKit); +} + +void ForwarderController::gotForwarderKit(QNetworkReply* reply) { + emit downloadComplete(FORWARDER_KIT); + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { + emit buildFailed(); + return; + } + + QFile fkZip(ConfigController::cacheDir() + "/forwarder-kit.zip"); + fkZip.open(QIODevice::WriteOnly | QIODevice::Truncate); + QByteArray arr; + do { + arr = reply->read(0x800); + fkZip.write(arr); + } while (!arr.isEmpty()); + fkZip.close(); + + VDir* fkDir = VFileDevice::openArchive(fkZip.fileName()); + + // This has to be done in multiple passes to avoid seeking breaking the listing + QStringList files; + for (VDirEntry* entry = fkDir->listNext(fkDir); entry; entry = fkDir->listNext(fkDir)) { + if (entry->type(entry) != VFS_FILE) { + continue; + } + files << entry->name(entry); + } + + for (const QString& source : files) { + VFile* sourceVf = fkDir->openFile(fkDir, source.toUtf8().constData(), O_RDONLY); + VFile* targetVf = VFileDevice::open(ConfigController::cacheDir() + "/" + source, O_CREAT | O_TRUNC | O_WRONLY); + VFileDevice::copyFile(sourceVf, targetVf); + sourceVf->close(sourceVf); + targetVf->close(targetVf); + + QFile target(ConfigController::cacheDir() + "/" + source); + target.setPermissions(target.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeUser); + } + + fkDir->close(fkDir); + fkZip.remove(); + + downloadManifest(); +} + +void ForwarderController::downloadManifest() { + QNetworkReply* reply = GBAApp::app()->httpGet(QUrl("https://mgba.io/latest.ini")); + connectReply(reply, MANIFEST, &ForwarderController::gotManifest); +} + +void ForwarderController::gotManifest(QNetworkReply* reply) { + emit downloadComplete(MANIFEST); + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { + emit buildFailed(); + return; + } + + QByteArray manifest = reply->readAll(); + QString platform = m_generator->systemName(); + + mUpdaterContext context; + if (!mUpdaterInit(&context, manifest.constData())) { + emit buildFailed(); + return; + } + QString bucket = QLatin1String(mUpdaterGetBucket(&context)); + + mUpdate update; + mUpdaterGetUpdateForChannel(&context, platform.toUtf8().constData(), m_channel.toUtf8().constData(), &update); + + downloadBuild({bucket + update.path}); + mUpdaterDeinit(&context); +} + +void ForwarderController::downloadBuild(const QUrl& url) { + QString extension(QFileInfo(url.path()).suffix()); + // TODO: cache this + QString configDir(ConfigController::cacheDir()); + m_sourceFile.setFileName(QString("%1/%2-%3-%4.%5").arg(configDir) + .arg(projectName) + .arg(m_generator->systemName()) + .arg(channel()) + .arg(extension)); + if (!m_sourceFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + emit buildFailed(); + return; + } + QNetworkReply* reply = GBAApp::app()->httpGet(url); + + connectReply(reply, BASE, &ForwarderController::gotBuild); + connect(reply, &QNetworkReply::readyRead, this, [this, reply]() { + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 != 2) { + return; + } + QByteArray data = reply->readAll(); + m_sourceFile.write(data); + }); +} + +void ForwarderController::gotBuild(QNetworkReply* reply) { + emit downloadComplete(BASE); + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { + emit buildFailed(); + return; + } + + QByteArray data = reply->readAll(); + m_sourceFile.write(data); + m_sourceFile.close(); + + QString extracted = m_generator->extract(m_sourceFile.fileName()); + if (extracted.isNull()) { + emit buildFailed(); + return; + } + m_generator->rebuild(extracted, m_outFilename); +} + +void ForwarderController::cleanup() { + if (m_sourceFile.exists()) { + m_sourceFile.remove(); + } + m_inProgress = false; + +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) + qputenv("PATH", m_originalPath); +#endif +} + +bool ForwarderController::toolInstalled(const QString& tool) { + QByteArray arr = qgetenv("PATH"); + QStringList path = QString::fromUtf8(arr).split(LIST_SPLIT); + for (QDir dir : path) { + QFileInfo exe(dir, tool + SUFFIX); + if (exe.isExecutable()) { + return true; + } + } + return false; +} + +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]() { +#else + connect(reply, qOverload<>(&QNetworkReply::error), this, [this, reply]() { +#endif + emit buildFailed(); + }); + + connect(reply, &QNetworkReply::finished, this, [this, reply, download, next]() { + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 == 3) { + QNetworkReply* newReply = GBAApp::app()->httpGet(reply->header(QNetworkRequest::LocationHeader).toString()); + connectReply(newReply, download, next); + } else { + (this->*next)(reply); + } + }); + connect(reply, &QNetworkReply::downloadProgress, this, [this, download](qint64 bytesReceived, qint64 bytesTotal) { + emit downloadProgress(download, bytesReceived, bytesTotal); + }); + emit downloadStarted(download); +} diff --git a/src/platform/qt/ForwarderController.h b/src/platform/qt/ForwarderController.h new file mode 100644 index 000000000..e53006bc2 --- /dev/null +++ b/src/platform/qt/ForwarderController.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include +#include + +#include "ForwarderGenerator.h" + +#include + +class QNetworkReply; + +namespace QGBA { + +class ForwarderController : public QObject { +Q_OBJECT + +public: + enum Download : int { + MANIFEST, + BASE, + FORWARDER_KIT + }; + ForwarderController(QObject* parent = nullptr); + + void setGenerator(std::unique_ptr&& generator); + ForwarderGenerator* generator() { return m_generator.get(); } + + void setBaseFilename(const QString& path) { m_baseFilename = path; } + void clearBaseFilename() { m_baseFilename = QString(); } + QString baseFilename() const { return m_baseFilename; } + + QString channel() const { return m_channel; } + bool inProgress() const { return m_inProgress; } + +public slots: + void startBuild(const QString& outFilename); + +signals: + void buildStarted(bool needsForwarderKit); + void downloadStarted(Download which); + void downloadComplete(Download which); + void downloadProgress(Download which, qint64 bytesGotten, qint64 bytesTotal); + void buildComplete(); + void buildFailed(); + +private slots: + void gotManifest(QNetworkReply*); + void gotBuild(QNetworkReply*); + void gotForwarderKit(QNetworkReply*); + +private: + void downloadForwarderKit(); + void downloadManifest(); + void downloadBuild(const QUrl&); + bool toolInstalled(const QString& tool); + void cleanup(); + + void connectReply(QNetworkReply*, Download, void (ForwarderController::*next)(QNetworkReply*)); + + QString m_channel{"dev"}; + QString m_outFilename; + std::unique_ptr m_generator; + QFile m_sourceFile; + QString m_baseFilename; + bool m_inProgress = false; + QByteArray m_originalPath; +}; + +} diff --git a/src/platform/qt/ForwarderGenerator.cpp b/src/platform/qt/ForwarderGenerator.cpp new file mode 100644 index 000000000..7230be1e0 --- /dev/null +++ b/src/platform/qt/ForwarderGenerator.cpp @@ -0,0 +1,128 @@ +/* Copyright (c) 2013-2022 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 "ForwarderGenerator.h" + +#include +#include + +#include "ForwarderGenerator3DS.h" +#include "ForwarderGeneratorVita.h" +#include "utils.h" +#include "VFileDevice.h" + +#include + +using namespace QGBA; + +std::unique_ptr ForwarderGenerator::createForSystem(System system) { + switch (system) { + case System::N3DS: + return std::make_unique(); + case System::VITA: + return std::make_unique(); + } + return nullptr; +} + +ForwarderGenerator::ForwarderGenerator(int imageTypes, QObject* parent) + : QObject(parent) +{ + m_images.resize(imageTypes); +} + +void ForwarderGenerator::setImage(int index, const QImage& image) { + if (index < 0 || index >= m_images.count()) { + return; + } + + m_images[index] = image; +} + +QImage ForwarderGenerator::image(int index) const { + if (index >= m_images.size()) { + return {}; + } + return m_images[index]; +} + +QByteArray ForwarderGenerator::hashRom() const { + if (m_romPath.isEmpty()) { + return {}; + } + + QFile romFile(m_romPath); + if (!romFile.open(QIODevice::ReadOnly)) { + return {}; + } + + QCryptographicHash hash(QCryptographicHash::Sha256); + if (!hash.addData(&romFile)) { + return {}; + } + + return hash.result(); +} + +QString ForwarderGenerator::systemName(ForwarderGenerator::System system) { + switch (system) { + case ForwarderGenerator::System::N3DS: + return QLatin1String("3ds"); + case ForwarderGenerator::System::VITA: + return QLatin1String("vita"); + } + + return {}; +} + +QString ForwarderGenerator::systemHumanName(ForwarderGenerator::System system) { + switch (system) { + case ForwarderGenerator::System::N3DS: + return tr("3DS"); + case ForwarderGenerator::System::VITA: + return tr("Vita"); + } + + return {}; +} + +QString ForwarderGenerator::extract(const QString& archive) { + VDir* inArchive = VFileDevice::openArchive(archive); + if (!inArchive) { + return {}; + } + bool gotFile = extractMatchingFile(inArchive, [this](VDirEntry* dirent) -> QString { + if (dirent->type(dirent) != VFS_FILE) { + return {}; + } + QString filename(dirent->name(dirent)); + if (!filename.endsWith("." + extension())) { + return {}; + } + return "tmp." + extension(); + }); + inArchive->close(inArchive); + + if (gotFile) { + return QLatin1String("tmp.") + extension(); + } + return {}; +} + +QString ForwarderGenerator::base36(const QByteArray& bytes, int length) { + static const char* alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QString buffer(length, 'X'); + quint32 running = 0; + for (int i = 0, j = 0; i < length; ++i) { + if (running < 36) { + running <<= 8; + running |= static_cast(bytes[j]); + ++j; + } + buffer[i] = alphabet[running % 36]; + running /= 36; + } + return buffer; +} diff --git a/src/platform/qt/ForwarderGenerator.h b/src/platform/qt/ForwarderGenerator.h new file mode 100644 index 000000000..0db97f148 --- /dev/null +++ b/src/platform/qt/ForwarderGenerator.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace QGBA { + +class ForwarderGenerator : public QObject { +Q_OBJECT + +public: + enum class System { + N3DS, + VITA, + }; + + static std::unique_ptr createForSystem(System); + + void setTitle(const QString& title) { m_title = title; } + void setRom(const QString& path) { m_romPath = path; } + void setImage(int index, const QImage&); + + QString title() const { return m_title; } + QString rom() const { return m_romPath; } + QImage image(int index) const; + + QByteArray hashRom() const; + + virtual QList> imageTypes() const = 0; + virtual System system() const = 0; + QString systemName() const { return systemName(system()); } + QString systemHumanName() const { return systemHumanName(system()); } + virtual QString extension() const = 0; + + virtual QStringList externalTools() const { return {}; } + + static QString systemName(System); + static QString systemHumanName(System); + + virtual QString extract(const QString& archive); + virtual void rebuild(const QString& source, const QString& target) = 0; + +signals: + void buildComplete(); + void buildFailed(); + +protected: + ForwarderGenerator(int imageTypes, QObject* parent = nullptr); + + static QString base36(const QByteArray&, int length); + +private: + QString m_title; + QString m_romPath; + QVector m_images; +}; + +} diff --git a/src/platform/qt/ForwarderGenerator3DS.cpp b/src/platform/qt/ForwarderGenerator3DS.cpp new file mode 100644 index 000000000..51edc6aa7 --- /dev/null +++ b/src/platform/qt/ForwarderGenerator3DS.cpp @@ -0,0 +1,333 @@ +/* Copyright (c) 2013-2022 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 "ForwarderGenerator3DS.h" + +#include "ConfigController.h" + +#include +#include +#include +#include + +#include +#include + +using namespace QGBA; + +ForwarderGenerator3DS::ForwarderGenerator3DS() + : ForwarderGenerator(2) +{ + connect(this, &ForwarderGenerator::buildFailed, this, &ForwarderGenerator3DS::cleanup); + connect(this, &ForwarderGenerator::buildComplete, this, &ForwarderGenerator3DS::cleanup); +} + +QList> ForwarderGenerator3DS::imageTypes() const { + return { + { tr("Icon"), QSize(48, 48) }, + { tr("Banner"), QSize(256, 128) } + }; +} + +void ForwarderGenerator3DS::rebuild(const QString& source, const QString& target) { + m_cia = source; + m_target = target; + extractCia(); +} + +void ForwarderGenerator3DS::extractCia() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("ctrtool"); + + QStringList args; + args << QString("--contents=%0/cxi").arg(ConfigController::cacheDir()); + args << m_cia; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::extractCxi); + m_currentProc->start(QIODevice::ReadOnly); +} + +void ForwarderGenerator3DS::extractCxi() { + QStringList output = QString::fromUtf8(m_currentProc->readAll()).split("\n"); + QString index; + for (const QString& line : output) { + if (!line.contains("|- ContentId:")) { + continue; + } + index = line.trimmed().right(8); + } + m_cxi = ConfigController::cacheDir() + "/cxi.0000." + index; + + m_currentProc = std::make_unique(); + m_currentProc->setProgram("3dstool"); + + QStringList args; + init3dstoolArgs(args, m_cxi); + args << "--exh" << ConfigController::cacheDir() + "/exheader.bin"; + args << "--header" << ConfigController::cacheDir() + "/header.bin"; + args << "--exefs" << ConfigController::cacheDir() + "/exefs.bin"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::extractExefs); + m_currentProc->start(QIODevice::ReadOnly); +} + +void ForwarderGenerator3DS::extractExefs() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("3dstool"); + + QStringList args; + init3dstoolArgs(args, ConfigController::cacheDir() + "/exefs.bin"); + args << "--header" << ConfigController::cacheDir() + "/exeheader.bin"; + args << "--exefs-dir" << ConfigController::cacheDir() + "/exefs"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::processCxi); + m_currentProc->start(QIODevice::ReadOnly); +} + +void ForwarderGenerator3DS::processCxi() { + QByteArray hash = hashRom(); + QByteArray tid = hash.left(4); + quint32 tidNum; + LOAD_32LE(tidNum, 0, tid.data()); + tidNum &= 0x7FFFFFF; + tidNum += 0x0300000; + STORE_32LE(tidNum, 0, tid.data()); + + QFile header(ConfigController::cacheDir() + "/header.bin"); + if (!header.open(QIODevice::ReadWrite)) { + emit buildFailed(); + return; + } + header.seek(0x108); + header.write(tid); + header.seek(0x118); + header.write(tid); + + QByteArray productCode("MGBA-"); + productCode += base36(hash, 11).toLatin1(); + header.seek(0x150); + header.write(productCode); + + header.seek(0x18D); + QByteArray type = header.read(3); + type[0] = type[0] | 1; // Has romfs + type[2] = type[2] & ~2; // Can mount romfs + header.seek(0x18D); + header.write(type); + header.close(); + + QFile exheader(ConfigController::cacheDir() + "/exheader.bin"); + if (!exheader.open(QIODevice::ReadWrite)) { + emit buildFailed(); + return; + } + exheader.seek(0x1C8); + exheader.write(tid); + exheader.seek(0x200); + exheader.write(tid); + exheader.seek(0x600); + exheader.write(tid); + exheader.close(); + + prepareRomfs(); +} + +void ForwarderGenerator3DS::prepareRomfs() { + QDir romfsDir(ConfigController::cacheDir()); + + romfsDir.mkdir("romfs"); + romfsDir.cd("romfs"); + + QFileInfo info(rom()); + QByteArray buffer(info.fileName().toUtf8()); + QFile filename(romfsDir.filePath("filename")); + if (!filename.open(QIODevice::Truncate | QIODevice::WriteOnly)) { + emit buildFailed(); + return; + } + if (filename.write(buffer) != filename.size()) { + emit buildFailed(); + return; + } + filename.close(); + + if (!QFile::copy(info.filePath(), romfsDir.filePath(info.fileName()))) { + emit buildFailed(); + return; + } + + buildRomfs(); +} + +void ForwarderGenerator3DS::buildRomfs() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("3dstool"); + + QStringList args; + init3dstoolArgs(args, ConfigController::cacheDir() + "/romfs.bin", "romfs"); + args << "--romfs-dir"; + args << ConfigController::cacheDir() + "/romfs"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildSmdh); + m_currentProc->start(QIODevice::NotOpen); +} + +void ForwarderGenerator3DS::buildSmdh() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("bannertool"); + + if (image(0).isNull()) { + QFile::copy(":/res/mgba-48.png", ConfigController::cacheDir() + "/smdh.png"); + } else { + image(0).save(ConfigController::cacheDir() + "/smdh.png", "PNG"); + } + + QStringList args; + args << "makesmdh"; + + args << "-s" << title(); + args << "-l" << title(); + args << "-p" << projectName + QString(" Forwarder"); + args << "-i" << ConfigController::cacheDir() + "/smdh.png"; + args << "-o" << ConfigController::cacheDir() + "/exefs/icon.icn"; + m_currentProc->setArguments(args); + + if (image(1).isNull()) { + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildExefs); + } else { + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildBanner); + } + m_currentProc->start(QIODevice::ReadOnly); +} + +void ForwarderGenerator3DS::buildBanner() { + QFile banner(ConfigController::cacheDir() + "/exefs/banner.bnr"); + if (!banner.open(QIODevice::ReadOnly)) { + emit buildFailed(); + return; + } + + banner.seek(0x84); + QByteArray bcwavOffsetBuffer(banner.read(4)); + qint64 bcwavOffset; + LOAD_64LE(bcwavOffset, 0, bcwavOffsetBuffer.data()); + banner.seek(bcwavOffset); + QByteArray bcwav(banner.readAll()); + QFile bcwavFile(ConfigController::cacheDir() + "/banner.bcwav"); + if (!bcwavFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + emit buildFailed(); + return; + } + bcwavFile.write(bcwav); + banner.close(); + + m_currentProc = std::make_unique(); + m_currentProc->setProgram("bannertool"); + + image(1).save(ConfigController::cacheDir() + "/banner.png", "PNG"); + + QStringList args; + args << "makebanner"; + + args << "-i" << ConfigController::cacheDir() + "/banner.png"; + args << "-ca" << ConfigController::cacheDir() + "/banner.bcwav"; + args << "-o" << ConfigController::cacheDir() + "/exefs/banner.bnr"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildExefs); + m_currentProc->start(QIODevice::ReadOnly); +} + +void ForwarderGenerator3DS::buildExefs() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("3dstool"); + + QStringList args; + init3dstoolArgs(args, ConfigController::cacheDir() + "/exefs.bin", "exefs"); + args << "--header" << ConfigController::cacheDir() + "/exeheader.bin"; + args << "--exefs-dir" << ConfigController::cacheDir() + "/exefs"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildCxi); + m_currentProc->start(QIODevice::NotOpen); +} + +void ForwarderGenerator3DS::buildCxi() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("3dstool"); + + QFile cxi(m_cxi); + cxi.remove(); + + QStringList args; + init3dstoolArgs(args, m_cxi, "cxi"); + args << "--exh" << ConfigController::cacheDir() + "/exheader.bin"; + args << "--header" << ConfigController::cacheDir() + "/header.bin"; + args << "--exefs" << ConfigController::cacheDir() + "/exefs.bin"; + args << "--romfs" << ConfigController::cacheDir() + "/romfs.bin"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildCia); + m_currentProc->start(QIODevice::NotOpen); +} + +void ForwarderGenerator3DS::buildCia() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("makerom"); + + QStringList args; + args << "-f" << "cia"; + args << "-o" << m_target; + args << "-content" << m_cxi + ":0:0"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildComplete); + m_currentProc->start(QIODevice::NotOpen); +} + +void ForwarderGenerator3DS::cleanup() { + for (const QString& path : {m_cia, m_cxi}) { + QFile file(path); + if (file.exists()) { + file.remove(); + } + } + + QDir cacheDir(ConfigController::cacheDir()); + QStringList files{ + "romfs.bin", + "exefs.bin", + "exheader.bin", + "exeheader.bin", + "header.bin", + "smdh.png", + "banner.png", + "banner.bcwav", + }; + for (QString path : files) { + QFile file(cacheDir.filePath(path)); + if (file.exists()) { + file.remove(); + } + } + + for (QString path : {"romfs", "exefs"}) { + QDir dir(cacheDir.filePath(path)); + dir.removeRecursively(); + } +} + +void ForwarderGenerator3DS::init3dstoolArgs(QStringList& args, const QString& file, const QString& createType) { + if (createType.isEmpty()) { + args << "-xf" << file; + } else { + args << "-cf" << file; + args << "-t" << createType; + } +} diff --git a/src/platform/qt/ForwarderGenerator3DS.h b/src/platform/qt/ForwarderGenerator3DS.h new file mode 100644 index 000000000..b22585063 --- /dev/null +++ b/src/platform/qt/ForwarderGenerator3DS.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include "ForwarderGenerator.h" + +#include + +#include + +namespace QGBA { + +class ForwarderGenerator3DS final : public ForwarderGenerator { +Q_OBJECT + +public: + ForwarderGenerator3DS(); + + QList> imageTypes() const override; + System system() const override { return System::N3DS; } + QString extension() const override { return QLatin1String("cia"); } + + virtual QStringList externalTools() const { return {"bannertool", "3dstool", "ctrtool", "makerom"}; } + + void rebuild(const QString& source, const QString& target) override; + +private slots: + void extractCia(); + void extractCxi(); + void extractExefs(); + void processCxi(); + void prepareRomfs(); + void buildRomfs(); + void buildSmdh(); + void buildBanner(); + void buildExefs(); + void buildCxi(); + void buildCia(); + + void cleanup(); + +private: + void init3dstoolArgs(QStringList& args, const QString& file, const QString& createType = {}); + + std::unique_ptr m_currentProc; + QString m_cia; + QString m_cxi; + QString m_target; +}; + +} diff --git a/src/platform/qt/ForwarderGeneratorVita.cpp b/src/platform/qt/ForwarderGeneratorVita.cpp new file mode 100644 index 000000000..f6e97e5d3 --- /dev/null +++ b/src/platform/qt/ForwarderGeneratorVita.cpp @@ -0,0 +1,143 @@ +/* Copyright (c) 2013-2022 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 "ForwarderGeneratorVita.h" + +#include +#include + +#include "VFileDevice.h" + +#include +#include + +using namespace QGBA; + +ForwarderGeneratorVita::ForwarderGeneratorVita() + : ForwarderGenerator(3) +{ +} + +QList> ForwarderGeneratorVita::imageTypes() const { + return { + { tr("Bubble"), QSize(128, 128) }, + { tr("Background"), QSize(840, 500) }, + { tr("Startup"), QSize(280, 158) } + }; +} + +void ForwarderGeneratorVita::rebuild(const QString& source, const QString& target) { + QFile vpkFile(source); + VDir* outdir = VDirOpenZip(target.toLocal8Bit().constData(), O_WRONLY | O_CREAT | O_TRUNC); + if (outdir && !copyAssets(source, outdir)) { + outdir->close(outdir); + outdir = nullptr; + } + vpkFile.remove(); + if (!outdir) { + emit buildFailed(); + return; + } + + VFile* sfo = outdir->openFile(outdir, "sce_sys/param.sfo", O_WRONLY); + writeSfo(sfo); + sfo->close(sfo); + + QFileInfo info(rom()); + QByteArray buffer(info.fileName().toUtf8()); + VFile* filename = outdir->openFile(outdir, "filename", O_WRONLY); + filename->write(filename, buffer.constData(), buffer.size()); + filename->close(filename); + + VFile* romfileOut = outdir->openFile(outdir, buffer.constData(), O_WRONLY); + VFileDevice romfileIn(rom(), QIODevice::ReadOnly); + VFileDevice::copyFile(romfileIn, romfileOut); + romfileIn.close(); + romfileOut->close(romfileOut); + + if (!image(0).isNull()) { + injectImage(outdir, "sce_sys/icon0.png", 0); + } + if (!image(1).isNull()) { + injectImage(outdir, "sce_sys/livearea/contents/bg.png", 1); + } + if (!image(2).isNull()) { + injectImage(outdir, "sce_sys/livearea/contents/startup.png", 2); + } + + outdir->close(outdir); + + emit buildComplete(); +} + +bool ForwarderGeneratorVita::copyAssets(const QString& vpk, VDir* outdir) { + VDir* indir = VDirOpenZip(vpk.toLocal8Bit().constData(), O_RDONLY); + if (!indir) { + return false; + } + + bool ok = true; + for (VDirEntry* dirent = indir->listNext(indir); dirent; dirent = indir->listNext(indir)) { + if (dirent->name(dirent) == QLatin1String("sce_sys/param.sfo")) { + continue; + } + if (dirent->name(dirent) == QLatin1String("sce_sys/icon0.png") && !image(0).isNull()) { + continue; + } + if (dirent->name(dirent) == QLatin1String("sce_sys/livearea/contents/bg.png") && !image(1).isNull()) { + continue; + } + if (dirent->name(dirent) == QLatin1String("sce_sys/livearea/contents/startup.png") && !image(2).isNull()) { + continue; + } + if (dirent->type(dirent) != VFS_FILE) { + continue; + } + + VFile* infile = indir->openFile(indir, dirent->name(dirent), O_RDONLY); + if (!infile) { + ok = false; + break; + } + + VFile* outfile = outdir->openFile(outdir, dirent->name(dirent), O_WRONLY); + if (!outfile) { + infile->close(infile); + ok = false; + break; + } + + VFileDevice::copyFile(infile, outfile); + + infile->close(infile); + outfile->close(outfile); + } + + indir->close(indir); + return ok; +} + +QString ForwarderGeneratorVita::makeSerial() const { + QString serial("MF"); + serial += base36(hashRom(), 7); + return serial; +} + +void ForwarderGeneratorVita::writeSfo(VFile* out) { + Table sfo; + QByteArray serial(makeSerial().toLocal8Bit()); + QByteArray titleBytes(title().toUtf8()); + SfoInit(&sfo); + SfoSetTitle(&sfo, titleBytes.constData()); + SfoAddStrValue(&sfo, "TITLE_ID", serial.constData()); + SfoWrite(&sfo, out); + SfoDeinit(&sfo); +} + +void ForwarderGeneratorVita::injectImage(VDir* out, const char* name, int index) { + VFile* outfile = out->openFile(out, name, O_WRONLY); + VFileDevice outdev(outfile); + image(index).convertToFormat(QImage::Format_Indexed8).save(&outdev, "PNG"); +} diff --git a/src/platform/qt/ForwarderGeneratorVita.h b/src/platform/qt/ForwarderGeneratorVita.h new file mode 100644 index 000000000..e3a083e6e --- /dev/null +++ b/src/platform/qt/ForwarderGeneratorVita.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include "ForwarderGenerator.h" + +struct VDir; +struct VFile; + +namespace QGBA { + +class ForwarderGeneratorVita final : public ForwarderGenerator { +Q_OBJECT + +public: + ForwarderGeneratorVita(); + + QList> imageTypes() const override; + System system() const override { return System::VITA; } + QString extension() const override { return QLatin1String("vpk"); } + + void rebuild(const QString& source, const QString& target) override; + +private: + bool copyAssets(const QString& vpk, VDir* out); + QString makeSerial() const; + void writeSfo(VFile* out); + void injectImage(VDir* out, const char* name, int index); +}; + +} diff --git a/src/platform/qt/ForwarderView.cpp b/src/platform/qt/ForwarderView.cpp new file mode 100644 index 000000000..0546e7180 --- /dev/null +++ b/src/platform/qt/ForwarderView.cpp @@ -0,0 +1,249 @@ +/* Copyright (c) 2013-2022 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 "ForwarderView.h" + +#include +#include + +#include "ForwarderGenerator.h" +#include "GBAApp.h" +#include "utils.h" + +using namespace QGBA; + +ForwarderView::ForwarderView(QWidget* parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + + connectBrowseButton(m_ui.romBrowse, m_ui.romFilename, tr("Select ROM file"), false, romFilters()); + connectBrowseButton(m_ui.outputBrowse, m_ui.outputFilename, tr("Select output filename"), true); + connectBrowseButton(m_ui.baseBrowse, m_ui.baseFilename, tr("Select base file")); + + connect(m_ui.romFilename, &QLineEdit::textChanged, this, &ForwarderView::validate); + connect(m_ui.outputFilename, &QLineEdit::textChanged, this, &ForwarderView::validate); + connect(m_ui.baseFilename, &QLineEdit::textChanged, this, &ForwarderView::validate); + connect(m_ui.title, &QLineEdit::textChanged, this, &ForwarderView::validate); + connect(m_ui.baseType, qOverload(&QComboBox::currentIndexChanged), this, &ForwarderView::validate); + + connect(m_ui.imageSelect, qOverload(&QComboBox::currentIndexChanged), this, &ForwarderView::setActiveImage); + connect(m_ui.imageBrowse, &QAbstractButton::clicked, this, &ForwarderView::selectImage); + + connect(&m_controller, &ForwarderController::buildComplete, this, [this]() { + QMessageBox* message = new QMessageBox(QMessageBox::Information, tr("Build finished"), + tr("Forwarder finished building"), + QMessageBox::Ok, parentWidget(), Qt::Sheet); + message->setAttribute(Qt::WA_DeleteOnClose); + message->show(); + accept(); + }); + connect(&m_controller, &ForwarderController::buildFailed, this, [this]() { + QMessageBox* error = new QMessageBox(QMessageBox::Critical, tr("Build failed"), + tr("Failed to build forwarder"), + QMessageBox::Ok, this, Qt::Sheet); + error->setAttribute(Qt::WA_DeleteOnClose); + error->show(); + + m_ui.progressBar->setValue(0); + m_ui.progressBar->setEnabled(false); + validate(); + }); + connect(&m_controller, &ForwarderController::downloadStarted, this, [this](ForwarderController::Download download) { + m_currentDownload = download; + m_downloadProgress = 0; + if (download == ForwarderController::FORWARDER_KIT) { + m_needsForwarderKit = true; + } + updateProgress(); + }); + connect(&m_controller, &ForwarderController::downloadComplete, this, [this](ForwarderController::Download download) { + if (m_currentDownload != download) { + return; + } + m_downloadProgress = 1; + updateProgress(); + }); + connect(&m_controller, &ForwarderController::downloadProgress, this, [this](ForwarderController::Download download, qint64 bytesReceived, qint64 bytesTotal) { + if (m_currentDownload != download) { + return; + } + if (bytesTotal <= 0 || bytesTotal < bytesReceived) { + return; + } + m_downloadProgress = bytesReceived / static_cast(bytesTotal); + updateProgress(); + }); + + connect(m_ui.system3DS, &QAbstractButton::clicked, this, [this]() { + setSystem(ForwarderGenerator::System::N3DS); + }); + connect(m_ui.systemVita, &QAbstractButton::clicked, this, [this]() { + setSystem(ForwarderGenerator::System::VITA); + }); + + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, this, &ForwarderView::build); +} + +void ForwarderView::build() { + if (!m_controller.generator()) { + return; + } + m_controller.generator()->setTitle(m_ui.title->text()); + m_controller.generator()->setRom(m_ui.romFilename->text()); + if (m_ui.baseType->currentIndex() == 2) { + m_controller.setBaseFilename(m_ui.baseFilename->text()); + } else { + m_controller.clearBaseFilename(); + } + m_controller.startBuild(m_ui.outputFilename->text()); + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + m_ui.progressBar->setEnabled(true); + + m_currentDownload = ForwarderController::FORWARDER_KIT; + m_downloadProgress = 0; + m_needsForwarderKit = false; + updateProgress(); +} + +void ForwarderView::validate() { + bool valid = true; + if (m_ui.romFilename->text().isEmpty()) { + valid = false; + } else if (!QFileInfo(m_ui.romFilename->text()).exists()) { + valid = false; + } + if (m_ui.outputFilename->text().isEmpty()) { + valid = false; + } + if (m_ui.title->text().isEmpty()) { + valid = false; + } + if (!m_ui.system->checkedButton()) { + valid = false; + } + if (m_ui.baseType->currentIndex() < 1) { + valid = false; + } + if (m_ui.baseType->currentIndex() == 2) { + m_ui.baseFilename->setEnabled(true); + m_ui.baseLabel->setEnabled(true); + m_ui.baseBrowse->setEnabled(true); + if (m_ui.baseFilename->text().isEmpty()) { + valid = false; + } + } else { + m_ui.baseFilename->setEnabled(true); + m_ui.baseLabel->setEnabled(true); + m_ui.baseBrowse->setEnabled(true); + } + if (m_controller.inProgress()) { + valid = false; + } + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); +} + +void ForwarderView::setSystem(ForwarderGenerator::System system) { + m_controller.setGenerator(ForwarderGenerator::createForSystem(system)); + auto types = m_controller.generator()->imageTypes(); + m_images.clear(); + m_images.resize(types.count()); + m_ui.imageSelect->clear(); + for (const auto& pair : types) { + m_ui.imageSelect->addItem(pair.first); + } + m_ui.imageSelect->setEnabled(true); + m_ui.imagePreview->setEnabled(true); + m_ui.imageBrowse->setEnabled(true); + m_ui.imagesLabel->setEnabled(true); + m_ui.preferredLabel->setEnabled(true); + m_ui.preferredWidth->setEnabled(true); + m_ui.preferredX->setEnabled(true); + m_ui.preferredHeight->setEnabled(true); +} + +void ForwarderView::connectBrowseButton(QAbstractButton* button, QLineEdit* lineEdit, const QString& title, bool save, const QString& filter) { + connect(button, &QAbstractButton::clicked, lineEdit, [this, lineEdit, save, title, filter]() { + QString filename; + QString usedFilter = filter; + if (filter.isEmpty()) { + // Use the forwarder type, if selected + ForwarderGenerator* generator = m_controller.generator(); + if (generator) { + usedFilter = tr("%1 installable package (*.%2)").arg(generator->systemHumanName()).arg(generator->extension()); + } + } + if (save) { + filename = GBAApp::app()->getSaveFileName(this, title, usedFilter); + } else { + filename = GBAApp::app()->getOpenFileName(this, title, usedFilter); + } + if (filename.isEmpty()) { + return; + } + lineEdit->setText(filename); + }); +} + +void ForwarderView::selectImage() { + QString filename = GBAApp::app()->getOpenFileName(this, tr("Select an image"), {}); + if (filename.isEmpty()) { + return; + } + + QImage image(filename); + if (image.isNull()) { + return; + } + image = image.scaled(m_activeSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + + m_ui.imagePreview->setPixmap(QPixmap::fromImage(image)); + m_ui.useDefaultImage->setChecked(false); + m_controller.generator()->setImage(m_currentImage, image); +} + +void ForwarderView::setActiveImage(int index) { + if (index < 0) { + m_currentImage = -1; + m_activeSize = QSize(); + return; + } + if (!m_controller.generator()) { + return; + } + auto types = m_controller.generator()->imageTypes(); + if (index >= types.count()) { + return; + } + m_currentImage = index; + m_activeSize = types[index].second; + m_ui.preferredWidth->setText(QString::number(m_activeSize.width())); + m_ui.preferredHeight->setText(QString::number(m_activeSize.height())); + m_ui.imagePreview->setMaximumSize(m_activeSize); + m_ui.imagePreview->setPixmap(QPixmap::fromImage(m_controller.generator()->image(index))); +} + +void ForwarderView::updateProgress() { + switch (m_currentDownload) { + case ForwarderController::FORWARDER_KIT: + m_ui.progressBar->setValue(m_downloadProgress * 450); + break; + case ForwarderController::MANIFEST: + if (m_needsForwarderKit) { + m_ui.progressBar->setValue(450 + m_downloadProgress * 50); + } else { + m_ui.progressBar->setValue(m_downloadProgress * 100); + } + break; + case ForwarderController::BASE: + if (m_needsForwarderKit) { + m_ui.progressBar->setValue(500 + m_downloadProgress * 500); + } else { + m_ui.progressBar->setValue(100 + m_downloadProgress * 900); + } + break; + } +} diff --git a/src/platform/qt/ForwarderView.h b/src/platform/qt/ForwarderView.h new file mode 100644 index 000000000..51ca5356c --- /dev/null +++ b/src/platform/qt/ForwarderView.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include "ForwarderController.h" +#include "ForwarderGenerator.h" + +#include +#include + +#include "ui_ForwarderView.h" + +namespace QGBA { + +class ForwarderView : public QDialog { +Q_OBJECT + +public: + ForwarderView(QWidget* parent = nullptr); + +private slots: + void build(); + void validate(); + +private: + void setSystem(ForwarderGenerator::System); + void connectBrowseButton(QAbstractButton* button, QLineEdit* lineEdit, const QString& title, bool save = false, const QString& filter = {}); + void selectImage(); + void setActiveImage(int); + void updateProgress(); + + ForwarderController m_controller; + QVector m_images; + int m_currentImage; + QSize m_activeSize; + + qreal m_downloadProgress; + ForwarderController::Download m_currentDownload; + bool m_needsForwarderKit; + + Ui::ForwarderView m_ui; +}; + +} diff --git a/src/platform/qt/ForwarderView.ui b/src/platform/qt/ForwarderView.ui new file mode 100644 index 000000000..f83facf1c --- /dev/null +++ b/src/platform/qt/ForwarderView.ui @@ -0,0 +1,439 @@ + + + QGBA::ForwarderView + + + + 0 + 0 + 710 + 465 + + + + Create forwarder + + + + + + Files + + + + + + ROM file: + + + + + + + + + + Browse + + + + + + + Output filename: + + + + + + + + + + Browse + + + + + + + Forwarder base: + + + + + + + + 0 + 0 + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + + + + false + + + Base file: + + + + + + + false + + + + + + + false + + + Browse + + + + + + + Qt::Horizontal + + + + + + + + + + + 0 + 0 + + + + System + + + + + + 3DS + + + system + + + + + + + Vita + + + system + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Presentation + + + + + + + + Title: + + + + + + + + + + false + + + Images: + + + + + + + false + + + + 0 + 0 + + + + + + + + Use default image + + + true + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + false + + + + 0 + 0 + + + + + 32 + 32 + + + + + 128 + 128 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + true + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + + + + false + + + Preferred size: + + + + + + + false + + + 0 + + + + + + + false + + + + 0 + 0 + + + + × + + + + + + + false + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + false + + + + 0 + 0 + + + + Select image file + + + + + + + + + + false + + + 1000 + + + false + + + + + + + + + buttonBox + rejected() + QGBA::ForwarderView + deleteLater() + + + 354 + 439 + + + 354 + 232 + + + + + + + + diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index d53985313..c7c380a7c 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,10 @@ #include "DiscordCoordinator.h" #endif +#ifdef BUILD_SDL +#include "input/SDLInputDriver.h" +#endif + using namespace QGBA; static GBAApp* g_app = nullptr; @@ -81,6 +86,10 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config) m_configController->updateOption("useDiscordPresence"); #endif +#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) + m_netman.setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); +#endif + cleanupAfterUpdate(); connect(this, &GBAApp::aboutToQuit, this, &GBAApp::cleanup); @@ -240,6 +249,19 @@ bool GBAApp::reloadGameDB() { } #endif +QNetworkAccessManager* GBAApp::netman() { + return &m_netman; +} + +QNetworkReply* GBAApp::httpGet(const QUrl& url) { + QNetworkRequest req(url); + req.setHeader(QNetworkRequest::UserAgentHeader, + QString("%1/%2 (+https://mgba.io) is definitely not Mozilla/5.0") + .arg(projectName) + .arg(projectVersion)); + return m_netman.get(req); +} + qint64 GBAApp::submitWorkerJob(std::function job, std::function callback) { return submitWorkerJob(job, nullptr, callback); } @@ -291,6 +313,25 @@ bool GBAApp::waitOnJob(qint64 jobId, QObject* context, std::function ca return true; } +void GBAApp::suspendScreensaver() { +#ifdef BUILD_SDL + SDL::suspendScreensaver(); +#endif +} + +void GBAApp::resumeScreensaver() { +#ifdef BUILD_SDL + SDL::resumeScreensaver(); +#endif +} + +void GBAApp::setScreensaverSuspendable(bool suspendable) { + UNUSED(suspendable); +#ifdef BUILD_SDL + SDL::setScreensaverSuspendable(suspendable); +#endif +} + void GBAApp::cleanupAfterUpdate() { // Remove leftover updater if there's one present QDir configDir(ConfigController::configDir()); diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index d39f3a4a8..3110b0733 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -11,7 +11,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -70,6 +71,9 @@ public: const NoIntroDB* gameDB() const { return m_db; } bool reloadGameDB(); + QNetworkAccessManager* netman(); + QNetworkReply* httpGet(const QUrl&); + qint64 submitWorkerJob(std::function job, std::function callback = {}); qint64 submitWorkerJob(std::function job, QObject* context, std::function callback); bool removeWorkerJob(qint64 jobId); @@ -82,6 +86,10 @@ public slots: void restartForUpdate(); Window* newWindow(); + void suspendScreensaver(); + void resumeScreensaver(); + void setScreensaverSuspendable(bool); + signals: void jobFinished(qint64 jobId); @@ -128,6 +136,8 @@ private: QFont m_monospace; NoIntroDB* m_db = nullptr; + + QNetworkAccessManager m_netman; }; } diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 7890812e7..88963afa2 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -13,6 +13,7 @@ #include #include +#include "input/InputMapper.h" #include "InputController.h" #include "KeyEditor.h" @@ -214,8 +215,9 @@ void GBAKeyEditor::setNext() { void GBAKeyEditor::save() { #ifdef BUILD_SDL - m_controller->unbindAllAxes(m_type); - m_controller->unbindAllHats(m_type); + InputMapper mapper = m_controller->mapper(m_type); + mapper.unbindAllAxes(); + mapper.unbindAllHats(); #endif bindKey(m_keyDU, GBA_KEY_UP); @@ -260,7 +262,7 @@ void GBAKeyEditor::refresh() { #endif } -void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBAKey key) { +void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, int key) { #ifdef BUILD_SDL if (m_type == SDL_BINDING_BUTTON) { int value = mInputQueryBinding(map, m_type, key); @@ -275,14 +277,14 @@ void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBA void GBAKeyEditor::lookupAxes(const mInputMap* map) { mInputEnumerateAxes(map, m_type, [](int axis, const mInputAxis* description, void* user) { GBAKeyEditor* self = static_cast(user); - if (description->highDirection != GBA_KEY_NONE) { - KeyEditor* key = self->keyById(static_cast(description->highDirection)); + if (description->highDirection != -1) { + KeyEditor* key = self->keyById(description->highDirection); if (key) { key->setValueAxis(axis, GamepadAxisEvent::POSITIVE); } } - if (description->lowDirection != GBA_KEY_NONE) { - KeyEditor* key = self->keyById(static_cast(description->lowDirection)); + if (description->lowDirection != -1) { + KeyEditor* key = self->keyById(description->lowDirection); if (key) { key->setValueAxis(axis, GamepadAxisEvent::NEGATIVE); } @@ -295,25 +297,25 @@ void GBAKeyEditor::lookupHats(const mInputMap* map) { int i = 0; while (mInputQueryHat(map, m_type, i, &bindings)) { if (bindings.up >= 0) { - KeyEditor* key = keyById(static_cast(bindings.up)); + KeyEditor* key = keyById(bindings.up); if (key) { key->setValueHat(i, GamepadHatEvent::UP); } } if (bindings.right >= 0) { - KeyEditor* key = keyById(static_cast(bindings.right)); + KeyEditor* key = keyById(bindings.right); if (key) { key->setValueHat(i, GamepadHatEvent::RIGHT); } } if (bindings.down >= 0) { - KeyEditor* key = keyById(static_cast(bindings.down)); + KeyEditor* key = keyById(bindings.down); if (key) { key->setValueHat(i, GamepadHatEvent::DOWN); } } if (bindings.left >= 0) { - KeyEditor* key = keyById(static_cast(bindings.left)); + KeyEditor* key = keyById(bindings.left); if (key) { key->setValueHat(i, GamepadHatEvent::LEFT); } @@ -323,16 +325,17 @@ void GBAKeyEditor::lookupHats(const mInputMap* map) { } #endif -void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) { +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) { - m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key); + mapper.bindAxis(keyEditor->axis(), keyEditor->direction(), key); } if (m_type == SDL_BINDING_BUTTON && keyEditor->hat() >= 0) { - m_controller->bindHat(m_type, keyEditor->hat(), keyEditor->hatDirection(), key); + mapper.bindHat(keyEditor->hat(), keyEditor->hatDirection(), key); } #endif - m_controller->bindKey(m_type, keyEditor->value(), key); + mapper.bindKey(keyEditor->value(), key); } bool GBAKeyEditor::findFocus(KeyEditor* needle) { @@ -358,7 +361,7 @@ void GBAKeyEditor::selectGamepad(int index) { } #endif -KeyEditor* GBAKeyEditor::keyById(GBAKey key) { +KeyEditor* GBAKeyEditor::keyById(int key) { switch (key) { case GBA_KEY_UP: return m_keyDU; @@ -395,14 +398,13 @@ void GBAKeyEditor::setLocation(QWidget* widget, qreal x, qreal y) { #ifdef BUILD_SDL void GBAKeyEditor::updateJoysticks() { - m_controller->updateJoysticks(); - m_controller->recalibrateAxes(); + m_controller->update(); // Block the currentIndexChanged signal while rearranging the combo box auto wasBlocked = m_profileSelect->blockSignals(true); m_profileSelect->clear(); m_profileSelect->addItems(m_controller->connectedGamepads(m_type)); - int activeGamepad = m_controller->gamepad(m_type); + int activeGamepad = m_controller->gamepadIndex(m_type); m_profileSelect->setCurrentIndex(activeGamepad); m_profileSelect->blockSignals(wasBlocked); diff --git a/src/platform/qt/GBAKeyEditor.h b/src/platform/qt/GBAKeyEditor.h index 24b1e5c4a..9578abda9 100644 --- a/src/platform/qt/GBAKeyEditor.h +++ b/src/platform/qt/GBAKeyEditor.h @@ -54,8 +54,8 @@ private: void setLocation(QWidget* widget, qreal x, qreal y); - void lookupBinding(const mInputMap*, KeyEditor*, GBAKey); - void bindKey(const KeyEditor*, GBAKey); + void lookupBinding(const mInputMap*, KeyEditor*, int key); + void bindKey(const KeyEditor*, int key); bool findFocus(KeyEditor* needle = nullptr); @@ -64,7 +64,7 @@ private: void lookupHats(const mInputMap*); #endif - KeyEditor* keyById(GBAKey); + KeyEditor* keyById(int); QComboBox* m_profileSelect = nullptr; QWidget* m_clear = nullptr; diff --git a/src/platform/qt/GameBoy.cpp b/src/platform/qt/GameBoy.cpp index e0271d322..b07982721 100644 --- a/src/platform/qt/GameBoy.cpp +++ b/src/platform/qt/GameBoy.cpp @@ -35,10 +35,15 @@ static const QList s_mbcList{ GB_HuC3, GB_UNL_WISDOM_TREE, GB_UNL_PKJD, + GB_UNL_NT_OLD_1, + GB_UNL_NT_OLD_2, GB_UNL_NT_NEW, GB_UNL_BBD, GB_UNL_HITEK, + GB_UNL_GGB81, + GB_UNL_LI_CHENG, GB_UNL_SACHEN_MMC1, + GB_UNL_SACHEN_MMC2, }; static QMap s_gbModelNames; @@ -87,10 +92,14 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) { s_mbcNames[GB_POCKETCAM] = tr("Pocket Cam"); s_mbcNames[GB_TAMA5] = tr("TAMA5"); s_mbcNames[GB_UNL_WISDOM_TREE] = tr("Wisdom Tree"); + s_mbcNames[GB_UNL_NT_OLD_1] = tr("NT (old 1)"); + s_mbcNames[GB_UNL_NT_OLD_2] = tr("NT (old 2)"); s_mbcNames[GB_UNL_NT_NEW] = tr("NT (new)"); s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond"); s_mbcNames[GB_UNL_BBD] = tr("BBD"); s_mbcNames[GB_UNL_HITEK] = tr("Hitek"); + s_mbcNames[GB_UNL_GGB81] = tr("GGB-81"); + s_mbcNames[GB_UNL_LI_CHENG] = tr("Li Cheng"); s_mbcNames[GB_UNL_SACHEN_MMC1] = tr("Sachen (MMC1)"); s_mbcNames[GB_UNL_SACHEN_MMC2] = tr("Sachen (MMC2)"); } diff --git a/src/platform/qt/IOViewer.cpp b/src/platform/qt/IOViewer.cpp index 46c0d8dbb..f89b6c091 100644 --- a/src/platform/qt/IOViewer.cpp +++ b/src/platform/qt/IOViewer.cpp @@ -1580,7 +1580,7 @@ IOViewer::IOViewer(std::shared_ptr controller, QWidget* parent) case mPLATFORM_GBA: regs = GBAIORegisterNames; maxRegs = REG_MAX >> 1; - m_base = BASE_IO; + m_base = GBA_BASE_IO; m_width = 1; break; #endif diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index a0fb91397..d8b5dd783 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 @@ -6,8 +6,8 @@ #include "InputController.h" #include "ConfigController.h" -#include "GamepadAxisEvent.h" -#include "GamepadButtonEvent.h" +#include "input/Gamepad.h" +#include "input/GamepadButtonEvent.h" #include "InputProfile.h" #include "LogController.h" #include "utils.h" @@ -25,11 +25,6 @@ using namespace QGBA; -#ifdef BUILD_SDL -int InputController::s_sdlInited = 0; -mSDLEvents InputController::s_sdlEvents; -#endif - InputController::InputController(int playerId, QWidget* topLevel, QObject* parent) : QObject(parent) , m_playerId(playerId) @@ -38,24 +33,18 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren { mInputMapInit(&m_inputMap, &GBAInputInfo); -#ifdef BUILD_SDL - if (s_sdlInited == 0) { - mSDLInitEvents(&s_sdlEvents); - } - ++s_sdlInited; - m_sdlPlayer.bindings = &m_inputMap; - updateJoysticks(); -#endif - -#ifdef BUILD_SDL connect(&m_gamepadTimer, &QTimer::timeout, [this]() { - testGamepad(SDL_BINDING_BUTTON); + for (auto& driver : m_inputDrivers) { + if (driver->supportsPolling() && driver->supportsGamepads()) { + testGamepad(driver->type()); + } + } if (m_playerId == 0) { - updateJoysticks(); + update(); } }); -#endif - m_gamepadTimer.setInterval(50); + + m_gamepadTimer.setInterval(15); m_gamepadTimer.start(); #ifdef BUILD_QT_MULTIMEDIA @@ -141,43 +130,32 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren InputController::~InputController() { mInputMapDeinit(&m_inputMap); +} -#ifdef BUILD_SDL - if (m_playerAttached) { - mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer); +void InputController::addInputDriver(std::shared_ptr driver) { + m_inputDrivers[driver->type()] = driver; + if (!m_sensorDriver && driver->supportsSensors()) { + m_sensorDriver = driver->type(); } - - --s_sdlInited; - if (s_sdlInited == 0) { - mSDLDeinitEvents(&s_sdlEvents); - } -#endif } void InputController::setConfiguration(ConfigController* config) { m_config = config; loadConfiguration(KEYBOARD); -#ifdef BUILD_SDL - mSDLEventsLoadConfig(&s_sdlEvents, config->input()); - if (!m_playerAttached) { - m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer); + for (auto& driver : m_inputDrivers) { + driver->loadConfiguration(config); } - if (!loadConfiguration(SDL_BINDING_BUTTON)) { - mSDLInitBindingsGBA(&m_inputMap); - } - loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); -#endif } bool InputController::loadConfiguration(uint32_t type) { if (!mInputMapLoad(&m_inputMap, type, m_config->input())) { return false; } -#ifdef BUILD_SDL - if (m_playerAttached) { - mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input()); + auto driver = m_inputDrivers.value(type); + if (!driver) { + return false; } -#endif + driver->loadConfiguration(m_config); return true; } @@ -186,7 +164,6 @@ bool InputController::loadProfile(uint32_t type, const QString& profile) { return false; } bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); - recalibrateAxes(); if (!loaded) { const InputProfile* ip = InputProfile::findProfile(profile); if (ip) { @@ -200,18 +177,18 @@ bool InputController::loadProfile(uint32_t type, const QString& profile) { void InputController::saveConfiguration() { saveConfiguration(KEYBOARD); -#ifdef BUILD_SDL - saveConfiguration(SDL_BINDING_BUTTON); - saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); - if (m_playerAttached) { - mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input()); + for (auto& driver : m_inputDrivers) { + driver->saveConfiguration(m_config); } -#endif m_config->write(); } void InputController::saveConfiguration(uint32_t type) { mInputMapSave(&m_inputMap, type, m_config->input()); + auto driver = m_inputDrivers.value(type); + if (driver) { + driver->saveConfiguration(m_config); + } m_config->write(); } @@ -223,373 +200,239 @@ void InputController::saveProfile(uint32_t type, const QString& profile) { m_config->write(); } -const char* InputController::profileForType(uint32_t type) { - UNUSED(type); -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { -#if SDL_VERSION_ATLEAST(2, 0, 0) - return SDL_JoystickName(m_sdlPlayer.joystick->joystick); -#else - return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick)); -#endif +QString InputController::profileForType(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver) { + return {}; } -#endif - return 0; + return driver->currentProfile(); +} + +void InputController::setGamepadDriver(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver || !driver->supportsGamepads()) { + return; + } + m_gamepadDriver = type; } QStringList InputController::connectedGamepads(uint32_t type) const { - UNUSED(type); - -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - QStringList pads; - for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { - const char* name; -#if SDL_VERSION_ATLEAST(2, 0, 0) - name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick); -#else - name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick)); -#endif - if (name) { - pads.append(QString(name)); - } else { - pads.append(QString()); - } - } - return pads; + if (!type) { + type = m_gamepadDriver; + } + auto driver = m_inputDrivers.value(type); + if (!driver) { + return {}; } -#endif - return QStringList(); + QStringList pads; + for (auto pad : driver->connectedGamepads()) { + pads.append(pad->visibleName()); + } + return pads; } -int InputController::gamepad(uint32_t type) const { -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; +int InputController::gamepadIndex(uint32_t type) const { + if (!type) { + type = m_gamepadDriver; } -#endif - return 0; + auto driver = m_inputDrivers.value(type); + if (!driver) { + return -1; + } + return driver->activeGamepadIndex(); } void InputController::setGamepad(uint32_t type, int index) { -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); + if (!type) { + type = m_gamepadDriver; } -#endif + auto driver = m_inputDrivers.value(type); + if (!driver) { + return; + } + driver->setActiveGamepad(index); +} + +void InputController::setGamepad(int index) { + setGamepad(0, index); } void InputController::setPreferredGamepad(uint32_t type, int index) { if (!m_config) { return; } -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - char name[34] = {0}; - SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name)); -#else - const char* name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick)); - if (!name) { + if (!type) { + type = m_gamepadDriver; + } + auto driver = m_inputDrivers.value(type); + if (!driver) { return; } -#endif - mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name); -#else - UNUSED(type); - UNUSED(index); -#endif + + auto pads = driver->connectedGamepads(); + if (index >= pads.count()) { + return; + } + + QString name = pads[index]->name(); + if (name.isEmpty()) { + return; + } + mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name.toUtf8().constData()); } -mRumble* InputController::rumble() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - if (m_playerAttached) { - return &m_sdlPlayer.rumble.d; +void InputController::setPreferredGamepad(int index) { + setPreferredGamepad(0, index); +} + +InputMapper InputController::mapper(uint32_t type) { + return InputMapper(&m_inputMap, type); +} + +InputMapper InputController::mapper(InputDriver* driver) { + return InputMapper(&m_inputMap, driver->type()); +} + +InputMapper InputController::mapper(InputSource* source) { + return InputMapper(&m_inputMap, source->type()); +} + +void InputController::setSensorDriver(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver || !driver->supportsSensors()) { + return; + } + m_sensorDriver = type; +} + + +mRumble* InputController::rumble() { + auto driver = m_inputDrivers.value(m_sensorDriver); + if (driver) { + return driver->rumble(); } -#endif -#endif return nullptr; } mRotationSource* InputController::rotationSource() { -#ifdef BUILD_SDL - if (m_playerAttached) { - return &m_sdlPlayer.rotation.d; + auto driver = m_inputDrivers.value(m_sensorDriver); + if (driver) { + return driver->rotationSource(); } -#endif return nullptr; } -void InputController::registerTiltAxisX(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.axisX = axis; - } -#endif +int InputController::mapKeyboard(int key) const { + return mInputMapKey(&m_inputMap, KEYBOARD, key); } -void InputController::registerTiltAxisY(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.axisY = axis; - } -#endif -} - -void InputController::registerGyroAxisX(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroX = axis; - if (m_sdlPlayer.rotation.gyroY == axis) { - m_sdlPlayer.rotation.gyroZ = axis; - } else { - m_sdlPlayer.rotation.gyroZ = -1; +void InputController::update() { + for (auto& driver : m_inputDrivers) { + QString profile = profileForType(driver->type()); + driver->update(); + QString newProfile = profileForType(driver->type()); + if (profile != newProfile) { + loadProfile(driver->type(), newProfile); } } -#endif -} - -void InputController::registerGyroAxisY(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroY = axis; - if (m_sdlPlayer.rotation.gyroX == axis) { - m_sdlPlayer.rotation.gyroZ = axis; - } else { - m_sdlPlayer.rotation.gyroZ = -1; - } - } -#endif -} - -float InputController::gyroSensitivity() const { -#ifdef BUILD_SDL - if (m_playerAttached) { - return m_sdlPlayer.rotation.gyroSensitivity; - } -#endif - return 0; -} - -void InputController::setGyroSensitivity(float sensitivity) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroSensitivity = sensitivity; - } -#endif -} - -GBAKey InputController::mapKeyboard(int key) const { - return static_cast(mInputMapKey(&m_inputMap, KEYBOARD, key)); -} - -void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) { - return mInputBindKey(&m_inputMap, type, key, gbaKey); -} - -void InputController::updateJoysticks() { -#ifdef BUILD_SDL - QString profile = profileForType(SDL_BINDING_BUTTON); - mSDLUpdateJoysticks(&s_sdlEvents, m_config->input()); - QString newProfile = profileForType(SDL_BINDING_BUTTON); - if (profile != newProfile) { - loadProfile(SDL_BINDING_BUTTON, newProfile); - } -#endif + emit updated(); } int InputController::pollEvents() { int activeButtons = 0; -#ifdef BUILD_SDL - if (m_playerAttached && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numButtons = SDL_JoystickNumButtons(joystick); - int i; - QReadLocker l(&m_eventsLock); - for (i = 0; i < numButtons; ++i) { - GBAKey key = static_cast(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i)); - if (key == GBA_KEY_NONE) { - continue; - } - if (hasPendingEvent(key)) { - continue; - } - if (SDL_JoystickGetButton(joystick, i)) { - activeButtons |= 1 << key; - } - } - l.unlock(); - int numHats = SDL_JoystickNumHats(joystick); - for (i = 0; i < numHats; ++i) { - int hat = SDL_JoystickGetHat(joystick, i); - activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat); - } - - int numAxes = SDL_JoystickNumAxes(joystick); - for (i = 0; i < numAxes; ++i) { - int value = SDL_JoystickGetAxis(joystick, i); - - enum GBAKey key = static_cast(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value)); - if (key != GBA_KEY_NONE) { - activeButtons |= 1 << key; - } + for (auto pad : gamepads()) { + InputMapper im(mapper(pad)); + activeButtons |= im.mapKeys(pad->currentButtons()); + activeButtons |= im.mapAxes(pad->currentAxes()); + activeButtons |= im.mapHats(pad->currentHats()); + } + for (int i = 0; i < GBA_KEY_MAX; ++i) { + if ((activeButtons & (1 << i)) && hasPendingEvent(i)) { + activeButtons ^= 1 << i; } } -#endif return activeButtons; } -QSet InputController::activeGamepadButtons(int type) { +Gamepad* InputController::gamepad(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver) { + return nullptr; + } + if (!driver->supportsGamepads()) { + return nullptr; + } + + return driver->activeGamepad(); +} + +QList InputController::gamepads() { + QList pads; + for (auto& driver : m_inputDrivers) { + if (!driver->supportsGamepads()) { + continue; + } + Gamepad* pad = driver->activeGamepad(); + if (pad) { + pads.append(pad); + } + } + return pads; +} + +QSet InputController::activeGamepadButtons(uint32_t type) { QSet activeButtons; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numButtons = SDL_JoystickNumButtons(joystick); - int i; - for (i = 0; i < numButtons; ++i) { - if (SDL_JoystickGetButton(joystick, i)) { - activeButtons.insert(i); - } + Gamepad* pad = gamepad(type); + if (!pad) { + return {}; + } + auto allButtons = pad->currentButtons(); + for (int i = 0; i < allButtons.size(); ++i) { + if (allButtons[i]) { + activeButtons.insert(i); } } -#endif return activeButtons; } -void InputController::recalibrateAxes() { -#ifdef BUILD_SDL - if (m_playerAttached && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numAxes = SDL_JoystickNumAxes(joystick); - if (numAxes < 1) { - return; - } - m_deadzones.resize(numAxes); - int i; - for (i = 0; i < numAxes; ++i) { - m_deadzones[i] = SDL_JoystickGetAxis(joystick, i); - } - } -#endif -} - -QSet> InputController::activeGamepadAxes(int type) { +QSet> InputController::activeGamepadAxes(uint32_t type) { QSet> activeAxes; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numAxes = SDL_JoystickNumAxes(joystick); - if (numAxes < 1) { - return activeAxes; + Gamepad* pad = gamepad(type); + if (!pad) { + return {}; + } + InputMapper im(mapper(type)); + auto allAxes = pad->currentAxes(); + for (int i = 0; i < allAxes.size(); ++i) { + if (allAxes[i] - im.axisCenter(i) >= im.axisThreshold(i)) { + activeAxes.insert(qMakePair(i, GamepadAxisEvent::POSITIVE)); + continue; } - m_deadzones.resize(numAxes); - int i; - for (i = 0; i < numAxes; ++i) { - int32_t axis = SDL_JoystickGetAxis(joystick, i); - axis -= m_deadzones[i]; - if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) { - activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE)); - } + if (allAxes[i] - im.axisCenter(i) <= -im.axisThreshold(i)) { + activeAxes.insert(qMakePair(i, GamepadAxisEvent::NEGATIVE)); + continue; } } -#endif return activeAxes; } -void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) { - const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis); - mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD }; - if (old) { - description = *old; - } - int deadzone = 0; - if (axis > 0 && m_deadzones.size() > axis) { - deadzone = m_deadzones[axis]; - } - switch (direction) { - case GamepadAxisEvent::NEGATIVE: - description.lowDirection = key; - - description.deadLow = deadzone - AXIS_THRESHOLD; - break; - case GamepadAxisEvent::POSITIVE: - description.highDirection = key; - description.deadHigh = deadzone + AXIS_THRESHOLD; - break; - default: - return; - } - mInputBindAxis(&m_inputMap, type, axis, &description); -} - -void InputController::unbindAllAxes(uint32_t type) { - mInputUnbindAllAxes(&m_inputMap, type); -} - -QSet> InputController::activeGamepadHats(int type) { +QSet> InputController::activeGamepadHats(uint32_t type) { QSet> activeHats; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numHats = SDL_JoystickNumHats(joystick); - if (numHats < 1) { - return activeHats; - } - - int i; - for (i = 0; i < numHats; ++i) { - int hat = SDL_JoystickGetHat(joystick, i); - if (hat & GamepadHatEvent::UP) { - activeHats.insert(qMakePair(i, GamepadHatEvent::UP)); - } - if (hat & GamepadHatEvent::RIGHT) { - activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT)); - } - if (hat & GamepadHatEvent::DOWN) { - activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN)); - } - if (hat & GamepadHatEvent::LEFT) { - activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT)); - } + Gamepad* pad = gamepad(type); + if (!pad) { + return {}; + } + auto allHats = pad->currentHats(); + for (int i = 0; i < allHats.size(); ++i) { + if (allHats[i] != GamepadHatEvent::CENTER) { + activeHats.insert(qMakePair(i, allHats[i])); } } -#endif return activeHats; } -void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) { - mInputHatBindings bindings{ -1, -1, -1, -1 }; - mInputQueryHat(&m_inputMap, type, hat, &bindings); - switch (direction) { - case GamepadHatEvent::UP: - bindings.up = gbaKey; - break; - case GamepadHatEvent::RIGHT: - bindings.right = gbaKey; - break; - case GamepadHatEvent::DOWN: - bindings.down = gbaKey; - break; - case GamepadHatEvent::LEFT: - bindings.left = gbaKey; - break; - default: - return; - } - mInputBindHat(&m_inputMap, type, hat, &bindings); -} - -void InputController::unbindAllHats(uint32_t type) { - mInputUnbindAllHats(&m_inputMap, type); -} - -void InputController::testGamepad(int type) { +void InputController::testGamepad(uint32_t type) { QWriteLocker l(&m_eventsLock); auto activeAxes = activeGamepadAxes(type); auto oldAxes = m_activeAxes; @@ -614,16 +457,16 @@ void InputController::testGamepad(int type) { bool newlyAboveThreshold = activeAxes.contains(axis); if (newlyAboveThreshold) { GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this); - postPendingEvent(event->gbaKey()); + postPendingEvent(event->platformKey()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); } } } for (auto axis : oldAxes) { GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this); - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); sendGamepadEvent(event); } @@ -636,15 +479,15 @@ void InputController::testGamepad(int type) { for (int button : activeButtons) { GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this); - postPendingEvent(event->gbaKey()); + postPendingEvent(event->platformKey()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); } } for (int button : oldButtons) { GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this); - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); sendGamepadEvent(event); } @@ -653,15 +496,15 @@ void InputController::testGamepad(int type) { for (auto& hat : activeHats) { GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this); - postPendingEvent(event->gbaKey()); + postPendingEvents(event->platformKeys()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); + clearPendingEvents(event->platformKeys()); } } for (auto& hat : oldHats) { GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this); - clearPendingEvent(event->gbaKey()); + clearPendingEvents(event->platformKeys()); sendGamepadEvent(event); } } @@ -679,42 +522,34 @@ void InputController::sendGamepadEvent(QEvent* event) { QApplication::postEvent(focusWidget, event, Qt::HighEventPriority); } -void InputController::postPendingEvent(GBAKey key) { +void InputController::postPendingEvent(int key) { m_pendingEvents.insert(key); } -void InputController::clearPendingEvent(GBAKey key) { +void InputController::clearPendingEvent(int key) { m_pendingEvents.remove(key); } -bool InputController::hasPendingEvent(GBAKey key) const { +void InputController::postPendingEvents(int keys) { + for (int i = 0; keys; ++i, keys >>= 1) { + if (keys & 1) { + m_pendingEvents.insert(i); + } + } +} + +void InputController::clearPendingEvents(int keys) { + for (int i = 0; keys; ++i, keys >>= 1) { + if (keys & 1) { + m_pendingEvents.remove(i); + } + } +} + +bool InputController::hasPendingEvent(int key) const { return m_pendingEvents.contains(key); } -void InputController::suspendScreensaver() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLSuspendScreensaver(&s_sdlEvents); -#endif -#endif -} - -void InputController::resumeScreensaver() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLResumeScreensaver(&s_sdlEvents); -#endif -#endif -} - -void InputController::setScreensaverSuspendable(bool suspendable) { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable); -#endif -#endif -} - void InputController::stealFocus(QWidget* focus) { m_focusParent = focus; } diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index a370ba80e..12f4e8915 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -1,13 +1,16 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2023 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/. */ #pragma once -#include "GamepadAxisEvent.h" -#include "GamepadHatEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadHatEvent.h" +#include "input/InputDriver.h" +#include "input/InputMapper.h" +#include #include #include #include @@ -18,13 +21,8 @@ #include +#include #include -#include - -#ifdef BUILD_SDL -#include "platform/sdl/sdl-events.h" -#endif - #ifdef BUILD_QT_MULTIMEDIA #include "VideoDumper.h" @@ -37,6 +35,8 @@ struct mRumble; namespace QGBA { class ConfigController; +class Gamepad; +class InputSource; class InputController : public QObject { Q_OBJECT @@ -54,46 +54,45 @@ public: InputController(int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr); ~InputController(); + void addInputDriver(std::shared_ptr); + + int playerId() const { return m_playerId; } + void setConfiguration(ConfigController* config); void saveConfiguration(); bool loadConfiguration(uint32_t type); bool loadProfile(uint32_t type, const QString& profile); void saveConfiguration(uint32_t type); void saveProfile(uint32_t type, const QString& profile); - const char* profileForType(uint32_t type); + QString profileForType(uint32_t type); - GBAKey mapKeyboard(int key) const; - - void bindKey(uint32_t type, int key, GBAKey); + int mapKeyboard(int key) const; + mInputMap* map() { return &m_inputMap; } const mInputMap* map() const { return &m_inputMap; } int pollEvents(); static const int32_t AXIS_THRESHOLD = 0x3000; - QSet activeGamepadButtons(int type); - QSet> activeGamepadAxes(int type); - QSet> activeGamepadHats(int type); - void recalibrateAxes(); - void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, GBAKey); - void unbindAllAxes(uint32_t type); + void setGamepadDriver(uint32_t type); + const InputDriver* gamepadDriver() const { return m_inputDrivers.value(m_gamepadDriver).get(); } + InputDriver* gamepadDriver() { return m_inputDrivers.value(m_gamepadDriver).get(); } - void bindHat(uint32_t type, int hat, GamepadHatEvent::Direction, GBAKey); - void unbindAllHats(uint32_t type); - - QStringList connectedGamepads(uint32_t type) const; - int gamepad(uint32_t type) const; + QStringList connectedGamepads(uint32_t type = 0) const; + int gamepadIndex(uint32_t type = 0) const; void setGamepad(uint32_t type, int index); + void setGamepad(int index); void setPreferredGamepad(uint32_t type, int index); + void setPreferredGamepad(int index); - void registerTiltAxisX(int axis); - void registerTiltAxisY(int axis); - void registerGyroAxisX(int axis); - void registerGyroAxisY(int axis); + InputMapper mapper(uint32_t type); + InputMapper mapper(InputDriver*); + InputMapper mapper(InputSource*); - float gyroSensitivity() const; - void setGyroSensitivity(float sensitivity); + void setSensorDriver(uint32_t type); + const InputDriver* sensorDriver() const { return m_inputDrivers.value(m_sensorDriver).get(); } + InputDriver* sensorDriver() { return m_inputDrivers.value(m_sensorDriver).get(); } void stealFocus(QWidget* focus); void releaseFocus(QWidget* focus); @@ -106,17 +105,13 @@ public: GBALuminanceSource* luminance() { return &m_lux; } signals: + void updated(); void profileLoaded(const QString& profile); void luminanceValueChanged(int value); public slots: - void testGamepad(int type); - void updateJoysticks(); - - // TODO: Move these to somewhere that makes sense - void suspendScreensaver(); - void resumeScreensaver(); - void setScreensaverSuspendable(bool); + void testGamepad(uint32_t type); + void update(); void increaseLuminanceLevel(); void decreaseLuminanceLevel(); @@ -136,11 +131,20 @@ private slots: void teardownCam(); private: - void postPendingEvent(GBAKey); - void clearPendingEvent(GBAKey); - bool hasPendingEvent(GBAKey) const; + void postPendingEvent(int key); + void clearPendingEvent(int key); + void postPendingEvents(int keys); + void clearPendingEvents(int keys); + bool hasPendingEvent(int key) const; void sendGamepadEvent(QEvent*); + Gamepad* gamepad(uint32_t type); + QList gamepads(); + + QSet activeGamepadButtons(uint32_t type); + QSet> activeGamepadAxes(uint32_t type); + QSet> activeGamepadHats(uint32_t type); + struct InputControllerLux : GBALuminanceSource { InputController* p; uint8_t value; @@ -170,21 +174,16 @@ private: QWidget* m_topLevel; QWidget* m_focusParent; -#ifdef BUILD_SDL - static int s_sdlInited; - static mSDLEvents s_sdlEvents; - mSDLPlayer m_sdlPlayer{}; - bool m_playerAttached = false; -#endif - - QVector m_deadzones; + QHash> m_inputDrivers; + uint32_t m_gamepadDriver; + uint32_t m_sensorDriver; QSet m_activeButtons; QSet> m_activeAxes; QSet> m_activeHats; QTimer m_gamepadTimer{nullptr}; - QSet m_pendingEvents; + QSet m_pendingEvents; QReadWriteLock m_eventsLock; }; diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp index aedc47f6e..e8f7f1711 100644 --- a/src/platform/qt/InputProfile.cpp +++ b/src/platform/qt/InputProfile.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "InputProfile.h" +#include "input/InputMapper.h" #include "InputController.h" #include @@ -210,17 +211,23 @@ const InputProfile* InputProfile::findProfile(const QString& name) { } void InputProfile::apply(InputController* controller) const { - for (size_t i = 0; i < GBA_KEY_MAX; ++i) { -#ifdef BUILD_SDL - controller->bindKey(SDL_BINDING_BUTTON, m_keys[i], static_cast(i)); - controller->bindAxis(SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast(i)); -#endif + auto gamepadDriver = controller->gamepadDriver(); + if (gamepadDriver) { + InputMapper mapper = controller->mapper(gamepadDriver); + for (size_t i = 0; i < GBA_KEY_MAX; ++i) { + mapper.bindKey(m_keys[i], i); + mapper.bindAxis(m_axes[i].axis, m_axes[i].direction, i); + } + } + + InputDriver* sensorDriver = controller->sensorDriver(); + if (sensorDriver) { + sensorDriver->registerTiltAxisX(m_tiltAxis.x); + sensorDriver->registerTiltAxisY(m_tiltAxis.y); + sensorDriver->registerGyroAxisX(m_gyroAxis.x); + sensorDriver->registerGyroAxisY(m_gyroAxis.y); + sensorDriver->setGyroSensitivity(m_gyroSensitivity); } - controller->registerTiltAxisX(m_tiltAxis.x); - controller->registerTiltAxisY(m_tiltAxis.y); - controller->registerGyroAxisX(m_gyroAxis.x); - controller->registerGyroAxisY(m_gyroAxis.y); - controller->setGyroSensitivity(m_gyroSensitivity); } bool InputProfile::lookupShortcutButton(const QString& shortcutName, int* button) const { diff --git a/src/platform/qt/InputProfile.h b/src/platform/qt/InputProfile.h index 4e43d16a7..a7670a40c 100644 --- a/src/platform/qt/InputProfile.h +++ b/src/platform/qt/InputProfile.h @@ -5,9 +5,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include +#include namespace QGBA { diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index 95b83a9e8..e8da70970 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "KeyEditor.h" -#include "GamepadAxisEvent.h" -#include "GamepadButtonEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadButtonEvent.h" #include "ShortcutController.h" #include diff --git a/src/platform/qt/KeyEditor.h b/src/platform/qt/KeyEditor.h index e74df7f27..c1bb56fae 100644 --- a/src/platform/qt/KeyEditor.h +++ b/src/platform/qt/KeyEditor.h @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include "GamepadAxisEvent.h" -#include "GamepadHatEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadHatEvent.h" #include #include diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index 3198a4c90..f5dad2336 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -6,8 +6,8 @@ #include "LoadSaveState.h" #include "CoreController.h" -#include "GamepadAxisEvent.h" -#include "GamepadButtonEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadButtonEvent.h" #include "VFileDevice.h" #include "utils.h" @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -42,13 +43,12 @@ LoadSaveState::LoadSaveState(std::shared_ptr controller, QWidget m_slots[7] = m_ui.state8; m_slots[8] = m_ui.state9; - unsigned width, height; - controller->thread()->core->desiredVideoDimensions(controller->thread()->core, &width, &height); + QSize size = controller->screenDimensions(); int i; for (i = 0; i < NUM_SLOTS; ++i) { loadState(i + 1); m_slots[i]->installEventFilter(this); - m_slots[i]->setMaximumSize(width + 2, height + 2); + m_slots[i]->setMaximumSize(size.width() + 2, size.height() + 2); connect(m_slots[i], &QAbstractButton::clicked, this, [this, i]() { triggerState(i + 1); }); } @@ -131,13 +131,13 @@ bool LoadSaveState::eventFilter(QObject* object, QEvent* event) { if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadAxisEvent::Type()) { int column = m_currentFocus % 3; int row = m_currentFocus - column; - GBAKey key = GBA_KEY_NONE; + int key = -1; if (event->type() == GamepadButtonEvent::Down()) { - key = static_cast(event)->gbaKey(); + key = static_cast(event)->platformKey(); } else if (event->type() == GamepadAxisEvent::Type()) { GamepadAxisEvent* gae = static_cast(event); if (gae->isNew()) { - key = gae->gbaKey(); + key = gae->platformKey(); } else { return false; } @@ -198,11 +198,17 @@ void LoadSaveState::loadState(int slot) { QDateTime creation; QImage stateImage; - unsigned width, height; - thread->core->desiredVideoDimensions(thread->core, &width, &height); + QSize size = m_controller->screenDimensions(); mStateExtdataItem item; - if (mStateExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item) && item.size >= static_cast(width * height * 4)) { - stateImage = QImage((uchar*) item.data, width, height, QImage::Format_ARGB32).rgbSwapped(); + if (mStateExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item)) { + mStateExtdataItem dims; + if (mStateExtdataGet(&extdata, EXTDATA_SCREENSHOT_DIMENSIONS, &dims) && dims.size == sizeof(uint16_t[2])) { + size.setWidth(static_cast(dims.data)[0]); + size.setHeight(static_cast(dims.data)[1]); + } + if (item.size >= static_cast(size.width() * size.height() * 4)) { + stateImage = QImage((uchar*) item.data, size.width(), size.height(), QImage::Format_ARGB32).rgbSwapped(); + } } if (mStateExtdataGet(&extdata, EXTDATA_META_TIME, &item) && item.size == sizeof(uint64_t)) { diff --git a/src/platform/qt/MapView.cpp b/src/platform/qt/MapView.cpp index ba2255ee9..da65abf9d 100644 --- a/src/platform/qt/MapView.cpp +++ b/src/platform/qt/MapView.cpp @@ -43,7 +43,7 @@ MapView::MapView(std::shared_ptr controller, QWidget* parent) case mPLATFORM_GBA: m_boundary = 2048; m_ui.tile->setMaxTile(3096); - m_addressBase = BASE_VRAM; + m_addressBase = GBA_BASE_VRAM; m_addressWidth = 8; m_ui.bgInfo->addCustomProperty("priority", tr("Priority")); m_ui.bgInfo->addCustomProperty("screenBase", tr("Map base")); diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index 7f29100bd..f44323603 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -14,24 +14,46 @@ #include #endif +#include + using namespace QGBA; #ifdef M_CORE_GB -MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node) +MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* gbNode) : controller(coreController) - , gbNode(node) { + node.gb = gbNode; } #endif #ifdef M_CORE_GBA -MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node) +MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* gbaNode) : controller(coreController) - , gbaNode(node) { + node.gba = gbaNode; } #endif +int MultiplayerController::Player::id() const { + switch (controller->platform()) { +#ifdef M_CORE_GBA + case mPLATFORM_GBA: + return node.gba->id; +#endif +#ifdef M_CORE_GB + case mPLATFORM_GB: + return node.gb->id; +#endif + case mPLATFORM_NONE: + break; + } + return -1; +} + +bool MultiplayerController::Player::operator<(const MultiplayerController::Player& other) const { + return id() < other.id(); +} + MultiplayerController::MultiplayerController() { mLockstepInit(&m_lockstep); m_lockstep.context = this; @@ -65,6 +87,7 @@ MultiplayerController::MultiplayerController() { player->awake = 0; slept = true; } + player->controller->setSync(true); return slept; }; m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) { @@ -72,44 +95,51 @@ MultiplayerController::MultiplayerController() { abort(); } MultiplayerController* controller = static_cast(lockstep->context); - if (!id) { - for (int i = 1; i < controller->m_players.count(); ++i) { - Player* player = &controller->m_players[i]; + Player* player = controller->player(id); + switch (player->controller->platform()) { #ifdef M_CORE_GBA - if (player->controller->platform() == mPLATFORM_GBA && player->gbaNode->d.p->mode != controller->m_players[0].gbaNode->d.p->mode) { - player->controller->setSync(true); - continue; - } -#endif - player->controller->setSync(false); - player->cyclesPosted += cycles; - if (player->awake < 1) { - switch (player->controller->platform()) { -#ifdef M_CORE_GBA - case mPLATFORM_GBA: - player->gbaNode->nextEvent += player->cyclesPosted; - break; -#endif -#ifdef M_CORE_GB - case mPLATFORM_GB: - player->gbNode->nextEvent += player->cyclesPosted; - break; -#endif - default: - break; + case mPLATFORM_GBA: + if (!id) { + for (int i = 1; i < controller->m_players.count(); ++i) { + player = controller->player(i); + player->controller->setSync(false); + player->cyclesPosted += cycles; + if (player->awake < 1) { + player->node.gba->nextEvent += player->cyclesPosted; } mCoreThreadStopWaiting(player->controller->thread()); player->awake = 1; } + } else { + player->controller->setSync(true); + player->cyclesPosted += cycles; } - } else { - controller->m_players[id].controller->setSync(true); - controller->m_players[id].cyclesPosted += cycles; + break; +#endif +#ifdef M_CORE_GB + case mPLATFORM_GB: + if (!id) { + player = controller->player(1); + player->controller->setSync(false); + player->cyclesPosted += cycles; + if (player->awake < 1) { + player->node.gb->nextEvent += player->cyclesPosted; + } + mCoreThreadStopWaiting(player->controller->thread()); + player->awake = 1; + } else { + player->controller->setSync(true); + player->cyclesPosted += cycles; + } + break; +#endif + default: + break; } }; m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) { MultiplayerController* controller = static_cast(lockstep->context); - Player* player = &controller->m_players[id]; + Player* player = controller->player(id); player->cyclesPosted -= cycles; if (player->cyclesPosted <= 0) { mCoreThreadWaitFromThread(player->controller->thread()); @@ -118,21 +148,21 @@ MultiplayerController::MultiplayerController() { cycles = player->cyclesPosted; return cycles; }; - m_lockstep.unusedCycles= [](mLockstep* lockstep, int id) { + m_lockstep.unusedCycles = [](mLockstep* lockstep, int id) { MultiplayerController* controller = static_cast(lockstep->context); - Player* player = &controller->m_players[id]; + Player* player = controller->player(id); auto cycles = player->cyclesPosted; return cycles; }; m_lockstep.unload = [](mLockstep* lockstep, int id) { MultiplayerController* controller = static_cast(lockstep->context); if (id) { - Player* player = &controller->m_players[id]; + Player* player = controller->player(id); player->controller->setSync(true); player->cyclesPosted = 0; // release master GBA if it is waiting for this GBA - player = &controller->m_players[0]; + player = controller->player(0); player->waitMask &= ~(1 << id); if (!player->waitMask && player->awake < 1) { mCoreThreadStopWaiting(player->controller->thread()); @@ -140,7 +170,7 @@ MultiplayerController::MultiplayerController() { } } else { for (int i = 1; i < controller->m_players.count(); ++i) { - Player* player = &controller->m_players[i]; + Player* player = controller->player(i); player->controller->setSync(true); switch (player->controller->platform()) { #ifdef M_CORE_GBA @@ -160,12 +190,12 @@ MultiplayerController::MultiplayerController() { switch (player->controller->platform()) { #ifdef M_CORE_GBA case mPLATFORM_GBA: - player->gbaNode->nextEvent += player->cyclesPosted; + player->node.gba->nextEvent += player->cyclesPosted; break; #endif #ifdef M_CORE_GB case mPLATFORM_GB: - player->gbNode->nextEvent += player->cyclesPosted; + player->node.gb->nextEvent += player->cyclesPosted; break; #endif default: @@ -315,3 +345,29 @@ int MultiplayerController::attached() { num = m_lockstep.attached; return num; } + +MultiplayerController::Player* MultiplayerController::player(int id) { + Player* player = &m_players[id]; + switch (player->controller->platform()) { +#ifdef M_CORE_GBA + case mPLATFORM_GBA: + if (player->node.gba->id != id) { + std::sort(m_players.begin(), m_players.end()); + player = &m_players[id]; + } + break; +#endif +#ifdef M_CORE_GB + case mPLATFORM_GB: + if (player->node.gb->id != id) { + std::swap(m_players[0], m_players[1]); + player = &m_players[id]; + } + break; +#endif + case mPLATFORM_NONE: + break; + } + + return player; +} diff --git a/src/platform/qt/MultiplayerController.h b/src/platform/qt/MultiplayerController.h index 03aad86d8..5ad6124db 100644 --- a/src/platform/qt/MultiplayerController.h +++ b/src/platform/qt/MultiplayerController.h @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include #include +#include #include #include @@ -44,6 +44,10 @@ signals: void gameDetached(); private: + union Node { + GBSIOLockstepNode* gb; + GBASIOLockstepNode* gba; + }; struct Player { #ifdef M_CORE_GB Player(CoreController* controller, GBSIOLockstepNode* node); @@ -52,13 +56,18 @@ private: Player(CoreController* controller, GBASIOLockstepNode* node); #endif + int id() const; + bool operator<(const Player&) const; + CoreController* controller; - GBSIOLockstepNode* gbNode = nullptr; - GBASIOLockstepNode* gbaNode = nullptr; + Node node = {nullptr}; int awake = 1; int32_t cyclesPosted = 0; unsigned waitMask = 0; }; + + Player* player(int id); + union { mLockstep m_lockstep; #ifdef M_CORE_GB diff --git a/src/platform/qt/ObjView.cpp b/src/platform/qt/ObjView.cpp index f1b605c0d..f4482aba6 100644 --- a/src/platform/qt/ObjView.cpp +++ b/src/platform/qt/ObjView.cpp @@ -157,7 +157,7 @@ void ObjView::updateTilesGBA(bool force) { m_ui.w->setText(QString::number(newInfo.width * 8)); m_ui.h->setText(QString::number(newInfo.height * 8)); - m_ui.address->setText(tr("0x%0").arg(BASE_OAM + m_objId * sizeof(*obj), 8, 16, QChar('0'))); + m_ui.address->setText(tr("0x%0").arg(GBA_BASE_OAM + m_objId * sizeof(*obj), 8, 16, QChar('0'))); m_ui.priority->setText(QString::number(newInfo.priority)); m_ui.flippedH->setChecked(newInfo.hflip); m_ui.flippedV->setChecked(newInfo.vflip); diff --git a/src/platform/qt/OpenGLBug.cpp b/src/platform/qt/OpenGLBug.cpp new file mode 100644 index 000000000..089d7d754 --- /dev/null +++ b/src/platform/qt/OpenGLBug.cpp @@ -0,0 +1,50 @@ +/* Copyright (c) 2013-2022 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 "OpenGLBug.h" + +#include +#include + +namespace QGBA { + +bool glContextHasBug(OpenGLBug bug) { + QOpenGLContext* context = QOpenGLContext::currentContext(); + if (!context) { + abort(); + } + QOpenGLFunctions* fn = context->functions(); + QString vendor(reinterpret_cast(fn->glGetString(GL_VENDOR))); + QString renderer(reinterpret_cast(fn->glGetString(GL_RENDERER))); + QString version(reinterpret_cast(fn->glGetString(GL_VERSION))); + + switch (bug) { + case OpenGLBug::CROSS_THREAD_FLUSH: +#ifndef Q_OS_WIN + return false; +#else + return vendor == "Intel"; +#endif + + case OpenGLBug::GLTHREAD_BLOCKS_SWAP: + return version.contains(" Mesa "); + + case OpenGLBug::IG4ICD_CRASH: +#ifdef Q_OS_WIN + if (vendor != "Intel") { + return false; + } + if (renderer == "Intel Pineview Platform") { + return true; + } +#endif + return false; + + default: + return false; + } +} + +} diff --git a/src/platform/qt/OpenGLBug.h b/src/platform/qt/OpenGLBug.h new file mode 100644 index 000000000..f63fe282e --- /dev/null +++ b/src/platform/qt/OpenGLBug.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +namespace QGBA { + +enum class OpenGLBug { + CROSS_THREAD_FLUSH, // mgba.io/i/2761 + GLTHREAD_BLOCKS_SWAP, // mgba.io/i/2767 + IG4ICD_CRASH, // mgba.io/i/2136 +}; + +bool glContextHasBug(OpenGLBug); + +} diff --git a/src/platform/qt/ReportView.cpp b/src/platform/qt/ReportView.cpp index 61a7a2cd1..c38c84a03 100644 --- a/src/platform/qt/ReportView.cpp +++ b/src/platform/qt/ReportView.cpp @@ -308,15 +308,14 @@ void ReportView::generateReport() { deferredBinaries.append(qMakePair(QString("Save %1").arg(winId), save)); } mStateExtdataDeinit(&extdata); + vf->close(vf); } } } else { windowReport << QString("ROM open: No"); } -#ifdef BUILD_SDL InputController* input = window->inputController(); - windowReport << QString("Active gamepad: %1").arg(input->gamepad(SDL_BINDING_BUTTON)); -#endif + windowReport << QString("Active gamepad: %1").arg(input->gamepadIndex()); windowReport << QString("Configuration: %1").arg(configs.indexOf(config) + 1); addReport(QString("Window %1").arg(winId), windowReport.join('\n')); } @@ -476,9 +475,8 @@ void ReportView::addGLInfo(QStringList& report) { } void ReportView::addGamepadInfo(QStringList& report) { -#ifdef BUILD_SDL InputController* input = GBAApp::app()->windows()[0]->inputController(); - QStringList gamepads = input->connectedGamepads(SDL_BINDING_BUTTON); + QStringList gamepads = input->connectedGamepads(); report << QString("Connected gamepads: %1").arg(gamepads.size()); int i = 0; for (const auto& gamepad : gamepads) { @@ -489,10 +487,9 @@ void ReportView::addGamepadInfo(QStringList& report) { i = 0; for (Window* window : GBAApp::app()->windows()) { ++i; - report << QString("Window %1 gamepad: %2").arg(i).arg(window->inputController()->gamepad(SDL_BINDING_BUTTON)); + report << QString("Window %1 gamepad: %2").arg(i).arg(window->inputController()->gamepadIndex()); } } -#endif } void ReportView::addROMInfo(QStringList& report, CoreController* controller) { diff --git a/src/platform/qt/SaveConverter.cpp b/src/platform/qt/SaveConverter.cpp index fd242af98..712396517 100644 --- a/src/platform/qt/SaveConverter.cpp +++ b/src/platform/qt/SaveConverter.cpp @@ -197,20 +197,20 @@ void SaveConverter::detectFromSavestate(VFile* vf) { void SaveConverter::detectFromSize(std::shared_ptr vf) { #ifdef M_CORE_GBA switch (vf->size()) { - case SIZE_CART_SRAM: + case GBA_SIZE_SRAM: m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, vf}); break; - case SIZE_CART_FLASH512: + case GBA_SIZE_FLASH512: m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH512, vf}); break; - case SIZE_CART_FLASH1M: + case GBA_SIZE_FLASH1M: m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, vf}); break; - case SIZE_CART_EEPROM: + case GBA_SIZE_EEPROM: m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, vf, Endian::LITTLE}); m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, vf, Endian::BIG}); break; - case SIZE_CART_EEPROM512: + case GBA_SIZE_EEPROM512: m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, vf, Endian::LITTLE}); m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, vf, Endian::BIG}); break; @@ -267,13 +267,13 @@ void SaveConverter::detectFromHeaders(std::shared_ptr vf) { if (data) { QByteArray bytes = QByteArray::fromRawData(static_cast(data), size); bytes.data(); // Trigger a deep copy before we delete the backing - if (size == SIZE_CART_FLASH1M) { + if (size == GBA_SIZE_FLASH1M) { m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::SHARKPORT}); } else { - m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, std::make_shared(bytes.left(SIZE_CART_SRAM)), Endian::NONE, Container::SHARKPORT}); - m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH512, std::make_shared(bytes.left(SIZE_CART_FLASH512)), Endian::NONE, Container::SHARKPORT}); - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, std::make_shared(bytes.left(SIZE_CART_EEPROM)), Endian::BIG, Container::SHARKPORT}); - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, std::make_shared(bytes.left(SIZE_CART_EEPROM512)), Endian::BIG, Container::SHARKPORT}); + 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}); } free(data); } @@ -284,21 +284,21 @@ 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 switch (size) { - case SIZE_CART_FLASH1M: + case GBA_SIZE_FLASH1M: m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::GSV}); break; - case SIZE_CART_FLASH512: + 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}); break; - case SIZE_CART_SRAM: - m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, std::make_shared(bytes.left(SIZE_CART_SRAM)), Endian::NONE, Container::GSV}); + case GBA_SIZE_SRAM: + m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, std::make_shared(bytes.left(GBA_SIZE_SRAM)), Endian::NONE, Container::GSV}); break; - case SIZE_CART_EEPROM: - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, std::make_shared(bytes.left(SIZE_CART_EEPROM)), Endian::BIG, Container::GSV}); + case GBA_SIZE_EEPROM: + m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, std::make_shared(bytes.left(GBA_SIZE_EEPROM)), Endian::BIG, Container::GSV}); break; - case SIZE_CART_EEPROM512: - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, std::make_shared(bytes.left(SIZE_CART_EEPROM512)), Endian::BIG, Container::GSV}); + case GBA_SIZE_EEPROM512: + m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, std::make_shared(bytes.left(GBA_SIZE_EEPROM512)), Endian::BIG, Container::GSV}); break; } free(data); diff --git a/src/platform/qt/SensorView.cpp b/src/platform/qt/SensorView.cpp index 22c2f5f3f..ba1cf0ce9 100644 --- a/src/platform/qt/SensorView.cpp +++ b/src/platform/qt/SensorView.cpp @@ -6,7 +6,8 @@ #include "SensorView.h" #include "CoreController.h" -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/InputDriver.h" #include "InputController.h" #include "utils.h" @@ -47,14 +48,20 @@ SensorView::SensorView(InputController* input, QWidget* parent) m_timer.start(); } - jiggerer(m_ui.tiltSetX, &InputController::registerTiltAxisX); - jiggerer(m_ui.tiltSetY, &InputController::registerTiltAxisY); - jiggerer(m_ui.gyroSetX, &InputController::registerGyroAxisX); - jiggerer(m_ui.gyroSetY, &InputController::registerGyroAxisY); + jiggerer(m_ui.tiltSetX, &InputDriver::registerTiltAxisX); + jiggerer(m_ui.tiltSetY, &InputDriver::registerTiltAxisY); + jiggerer(m_ui.gyroSetX, &InputDriver::registerGyroAxisX); + jiggerer(m_ui.gyroSetY, &InputDriver::registerGyroAxisY); - m_ui.gyroSensitivity->setValue(m_input->gyroSensitivity() / 1e8f); + InputDriver* sensorDriver = m_input->sensorDriver(); + if (sensorDriver) { + m_ui.gyroSensitivity->setValue(sensorDriver->gyroSensitivity() / 1e8f); + } connect(m_ui.gyroSensitivity, static_cast(&QDoubleSpinBox::valueChanged), [this](double value) { - m_input->setGyroSensitivity(value * 1e8f); + InputDriver* sensorDriver = m_input->sensorDriver(); + if (sensorDriver) { + sensorDriver->setGyroSensitivity(value * 1e8f); + } }); m_input->stealFocus(this); connect(m_input, &InputController::luminanceValueChanged, this, &SensorView::luminanceValueChanged); @@ -84,7 +91,7 @@ void SensorView::setController(std::shared_ptr controller) { }); } -void SensorView::jiggerer(QAbstractButton* button, void (InputController::*setter)(int)) { +void SensorView::jiggerer(QAbstractButton* button, void (InputDriver::*setter)(int)) { connect(button, &QAbstractButton::toggled, [this, button, setter](bool checked) { if (!checked) { m_button = nullptr; @@ -115,7 +122,10 @@ bool SensorView::eventFilter(QObject*, QEvent* event) { m_button->removeEventFilter(this); m_button->clearFocus(); m_button->setChecked(false); - (m_input->*m_setter)(gae->axis()); + InputDriver* sensorDriver = m_input->sensorDriver(); + if (sensorDriver) { + (sensorDriver->*m_setter)(gae->axis()); + } m_button = nullptr; } return true; diff --git a/src/platform/qt/SensorView.h b/src/platform/qt/SensorView.h index 5ccc40ec8..2bb5047cd 100644 --- a/src/platform/qt/SensorView.h +++ b/src/platform/qt/SensorView.h @@ -21,6 +21,7 @@ class ConfigController; class CoreController; class GamepadAxisEvent; class InputController; +class InputDriver; class SensorView : public QDialog { Q_OBJECT @@ -43,14 +44,14 @@ private: Ui::SensorView m_ui; QAbstractButton* m_button = nullptr; - void (InputController::*m_setter)(int); + void (InputDriver::*m_setter)(int); std::shared_ptr m_controller; InputController* m_input; mRotationSource* m_rotation; QTimer m_timer; - void jiggerer(QAbstractButton*, void (InputController::*)(int)); + void jiggerer(QAbstractButton*, void (InputDriver::*)(int)); }; } diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 570b15d6c..4eca3cb46 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -25,6 +25,10 @@ #include #include +#ifdef BUILD_SDL +#include "platform/sdl/sdl-events.h" +#endif + using namespace QGBA; SettingsView::SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent) @@ -296,9 +300,14 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC } const GBColorPreset* colorPresets; + QString usedPreset = m_controller->getQtOption("gb.pal").toString(); size_t nPresets = GBColorPresetList(&colorPresets); for (size_t i = 0; i < nPresets; ++i) { - m_ui.colorPreset->addItem(QString(colorPresets[i].name)); + QString presetName(colorPresets[i].name); + m_ui.colorPreset->addItem(presetName); + if (usedPreset == presetName) { + m_ui.colorPreset->setCurrentIndex(i); + } } connect(m_ui.colorPreset, static_cast(&QComboBox::currentIndexChanged), this, [this, colorPresets](int n) { const GBColorPreset* preset = &colorPresets[n]; @@ -321,8 +330,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC GBAKeyEditor* buttonEditor = nullptr; #ifdef BUILD_SDL - inputController->recalibrateAxes(); - const char* profile = inputController->profileForType(SDL_BINDING_BUTTON); + QString profile = inputController->profileForType(SDL_BINDING_BUTTON); buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile); addPage(tr("Controllers"), buttonEditor, Page::CONTROLLERS); connect(m_ui.buttonBox, &QDialogButtonBox::accepted, buttonEditor, &GBAKeyEditor::save); @@ -640,6 +648,7 @@ void SettingsView::updateConfig() { m_controller->setOption(color.toUtf8().constData(), m_gbColors[colorId] & ~0xFF000000); } + m_controller->setQtOption("gb.pal", m_ui.colorPreset->currentText()); int gbColors = GB_COLORS_CGB; if (m_ui.gbColor->isChecked()) { diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index f0869aca5..95f71565b 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -6,8 +6,8 @@ 0 0 - 885 - 797 + 880 + 700 @@ -95,7 +95,7 @@ - 9 + 1 @@ -2091,45 +2091,37 @@ - - - - Default color palette only - - - gbColors - - - - - - - SGB color palette if available - - - gbColors - - - - - - - GBC color palette if available - - - gbColors - - - - - - - SGB (preferred) or GBC color palette if available - - - gbColors - - + + + + + + SGB color palette if available + + + + + + + Default color palette only + + + + + + + GBC color palette if available + + + + + + + SGB (preferred) or GBC color palette if available + + + + diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index d401fd1ba..ccfcb10d7 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -6,8 +6,10 @@ #include "ShortcutController.h" #include "ConfigController.h" -#include "GamepadButtonEvent.h" +#include "input/GamepadButtonEvent.h" +#include "input/GamepadHatEvent.h" #include "InputProfile.h" +#include "scripting/ScriptingController.h" #include #include @@ -32,6 +34,10 @@ void ShortcutController::setActionMapper(ActionMapper* actions) { rebuildItems(); } +void ShortcutController::setScriptingController(ScriptingController* controller) { + m_scripting = controller; +} + void ShortcutController::updateKey(const QString& name, int keySequence) { auto item = m_items[name]; if (!item) { @@ -132,9 +138,14 @@ void ShortcutController::rebuildItems() { onSubitems({}, std::bind(&ShortcutController::generateItem, this, std::placeholders::_1)); } -bool ShortcutController::eventFilter(QObject*, QEvent* event) { +bool ShortcutController::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { QKeyEvent* keyEvent = static_cast(event); +#ifdef ENABLE_SCRIPTING + if (m_scripting) { + m_scripting->event(obj, event); + } +#endif if (keyEvent->isAutoRepeat()) { return false; } @@ -153,6 +164,11 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { } } if (event->type() == GamepadButtonEvent::Down()) { +#ifdef ENABLE_SCRIPTING + if (m_scripting) { + m_scripting->event(obj, event); + } +#endif auto item = m_buttons.find(static_cast(event)->value()); if (item == m_buttons.end()) { return false; @@ -169,6 +185,11 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { return true; } if (event->type() == GamepadButtonEvent::Up()) { +#ifdef ENABLE_SCRIPTING + if (m_scripting) { + m_scripting->event(obj, event); + } +#endif auto item = m_buttons.find(static_cast(event)->value()); if (item == m_buttons.end()) { return false; @@ -201,6 +222,13 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { event->accept(); return true; } +#ifdef ENABLE_SCRIPTING + if (event->type() == GamepadHatEvent::Type()) { + if (m_scripting) { + m_scripting->event(obj, event); + } + } +#endif return false; } diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index eb24cdc96..7eed7e1d7 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -6,7 +6,7 @@ #pragma once #include "ActionMapper.h" -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include #include @@ -21,6 +21,7 @@ namespace QGBA { class ConfigController; class InputProfile; +class ScriptingController; class Shortcut : public QObject { Q_OBJECT @@ -74,6 +75,7 @@ public: void setConfigController(ConfigController* controller); void setActionMapper(ActionMapper* actionMapper); + void setScriptingController(ScriptingController* scriptingController); void setProfile(const QString& profile); @@ -121,6 +123,7 @@ private: QHash> m_heldKeys; ActionMapper* m_actions = nullptr; ConfigController* m_config = nullptr; + ScriptingController* m_scripting = nullptr; QString m_profileName; const InputProfile* m_profile = nullptr; }; diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index e82c2dd9b..5efbc4d62 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ShortcutView.h" -#include "GamepadButtonEvent.h" #include "InputController.h" +#include "input/GamepadButtonEvent.h" #include "ShortcutController.h" #include "ShortcutModel.h" diff --git a/src/platform/qt/ShortcutView.h b/src/platform/qt/ShortcutView.h index 903d365c8..d2e307419 100644 --- a/src/platform/qt/ShortcutView.h +++ b/src/platform/qt/ShortcutView.h @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include diff --git a/src/platform/qt/VFileDevice.cpp b/src/platform/qt/VFileDevice.cpp index bce0ed830..18231a263 100644 --- a/src/platform/qt/VFileDevice.cpp +++ b/src/platform/qt/VFileDevice.cpp @@ -172,8 +172,8 @@ VFile* VFileDevice::open(const QString& path, int mode) { return VFileOpen(path.toUtf8().constData(), mode); } -VFile* VFileDevice::openMemory() { - return VFileMemChunk(nullptr, 0); +VFile* VFileDevice::openMemory(quint64 size) { + return VFileMemChunk(nullptr, size); } VDir* VFileDevice::openDir(const QString& path) { @@ -184,6 +184,19 @@ VDir* VFileDevice::openArchive(const QString& path) { return VDirOpenArchive(path.toUtf8().constData()); } +bool VFileDevice::copyFile(VFile* input, VFile* output) { + uint8_t buffer[0x800]; + + input->seek(input, 0, SEEK_SET); + output->seek(output, 0, SEEK_SET); + + ssize_t size; + while ((size = input->read(input, buffer, sizeof(buffer))) > 0) { + output->write(output, buffer, size); + } + return size >= 0; +} + VFileAbstractWrapper::VFileAbstractWrapper(QIODevice* iodev) : m_iodev(iodev) { diff --git a/src/platform/qt/VFileDevice.h b/src/platform/qt/VFileDevice.h index 9df1aff93..15f79bbcb 100644 --- a/src/platform/qt/VFileDevice.h +++ b/src/platform/qt/VFileDevice.h @@ -38,10 +38,12 @@ public: static VFile* wrap(QBuffer*, QIODevice::OpenMode); static VFile* open(const QString& path, int mode); - static VFile* openMemory(); + static VFile* openMemory(quint64 size = 0); static VDir* openDir(const QString& path); static VDir* openArchive(const QString& path); + static bool copyFile(VFile* input, VFile* output); + protected: virtual qint64 readData(char* data, qint64 maxSize) override; virtual qint64 writeData(const char* data, qint64 maxSize) override; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 8fb271521..6626d7204 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -30,11 +30,15 @@ #include "Display.h" #include "DolphinConnector.h" #include "CoreController.h" +#include "ForwarderView.h" #include "FrameView.h" #include "GBAApp.h" #include "GDBController.h" #include "GDBWindow.h" #include "GIFView.h" +#ifdef BUILD_SDL +#include "input/SDLInputDriver.h" +#endif #include "IOViewer.h" #include "LoadSaveState.h" #include "LogView.h" @@ -75,6 +79,7 @@ #include #endif #include +#include #include #include @@ -169,6 +174,12 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi m_mustReset.setInterval(MUST_RESTART_TIMEOUT); m_mustReset.setSingleShot(true); +#ifdef BUILD_SDL + m_inputController.addInputDriver(std::make_shared(&m_inputController)); + m_inputController.setGamepadDriver(SDL_BINDING_BUTTON); + m_inputController.setSensorDriver(SDL_BINDING_BUTTON); +#endif + m_shortcutController->setConfigController(m_config); m_shortcutController->setActionMapper(&m_actions); setupMenu(menuBar()); @@ -296,7 +307,7 @@ void Window::reloadConfig() { m_display->resizeContext(); } - m_inputController.setScreensaverSuspendable(opts->suspendScreensaver); + GBAApp::app()->setScreensaverSuspendable(opts->suspendScreensaver); } void Window::saveConfig() { @@ -304,53 +315,6 @@ void Window::saveConfig() { m_config->write(); } -QString Window::getFilters() const { - QStringList filters; - QStringList formats; - -#ifdef M_CORE_GBA - QStringList gbaFormats{ - "*.gba", -#if defined(USE_LIBZIP) || defined(USE_MINIZIP) - "*.zip", -#endif -#ifdef USE_LZMA - "*.7z", -#endif -#ifdef USE_ELF - "*.elf", -#endif - "*.agb", - "*.mb", - "*.rom", - "*.bin"}; - formats.append(gbaFormats); - filters.append(tr("Game Boy Advance ROMs (%1)").arg(gbaFormats.join(QChar(' ')))); -#endif - -#ifdef M_CORE_GB - QStringList gbFormats{ - "*.gb", - "*.gbc", - "*.sgb", -#if defined(USE_LIBZIP) || defined(USE_MINIZIP) - "*.zip", -#endif -#ifdef USE_LZMA - "*.7z", -#endif - "*.rom", - "*.bin"}; - formats.append(gbFormats); - filters.append(tr("Game Boy ROMs (%1)").arg(gbFormats.join(QChar(' ')))); -#endif - - formats.removeDuplicates(); - filters.prepend(tr("All ROMs (%1)").arg(formats.join(QChar(' ')))); - filters.append(tr("%1 Video Logs (*.mvl)").arg(projectName)); - return filters.join(";;"); -} - QString Window::getFiltersArchive() const { QStringList filters; @@ -367,7 +331,7 @@ QString Window::getFiltersArchive() const { } void Window::selectROM() { - QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), getFilters()); + QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), romFilters(true)); if (!filename.isEmpty()) { setController(m_manager->loadGame(filename), filename); } @@ -410,7 +374,7 @@ void Window::addDirToLibrary() { #endif void Window::replaceROM() { - QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), getFilters()); + QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), romFilters()); if (!filename.isEmpty()) { m_controller->replaceGame(filename); } @@ -663,8 +627,11 @@ void Window::consoleOpen() { void Window::scriptingOpen() { if (!m_scripting) { m_scripting = std::make_unique(); + m_scripting->setInputController(&m_inputController); + m_shortcutController->setScriptingController(m_scripting.get()); if (m_controller) { m_scripting->setController(m_controller); + m_display->installEventFilter(m_scripting.get()); } } ScriptingView* view = new ScriptingView(m_scripting.get(), m_config); @@ -677,8 +644,8 @@ void Window::keyPressEvent(QKeyEvent* event) { QWidget::keyPressEvent(event); return; } - GBAKey key = m_inputController.mapKeyboard(event->key()); - if (key == GBA_KEY_NONE) { + int key = m_inputController.mapKeyboard(event->key()); + if (key == -1) { QWidget::keyPressEvent(event); return; } @@ -693,8 +660,8 @@ void Window::keyReleaseEvent(QKeyEvent* event) { QWidget::keyReleaseEvent(event); return; } - GBAKey key = m_inputController.mapKeyboard(event->key()); - if (key == GBA_KEY_NONE) { + int key = m_inputController.mapKeyboard(event->key()); + if (key == -1) { QWidget::keyPressEvent(event); return; } @@ -1107,6 +1074,8 @@ void Window::changeRenderer() { if (!m_controller) { return; } + + CoreController::Interrupter interrupter(m_controller); if (m_config->getOption("hwaccelVideo").toInt() && m_display->supportsShaders() && m_controller->supportsFeature(CoreController::Feature::OPENGL)) { std::shared_ptr proxy = m_display->videoProxy(); if (!proxy) { @@ -1121,6 +1090,11 @@ void Window::changeRenderer() { m_config->updateOption("videoScale"); } } else { + std::shared_ptr proxy = m_display->videoProxy(); + if (proxy) { + proxy->detach(m_controller.get()); + m_display->setVideoProxy({}); + } m_controller->setFramebufferHandle(-1); } } @@ -1657,6 +1631,8 @@ void Window::setupMenu(QMenuBar* menubar) { m_actions.addAction(tr("Scripting..."), "scripting", this, &Window::scriptingOpen, "tools"); #endif + m_actions.addAction(tr("Create forwarder..."), "createForwarder", openTView(), "tools"); + m_actions.addSeparator("tools"); m_actions.addAction(tr("Settings..."), "settings", this, &Window::openSettingsWindow, "tools")->setRole(Action::Role::SETTINGS); m_actions.addAction(tr("Make portable"), "makePortable", this, &Window::tryMakePortable, "tools"); @@ -2036,7 +2012,6 @@ void Window::setController(CoreController* controller, const QString& fname) { } m_controller = std::shared_ptr(controller); - m_inputController.recalibrateAxes(); m_controller->setInputController(&m_inputController); m_controller->setLogger(&m_log); @@ -2049,14 +2024,14 @@ void Window::setController(CoreController* controller, const QString& fname) { }); connect(m_controller.get(), &CoreController::started, this, &Window::gameStarted); - connect(m_controller.get(), &CoreController::started, &m_inputController, &InputController::suspendScreensaver); + connect(m_controller.get(), &CoreController::started, GBAApp::app(), &GBAApp::suspendScreensaver); connect(m_controller.get(), &CoreController::stopping, this, &Window::gameStopped); { connect(m_controller.get(), &CoreController::stopping, [this]() { m_controller.reset(); }); } - connect(m_controller.get(), &CoreController::stopping, &m_inputController, &InputController::resumeScreensaver); + connect(m_controller.get(), &CoreController::stopping, GBAApp::app(), &GBAApp::resumeScreensaver); connect(m_controller.get(), &CoreController::paused, this, &Window::updateFrame); #ifndef Q_OS_MAC @@ -2068,14 +2043,14 @@ void Window::setController(CoreController* controller, const QString& fname) { }); #endif - connect(m_controller.get(), &CoreController::paused, &m_inputController, &InputController::resumeScreensaver); + connect(m_controller.get(), &CoreController::paused, GBAApp::app(), &GBAApp::resumeScreensaver); connect(m_controller.get(), &CoreController::paused, [this]() { emit paused(true); }); connect(m_controller.get(), &CoreController::unpaused, [this]() { emit paused(false); }); - connect(m_controller.get(), &CoreController::unpaused, &m_inputController, &InputController::suspendScreensaver); + connect(m_controller.get(), &CoreController::unpaused, GBAApp::app(), &GBAApp::suspendScreensaver); connect(m_controller.get(), &CoreController::frameAvailable, this, &Window::recordFrame); connect(m_controller.get(), &CoreController::crashed, this, &Window::gameCrashed); connect(m_controller.get(), &CoreController::failed, this, &Window::gameFailed); @@ -2153,6 +2128,12 @@ void Window::attachDisplay() { m_display->attach(m_controller); connect(m_display.get(), &QGBA::Display::drawingStarted, this, &Window::changeRenderer); m_display->startDrawing(m_controller); + +#ifdef ENABLE_SCRIPTING + if (m_scripting) { + m_display->installEventFilter(m_scripting.get()); + } +#endif } void Window::updateMute() { diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 52e0da965..9495cb3ee 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -187,7 +187,6 @@ private: void updateTitle(float fps = -1); - QString getFilters() const; QString getFiltersArchive() const; CoreManager* m_manager; diff --git a/src/platform/qt/input/Gamepad.cpp b/src/platform/qt/input/Gamepad.cpp new file mode 100644 index 000000000..183c9c0ab --- /dev/null +++ b/src/platform/qt/input/Gamepad.cpp @@ -0,0 +1,13 @@ +/* Copyright (c) 2013-2023 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 "input/Gamepad.h" + +using namespace QGBA; + +Gamepad::Gamepad(InputDriver* driver, QObject* parent) + : InputSource(driver, parent) +{ +} diff --git a/src/platform/qt/input/Gamepad.h b/src/platform/qt/input/Gamepad.h new file mode 100644 index 000000000..1781885cd --- /dev/null +++ b/src/platform/qt/input/Gamepad.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include "GamepadHatEvent.h" +#include "input/InputSource.h" + +namespace QGBA { + +class InputDriver; + +class Gamepad : public InputSource { +Q_OBJECT + +public: + Gamepad(InputDriver* driver, QObject* parent = nullptr); + + virtual QList currentButtons() = 0; + virtual QList currentAxes() = 0; + virtual QList currentHats() = 0; + + virtual int buttonCount() const = 0; + virtual int axisCount() const = 0; + virtual int hatCount() const = 0; +}; + +} diff --git a/src/platform/qt/GamepadAxisEvent.cpp b/src/platform/qt/input/GamepadAxisEvent.cpp similarity index 78% rename from src/platform/qt/GamepadAxisEvent.cpp rename to src/platform/qt/input/GamepadAxisEvent.cpp index 937e055ed..396574720 100644 --- a/src/platform/qt/GamepadAxisEvent.cpp +++ b/src/platform/qt/input/GamepadAxisEvent.cpp @@ -1,9 +1,9 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include "InputController.h" @@ -16,11 +16,11 @@ GamepadAxisEvent::GamepadAxisEvent(int axis, Direction direction, bool isNew, in , m_axis(axis) , m_direction(direction) , m_isNew(isNew) - , m_key(GBA_KEY_NONE) + , m_key(-1) { ignore(); if (controller) { - m_key = static_cast(mInputMapAxis(controller->map(), type, axis, direction * INT_MAX)); + m_key = mInputMapAxis(controller->map(), type, axis, direction * INT_MAX); } } diff --git a/src/platform/qt/GamepadAxisEvent.h b/src/platform/qt/input/GamepadAxisEvent.h similarity index 89% rename from src/platform/qt/GamepadAxisEvent.h rename to src/platform/qt/input/GamepadAxisEvent.h index 75d887425..3f88f4be0 100644 --- a/src/platform/qt/GamepadAxisEvent.h +++ b/src/platform/qt/input/GamepadAxisEvent.h @@ -7,8 +7,6 @@ #include -#include - namespace QGBA { class InputController; @@ -26,7 +24,7 @@ public: int axis() const { return m_axis; } Direction direction() const { return m_direction; } bool isNew() const { return m_isNew; } - GBAKey gbaKey() const { return m_key; } + int platformKey() const { return m_key; } static enum Type Type(); @@ -36,7 +34,7 @@ private: int m_axis; Direction m_direction; bool m_isNew; - GBAKey m_key; + int m_key; }; } diff --git a/src/platform/qt/GamepadButtonEvent.cpp b/src/platform/qt/input/GamepadButtonEvent.cpp similarity index 83% rename from src/platform/qt/GamepadButtonEvent.cpp rename to src/platform/qt/input/GamepadButtonEvent.cpp index 6657a2c69..5ad9ba8f1 100644 --- a/src/platform/qt/GamepadButtonEvent.cpp +++ b/src/platform/qt/input/GamepadButtonEvent.cpp @@ -1,9 +1,9 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 "GamepadButtonEvent.h" +#include "input/GamepadButtonEvent.h" #include "InputController.h" @@ -15,11 +15,11 @@ QEvent::Type GamepadButtonEvent::s_upType = QEvent::None; GamepadButtonEvent::GamepadButtonEvent(QEvent::Type pressType, int button, int type, InputController* controller) : QEvent(pressType) , m_button(button) - , m_key(GBA_KEY_NONE) + , m_key(-1) { ignore(); if (controller) { - m_key = static_cast(mInputMapKey(controller->map(), type, button)); + m_key = mInputMapKey(controller->map(), type, button); } } diff --git a/src/platform/qt/GamepadButtonEvent.h b/src/platform/qt/input/GamepadButtonEvent.h similarity index 81% rename from src/platform/qt/GamepadButtonEvent.h rename to src/platform/qt/input/GamepadButtonEvent.h index 5ba444490..ed2200d91 100644 --- a/src/platform/qt/GamepadButtonEvent.h +++ b/src/platform/qt/input/GamepadButtonEvent.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 @@ -7,8 +7,6 @@ #include -#include - namespace QGBA { class InputController; @@ -18,7 +16,7 @@ public: GamepadButtonEvent(Type pressType, int button, int type, InputController* controller = nullptr); int value() const { return m_button; } - GBAKey gbaKey() const { return m_key; } + int platformKey() const { return m_key; } static Type Down(); static Type Up(); @@ -28,7 +26,7 @@ private: static Type s_upType; int m_button; - GBAKey m_key; + int m_key; }; } diff --git a/src/platform/qt/GamepadHatEvent.cpp b/src/platform/qt/input/GamepadHatEvent.cpp similarity index 83% rename from src/platform/qt/GamepadHatEvent.cpp rename to src/platform/qt/input/GamepadHatEvent.cpp index 052783e8e..25033997f 100644 --- a/src/platform/qt/GamepadHatEvent.cpp +++ b/src/platform/qt/input/GamepadHatEvent.cpp @@ -1,9 +1,9 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 "GamepadHatEvent.h" +#include "input/GamepadHatEvent.h" #include "InputController.h" @@ -16,11 +16,11 @@ GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction di : QEvent(pressType) , m_hatId(hatId) , m_direction(direction) - , m_key(GBA_KEY_NONE) + , m_keys(0) { ignore(); if (controller) { - m_key = static_cast(mInputMapHat(controller->map(), type, hatId, direction)); + m_keys = mInputMapHat(controller->map(), type, hatId, direction); } } diff --git a/src/platform/qt/GamepadHatEvent.h b/src/platform/qt/input/GamepadHatEvent.h similarity index 89% rename from src/platform/qt/GamepadHatEvent.h rename to src/platform/qt/input/GamepadHatEvent.h index ecaf1cd2b..c6c49fe3c 100644 --- a/src/platform/qt/GamepadHatEvent.h +++ b/src/platform/qt/input/GamepadHatEvent.h @@ -7,8 +7,6 @@ #include -#include - namespace QGBA { class InputController; @@ -27,7 +25,7 @@ public: int hatId() const { return m_hatId; } Direction direction() const { return m_direction; } - GBAKey gbaKey() const { return m_key; } + int platformKeys() const { return m_keys; } static Type Down(); static Type Up(); @@ -38,7 +36,7 @@ private: int m_hatId; Direction m_direction; - GBAKey m_key; + int m_keys; }; } diff --git a/src/platform/qt/input/InputDriver.cpp b/src/platform/qt/input/InputDriver.cpp new file mode 100644 index 000000000..f76649bd8 --- /dev/null +++ b/src/platform/qt/input/InputDriver.cpp @@ -0,0 +1,103 @@ +/* Copyright (c) 2013-2023 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 "input/InputDriver.h" + +using namespace QGBA; + +InputDriver::InputDriver(QObject* parent) + : QObject(parent) +{} + +void InputDriver::loadConfiguration(ConfigController*) { +} + +void InputDriver::saveConfiguration(ConfigController*) { +} + +bool InputDriver::supportsPolling() const { + return false; +} + +bool InputDriver::supportsGamepads() const { + return false; +} + +bool InputDriver::supportsSensors() const { + return false; +} + +void InputDriver::bindDefaults(InputController*) { +} + +QList InputDriver::connectedKeySources() const { + return {}; +} + +QList InputDriver::connectedGamepads() const { + return {}; +} + +int InputDriver::activeKeySourceIndex() const { + return -1; +} + +int InputDriver::activeGamepadIndex() const { + return -1; +} + +KeySource* InputDriver::activeKeySource() { + QList ks(connectedKeySources()); + int activeKeySource = activeKeySourceIndex(); + if (activeKeySource < 0 || activeKeySource >= ks.count()) { + return nullptr; + } + return ks[activeKeySource]; +} + +Gamepad* InputDriver::activeGamepad() { + QList pads(connectedGamepads()); + int activeGamepad = activeGamepadIndex(); + if (activeGamepad < 0 || activeGamepad >= pads.count()) { + return nullptr; + } + return pads[activeGamepad]; +} + +void InputDriver::setActiveKeySource(int) { +} + +void InputDriver::setActiveGamepad(int) { +} + +void InputDriver::registerTiltAxisX(int) { +} + +void InputDriver::registerTiltAxisY(int) { +} + +void InputDriver::registerGyroAxisX(int) { +} + +void InputDriver::registerGyroAxisY(int) { +} + +void InputDriver::registerGyroAxisZ(int) { +} + +float InputDriver::gyroSensitivity() const { + return 0; +} + +void InputDriver::setGyroSensitivity(float) { +} + +mRumble* InputDriver::rumble() { + return nullptr; +} + +mRotationSource* InputDriver::rotationSource() { + return nullptr; +} diff --git a/src/platform/qt/input/InputDriver.h b/src/platform/qt/input/InputDriver.h new file mode 100644 index 000000000..cc0a6e0cd --- /dev/null +++ b/src/platform/qt/input/InputDriver.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include +#include +#include + +struct mRotationSource; +struct mRumble; + +namespace QGBA { + +class ConfigController; +class Gamepad; +class InputController; +class KeySource; + +class InputDriver : public QObject { +Q_OBJECT + +public: + InputDriver(QObject* parent = nullptr); + virtual ~InputDriver() = default; + + virtual uint32_t type() const = 0; + virtual QString visibleName() const = 0; + virtual QString currentProfile() const = 0; + + virtual bool supportsPolling() const; + virtual bool supportsGamepads() const; + virtual bool supportsSensors() const; + + virtual void loadConfiguration(ConfigController*); + virtual void saveConfiguration(ConfigController*); + + virtual void bindDefaults(InputController*); + + virtual bool update() = 0; + + virtual QList connectedKeySources() const; + virtual QList connectedGamepads() const; + + virtual int activeKeySourceIndex() const; + virtual int activeGamepadIndex() const; + + KeySource* activeKeySource(); + Gamepad* activeGamepad(); + + virtual void setActiveKeySource(int); + virtual void setActiveGamepad(int); + + virtual void registerTiltAxisX(int axis); + virtual void registerTiltAxisY(int axis); + virtual void registerGyroAxisX(int axis); + virtual void registerGyroAxisY(int axis); + virtual void registerGyroAxisZ(int axis); + + virtual float gyroSensitivity() const; + virtual void setGyroSensitivity(float sensitivity); + + virtual mRumble* rumble(); + virtual mRotationSource* rotationSource(); +}; + +} diff --git a/src/platform/qt/input/InputMapper.cpp b/src/platform/qt/input/InputMapper.cpp new file mode 100644 index 000000000..c84316efd --- /dev/null +++ b/src/platform/qt/input/InputMapper.cpp @@ -0,0 +1,181 @@ +/* Copyright (c) 2013-2023 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 "input/InputMapper.h" + +#include + +using namespace QGBA; + +InputMapper::InputMapper(mInputMap* map, uint32_t type) + : m_map(map) + , m_type(type) +{ +} + +int InputMapper::mapKey(int key) const { + return mInputMapKey(m_map, m_type, key); +} + +int InputMapper::mapAxis(int axis, int16_t value) const { + return mInputMapAxis(m_map, m_type, axis, value); +} + +int InputMapper::mapHat(int hat, GamepadHatEvent::Direction direction) const { + return mInputMapHat(m_map, m_type, hat, direction); +} + +int InputMapper::mapKeys(QList keys) const { + int platformKeys = 0; + for (int i = 0; i < keys.count(); ++i) { + if (!keys[i]) { + continue; + } + int platformKey = mInputMapKey(m_map, m_type, i); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + +int InputMapper::mapKeys(QSet keys) const { + int platformKeys = 0; + for (int key : keys) { + int platformKey = mInputMapKey(m_map, m_type, key); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + +int InputMapper::mapAxes(QList axes) const { + int platformKeys = 0; + for (int i = 0; i < axes.count(); ++i) { + int platformKey = mInputMapAxis(m_map, m_type, i, axes[i]); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + +int InputMapper::mapHats(QList hats) const { + int platformKeys = 0; + for (int i = 0; i < hats.count(); ++i) { + platformKeys |= mInputMapHat(m_map, m_type, i, hats[i]); + } + return platformKeys; +} + +void InputMapper::bindKey(int key, int platformKey) { + mInputBindKey(m_map, m_type, key, platformKey); +} + +void InputMapper::bindAxis(int axis, GamepadAxisEvent::Direction direction, int platformKey) { + const mInputAxis* old = mInputQueryAxis(m_map, m_type, axis); + mInputAxis description = { -1, -1, -axisThreshold(axis), axisThreshold(axis) }; + if (old) { + description = *old; + } + switch (direction) { + case GamepadAxisEvent::NEGATIVE: + description.lowDirection = platformKey; + description.deadLow = axisCenter(axis) - axisThreshold(axis); + break; + case GamepadAxisEvent::POSITIVE: + description.highDirection = platformKey; + description.deadHigh = axisCenter(axis) + axisThreshold(axis); + break; + default: + return; + } + mInputBindAxis(m_map, m_type, axis, &description); +} + +void InputMapper::bindHat(int hat, GamepadHatEvent::Direction direction, int platformKey) { + mInputHatBindings bindings{ -1, -1, -1, -1 }; + mInputQueryHat(m_map, m_type, hat, &bindings); + switch (direction) { + case GamepadHatEvent::UP: + bindings.up = platformKey; + break; + case GamepadHatEvent::RIGHT: + bindings.right = platformKey; + break; + case GamepadHatEvent::DOWN: + bindings.down = platformKey; + break; + case GamepadHatEvent::LEFT: + bindings.left = platformKey; + break; + default: + return; + } + mInputBindHat(m_map, m_type, hat, &bindings); +} + +QSet InputMapper::queryKeyBindings(int platformKey) const { + return {mInputQueryBinding(m_map, m_type, platformKey)}; +} + +QSet> InputMapper::queryAxisBindings(int platformKey) const { + QPair>> userdata; + userdata.first = platformKey; + + mInputEnumerateAxes(m_map, m_type, [](int axis, const struct mInputAxis* description, void* user) { + auto userdata = static_cast>>*>(user); + int platformKey = userdata->first; + auto& bindings = userdata->second; + + if (description->lowDirection == platformKey) { + bindings.insert({axis, GamepadAxisEvent::NEGATIVE}); + } + if (description->highDirection == platformKey) { + bindings.insert({axis, GamepadAxisEvent::POSITIVE}); + } + }, &userdata); + + return userdata.second; +} + +QSet> InputMapper::queryHatBindings(int platformKey) const { + QPair>> userdata; + userdata.first = platformKey; + + mInputEnumerateHats(m_map, m_type, [](int hat, const struct mInputHatBindings* description, void* user) { + auto userdata = static_cast>>*>(user); + int platformKey = userdata->first; + auto& bindings = userdata->second; + + if (description->up == platformKey) { + bindings.insert({hat, GamepadHatEvent::UP}); + } + if (description->right == platformKey) { + bindings.insert({hat, GamepadHatEvent::RIGHT}); + } + if (description->down == platformKey) { + bindings.insert({hat, GamepadHatEvent::DOWN}); + } + if (description->left == platformKey) { + bindings.insert({hat, GamepadHatEvent::LEFT}); + } + }, &userdata); + + return userdata.second; +} + +void InputMapper::unbindAllKeys() { + mInputUnbindAllKeys(m_map, m_type); +} + +void InputMapper::unbindAllAxes() { + mInputUnbindAllAxes(m_map, m_type); +} + +void InputMapper::unbindAllHats() { + mInputUnbindAllHats(m_map, m_type); +} diff --git a/src/platform/qt/input/InputMapper.h b/src/platform/qt/input/InputMapper.h new file mode 100644 index 000000000..0e8268356 --- /dev/null +++ b/src/platform/qt/input/InputMapper.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include +#include + +#include "GamepadAxisEvent.h" +#include "GamepadHatEvent.h" + +struct mInputMap; + +namespace QGBA { + +class InputMapper final { +public: + static const int32_t DEFAULT_AXIS_THRESHOLD = 0x3000; + + InputMapper(mInputMap* map, uint32_t type); + + mInputMap* inputMap() const { return m_map; } + uint32_t type() const { return m_type; } + + int mapKey(int key) const; + int mapAxis(int axis, int16_t value) const; + int mapHat(int hat, GamepadHatEvent::Direction) const; + + int mapKeys(QList keys) const; + int mapKeys(QSet keys) const; + int mapAxes(QList axes) const; + int mapHats(QList hats) const; + + void bindKey(int key, int platformKey); + void bindAxis(int axis, GamepadAxisEvent::Direction, int platformKey); + void bindHat(int hat, GamepadHatEvent::Direction, int platformKey); + + QSet queryKeyBindings(int platformKey) const; + QSet> queryAxisBindings(int platformKey) const; + QSet> queryHatBindings(int platformKey) const; + + int16_t axisThreshold(int) const { return DEFAULT_AXIS_THRESHOLD; } + int16_t axisCenter(int) const { return 0; } + + void unbindAllKeys(); + void unbindAllAxes(); + void unbindAllHats(); + +private: + mInputMap* m_map; + uint32_t m_type; +}; + +} diff --git a/src/platform/qt/input/InputSource.cpp b/src/platform/qt/input/InputSource.cpp new file mode 100644 index 000000000..ec1dc8bc5 --- /dev/null +++ b/src/platform/qt/input/InputSource.cpp @@ -0,0 +1,14 @@ +/* Copyright (c) 2013-2023 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 "input/InputSource.h" + +using namespace QGBA; + +InputSource::InputSource(InputDriver* driver, QObject* parent) + : QObject(parent) + , m_driver(driver) +{ +} diff --git a/src/platform/qt/input/InputSource.h b/src/platform/qt/input/InputSource.h new file mode 100644 index 000000000..3bf7350d7 --- /dev/null +++ b/src/platform/qt/input/InputSource.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include +#include + +#include "input/InputDriver.h" + +namespace QGBA { + +class InputSource : public QObject { +Q_OBJECT + +public: + InputSource(InputDriver* driver, QObject* parent = nullptr); + virtual ~InputSource() = default; + + InputDriver* driver() { return m_driver; } + uint32_t type() { return m_driver->type(); } + + virtual QString name() const = 0; + virtual QString visibleName() const = 0; + +protected: + InputDriver* const m_driver; +}; + +} diff --git a/src/platform/qt/input/KeySource.cpp b/src/platform/qt/input/KeySource.cpp new file mode 100644 index 000000000..32f0ca740 --- /dev/null +++ b/src/platform/qt/input/KeySource.cpp @@ -0,0 +1,13 @@ +/* Copyright (c) 2013-2023 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 "input/KeySource.h" + +using namespace QGBA; + +KeySource::KeySource(InputDriver* driver, QObject* parent) + : InputSource(driver, parent) +{ +} diff --git a/src/platform/qt/input/KeySource.h b/src/platform/qt/input/KeySource.h new file mode 100644 index 000000000..e3df912a6 --- /dev/null +++ b/src/platform/qt/input/KeySource.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include "input/InputSource.h" + +namespace QGBA { + +class InputDriver; + +class KeySource : public InputSource { +Q_OBJECT + +public: + KeySource(InputDriver* driver, QObject* parent = nullptr); + + virtual QSet currentKeys() = 0; +}; + +} diff --git a/src/platform/qt/input/SDLInputDriver.cpp b/src/platform/qt/input/SDLInputDriver.cpp new file mode 100644 index 000000000..5b68462d1 --- /dev/null +++ b/src/platform/qt/input/SDLInputDriver.cpp @@ -0,0 +1,363 @@ +/* Copyright (c) 2013-2023 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 "input/SDLInputDriver.h" + +#include "ConfigController.h" +#include "InputController.h" + +#include + +using namespace QGBA; + +int s_sdlInited = 0; +mSDLEvents s_sdlEvents; + +void SDL::suspendScreensaver() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (s_sdlInited) { + mSDLSuspendScreensaver(&s_sdlEvents); + } +#endif +} + +void SDL::resumeScreensaver() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (s_sdlInited) { + mSDLResumeScreensaver(&s_sdlEvents); + } +#endif +} + +void SDL::setScreensaverSuspendable(bool suspendable) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (s_sdlInited) { + mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable); + } +#endif +} + +SDLInputDriver::SDLInputDriver(InputController* controller, QObject* parent) + : InputDriver(parent) + , m_controller(controller) +{ + if (s_sdlInited == 0) { + mSDLInitEvents(&s_sdlEvents); + } + ++s_sdlInited; + m_sdlPlayer.bindings = m_controller->map(); + + for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { + m_gamepads.append(std::make_shared(this, i)); + } +} + +SDLInputDriver::~SDLInputDriver() { + if (m_playerAttached) { + mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer); + } + + --s_sdlInited; + if (s_sdlInited == 0) { + mSDLDeinitEvents(&s_sdlEvents); + } +} + +bool SDLInputDriver::supportsPolling() const { + return true; +} + +bool SDLInputDriver::supportsGamepads() const { + return true; +} + +bool SDLInputDriver::supportsSensors() const { + return true; +} + +QString SDLInputDriver::currentProfile() const { + if (m_sdlPlayer.joystick) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_JoystickName(m_sdlPlayer.joystick->joystick); +#else + return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick)); +#endif + } + return {}; +} + +void SDLInputDriver::loadConfiguration(ConfigController* config) { + m_config = config; + mSDLEventsLoadConfig(&s_sdlEvents, config->input()); + if (!m_playerAttached) { + m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer); + } + if (m_playerAttached) { + mSDLPlayerLoadConfig(&m_sdlPlayer, config->input()); + } +} + +void SDLInputDriver::saveConfiguration(ConfigController* config) { + if (m_playerAttached) { + mSDLPlayerSaveConfig(&m_sdlPlayer, config->input()); + } +} + +void SDLInputDriver::bindDefaults(InputController* controller) { + mSDLInitBindingsGBA(controller->map()); +} + +mRumble* SDLInputDriver::rumble() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (m_playerAttached) { + return &m_sdlPlayer.rumble.d; + } +#endif + return nullptr; +} + +mRotationSource* SDLInputDriver::rotationSource() { + if (m_playerAttached) { + return &m_sdlPlayer.rotation.d; + } + return nullptr; +} + + +bool SDLInputDriver::update() { + if (!m_playerAttached) { + return false; + } + + SDL_JoystickUpdate(); +#if SDL_VERSION_ATLEAST(2, 0, 0) + updateGamepads(); +#endif + + return true; +} + +QList SDLInputDriver::connectedGamepads() const { + QList pads; + for (auto& pad : m_gamepads) { + pads.append(pad.get()); + } + return pads; +} + +#if SDL_VERSION_ATLEAST(2, 0, 0) +void SDLInputDriver::updateGamepads() { + if (m_config) { + mSDLUpdateJoysticks(&s_sdlEvents, m_config->input()); + } + for (int i = 0; i < m_gamepads.size(); ++i) { + if (m_gamepads.at(i)->updateIndex()) { + continue; + } + m_gamepads.removeAt(i); + --i; + } + std::sort(m_gamepads.begin(), m_gamepads.end(), [](const auto& a, const auto b) { + return a->m_index < b->m_index; + }); + + for (size_t i = 0, j = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { + if ((ssize_t) j < m_gamepads.size()) { + std::shared_ptr gamepad = m_gamepads.at(j); + if (gamepad->m_index == i) { + ++j; + continue; + } + } + m_gamepads.append(std::make_shared(this, i)); + } + std::sort(m_gamepads.begin(), m_gamepads.end(), [](const auto& a, const auto b) { + return a->m_index < b->m_index; + }); +} +#endif + +int SDLInputDriver::activeGamepadIndex() const { + return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; +} + +void SDLInputDriver::setActiveGamepad(int index) { + mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); +} + +void SDLInputDriver::registerTiltAxisX(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.axisX = axis; + } +} + +void SDLInputDriver::registerTiltAxisY(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.axisY = axis; + } +} + +void SDLInputDriver::registerGyroAxisX(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroX = axis; + if (m_sdlPlayer.rotation.gyroY == axis) { + m_sdlPlayer.rotation.gyroZ = axis; + } else { + m_sdlPlayer.rotation.gyroZ = -1; + } + } +} + +void SDLInputDriver::registerGyroAxisY(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroY = axis; + if (m_sdlPlayer.rotation.gyroX == axis) { + m_sdlPlayer.rotation.gyroZ = axis; + } else { + m_sdlPlayer.rotation.gyroZ = -1; + } + } +} + +void SDLInputDriver::registerGyroAxisZ(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroZ = axis; + m_sdlPlayer.rotation.gyroX = -1; + m_sdlPlayer.rotation.gyroY = -1; + } +} + +float SDLInputDriver::gyroSensitivity() const { + if (m_playerAttached) { + return m_sdlPlayer.rotation.gyroSensitivity; + } + return 0; +} + +void SDLInputDriver::setGyroSensitivity(float sensitivity) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroSensitivity = sensitivity; + } +} + +SDLGamepad::SDLGamepad(SDLInputDriver* driver, int index, QObject* parent) + : Gamepad(driver, parent) + , m_index(index) +{ +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), m_guid, sizeof(m_guid)); +#endif +} + +QList SDLGamepad::currentButtons() { + if (!verify()) { + return {}; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + QList buttons; + + int numButtons = SDL_JoystickNumButtons(joystick); + for (int i = 0; i < numButtons; ++i) { + buttons.append(SDL_JoystickGetButton(joystick, i)); + } + + return buttons; +} + +QList SDLGamepad::currentAxes() { + if (!verify()) { + return {}; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + QList axes; + + int numAxes = SDL_JoystickNumAxes(joystick); + for (int i = 0; i < numAxes; ++i) { + axes.append(SDL_JoystickGetAxis(joystick, i)); + } + + return axes; +} + +QList SDLGamepad::currentHats() { + if (!verify()) { + return {}; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + QList hats; + + int numHats = SDL_JoystickNumHats(joystick); + for (int i = 0; i < numHats; ++i) { + hats.append(static_cast(SDL_JoystickGetHat(joystick, i))); + } + + return hats; +} + +int SDLGamepad::buttonCount() const { + if (!verify()) { + return -1; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + return SDL_JoystickNumButtons(joystick); +} + +int SDLGamepad::axisCount() const { + if (!verify()) { + return -1; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + return SDL_JoystickNumAxes(joystick); +} + +int SDLGamepad::hatCount() const { + if (!verify()) { + return -1; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + return SDL_JoystickNumHats(joystick); +} + +QString SDLGamepad::name() const { +#if SDL_VERSION_ATLEAST(2, 0, 0) + return m_guid; +#else + return visibleName(); +#endif +} + +QString SDLGamepad::visibleName() const { +#if SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick); +#else + return SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick)); +#endif +} + +#if SDL_VERSION_ATLEAST(2, 0, 0) +bool SDLGamepad::updateIndex() { + char guid[34]; + for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick; + SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), guid, sizeof(guid)); + if (memcmp(guid, m_guid, 33) == 0) { + m_index = i; + return true; + } + } + return false; +} +#endif + +bool SDLGamepad::verify() const { + return m_index < SDL_JoystickListSize(&s_sdlEvents.joysticks); +} diff --git a/src/platform/qt/input/SDLInputDriver.h b/src/platform/qt/input/SDLInputDriver.h new file mode 100644 index 000000000..0ca2f4030 --- /dev/null +++ b/src/platform/qt/input/SDLInputDriver.h @@ -0,0 +1,108 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include "input/Gamepad.h" +#include "input/InputDriver.h" + +#include "platform/sdl/sdl-events.h" + +#include + +namespace QGBA { + +class SDLGamepad; + +namespace SDL { + void suspendScreensaver(); + void resumeScreensaver(); + void setScreensaverSuspendable(bool); +} + +class SDLInputDriver : public InputDriver { +Q_OBJECT + +public: + SDLInputDriver(InputController*, QObject* parent = nullptr); + ~SDLInputDriver(); + + uint32_t type() const override { return SDL_BINDING_BUTTON; } + QString visibleName() const override { return QLatin1String("SDL"); } + QString currentProfile() const override; + + bool supportsPolling() const override; + bool supportsGamepads() const override; + bool supportsSensors() const override; + + void loadConfiguration(ConfigController* config) override; + void saveConfiguration(ConfigController* config) override; + + void bindDefaults(InputController*) override; + + bool update() override; + + QList connectedGamepads() const override; + + int activeGamepadIndex() const override; + void setActiveGamepad(int) override; + + void registerTiltAxisX(int axis) override; + void registerTiltAxisY(int axis) override; + void registerGyroAxisX(int axis) override; + void registerGyroAxisY(int axis) override; + void registerGyroAxisZ(int axis) override; + + float gyroSensitivity() const override; + void setGyroSensitivity(float sensitivity) override; + + mRumble* rumble() override; + mRotationSource* rotationSource() override; + +private: + ConfigController* m_config = nullptr; + InputController* m_controller; + mSDLPlayer m_sdlPlayer{}; + bool m_playerAttached = false; + QList> m_gamepads; + +#if SDL_VERSION_ATLEAST(2, 0, 0) + void updateGamepads(); +#endif +}; + +class SDLGamepad : public Gamepad { +Q_OBJECT + +public: + SDLGamepad(SDLInputDriver*, int index, QObject* parent = nullptr); + + QList currentButtons() override; + QList currentAxes() override; + QList currentHats() override; + + int buttonCount() const override; + int axisCount() const override; + int hatCount() const override; + + QString name() const override; + QString visibleName() const override; + +#if SDL_VERSION_ATLEAST(2, 0, 0) + bool updateIndex(); +#endif + +private: + friend class SDLInputDriver; + + size_t m_index; +#if SDL_VERSION_ATLEAST(2, 0, 0) + char m_guid[34]{}; +#endif + + bool verify() const; +}; + +} diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index 67668b011..c53408e8e 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -14,6 +14,10 @@ #include #include +#ifdef BUILD_SDL +#include "platform/sdl/sdl-events.h" +#endif + #include #include @@ -38,6 +42,7 @@ Q_IMPORT_PLUGIN(AVFServicePlugin); #endif #elif defined(Q_OS_UNIX) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); +Q_IMPORT_PLUGIN(QWaylandIntegrationPlugin); #endif #endif diff --git a/src/platform/qt/resources.qrc b/src/platform/qt/resources.qrc index 737ec55f0..e4b8fab5f 100644 --- a/src/platform/qt/resources.qrc +++ b/src/platform/qt/resources.qrc @@ -3,6 +3,7 @@ ../../../res/mgba-1024.png ../../../res/mgba-256.png + ../../../res/mgba-48.png ../../../res/keymap.qpic ../../../res/patrons.txt ../../../res/no-cam.png diff --git a/src/platform/qt/scripting/ScriptingController.cpp b/src/platform/qt/scripting/ScriptingController.cpp index cd1e716d4..d8babf06d 100644 --- a/src/platform/qt/scripting/ScriptingController.cpp +++ b/src/platform/qt/scripting/ScriptingController.cpp @@ -5,10 +5,24 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "scripting/ScriptingController.h" +#include +#include +#include +#include + #include "CoreController.h" +#include "Display.h" +#include "input/Gamepad.h" +#include "input/GamepadButtonEvent.h" +#include "input/GamepadHatEvent.h" +#include "InputController.h" #include "scripting/ScriptingTextBuffer.h" #include "scripting/ScriptingTextBufferModel.h" +#include +#include +#include + using namespace QGBA; ScriptingController::ScriptingController(QObject* parent) @@ -37,12 +51,15 @@ ScriptingController::ScriptingController(QObject* parent) m_bufferModel = new ScriptingTextBufferModel(this); QObject::connect(m_bufferModel, &ScriptingTextBufferModel::textBufferCreated, this, &ScriptingController::textBufferCreated); + mScriptGamepadInit(&m_gamepad); + init(); } ScriptingController::~ScriptingController() { clearController(); mScriptContextDeinit(&m_scriptContext); + mScriptGamepadDeinit(&m_gamepad); } void ScriptingController::setController(std::shared_ptr controller) { @@ -59,6 +76,14 @@ void ScriptingController::setController(std::shared_ptr controll connect(m_controller.get(), &CoreController::stopping, this, &ScriptingController::clearController); } +void ScriptingController::setInputController(InputController* input) { + if (m_inputController) { + m_inputController->disconnect(this); + } + m_inputController = input; + connect(m_inputController, &InputController::updated, this, &ScriptingController::updateGamepad); +} + bool ScriptingController::loadFile(const QString& path) { VFileDevice vf(path, QIODevice::ReadOnly); if (!vf.isOpen()) { @@ -73,11 +98,20 @@ bool ScriptingController::load(VFileDevice& vf, const QString& name) { } QByteArray utf8 = name.toUtf8(); CoreController::Interrupter interrupter(m_controller); + if (m_controller) { + m_controller->setSync(false); + m_controller->unpaused(); + } + bool ok = true; if (!m_activeEngine->load(m_activeEngine, utf8.constData(), vf) || !m_activeEngine->run(m_activeEngine)) { emit error(QString::fromUtf8(m_activeEngine->getError(m_activeEngine))); - return false; + ok = false; } - return true; + if (m_controller && m_controller->isPaused()) { + m_controller->setSync(true); + m_controller->paused(); + } + return ok; } void ScriptingController::clearController() { @@ -110,10 +144,153 @@ void ScriptingController::runCode(const QString& code) { load(vf, "*prompt"); } +bool ScriptingController::eventFilter(QObject* obj, QEvent* ev) { + event(obj, ev); + return false; +} + +void ScriptingController::event(QObject* obj, QEvent* event) { + if (!m_controller) { + return; + } + + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: { + 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; + ev.key = qtToScriptingKey(keyEvent); + ev.modifiers = qtToScriptingModifiers(keyEvent->modifiers()); + mScriptContextFireEvent(&m_scriptContext, &ev.d); + return; + } + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: { + 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; + ev.button = 31 - clz32(mouseEvent->button()); + mScriptContextFireEvent(&m_scriptContext, &ev.d); + return; + } + case QEvent::MouseMove: { + 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); + ev.mouse = 0; + ev.x = pos.x(); + ev.y = pos.y(); + mScriptContextFireEvent(&m_scriptContext, &ev.d); + return; + } + case QEvent::Wheel: { + struct mScriptMouseWheelEvent ev{mSCRIPT_EV_TYPE_MOUSE_WHEEL}; + auto wheelEvent = static_cast(event); + QPoint adelta = wheelEvent->angleDelta(); + QPoint pdelta = wheelEvent->pixelDelta(); + ev.mouse = 0; + if (!pdelta.isNull()) { + ev.x = pdelta.x(); + ev.y = pdelta.y(); + } else { + ev.x = adelta.x(); + ev.y = adelta.y(); + } + mScriptContextFireEvent(&m_scriptContext, &ev.d); + return; + } + default: + break; + } + + auto type = event->type(); + if (type == GamepadButtonEvent::Down() || type == GamepadButtonEvent::Up()) { + 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; + ev.button = gamepadEvent->value(); + mScriptContextFireEvent(&m_scriptContext, &ev.d); + } + if (type == GamepadHatEvent::Type()) { + struct mScriptGamepadHatEvent ev{mSCRIPT_EV_TYPE_GAMEPAD_HAT}; + updateGamepad(); + auto gamepadEvent = static_cast(event); + ev.pad = 0; + ev.hat = gamepadEvent->hatId(); + ev.direction = gamepadEvent->direction(); + mScriptContextFireEvent(&m_scriptContext, &ev.d); + } +} + +void ScriptingController::updateGamepad() { + InputDriver* driver = m_inputController->gamepadDriver(); + if (!driver) { + detachGamepad(); + return; + } + Gamepad* gamepad = driver->activeGamepad(); + if (!gamepad) { + detachGamepad(); + return; + } + + QString name = gamepad->name(); + strlcpy(m_gamepad.internalName, name.toUtf8().constData(), sizeof(m_gamepad.internalName)); + name = gamepad->visibleName(); + strlcpy(m_gamepad.visibleName, name.toUtf8().constData(), sizeof(m_gamepad.visibleName)); + attachGamepad(); + + QList buttons = gamepad->currentButtons(); + int nButtons = gamepad->buttonCount(); + mScriptGamepadSetButtonCount(&m_gamepad, nButtons); + for (int i = 0; i < nButtons; ++i) { + mScriptGamepadSetButton(&m_gamepad, i, buttons.at(i)); + } + + QList axes = gamepad->currentAxes(); + int nAxes = gamepad->axisCount(); + mScriptGamepadSetAxisCount(&m_gamepad, nAxes); + for (int i = 0; i < nAxes; ++i) { + mScriptGamepadSetAxis(&m_gamepad, i, axes.at(i)); + } + + QList hats = gamepad->currentHats(); + int nHats = gamepad->hatCount(); + mScriptGamepadSetHatCount(&m_gamepad, nHats); + for (int i = 0; i < nHats; ++i) { + mScriptGamepadSetHat(&m_gamepad, i, hats.at(i)); + } +} + +void ScriptingController::attachGamepad() { + mScriptGamepad* pad = mScriptContextGamepadLookup(&m_scriptContext, 0); + if (pad == &m_gamepad) { + return; + } + if (pad) { + mScriptContextGamepadDetach(&m_scriptContext, 0); + } + mScriptContextGamepadAttach(&m_scriptContext, &m_gamepad); +} + +void ScriptingController::detachGamepad() { + mScriptGamepad* pad = mScriptContextGamepadLookup(&m_scriptContext, 0); + if (pad != &m_gamepad) { + return; + } + mScriptContextGamepadDetach(&m_scriptContext, 0); +} + void ScriptingController::init() { mScriptContextInit(&m_scriptContext); mScriptContextAttachStdlib(&m_scriptContext); mScriptContextAttachSocket(&m_scriptContext); + mScriptContextAttachInput(&m_scriptContext); mScriptContextRegisterEngines(&m_scriptContext); mScriptContextAttachLogger(&m_scriptContext, &m_logger); @@ -128,3 +305,130 @@ void ScriptingController::init() { m_activeEngine = *m_engines.begin(); } } + +uint32_t ScriptingController::qtToScriptingKey(const QKeyEvent* event) { + QString text(event->text()); + int key = event->key(); + + static const QHash keypadMap{ + {'0', mSCRIPT_KEY_KP_0}, + {'1', mSCRIPT_KEY_KP_1}, + {'2', mSCRIPT_KEY_KP_2}, + {'3', mSCRIPT_KEY_KP_3}, + {'4', mSCRIPT_KEY_KP_4}, + {'5', mSCRIPT_KEY_KP_5}, + {'6', mSCRIPT_KEY_KP_6}, + {'7', mSCRIPT_KEY_KP_7}, + {'8', mSCRIPT_KEY_KP_8}, + {'9', mSCRIPT_KEY_KP_9}, + {'+', mSCRIPT_KEY_KP_PLUS}, + {'-', mSCRIPT_KEY_KP_MINUS}, + {'*', mSCRIPT_KEY_KP_MULTIPLY}, + {'/', mSCRIPT_KEY_KP_DIVIDE}, + {',', mSCRIPT_KEY_KP_COMMA}, + {'.', mSCRIPT_KEY_KP_POINT}, + {Qt::Key_Enter, mSCRIPT_KEY_KP_ENTER}, + }; + static const QHash extraKeyMap{ + {Qt::Key_Escape, mSCRIPT_KEY_ESCAPE}, + {Qt::Key_Tab, mSCRIPT_KEY_TAB}, + {Qt::Key_Backtab, mSCRIPT_KEY_BACKSPACE}, + {Qt::Key_Backspace, mSCRIPT_KEY_BACKSPACE}, + {Qt::Key_Return, mSCRIPT_KEY_ENTER}, + {Qt::Key_Enter, mSCRIPT_KEY_ENTER}, + {Qt::Key_Insert, mSCRIPT_KEY_INSERT}, + {Qt::Key_Delete, mSCRIPT_KEY_DELETE}, + {Qt::Key_Pause, mSCRIPT_KEY_BREAK}, + {Qt::Key_Print, mSCRIPT_KEY_PRINT_SCREEN}, + {Qt::Key_SysReq, mSCRIPT_KEY_SYSRQ}, + {Qt::Key_Clear, mSCRIPT_KEY_CLEAR}, + {Qt::Key_Home, mSCRIPT_KEY_HOME}, + {Qt::Key_End, mSCRIPT_KEY_END}, + {Qt::Key_Left, mSCRIPT_KEY_LEFT}, + {Qt::Key_Up, mSCRIPT_KEY_UP}, + {Qt::Key_Right, mSCRIPT_KEY_RIGHT}, + {Qt::Key_Down, mSCRIPT_KEY_DOWN}, + {Qt::Key_PageUp, mSCRIPT_KEY_PAGE_UP}, + {Qt::Key_PageDown, mSCRIPT_KEY_DOWN}, + {Qt::Key_Shift, mSCRIPT_KEY_SHIFT}, + {Qt::Key_Control, mSCRIPT_KEY_CONTROL}, + {Qt::Key_Meta, mSCRIPT_KEY_SUPER}, + {Qt::Key_Alt, mSCRIPT_KEY_ALT}, + {Qt::Key_CapsLock, mSCRIPT_KEY_CAPS_LOCK}, + {Qt::Key_NumLock, mSCRIPT_KEY_NUM_LOCK}, + {Qt::Key_ScrollLock, mSCRIPT_KEY_SCROLL_LOCK}, + {Qt::Key_F1, mSCRIPT_KEY_F1}, + {Qt::Key_F2, mSCRIPT_KEY_F2}, + {Qt::Key_F3, mSCRIPT_KEY_F3}, + {Qt::Key_F4, mSCRIPT_KEY_F4}, + {Qt::Key_F5, mSCRIPT_KEY_F5}, + {Qt::Key_F6, mSCRIPT_KEY_F6}, + {Qt::Key_F7, mSCRIPT_KEY_F7}, + {Qt::Key_F8, mSCRIPT_KEY_F8}, + {Qt::Key_F9, mSCRIPT_KEY_F9}, + {Qt::Key_F10, mSCRIPT_KEY_F10}, + {Qt::Key_F11, mSCRIPT_KEY_F11}, + {Qt::Key_F12, mSCRIPT_KEY_F12}, + {Qt::Key_F13, mSCRIPT_KEY_F13}, + {Qt::Key_F14, mSCRIPT_KEY_F14}, + {Qt::Key_F15, mSCRIPT_KEY_F15}, + {Qt::Key_F16, mSCRIPT_KEY_F16}, + {Qt::Key_F17, mSCRIPT_KEY_F17}, + {Qt::Key_F18, mSCRIPT_KEY_F18}, + {Qt::Key_F19, mSCRIPT_KEY_F19}, + {Qt::Key_F20, mSCRIPT_KEY_F20}, + {Qt::Key_F21, mSCRIPT_KEY_F21}, + {Qt::Key_F22, mSCRIPT_KEY_F22}, + {Qt::Key_F23, mSCRIPT_KEY_F23}, + {Qt::Key_F24, mSCRIPT_KEY_F24}, + {Qt::Key_Menu, mSCRIPT_KEY_MENU}, + {Qt::Key_Super_L, mSCRIPT_KEY_SUPER}, + {Qt::Key_Super_R, mSCRIPT_KEY_SUPER}, + {Qt::Key_Help, mSCRIPT_KEY_HELP}, + {Qt::Key_Hyper_L, mSCRIPT_KEY_SUPER}, + {Qt::Key_Hyper_R, mSCRIPT_KEY_SUPER}, + }; + + if (event->modifiers() & Qt::KeypadModifier && keypadMap.contains(key)) { + return keypadMap[key]; + } + if (key >= 0 && key < 0x100) { + return key; + } + if (key < 0x01000000) { + if (text.isEmpty()) { + return 0; + } + QChar high = text[0]; + if (!high.isSurrogate()) { + return high.unicode(); + } + if (text.size() < 2) { + return 0; + } + return QChar::surrogateToUcs4(high, text[1]); + } + + if (extraKeyMap.contains(key)) { + return extraKeyMap[key]; + } + return 0; +} + + +uint16_t ScriptingController::qtToScriptingModifiers(Qt::KeyboardModifiers modifiers) { + uint16_t mod = 0; + if (modifiers & Qt::ShiftModifier) { + mod |= mSCRIPT_KMOD_SHIFT; + } + if (modifiers & Qt::ControlModifier) { + mod |= mSCRIPT_KMOD_CONTROL; + } + if (modifiers & Qt::AltModifier) { + mod |= mSCRIPT_KMOD_ALT; + } + if (modifiers & Qt::MetaModifier) { + mod |= mSCRIPT_KMOD_SUPER; + } + return mod; +} diff --git a/src/platform/qt/scripting/ScriptingController.h b/src/platform/qt/scripting/ScriptingController.h index 03e40f7cf..0e275561d 100644 --- a/src/platform/qt/scripting/ScriptingController.h +++ b/src/platform/qt/scripting/ScriptingController.h @@ -9,17 +9,20 @@ #include #include +#include #include #include "VFileDevice.h" #include +class QKeyEvent; class QTextDocument; namespace QGBA { class CoreController; +class InputController; class ScriptingTextBuffer; class ScriptingTextBufferModel; @@ -31,10 +34,13 @@ public: ~ScriptingController(); void setController(std::shared_ptr controller); + void setInputController(InputController* controller); bool loadFile(const QString& path); bool load(VFileDevice& vf, const QString& name); + void event(QObject* obj, QEvent* ev); + mScriptContext* context() { return &m_scriptContext; } ScriptingTextBufferModel* textBufferModel() const { return m_bufferModel; } @@ -49,10 +55,20 @@ public slots: void reset(); void runCode(const QString& code); +protected: + bool eventFilter(QObject*, QEvent*) override; + +private slots: + void updateGamepad(); + private: void init(); - static mScriptTextBuffer* createTextBuffer(void* context); + void attachGamepad(); + void detachGamepad(); + + static uint32_t qtToScriptingKey(const QKeyEvent*); + static uint16_t qtToScriptingModifiers(Qt::KeyboardModifiers); struct Logger : mLogger { ScriptingController* p; @@ -64,7 +80,10 @@ private: QHash m_engines; ScriptingTextBufferModel* m_bufferModel; + mScriptGamepad m_gamepad; + std::shared_ptr m_controller; + InputController* m_inputController = nullptr; }; } diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts index 59f297fb2..2f90b12d8 100644 --- a/src/platform/qt/ts/mgba-de.ts +++ b/src/platform/qt/ts/mgba-de.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + Game Boy Advance-ROMs (%1) + + + + Game Boy ROMs (%1) + Game Boy-ROMs (%1) + + + + All ROMs (%1) + Alle ROMs (%1) + + + + %1 Video Logs (*.mvl) + %1 Video-Logs (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Download-Größe: %3 QGBA::ApplicationUpdater - + Stable Stabil - + Development Entwicklung - + Unknown Unbekannt - + (None) (keiner) @@ -400,12 +423,12 @@ Download-Größe: %3 Das GamePak kann nur auf unterstützten Plattformen herausgezogen werden! - + Failed to open snapshot file for reading: %1 Konnte Snapshot-Datei %1 nicht zum Lesen öffnen - + Failed to open snapshot file for writing: %1 Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen @@ -413,17 +436,17 @@ Download-Größe: %3 QGBA::CoreManager - + Failed to open game file: %1 Fehler beim Öffnen der Spieldatei: %1 - + Could not load game. Are you sure it's in the correct format? Konnte das Spiel nicht laden. Bist Du sicher, dass es im korrekten Format vorliegt? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Fehler beim Laden der Spielstand-Datei; Speicherdaten können nicht aktualisiert werden. Bitte stelle sicher, dass das Speicherdaten-Verzeichnis ohne zusätzliche Berechtigungen (z.B. UAC in Windows) beschreibbar ist. @@ -502,6 +525,195 @@ Download-Größe: %3 Verbindung zu Dolphin konnte nicht hergestellt werden. + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Hintergrund + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + Durchsuchen + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Download-Größe: %3 QGBA::GBAApp - + Enable Discord Rich Presence Discord-Integration aktivieren @@ -601,22 +813,22 @@ Download-Größe: %3 QGBA::GBAKeyEditor - + Clear Button Button löschen - + Clear Analog Analog löschen - + Refresh Aktualisieren - + Set all Alle belegen @@ -750,148 +962,168 @@ Download-Größe: %3 QGBA::GameBoy - - + + Autodetect Automatisch erkennen - + Game Boy (DMG) Game Boy (DMG) - + Game Boy Pocket (MGB) Game Boy Pocket (MGB) - + Super Game Boy (SGB) Super Game Boy (SGB) - + Super Game Boy 2 (SGB) Super Game Boy 2 (SGB) - + Game Boy Color (CGB) Game Boy Color (CGB) - + Game Boy Advance (AGB) Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) Super Game Boy Color (SGB + CGB) - + ROM Only Nur ROM - + MBC1 MBC1 - + MBC2 MBC2 - + MBC3 MBC3 - + MBC3 + RTC MBC3 + RTC - + MBC5 MBC5 - + MBC5 + Rumble MBC5 + Rumble - + MBC6 MBC6 - + MBC7 (Tilt) MBC7 (Tilt) - + MMM01 MMM01 - + HuC-1 HuC-1 - + HuC-3 HuC-3 - + Pocket Cam Pocket Cam - + TAMA5 TAMA5 - + Wisdom Tree Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) NT (neu) - + Pokémon Jade/Diamond Pokémon Jade/Diamond - + BBD BBD - + Hitek Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) Sachen (MMC1) - + Sachen (MMC2) Sachen (MMC2) @@ -2910,7 +3142,7 @@ Download-Größe: %3 Defekt - + Slot %1 Speicherplatz %1 @@ -4070,12 +4302,12 @@ Download-Größe: %3 QGBA::ReportView - + Bug report archive Fehlerbericht speichern - + ZIP archive (*.zip) ZIP-Archiv (*.zip) @@ -4438,95 +4670,95 @@ Download-Größe: %3 QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (erzwinge Version 1.x) - + None Keiner - + None (Still Image) Keiner (Standbild) - + Keyboard Tastatur - + Controllers Gamepads - + Shortcuts Tastenkürzel - - + + Shaders Shader - + Select BIOS BIOS auswählen - + Select directory Verzeichnis auswählen - + (%1×%2) (%1×%2) - + Never Nie - + Just now Gerade eben - + Less than an hour ago Vor weniger als einer Stunde - + %n hour(s) ago Vor %n Stunde @@ -4534,7 +4766,7 @@ Download-Größe: %3 - + %n day(s) ago Vor %n Tag @@ -4851,37 +5083,37 @@ Download-Größe: %3 Jetzt überprüfen - + Default color palette only Nur Standard-Farbpalette - + SGB color palette if available SGB-Farbpalette, sofern verfügbar - + GBC color palette if available GBC-Farbpalette, sofern verfügbar - + SGB (preferred) or GBC color palette if available SGB (bevorzugt) oder GBC-Farbpalette, sofern verfügbar - + Game Boy Camera Game Boy Camera - + Driver: Treiber: - + Source: Quelle: @@ -5684,105 +5916,85 @@ Download-Größe: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - Game Boy Advance-ROMs (%1) - - - - Game Boy ROMs (%1) - Game Boy-ROMs (%1) - - - - All ROMs (%1) - Alle ROMs (%1) - - - - %1 Video Logs (*.mvl) - %1 Video-Logs (*.mvl) - - - + Archives (%1) Archive (%1) - - - + + + Select ROM ROM auswählen - - + + Select save Speicherdatei wählen - + Select patch Patch wählen - + Patches (*.ips *.ups *.bps) Korrekturen (*.ips *.ups *.bps) - + Select e-Reader card images Bilder der Lesegerät-Karte auswählen - + Image file (*.png *.jpg *.jpeg) Bilddatei (*.png *.jpg *.jpeg) - + Conversion finished Konvertierung abgeschlossen - + %1 of %2 e-Reader cards converted successfully. %1 von %2 Lesegerät-Karten erfolgreich konvertiert. - + Select image Bild auswählen - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Bild-Datei (*.png *.gif *.jpg *.jpeg);;Alle Dateien (*) - + GameShark saves (*.sps *.xps) GameShark-Speicherdaten (*.sps *.xps) - + Select video log Video-Log auswählen - + Video logs (*.mvl) Video-Logs (*.mvl) - + Crash Absturz - + The game has crashed with the following error: %1 @@ -5791,679 +6003,684 @@ Download-Größe: %3 %1 - + Unimplemented BIOS call Nicht implementierter BIOS-Aufruf - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Es konnte kein geeignetes Ausgabegerät erstellt werden, stattdessen wird Software-Rendering als Rückfalloption genutzt. Spiele laufen möglicherweise langsamer, besonders innerhalb großer Fenster. - + Really make portable? Portablen Modus wirklich aktivieren? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren? - + Restart needed Neustart benötigt - + Some changes will not take effect until the emulator is restarted. Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde. - + - Player %1 of %2 - Spieler %1 von %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 Bilder/Sekunde) - %4 - + &File &Datei - + Load &ROM... &ROM laden... - + Load ROM in archive... ROM aus Archiv laden... - + Save games Spielstände - + Automatically determine Automatisch erkennen - + Use player %0 save game Verwende Spielstand von Spieler %0 - + Load &patch... &Patch laden... - + Boot BIOS BIOS booten - + Replace ROM... ROM ersetzen... - + Convert e-Reader card image to raw... Lesegerät-Kartenbild in Rohdaten umwandeln … - + ROM &info... ROM-&Informationen... - + Recent Zuletzt verwendet - + Make portable Portablen Modus aktivieren - + &Load state Savestate (aktueller Zustand) &laden - + Load state file... Savestate-Datei laden... - + &Save state Savestate (aktueller Zustand) &speichern - + Save state file... Savestate-Datei speichern... - + Quick load Schnell laden - + Quick save Schnell speichern - + Load recent Lade zuletzt gespeicherten Savestate - + Save recent Speichere aktuellen Zustand - + Undo load state Laden des Savestate rückgängig machen - + Undo save state Speichern des Savestate rückgängig machen - - + + State &%1 Savestate &%1 - + Load camera image... Lade Kamerabild... - + Convert save game... Spielstand konvertieren... - + Reset needed Zurücksetzen erforderlich - + Some changes will not take effect until the game is reset. Einige Änderungen werden erst dann wirksam, wenn das Spiel zurückgesetzt wird. - + New multiplayer window Neues Multiplayer-Fenster - + Connect to Dolphin... Mit Dolphin verbinden... - + Report bug... Fehler melden... - + E&xit &Beenden - + &Emulation &Emulation - + &Reset Zu&rücksetzen - + Sh&utdown Schli&eßen - + Yank game pak Spielmodul herausziehen - + &Pause &Pause - + &Next frame &Nächstes Bild - + Fast forward (held) Schneller Vorlauf (gehalten) - + &Fast forward Schneller &Vorlauf - + Fast forward speed Vorlauf-Geschwindigkeit - + Unbounded Unbegrenzt - + %0x %0x - + Rewind (held) Zurückspulen (gehalten) - + Re&wind Zur&ückspulen - + Step backwards Schrittweiser Rücklauf - + Solar sensor Sonnen-Sensor - + Increase solar level Sonnen-Level erhöhen - + Decrease solar level Sonnen-Level verringern - + Brightest solar level Hellster Sonnen-Level - + Darkest solar level Dunkelster Sonnen-Level - + Brightness %1 Helligkeit %1 - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Audio/&Video - + Frame size Bildgröße - + Toggle fullscreen Vollbildmodus umschalten - + Lock aspect ratio Seitenverhältnis korrigieren - + Force integer scaling Pixelgenaue Skalierung (Integer scaling) - + Interframe blending Interframe-Überblendung - + Frame&skip Frame&skip - + Mute Stummschalten - + FPS target Bildwiederholrate - + Take &screenshot &Screenshot erstellen - + F12 F12 - + Scripting... Scripting... - + + Create forwarder... + + + + Game state views Spiel-Zustände ansehen - + Clear Leeren - + Game Boy Printer... Game Boy Printer... - + Video layers Video-Ebenen - + Audio channels Audio-Kanäle - + Adjust layer placement... Lage der Bildebenen anpassen... - + &Tools &Werkzeuge - + View &logs... &Logs ansehen... - + Game &overrides... Spiel-&Überschreibungen... - + &Cheats... &Cheats... - + Open debugger console... Debugger-Konsole öffnen... - + Start &GDB server... &GDB-Server starten... - + Settings... Einstellungen... - + Select folder Ordner auswählen - + Save games (%1) Spielstände (%1) - + Select save game Spielstand auswählen - + mGBA save state files (%1) mGBA-Savestates (%1) - - + + Select save state Savestate auswählen - + Select e-Reader dotcode e-Reader-Code auswählen - + e-Reader card (*.raw *.bin *.bmp) e-Reader-Karte (*.raw *.bin *.bmp) - + GameShark saves (*.gsv *.sps *.xps) GameShark-Spielstände (*.gsv *.sps *.xps) - + Couldn't Start Konnte nicht gestartet werden - + Could not start game. Spiel konnte nicht gestartet werden. - + Add folder to library... Ordner zur Bibliothek hinzufügen... - + Load alternate save game... Alternativen Spielstand laden... - + Load temporary save game... Temporären Spielstand laden... - + Scan e-Reader dotcodes... e-Reader-Code einlesen... - + Import GameShark Save... GameShare-Speicherstand importieren... - + Export GameShark Save... GameShark-Speicherstand exportieren... - + About... Über... - + %1× %1x - + Bilinear filtering Bilineare Filterung - + Native (59.7275) Nativ (59.7275) - + Record A/V... Audio/Video aufzeichnen... - + Record GIF/WebP/APNG... GIF/WebP/APNG aufzeichnen... - + Game Pak sensors... Spielmodul-Sensoren... - + View &palette... &Palette betrachten... - + View &sprites... &Sprites betrachten... - + View &tiles... &Tiles betrachten... - + View &map... &Map betrachten... - + &Frame inspector... &Bildbetrachter... - + View memory... Speicher betrachten... - + Search memory... Speicher durchsuchen... - + View &I/O registers... &I/O-Register betrachten... - + Record debug video log... Video-Protokoll aufzeichnen... - + Stop debug video log Aufzeichnen des Video-Protokolls beenden - + Exit fullscreen Vollbildmodus beenden - + GameShark Button (held) GameShark-Taste (gehalten) - + Autofire Autofeuer - + Autofire A Autofeuer A - + Autofire B Autofeuer B - + Autofire L Autofeuer L - + Autofire R Autofeuer R - + Autofire Start Autofeuer Start - + Autofire Select Autofeuer Select - + Autofire Up Autofeuer nach oben - + Autofire Right Autofeuer rechts - + Autofire Down Autofeuer nach unten - + Autofire Left Autofeuer links @@ -6471,32 +6688,32 @@ Download-Größe: %3 QObject - + %1 byte %1 Byte - + %1 kiB %1 KiB - + %1 MiB %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-en.ts b/src/platform/qt/ts/mgba-en.ts index fc1ea72c7..43a29402c 100644 --- a/src/platform/qt/ts/mgba-en.ts +++ b/src/platform/qt/ts/mgba-en.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + QGBA::AboutScreen @@ -86,22 +109,22 @@ Download size: %3 QGBA::ApplicationUpdater - + Stable - + Development - + Unknown - + (None) @@ -394,12 +417,12 @@ Download size: %3 - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -407,17 +430,17 @@ Download size: %3 QGBA::CoreManager - + Failed to open game file: %1 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -496,6 +519,195 @@ Download size: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -587,7 +799,7 @@ Download size: %3 QGBA::GBAApp - + Enable Discord Rich Presence @@ -595,22 +807,22 @@ Download size: %3 QGBA::GBAKeyEditor - + Clear Button - + Clear Analog - + Refresh - + Set all @@ -744,148 +956,168 @@ Download size: %3 QGBA::GameBoy - - + + Autodetect - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2904,7 +3136,7 @@ Download size: %3 - + Slot %1 @@ -4064,12 +4296,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -4432,95 +4664,95 @@ Download size: %3 QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4528,7 +4760,7 @@ Download size: %3 - + %n day(s) ago @@ -4855,37 +5087,37 @@ Download size: %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5678,784 +5910,769 @@ Download size: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - - - - - Game Boy ROMs (%1) - - - - - All ROMs (%1) - - - - - %1 Video Logs (*.mvl) - - - - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + + Create forwarder... + + + + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear @@ -6463,32 +6680,32 @@ Download size: %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA - + GB - + ? diff --git a/src/platform/qt/ts/mgba-es.ts b/src/platform/qt/ts/mgba-es.ts index e65866bfa..994566710 100644 --- a/src/platform/qt/ts/mgba-es.ts +++ b/src/platform/qt/ts/mgba-es.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + ROMs de Game Boy Advance (%1) + + + + Game Boy ROMs (%1) + ROMs de Game Boy (%1) + + + + All ROMs (%1) + Todas las ROMs (%1) + + + + %1 Video Logs (*.mvl) + Registros de vídeo de %1 (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Tamaño de descarga: %3 QGBA::ApplicationUpdater - + Stable Estable - + Development Desarrollo - + Unknown Desconocido - + (None) (Ninguno) @@ -400,12 +423,12 @@ Tamaño de descarga: %3 ¡No se puede quitar el cartucho en esta plataforma! - + Failed to open snapshot file for reading: %1 Error al leer del archivo de captura: %1 - + Failed to open snapshot file for writing: %1 Error al escribir al archivo de captura: %1 @@ -413,17 +436,17 @@ Tamaño de descarga: %3 QGBA::CoreManager - + Failed to open game file: %1 Error al abrir el archivo del juego: %1 - + Could not load game. Are you sure it's in the correct format? No se pudo cargar el juego. ¿Estás seguro de que está en el formato correcto? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Error al abrir el archivo de guardado; las partidas guardadas no se pueden actualizar. Por favor, asegúrate de que es posible escribir en el directorio de partidas guardadas sin necesidad de privilegios adicionales (por ejemplo, UAC en Windows). @@ -502,6 +525,195 @@ Tamaño de descarga: %3 No se pudo conectar a Dolphin. + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Fondo + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + Examinar + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Tamaño de descarga: %3 QGBA::GBAApp - + Enable Discord Rich Presence Habilitar Rich Presence en Discord @@ -601,22 +813,22 @@ Tamaño de descarga: %3 QGBA::GBAKeyEditor - + Clear Button Reestablecer botones - + Clear Analog Reestablecer sticks analógicos - + Refresh Actualizar - + Set all Configurar todo @@ -750,148 +962,168 @@ Tamaño de descarga: %3 QGBA::GameBoy - - + + Autodetect Detección automática - + Game Boy (DMG) Game Boy (DMG) - + Game Boy Pocket (MGB) Game Boy Pocket (MGB) - + Super Game Boy (SGB) Super Game Boy (SGB) - + Super Game Boy 2 (SGB) Super Game Boy 2 (SGB) - + Game Boy Color (CGB) Game Boy Color (CGB) - + Game Boy Advance (AGB) Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) Super Game Boy Color (SGB + CGB) - + ROM Only Solo ROM - + MBC1 MBC1 - + MBC2 MBC2 - + MBC3 MBC3 - + MBC3 + RTC MBC3 + RTC - + MBC5 MBC5 - + MBC5 + Rumble MBC5 + Vibración - + MBC6 MBC6 - + MBC7 (Tilt) MBC7 (Ladeado) - + MMM01 MMM01 - + HuC-1 HuC-1 - + HuC-3 HuC-3 - + Pocket Cam Pocket Cam - + TAMA5 TAMA5 - + Wisdom Tree Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) NT (nuevo) - + Pokémon Jade/Diamond Pokémon Jade/Diamante - + BBD BBD - + Hitek Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) Sachen (MMC1) - + Sachen (MMC2) Sachen (MMC2) @@ -2910,7 +3142,7 @@ Tamaño de descarga: %3 Dañado - + Slot %1 Espacio %1 @@ -4070,12 +4302,12 @@ Tamaño de descarga: %3 QGBA::ReportView - + Bug report archive Archivo del informe de errores - + ZIP archive (*.zip) Archivo ZIP (*.zip) @@ -4438,95 +4670,95 @@ Tamaño de descarga: %3 QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (forzar versión 1.x) - + None Ninguno - + None (Still Image) Nada (imagen estática) - + Keyboard Teclado - + Controllers Mandos - + Shortcuts Atajos de teclado - - + + Shaders Shaders - + Select BIOS Seleccionar BIOS - + Select directory Elegir carpeta - + (%1×%2) - + Never Nunca - + Just now Ahora mismo - + Less than an hour ago Hace menos de una hora - + %n hour(s) ago Hace %n hora @@ -4534,7 +4766,7 @@ Tamaño de descarga: %3 - + %n day(s) ago Hace %n dia @@ -4866,37 +5098,37 @@ Tamaño de descarga: %3 Comprobar ahora - + Default color palette only Sólo paleta de colores predeterminada - + SGB color palette if available Paleta de color SGB si está disponible - + GBC color palette if available Paleta de color GBC si está disponible - + SGB (preferred) or GBC color palette if available Paleta de colores SGB (preferida) o GBC si está disponible - + Game Boy Camera Cámara Game Boy - + Driver: Controlador: - + Source: Fuente: @@ -5684,100 +5916,80 @@ Tamaño de descarga: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - ROMs de Game Boy Advance (%1) - - - - Game Boy ROMs (%1) - ROMs de Game Boy (%1) - - - - All ROMs (%1) - Todas las ROMs (%1) - - - - %1 Video Logs (*.mvl) - Registros de vídeo de %1 (*.mvl) - - - + Archives (%1) Contenedores (%1) - - - + + + Select ROM Seleccionar ROM - + Select folder Seleccionar carpeta - - + + Select save Seleccionar guardado - + Select patch Seleccionar parche - + Patches (*.ips *.ups *.bps) Parches (*.ips *.ups *.bps) - + Select e-Reader dotcode Seleccionar dotcode del e-Reader - + e-Reader card (*.raw *.bin *.bmp) Tarjeta e-Reader (*.raw *.bin *.bmp) - + Select image Seleccionar imagen - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Archivo de imagen (*.png *.gif *.jpg *.jpeg);;Todos los archivos (*) - + GameShark saves (*.sps *.xps) Guardados de GameShark (*.sps *.xps) - + Select video log Seleccionar registro de vídeo - + Video logs (*.mvl) Registros de vídeo (*.mvl) - + Crash Cierre inesperado - + The game has crashed with the following error: %1 @@ -5786,684 +5998,689 @@ Tamaño de descarga: %3 %1 - + Unimplemented BIOS call Llamada a BIOS no implementada - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Este juego utiliza una llamada al BIOS que no se ha implementado. Utiliza el BIOS oficial para obtener la mejor experiencia. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. No se pudo crear un dispositivo de pantalla apropiado, recurriendo a software. Los juegos pueden funcionar lentamente, especialmente con ventanas grandes. - + Really make portable? ¿Hacer "portable"? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Esto hará que el emulador cargue su configuración desde el mismo directorio que el ejecutable. ¿Quieres continuar? - + Restart needed Reinicio necesario - + Some changes will not take effect until the emulator is restarted. Algunos cambios no surtirán efecto hasta que se reinicie el emulador. - + - Player %1 of %2 - Jugador %1 de %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Archivo - + Load &ROM... Cargar &ROM... - + Load ROM in archive... Cargar ROM desde contenedor... - + Add folder to library... Agregar carpeta a la biblioteca... - + Save games Datos de guardado - + Automatically determine Determinar automáticamente - + Use player %0 save game Usar la partida guardada del jugador %0 - + Load &patch... Cargar &parche... - + Boot BIOS Arrancar BIOS - + Replace ROM... Reemplazar ROM... - + ROM &info... &Información de la ROM... - + Recent Cargar reciente - + Make portable Crear instalación portable - + &Load state Ca&rgar estado - + Report bug... Reportar error... - + About... Acerca de... - + Game Pak sensors... Sensores del cartucho... - + Clear Limpiar - + Load state file... Cargar archivo de estado... - + Save games (%1) Juegos guardados (%1) - + Select save game Elegir juego guardado - + mGBA save state files (%1) Archivos estados guardados mGBA (%1) - - + + Select save state Elegir estado guardado - + Select e-Reader card images Elegir imágenes de tarjeta e-Reader - + Image file (*.png *.jpg *.jpeg) Archivo de imagen (*.png *.jpg *.jpeg) - + Conversion finished Conversión terminada - + %1 of %2 e-Reader cards converted successfully. %1 de %2 tarjetas e-Reader convertidas con éxito. - + Load alternate save game... Cargar partida guardada alternativa... - + Load temporary save game... Cargar partida guardada temporal... - + Convert e-Reader card image to raw... Convertir imagen de tarjeta e-Reader a archivo en bruto... - + &Save state Guardar e&stado - + Save state file... Guardar archivo de estado... - + Quick load Cargado rápido - + Quick save Guardado rápido - + Load recent Cargar reciente - + Save recent Guardar reciente - + Undo load state Deshacer cargar estado - + Undo save state Deshacer guardar estado - - + + State &%1 Estado &%1 - + Load camera image... Cargar imagen para cámara... - + Convert save game... Convertir juego guardado... - + GameShark saves (*.gsv *.sps *.xps) Partidas guardadas de GameShark (*.gsv *.sps *.xps) - + Reset needed Reinicio necesario - + Some changes will not take effect until the game is reset. Algunos cambios no tendrán efecto hasta que se reinicie el juego. - + New multiplayer window Nueva ventana multijugador - + Connect to Dolphin... Conectar a Dolphin... - + E&xit Salir (&X) - + &Emulation &Emulación - + &Reset &Reiniciar - + Sh&utdown Apagar (&U) - + Yank game pak Sacar cartucho - + &Pause &Pausar - + &Next frame Cuadro siguie&nte - + Fast forward (held) Avance rápido (mantener) - + &Fast forward &Avance rápido - + Fast forward speed Velocidad de avance rápido - + Unbounded Sin límite - + %0x %0x - + Rewind (held) Rebobinar (mantener) - + Re&wind Re&bobinar - + Step backwards Paso hacia atrás - + Solar sensor Sensor solar - + Increase solar level Subir nivel - + Decrease solar level Bajar nivel - + Brightest solar level Más claro - + Darkest solar level Más oscuro - + Brightness %1 Brillo %1 - + Audio/&Video Audio/&vídeo - + Frame size Tamaño del cuadro - + Toggle fullscreen Pantalla completa - + Lock aspect ratio Bloquear proporción de aspecto - + Force integer scaling Forzar escala a enteros - + Bilinear filtering Filtro bilineal - + Frame&skip &Salto de cuadros - + Mute Silenciar - + FPS target Objetivo de FPS - + Native (59.7275) Nativo (59,7275) - + Take &screenshot Tomar pan&tallazo - + F12 F12 - + Game Boy Printer... Game Boy Printer... - + BattleChip Gate... BattleChip Gate... - + %1× %1× - + Interframe blending Mezcla entre cuadros - + Record A/V... Grabar A/V... - + Video layers Capas de vídeo - + Audio channels Canales de audio - + Adjust layer placement... Ajustar ubicación de capas... - + &Tools Herramien&tas - + View &logs... Ver re&gistros... - + Game &overrides... Ajustes específic&os por juego... - + Couldn't Start No se pudo iniciar - + Could not start game. No se pudo iniciar el juego. - + Scan e-Reader dotcodes... Escanear dotcodes del e-Reader... - + Import GameShark Save... Importar desde GameShark... - + Export GameShark Save... Exportar a GameShark... - + Record GIF/WebP/APNG... Grabar GIF/WebP/APNG... - + &Cheats... Tru&cos... - + Settings... Ajustes... - + Open debugger console... Abrir consola de depuración... - + Start &GDB server... Iniciar servidor &GDB... - + Scripting... Scripts... - + + Create forwarder... + + + + Game state views Estado del juego - + View &palette... Ver &paleta... - + View &sprites... Ver &sprites... - + View &tiles... Ver m&osaicos... - + View &map... Ver &mapa... - + &Frame inspector... Inspec&tor de cuadros... - + View memory... Ver memoria... - + Search memory... Buscar memoria... - + View &I/O registers... Ver registros &I/O... - + Record debug video log... Grabar registro de depuración de vídeo... - + Stop debug video log Detener registro de depuración de vídeo - + Exit fullscreen Salir de pantalla completa - + GameShark Button (held) Botón GameShark (mantener) - + Autofire Disparo automático - + Autofire A Disparo automático A - + Autofire B Disparo automático B - + Autofire L Disparo automático L - + Autofire R Disparo automático R - + Autofire Start Disparo automático Start - + Autofire Select Disparo automático Select - + Autofire Up Disparo automático arriba - + Autofire Right Disparo automático derecha - + Autofire Down Disparo automático abajo - + Autofire Left Disparo automático izquierda @@ -6471,32 +6688,32 @@ Tamaño de descarga: %3 QObject - + %1 byte %1 byte - + %1 kiB %1 kiB - + %1 MiB %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-fr.ts b/src/platform/qt/ts/mgba-fr.ts index 474be1297..60d84bbd7 100644 --- a/src/platform/qt/ts/mgba-fr.ts +++ b/src/platform/qt/ts/mgba-fr.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + ROMs de Game Boy Advance (%1) + + + + Game Boy ROMs (%1) + ROMs de Game Boy (%1) + + + + All ROMs (%1) + Toutes les ROM (%1) + + + + %1 Video Logs (*.mvl) + %1 Journaux vidéo (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Taille du téléchargement : %3 QGBA::ApplicationUpdater - + Stable Stable - + Development Développement - + Unknown Inconnue - + (None) (Aucune) @@ -401,12 +424,12 @@ Taille du téléchargement : %3 - + Failed to open snapshot file for reading: %1 Échec de l'ouverture de l'instantané pour lire : %1 - + Failed to open snapshot file for writing: %1 Échec de l'ouverture de l'instantané pour écrire : %1 @@ -414,17 +437,17 @@ Taille du téléchargement : %3 QGBA::CoreManager - + Failed to open game file: %1 Échec de l'ouverture du fichier de jeu : %1 - + Could not load game. Are you sure it's in the correct format? Impossible de charger le jeu. Êtes-vous sûr qu'il est dans le bon format ? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Impossible d'ouvrir le fichier de sauvegarde ; les sauvegardes en jeu ne peuvent pas être mises à jour. Veuillez vous assurer que le répertoire de sauvegarde est accessible en écriture sans privilèges supplémentaires (par exemple, UAC sous Windows). @@ -503,6 +526,195 @@ Taille du téléchargement : %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Arrière plan + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + Parcourir + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -594,7 +806,7 @@ Taille du téléchargement : %3 QGBA::GBAApp - + Enable Discord Rich Presence Activer intégration avec Discord @@ -602,22 +814,22 @@ Taille du téléchargement : %3 QGBA::GBAKeyEditor - + Clear Button Bouton d'effacement - + Clear Analog Effacer l'analogique - + Refresh Rafraîchir - + Set all Tout définir @@ -751,148 +963,168 @@ Taille du téléchargement : %3 QGBA::GameBoy - - + + Autodetect Détection automatique - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2921,7 +3153,7 @@ Taille du téléchargement : %3 Corrompue - + Slot %1 Emplacement %1 @@ -4088,12 +4320,12 @@ Taille du téléchargement : %3 QGBA::ReportView - + Bug report archive Archive de signalement d'erreur - + ZIP archive (*.zip) Archive ZIP (*.zip) @@ -4457,95 +4689,95 @@ Taille du téléchargement : %3 QGBA::SettingsView - - + + Qt Multimedia Qt Multimédia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (version forcée 1.x) - + None Aucun - + None (Still Image) Aucun (Image fixe) - + Keyboard Clavier - + Controllers Contrôleurs - + Shortcuts Raccourcis - - + + Shaders Shaders - + Select BIOS Choisir le BIOS - + Select directory - + (%1×%2) (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4553,7 +4785,7 @@ Taille du téléchargement : %3 - + %n day(s) ago @@ -4951,37 +5183,37 @@ Taille du téléchargement : %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5703,120 +5935,100 @@ Taille du téléchargement : %3 QGBA::Window - - Game Boy Advance ROMs (%1) - ROMs de Game Boy Advance (%1) - - - - Game Boy ROMs (%1) - ROMs de Game Boy (%1) - - - - All ROMs (%1) - Toutes les ROM (%1) - - - - %1 Video Logs (*.mvl) - %1 Journaux vidéo (*.mvl) - - - + Archives (%1) Archives (%1) - - - + + + Select ROM Choisir une ROM - + Select folder Choisir un dossier - - + + Select save Choisir une sauvegarde - + Select patch Sélectionner un correctif - + Patches (*.ips *.ups *.bps) Correctifs/Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode Sélectionnez le numéro de point du e-Reader - + e-Reader card (*.raw *.bin *.bmp) e-Reader carte (*.raw *.bin *.bmp) - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Select image Choisir une image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Image (*.png *.gif *.jpg *.jpeg);;Tous les fichiers (*) - + GameShark saves (*.sps *.xps) Sauvegardes GameShark (*.sps *.xps) - + Select video log Sélectionner un journal vidéo - + Video logs (*.mvl) Journaux vidéo (*.mvl) - + Crash Plantage - + The game has crashed with the following error: %1 @@ -5825,664 +6037,669 @@ Taille du téléchargement : %3 %1 - + Unimplemented BIOS call Requête au BIOS non supporté - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Ce jeu utilise un appel BIOS qui n'est pas implémenté. Veuillez utiliser le BIOS officiel pour une meilleure expérience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Échec de la création d'un périphérique d'affichage approprié, retour à l'affichage du logiciel. Les jeux peuvent fonctionner lentement, en particulier avec des fenêtres plus grandes. - + Really make portable? Vraiment rendre portable ? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Cela amènera l'émulateur à charger sa configuration depuis le même répertoire que l'exécutable. Souhaitez vous continuer ? - + Restart needed Un redémarrage est nécessaire - + Some changes will not take effect until the emulator is restarted. Certains changements ne prendront effet qu'après le redémarrage de l'émulateur. - + - Player %1 of %2 - Joueur %1 of %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Fichier - + Load &ROM... Charger une &ROM… - + Load ROM in archive... Charger la ROM d'une archive… - + Add folder to library... Ajouter un dossier à la bibliothèque… - + Load &patch... Charger un c&orrectif… - + Boot BIOS Démarrer le BIOS - + Replace ROM... Remplacer la ROM… - + + Create forwarder... + + + + Game state views - + Convert e-Reader card image to raw... - + ROM &info... &Infos sur la ROM… - + Recent Récent - + Make portable Rendre portable - + &Load state &Charger un état - + &Save state &Sauvegarder un état - + Quick load Chargement rapide - + Quick save Sauvegarde rapide - + Load recent Charger un fichier récent - + Save recent Sauvegarder un fichier récent - + Undo load state Annuler le chargement de l'état - + Undo save state Annuler la sauvegarde de l'état - - + + State &%1 État &%1 - + Load camera image... Charger une image de la caméra… - + Convert save game... - + New multiplayer window Nouvelle fenêtre multijoueur - + Connect to Dolphin... - + Report bug... Signalement de l'erreur… - + E&xit &Quitter - + &Emulation &Émulation - + &Reset &Réinitialiser - + Sh&utdown Extin&ction - + Yank game pak Yank game pak - + &Pause &Pause - + &Next frame &Image suivante - + Fast forward (held) Avance rapide (maintenir) - + &Fast forward A&vance rapide - + Fast forward speed Vitesse de l'avance rapide - + Unbounded Sans limites - + %0x %0x - + Rewind (held) Rembobiner (maintenir) - + Re&wind Rem&bobiner - + Step backwards Retour en arrière - + Solar sensor Capteur solaire - + Increase solar level Augmenter le niveau solaire - + Decrease solar level Diminuer le niveau solaire - + Brightest solar level Tester le niveau solaire - + Darkest solar level Assombrir le niveau solaire - + Brightness %1 Luminosité %1 - + Audio/&Video Audio/&Vidéo - + Frame size Taille de l'image - + Toggle fullscreen Basculer en plein écran - + Lock aspect ratio Bloquer les proportions - + Force integer scaling Forcer la mise à l'échelle par des nombres entiers - + Bilinear filtering Filtrage bilinèaire - + Frame&skip &Saut d'image - + Mute Muet - + FPS target FPS ciblé - + Take &screenshot Prendre une ca&pture d'écran - + F12 F12 - + Game Boy Printer... Imprimante GameBoy… - + Video layers Couches vidéo - + Audio channels Canaux audio - + Adjust layer placement... Ajuster la disposition… - + &Tools Ou&tils - + View &logs... Voir les &journaux… - + Game &overrides... - + Couldn't Start N'a pas pu démarrer - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + GameShark saves (*.gsv *.sps *.xps) - + Could not start game. Impossible de démarrer le jeu. - + Load alternate save game... - + Load temporary save game... - + Scan e-Reader dotcodes... Scanner les dotcodes e-Reader... - + Load state file... Charger le fichier d'état... - + Save state file... Enregistrer le fichier d'état... - + Import GameShark Save... Importer la sauvegarde de GameShark... - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games Sauvegarder les jeux - + Export GameShark Save... Exporter la sauvegarde de GameShark... - + Automatically determine - + Use player %0 save game - + About... À propos de… - + BattleChip Gate... - + %1× %1× - + Interframe blending Mélange d'images - + Native (59.7275) Natif (59.7275) - + Record A/V... Enregistrer A/V... - + Record GIF/WebP/APNG... Enregistrer GIF/WebP/APNG... - + Game Pak sensors... Capteurs de la Game Pak... - + &Cheats... &Cheats… - + Settings... Paramètres… - + Open debugger console... Ouvrir la console de débug… - + Start &GDB server... Démarrer le serveur &GDB… - + Scripting... - + View &palette... Voir la &palette… - + View &sprites... Voir les &sprites… - + View &tiles... Voir les &tiles… - + View &map... Voir la &map… - + &Frame inspector... Inspecteur de &frame... - + View memory... Voir la mémoire… - + Search memory... Recherche dans la mémoire… - + View &I/O registers... Voir les registres d'&E/S... - + Record debug video log... Enregistrer le journal vidéo de débogage... - + Stop debug video log Arrêter le journal vidéo de débogage - + Exit fullscreen Quitter le plein écran - + GameShark Button (held) Bouton GameShark (maintenir) - + Autofire Tir automatique - + Autofire A Tir automatique A - + Autofire B Tir automatique B - + Autofire L Tir automatique L - + Autofire R Tir automatique R - + Autofire Start Tir automatique Start - + Autofire Select Tir automatique Select - + Autofire Up Tir automatique Up - + Autofire Right Tir automatique Right - + Autofire Down Tir automatique Down - + Autofire Left Tir automatique Gauche - + Clear Vider @@ -6490,32 +6707,32 @@ Taille du téléchargement : %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-hu.ts b/src/platform/qt/ts/mgba-hu.ts index 332ea410f..074576da6 100644 --- a/src/platform/qt/ts/mgba-hu.ts +++ b/src/platform/qt/ts/mgba-hu.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + QGBA::AboutScreen @@ -87,22 +110,22 @@ Download size: %3 QGBA::ApplicationUpdater - + Stable - + Development - + Unknown - + (None) @@ -395,12 +418,12 @@ Download size: %3 A játékkazettát nem lehet kirántani ismeretlen platformon! - + Failed to open snapshot file for reading: %1 A pillanatkép fájljának olvasásra való megnyitása sikertelen: %1 - + Failed to open snapshot file for writing: %1 A pillanatkép fájljának írásra való megnyitása sikertelen: %1 @@ -408,17 +431,17 @@ Download size: %3 QGBA::CoreManager - + Failed to open game file: %1 Nem sikerült a játékfájl megnyitása: %1 - + Could not load game. Are you sure it's in the correct format? A játék betöltése nem sikerült. Biztos vagy benne, hogy a megfelelő formátumú? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -497,6 +520,195 @@ Download size: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Háttér + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -588,7 +800,7 @@ Download size: %3 QGBA::GBAApp - + Enable Discord Rich Presence Discord Rich Presence engedélyezése @@ -596,22 +808,22 @@ Download size: %3 QGBA::GBAKeyEditor - + Clear Button Gombhozzárendelés törlése - + Clear Analog Analóg hozzárendelések törlése - + Refresh Frissítés - + Set all Mind beállítása @@ -745,148 +957,168 @@ Download size: %3 QGBA::GameBoy - - + + Autodetect Automatikus észlelés - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2905,7 +3137,7 @@ Download size: %3 - + Slot %1 @@ -4065,12 +4297,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -4433,102 +4665,102 @@ Download size: %3 QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None Nincs - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4793,37 +5025,37 @@ Download size: %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5677,784 +5909,769 @@ Download size: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - - - - - Game Boy ROMs (%1) - - - - - All ROMs (%1) - - - - - %1 Video Logs (*.mvl) - - - - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + + Create forwarder... + + + + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Napló törlése @@ -6462,32 +6679,32 @@ Download size: %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA - + GB - + ? diff --git a/src/platform/qt/ts/mgba-it.ts b/src/platform/qt/ts/mgba-it.ts index 3bf759ecf..7be66f1c1 100644 --- a/src/platform/qt/ts/mgba-it.ts +++ b/src/platform/qt/ts/mgba-it.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + ROM per Game Boy Advance (%1) + + + + Game Boy ROMs (%1) + ROM per Game Boy (%1) + + + + All ROMs (%1) + Tutte le ROM (%1) + + + + %1 Video Logs (*.mvl) + %1 log Video (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Dimensione del download: %3 QGBA::ApplicationUpdater - + Stable Stabile - + Development Sviluppo - + Unknown Sconosciuto - + (None) (Nessuno) @@ -400,12 +423,12 @@ Dimensione del download: %3 Non riesco a strappare il pacchetto in una piattaforma inaspettata! - + Failed to open snapshot file for reading: %1 Impossibile aprire il file snapshot per la lettura: %1 - + Failed to open snapshot file for writing: %1 Impossibile aprire il file snapshot per la scrittura: %1 @@ -413,17 +436,17 @@ Dimensione del download: %3 QGBA::CoreManager - + Failed to open game file: %1 Impossibile aprire il file di gioco: %1 - + Could not load game. Are you sure it's in the correct format? Impossibile caricare il gioco. Sei sicuro che sia nel formato corretto? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Impossibile aprire il file di salvataggio; i salvataggi in gioco non possono essere aggiornati. Assicurati che la directory di salvataggio sia scrivibile senza privilegi aggiuntivi (ad esempio UAC su Windows). @@ -502,6 +525,195 @@ Dimensione del download: %3 Impossibile collegarsi a Dolphin. + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Sfondo + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + Sfoglia + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Dimensione del download: %3 QGBA::GBAApp - + Enable Discord Rich Presence Abilita Discord Rich Presence @@ -601,22 +813,22 @@ Dimensione del download: %3 QGBA::GBAKeyEditor - + Clear Button Svuota i pulsanti - + Clear Analog Svuota Analogici - + Refresh Aggiorna - + Set all Imposta tutti @@ -750,148 +962,168 @@ Dimensione del download: %3 QGBA::GameBoy - - + + Autodetect Rilevamento automatico - + Game Boy (DMG) Game Boy (DMG) - + Game Boy Pocket (MGB) Game Boy Pocket (MGB) - + Super Game Boy (SGB) Super Game Boy (SGB) - + Super Game Boy 2 (SGB) Super Game Boy 2 (SGB) - + Game Boy Color (CGB) Game Boy Color (CGB) - + Game Boy Advance (AGB) Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) Super Game Boy Color (SGB + CGB) - + ROM Only Solo ROM - + MBC1 MBC1 - + MBC2 MBC2 - + MBC3 MBC3 - + MBC3 + RTC MBC3 + RTC - + MBC5 MBC5 - + MBC5 + Rumble MBC5 + Tremolio - + MBC6 MBC6 - + MBC7 (Tilt) MBC7 (Tilt) - + MMM01 MMM01 - + HuC-1 HuC-1 - + HuC-3 HuC-3 - + Pocket Cam Pocket Cam - + TAMA5 TAMA5 - + Wisdom Tree Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) NT (nuovo) - + Pokémon Jade/Diamond Pokemon Giada/Diamante - + BBD BBD - + Hitek Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) Sachen (MMC1) - + Sachen (MMC2) Sachen (MMC2) @@ -2910,7 +3142,7 @@ Dimensione del download: %3 Corrotto - + Slot %1 Slot %1 @@ -4070,12 +4302,12 @@ Dimensione del download: %3 QGBA::ReportView - + Bug report archive Archivio delle segnalazioni di bug - + ZIP archive (*.zip) Archivio ZIP (*.zip) @@ -4438,95 +4670,95 @@ Dimensione del download: %3 QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (forza la versione 1.x) - + None Nessuno - + None (Still Image) nessuno (immagine fissa) - + Keyboard Tastiera - + Controllers Controller - + Shortcuts Scorciatoie - - + + Shaders Shader - + Select BIOS Seleziona BIOS - + Select directory Seleziona directory - + (%1×%2) (%1×%2) - + Never Mai - + Just now Solo adesso - + Less than an hour ago Meno di un ora fa - + %n hour(s) ago %n ora fa @@ -4534,7 +4766,7 @@ Dimensione del download: %3 - + %n day(s) ago %n giorno fa @@ -4891,37 +5123,37 @@ Dimensione del download: %3 Tavolozza Game Boy - + Default color palette only Solo tavolozza colori predefinita - + SGB color palette if available Tavolozza colori SGB se disponibile - + GBC color palette if available Tavolozza colori GBC se disponibile - + SGB (preferred) or GBC color palette if available Tavolozza colori SGB (preferita) o GBC se disponibile - + Game Boy Camera Videocamera Game Boy - + Driver: Driver: - + Source: Sorgente: @@ -5684,115 +5916,95 @@ Dimensione del download: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - ROM per Game Boy Advance (%1) - - - - Game Boy ROMs (%1) - ROM per Game Boy (%1) - - - - All ROMs (%1) - Tutte le ROM (%1) - - - - %1 Video Logs (*.mvl) - %1 log Video (*.mvl) - - - + Archives (%1) Archivi (%1) - - - + + + Select ROM Seleziona ROM - - + + Select save Seleziona salvataggio - + Select patch Seleziona patch - + Patches (*.ips *.ups *.bps) Patch (*.ips *.ups *.bps) - + Select e-Reader dotcode Selezione e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) Scheda e-Reader (*.raw *.bin *.bmp) - + Select e-Reader card images Seleziona immagini carte e-Reader - + Image file (*.png *.jpg *.jpeg) File immagine (*.png *.jpg *.jpeg) - + Conversion finished Conversione terminata - + %1 of %2 e-Reader cards converted successfully. %1 di %2 carte e-Reader convertite con successo. - + Select image Seleziona immagine - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) File immagine (*.png *.gif *.jpg *.jpeg);;Tutti i file (*) - + GameShark saves (*.sps *.xps) Salvataggi GameShark (*.sps *.xps) - + Select video log Seleziona log video - + Video logs (*.mvl) Log video (*.mvl) - + Crash Errore fatale - + The game has crashed with the following error: %1 @@ -5801,669 +6013,674 @@ Dimensione del download: %3 %1 - + Unimplemented BIOS call BIOS non implementato - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Questo gioco utilizza una chiamata BIOS non implementata. Utilizza il BIOS ufficiale per una migliore esperienza. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Impossibile creare un dispositivo di visualizzazione appropriato, tornando alla visualizzazione software. I giochi possono funzionare lentamente, specialmente con finestre più grandi. - + Really make portable? Vuoi davvero rendere portatile l'applicazione? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? In questo modo l'emulatore carica la propria configurazione dalla stessa cartella dell'eseguibile. Vuoi continuare? - + Restart needed È necessario riavviare - + Some changes will not take effect until the emulator is restarted. Alcune modifiche non avranno effetto finché l'emulatore non verrà riavviato. - + - Player %1 of %2 - Giocatore %1 di %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File File - + Load &ROM... Carica ROM... - + Load ROM in archive... Carica la ROM in archivio... - + Load &patch... Carica patch... - + Boot BIOS Avvia BIOS - + Replace ROM... Sostituisci la ROM... - + Scan e-Reader dotcodes... Scansiona e-Reader dotcode... - + Convert e-Reader card image to raw... Converti immagini carte e-Reader in raw... - + ROM &info... Informazioni ROM... - + Recent Recenti - + Make portable Rendi portatile - + &Load state Carica stato - + &Save state Salva stato - + Quick load Caricamento rapido - + Quick save Salvataggio rapido - + Load recent Carica recente - + Save recent Salva recente - + Undo load state Annulla il caricamento dello stato - + Undo save state Annulla salvataggio stato - - + + State &%1 Stato %1 - + Load camera image... Carica immagine fotocamera... - + Convert save game... Converti salvataggi... - + GameShark saves (*.gsv *.sps *.xps) Salvataggi GameShark (*.gsv *.sps *.xps) - + Reset needed Reset necessario - + Some changes will not take effect until the game is reset. Alcune modifiche non si applicheranno finché il gioco non viene resettato. - + New multiplayer window Nuova finestra multigiocatore - + Connect to Dolphin... Connessione a Dolphin... - + Report bug... Segnala bug... - + E&xit Esci (&X) - + &Emulation Emulazione - + &Reset Reset - + Sh&utdown Spegni (&U) - + Yank game pak Stacca game pak - + &Pause Pausa - + &Next frame Salta il prossimo frame (&N) - + Fast forward (held) Avanzamento rapido (tieni premuto) - + &Fast forward Avanzamento rapido (&F) - + Fast forward speed Velocità di avanzamento rapido - + Unbounded Illimitata - + %0x %0x - + Rewind (held) Riavvolgimento (tieni premuto) - + Re&wind Riavvolgimento (&W) - + Step backwards Torna indietro - + Solar sensor Sensore solare - + Increase solar level Incrementa il livello solare - + Decrease solar level Riduci il livello solare - + Brightest solar level Livello solare massimo - + Darkest solar level Livello solare minimo - + Brightness %1 Luminosità %1 - + Audio/&Video Audio/Video - + Frame size Dimensione frame - + Toggle fullscreen Abilita schermo Intero - + Lock aspect ratio Blocca rapporti aspetto - + Frame&skip Salto frame - + Mute Muto - + FPS target FPS finali - + Take &screenshot Acquisisci schermata - + F12 F12 - + Record GIF/WebP/APNG... Registra GIF / WebP / APNG ... - + Video layers Layers video - + Audio channels Canali audio - + &Tools Strumenti - + View &logs... Visualizza registri (&log)... - + Game &overrides... Valore specifico per il gioco... - + &Cheats... Trucchi... - + + Create forwarder... + + + + Open debugger console... Apri console debugger... - + Start &GDB server... Avvia server GDB... - + Settings... Impostazioni... - + Select folder Seleziona cartella - + Couldn't Start Non è stato possibile avviare - + Could not start game. Non è stato possibile avviare il gioco. - + Add folder to library... Aggiungi cartella alla libreria... - + Load state file... Carica stato di salvataggio... - + Save state file... Salva stato di salvataggio... - + Import GameShark Save... Importa Salvataggio GameShark... - + Export GameShark Save... Esporta Salvataggio GameShark... - + About... Informazioni… - + Force integer scaling Forza ridimensionamento a interi - + Bilinear filtering Filtro bilineare - + Game Boy Printer... Stampante Game Boy... - + Save games (%1) Salvataggi (%1) - + Select save game Seleziona salvataggio - + mGBA save state files (%1) file di stati di salvataggio mGBA (%1) - - + + Select save state Seleziona stato di salvataggio - + Save games Salvataggi - + Load alternate save game... Carica stato di salvataggio alternativo... - + Load temporary save game... Carica salvataggio temporaneo... - + Automatically determine Determina automaticamente - + Use player %0 save game Usa il salvataggio del giocatore %0 - + BattleChip Gate... BattleChip Gate... - + %1× %1x - + Interframe blending Miscelazione dei frame - + Native (59.7275) Nativo (59.7) - + Record A/V... Registra A/V... - + Adjust layer placement... Regola posizionamento layer... - + Game Pak sensors... Sensori Game Pak... - + Scripting... Scripting... - + Game state views Viste degli stati del gioco - + View &palette... Mostra palette... - + View &sprites... Mostra sprites... - + View &tiles... Mostra tiles... - + View &map... Mostra mappa... - + &Frame inspector... &Frame inspector... - + View memory... Mostra memoria... - + Search memory... Ricerca memoria... - + View &I/O registers... Mostra registri I/O... - + Record debug video log... Registra video log di debug... - + Stop debug video log Ferma video log di debug - + Exit fullscreen Esci da Schermo Intero - + GameShark Button (held) Pulsante GameShark (tieni premuto) - + Autofire Pulsanti Autofire - + Autofire A Autofire A - + Autofire B Autofire B - + Autofire L Autofire L - + Autofire R Autofire R - + Autofire Start Autofire Start - + Autofire Select Autofire Select - + Autofire Up Autofire Su - + Autofire Right AAutofire Destra - + Autofire Down Autofire Giù - + Autofire Left Autofire Sinistra - + Clear Pulisci @@ -6471,32 +6688,32 @@ Dimensione del download: %3 QObject - + %1 byte %1 byte - + %1 kiB %1 kiB - + %1 MiB %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-ja.ts b/src/platform/qt/ts/mgba-ja.ts index 7b6b94086..b3c0cfbc1 100644 --- a/src/platform/qt/ts/mgba-ja.ts +++ b/src/platform/qt/ts/mgba-ja.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + ゲームボーイアドバンスファイル (%1) + + + + Game Boy ROMs (%1) + ゲームボーイファイル (%1) + + + + All ROMs (%1) + すべてのファイル (%1) + + + + %1 Video Logs (*.mvl) + %1ビデオログ (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Download size: %3 QGBA::ApplicationUpdater - + Stable 安定版 - + Development 開発版 - + Unknown 不明 - + (None) (なし) @@ -400,12 +423,12 @@ Download size: %3 予期しないプラットフォームでパックをヤンクすることはできません! - + Failed to open snapshot file for reading: %1 読み取り用のスナップショットファイルを開けませんでした: %1 - + Failed to open snapshot file for writing: %1 書き込み用のスナップショットファイルを開けませんでした: %1 @@ -413,17 +436,17 @@ Download size: %3 QGBA::CoreManager - + Failed to open game file: %1 ゲームファイルを開けませんでした: %1 - + Could not load game. Are you sure it's in the correct format? ゲームをロードできませんでした。ゲームのフォーマットが正しいことを確認してください。 - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -502,6 +525,195 @@ Download size: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + バックグラウンド + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + 参照 + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Download size: %3 QGBA::GBAApp - + Enable Discord Rich Presence DiscordのRich Presence有効 @@ -601,22 +813,22 @@ Download size: %3 QGBA::GBAKeyEditor - + Clear Button ボタンクリア - + Clear Analog アナログクリア - + Refresh 更新 - + Set all すべて設定 @@ -750,148 +962,168 @@ Download size: %3 QGBA::GameBoy - - + + Autodetect 自動検出 - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2910,7 +3142,7 @@ Download size: %3 破損 - + Slot %1 スロット %1 @@ -4070,12 +4302,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive バグレポートアーカイブ - + ZIP archive (*.zip) ZIPアーカイブ (*.zip) @@ -4438,102 +4670,102 @@ Download size: %3 QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) ソフト(Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL(バージョン1.xを強制) - + None なし - + None (Still Image) なし(静止画) - + Keyboard キーボード - + Controllers コントローラー - + Shortcuts ショートカット - - + + Shaders シェーダー - + Select BIOS BIOSを選択 - + Select directory - + (%1×%2) (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4864,37 +5096,37 @@ Download size: %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5682,100 +5914,80 @@ Download size: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - ゲームボーイアドバンスファイル (%1) - - - - Game Boy ROMs (%1) - ゲームボーイファイル (%1) - - - - All ROMs (%1) - すべてのファイル (%1) - - - - %1 Video Logs (*.mvl) - %1ビデオログ (*.mvl) - - - + Archives (%1) アーカイブファイル (%1) - - - + + + Select ROM ROMを開く - + Select folder フォルダを開く - - + + Select save セーブを開く - + Select patch パッチを開く - + Patches (*.ips *.ups *.bps) パッチファイル (*.ips *.ups *.bps) - + Select e-Reader dotcode カードeを開く - + e-Reader card (*.raw *.bin *.bmp) カードe (*.raw *.bin *.bmp) - + Select image 画像を開く - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) 画像ファイル (*.png *.gif *.jpg *.jpeg);;すべてのファイル (*) - + GameShark saves (*.sps *.xps) GameSharkセーブファイル (*.sps *.xps) - + Select video log ビデオログを開く - + Video logs (*.mvl) ビデオログ (*.mvl) - + Crash クラッシュ - + The game has crashed with the following error: %1 @@ -5784,684 +5996,689 @@ Download size: %3 %1 - + Couldn't Start 起動失敗 - + Could not start game. ゲームを起動できませんでした。 - + Unimplemented BIOS call 未実装のBIOS呼び出し - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. このゲームは実装されていないBIOS呼び出しを使用します。最高のエクスペリエンスを得るには公式のBIOSを使用してください。 - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. 適切なディスプレイデバイスの作成に失敗し、ソフトディスプレイにフォールバックしました。特に大きなウィンドウでは、ゲームの実行が遅い場合があります。 - + Really make portable? 本当にポータブルにしますか? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? これによりエミュレータは実行ファイルと同じディレクトリにある設定ファイルをロードします。続けますか? - + Restart needed 再起動が必要 - + Some changes will not take effect until the emulator is restarted. 一部の変更は、エミュレータを再起動するまで有効になりません。 - + - Player %1 of %2 - プレーヤー %1 of %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &ファイル (&F) - + Load &ROM... ROMをロード... - + Load ROM in archive... アーカイブにROMをロード... - + Add folder to library... ライブラリーにフォルダを追加... - + Load &patch... パッチをロード... (&P) - + Boot BIOS BIOSを起動 - + Replace ROM... ROMを交換... - + Scan e-Reader dotcodes... カードeをスキャン... - + ROM &info... ROM情報... (&I) - + Recent 最近開いたROM - + Make portable ポータブル化 - + &Load state ステートをロード (&L) - + Report bug... バグ報告 - + About... バージョン情報... - + Record GIF/WebP/APNG... GIF/WebP/APNGを記録 - + Game Pak sensors... カートリッジセンサー... - + Clear 消去 - + Load state file... ステートファイルをロード... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Convert e-Reader card image to raw... - + &Save state ステートをセーブ (&S) - + Save state file... ステートファイルをセーブ... - + Quick load クイックロード - + Quick save クイックセーブ - + Load recent 最近使ったクイックロットからロード - + Save recent 最近使ったクイックロットにセーブ - + Undo load state クイックロードを元に戻す - + Undo save state クイックセーブを元に戻す - - + + State &%1 クイックスロット &%1 - + Load camera image... カメラ画像をロード... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Import GameShark Save... GameSharkスナップショットを読み込む - + Export GameShark Save... GameSharkスナップショットを書き出す - + New multiplayer window 新しいウィンドウ(マルチプレイ) - + Connect to Dolphin... - + E&xit 終了 (&X) - + &Emulation エミュレーション (&E) - + &Reset リセット (&R) - + Sh&utdown 閉じる (&U) - + Yank game pak ゲームパックをヤンク - + &Pause 一時停止 (&P) - + &Next frame 次のフレーム (&N) - + Fast forward (held) 早送り(押し) - + &Fast forward 早送り (&F) - + Fast forward speed 早送り速度 - + Unbounded 制限なし - + %0x %0x - + Rewind (held) 巻戻し(押し) - + Re&wind 巻戻し (&R) - + Step backwards 1フレーム巻き戻す - + Solar sensor 太陽センサー - + Increase solar level 明るさを上げる - + Decrease solar level 明るさを下げる - + Brightest solar level 明るさ最高 - + Darkest solar level 明るさ最低 - + Brightness %1 明るさ %1 - + Audio/&Video オーディオ/ビデオ (&V) - + Frame size 画面サイズ - + Toggle fullscreen 全画面表示 - + Lock aspect ratio 縦横比を固定 - + Force integer scaling 整数スケーリングを強制 - + Bilinear filtering バイリニアフィルタリング - + Frame&skip フレームスキップ (&S) - + Mute ミュート - + FPS target FPS - + Native (59.7275) ネイティブ(59.7275) - + Take &screenshot スクリーンショット (&S) - + F12 F12 - + Game Boy Printer... ポケットプリンタ... - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games セーブデータ - + Automatically determine - + Use player %0 save game - + BattleChip Gate... チップゲート... - + %1× %1× - + Interframe blending フレーム間混合 - + Record A/V... ビデオ録画... - + Video layers ビデオレイヤー - + Audio channels オーディオチャンネル - + Adjust layer placement... レイヤーの配置を調整... - + &Tools ツール (&T) - + View &logs... ログビューアー... (&L) - + Game &overrides... ゲーム別設定... (&O) - + &Cheats... チート... (&C) - + Settings... 設定... - + Open debugger console... デバッガコンソールを開く... - + Start &GDB server... GDBサーバを起動... (&G) - + Scripting... - + + Create forwarder... + + + + Game state views - + View &palette... パレットビューアー... (&P) - + View &sprites... スプライトビューアー... (&S) - + View &tiles... タイルビューアー... (&T) - + View &map... マップビューアー... (&M) - + &Frame inspector... フレームインスペクタ... (&F) - + View memory... メモリビューアー... - + Search memory... メモリ検索... - + View &I/O registers... IOビューアー... (&I) - + Record debug video log... デバッグビデオログ... - + Stop debug video log デバッグビデオログを停止 - + Exit fullscreen 全画面表示を終了 - + GameShark Button (held) GameSharkボタン(押し) - + Autofire 連打 - + Autofire A 連打 A - + Autofire B 連打 B - + Autofire L 連打 L - + Autofire R 連打 R - + Autofire Start 連打 Start - + Autofire Select 連打 Select - + Autofire Up 連打 上 - + Autofire Right 連打 右 - + Autofire Down 連打 下 - + Autofire Left 連打 左 @@ -6469,32 +6686,32 @@ Download size: %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-ko.ts b/src/platform/qt/ts/mgba-ko.ts index bac74fb06..567688861 100644 --- a/src/platform/qt/ts/mgba-ko.ts +++ b/src/platform/qt/ts/mgba-ko.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + 게임 보이 어드밴스 롬 (%1) + + + + Game Boy ROMs (%1) + 게임 보이 (%1) + + + + All ROMs (%1) + 모든 롬 (%1) + + + + %1 Video Logs (*.mvl) + %1 비디오 로그 (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Download size: %3 QGBA::ApplicationUpdater - + Stable 안정적인 - + Development 개발 - + Unknown 미확인 - + (None) (없음) @@ -400,12 +423,12 @@ Download size: %3 예기치 않은 플랫폼에서 팩을 잡아당길 수 없습니다! - + Failed to open snapshot file for reading: %1 읽기 용 스냅샷 파일을 열지 못했습니다: %1 - + Failed to open snapshot file for writing: %1 쓰기 용 스냅샷 파일을 열지 못했습니다: %1 @@ -413,17 +436,17 @@ Download size: %3 QGBA::CoreManager - + Failed to open game file: %1 게임 파일을 열지 못했습니다: %1 - + Could not load game. Are you sure it's in the correct format? 게임을 로드할 수 없습니다. 올바른 형식인지 확인했나요? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). 저장 파일을 열지 못했습니다. 게임 내 저장은 업데이트할 수 없습니다. 저장 디렉토리가 추가 권한 없이 쓰기 가능한지 확인하세요 (예: 윈도우즈의 UAC). @@ -502,6 +525,195 @@ Download size: %3 돌핀에 연결할 수 없습니다. + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + 배경 + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + 브라우저 + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Download size: %3 QGBA::GBAApp - + Enable Discord Rich Presence 디스코드 Rich Presence 활성화 @@ -601,22 +813,22 @@ Download size: %3 QGBA::GBAKeyEditor - + Clear Button 버튼 정리 - + Clear Analog 아날로그 정리 - + Refresh 새로 고침 - + Set all 모두 설정 @@ -750,148 +962,168 @@ Download size: %3 QGBA::GameBoy - - + + Autodetect 자동 감지 - + Game Boy (DMG) 게임 보이 (DMG) - + Game Boy Pocket (MGB) 게임 보이 포켓 (MGB) - + Super Game Boy (SGB) 슈퍼 게임 보이 (SGB) - + Super Game Boy 2 (SGB) 슈퍼 게임 보이 2 (SGB) - + Game Boy Color (CGB) 게임 보이 컬러 (CGB) - + Game Boy Advance (AGB) 게임 보이 어드밴스 (AGB) - + Super Game Boy Color (SGB + CGB) 슈퍼 게임 보이 컬러 (SGB + CGB) - + ROM Only 롬 전용 - + MBC1 MBC1 - + MBC2 MBC2 - + MBC3 MBC3 - + MBC3 + RTC MBC3 + RTC - + MBC5 MBC5 - + MBC5 + Rumble MBC5 + 진동 - + MBC6 MBC6 - + MBC7 (Tilt) MBC7 (틸트) - + MMM01 MMM01 - + HuC-1 HuC-1 - + HuC-3 HuC-3 - + Pocket Cam 포켓 캠 - + TAMA5 TAMA5 - + Wisdom Tree 지혜의 나무 - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) NT (신규) - + Pokémon Jade/Diamond 포켓몬 제이드/다이아몬드 - + BBD BBD - + Hitek 하이텍 - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) 사첸 (MMC1) - + Sachen (MMC2) 사첸 (MMC2) @@ -2910,7 +3142,7 @@ Download size: %3 손상 - + Slot %1 슬롯 %1 @@ -4070,12 +4302,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive 버그 보고서 파일 - + ZIP archive (*.zip) ZIP 파일 (*.zip) @@ -4438,102 +4670,102 @@ Download size: %3 QGBA::SettingsView - - + + Qt Multimedia Qt 멀티미디어 - + SDL SDL - + Software (Qt) 소프트웨어 (Qt) - + OpenGL 오픈GL - + OpenGL (force version 1.x) 오픈GL (버전 1.x 강제) - + None 없음 - + None (Still Image) 없음 (정지 이미지) - + Keyboard 키보드 - + Controllers 컨트롤러 - + Shortcuts 단축키 - - + + Shaders 쉐이더 - + Select BIOS 바이오스 선택 - + Select directory 디렉토리 선택 - + (%1×%2) (%1×%2) - + Never 절대 - + Just now 방금 - + Less than an hour ago 1시간 미만 전 - + %n hour(s) ago %n 시간 전 - + %n day(s) ago %n 일 전 @@ -4844,37 +5076,37 @@ Download size: %3 지금 확인 - + Default color palette only 기본 색상 팔레트만 - + SGB color palette if available 가능한 경우 SGB 색상 팔레트 - + GBC color palette if available 가능한 경우 GBC 색상 팔레트 - + SGB (preferred) or GBC color palette if available 사용 가능한 경우 SGB (선호) 또는 GBC 색상 팔레트 - + Game Boy Camera 게임 보이 카메라 - + Driver: 드라이버: - + Source: 소스: @@ -5682,115 +5914,95 @@ Download size: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - 게임 보이 어드밴스 롬 (%1) - - - - Game Boy ROMs (%1) - 게임 보이 (%1) - - - - All ROMs (%1) - 모든 롬 (%1) - - - - %1 Video Logs (*.mvl) - %1 비디오 로그 (*.mvl) - - - + Archives (%1) 아카이브 (%1) - - - + + + Select ROM 롬 선택 - - + + Select save 저장 파일 선택 - + Select patch 패치 선택 - + Patches (*.ips *.ups *.bps) 패치 (*.ips *.ups *.bps) - + Select e-Reader dotcode e-리더 도트코드 선택 - + e-Reader card (*.raw *.bin *.bmp) e-리더 카드 (*.raw *.bin *.bmp) - + Select e-Reader card images e-리더 카드 이미지 선택 - + Image file (*.png *.jpg *.jpeg) 이미지 파일 (*.png *.jpg *.jpeg) - + Conversion finished 변환 완료 - + %1 of %2 e-Reader cards converted successfully. %2 e-리더 카드 중 %1이(가) 성공적으로 변환되었습니다. - + Select image 이미지 선택 - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) 이미지 파일 (*.png *.gif *.jpg *.jpeg);;모든 파일 (*) - + GameShark saves (*.sps *.xps) 게임샤크 저장 파일 (*.sps *.xps) - + Select video log 비디오 로그 선택 - + Video logs (*.mvl) 비디오 로그 (*.mvl) - + Crash 치명적인 오류 - + The game has crashed with the following error: %1 @@ -5799,669 +6011,674 @@ Download size: %3 %1 - + Unimplemented BIOS call 구현되지 않은 바이오스 호출 - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. 이 게임은 구현되지 않은 바이오스 호출을 사용합니다. 최상의 성능을 얻으려면 공식 바이오스를 사용하십시오. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. 적절한 디스플레이 장치를 생성하지 못했습니다. 소프트웨어 디스플레이로 돌아갑니다. 특히 큰 창에서는 게임이 느리게 실행될 수 있습니다. - + Really make portable? 정말로 휴대용을 만듭니까? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? 이렇게하면 에뮬레이터가 실행 파일과 동일한 디렉토리에서 구성을 로드하게됩니다. 계속 하시겠습니까? - + Restart needed 재시작 필요 - + Some changes will not take effect until the emulator is restarted. 일부 변경 사항은 에뮬레이터가 다시 시작될 때까지 적용되지 않습니다. - + Reset needed 재설정 필요 - + Some changes will not take effect until the game is reset. 일부 변경 사항은 게임이 재설정될 때까지 적용되지 않습니다. - + - Player %1 of %2 - 플레이어 %1 의 %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &파일 - + Load &ROM... 로드 &롬... - + Load ROM in archive... 롬을 아카이브에 로드... - + Save games 게임 저장 - + Automatically determine 자동으로 결정 - + Use player %0 save game 플레이어 %0 저장 게임 사용 - + Load &patch... 로드 &패치... - + Boot BIOS BIOS 부팅 - + Replace ROM... 롬 교체... - + Scan e-Reader dotcodes... e-리더 도트코드 스캔... - + Game state views 게임 상태 보기 - + Convert e-Reader card image to raw... e-리더 카드 이미지를 원시 데이터로 변환... - + ROM &info... 롬 &정보... - + Recent 최근 실행 - + Make portable 휴대용 만들기 - + &Load state &로드 파일 상태 - + &Save state &저장 파일 상태 - + Quick load 빠른 로드 - + Quick save 빠른 저장 - + Load recent 최근 실행 로드 - + Save recent 최근 실행 저장 - + Undo load state 로드 파일 상태 복원 - + Undo save state 저장 파일 상태 복원 - - + + State &%1 상태 &%1 - + Load camera image... 카메라 이미지 로드... - + Convert save game... 저장 게임 변환... - + GameShark saves (*.gsv *.sps *.xps) 게임샤크 저장 (*.gsv *.sps *.xps) - + New multiplayer window 새로운 멀티 플레이어 창 - + Connect to Dolphin... 돌핀에 연결... - + E&xit 종&료 - + &Emulation &에뮬레이션 - + &Reset &재설정 - + Sh&utdown 종&료 - + Yank game pak 양키 게임 팩 - + &Pause &정지 - + &Next frame &다음 프레임 - + Fast forward (held) 빨리 감기 (누름) - + &Fast forward &빨리 감기 - + Fast forward speed 빨리 감기 속도 - + Unbounded 무제한 - + %0x %0x - + Rewind (held) 되김기 (누름) - + Re&wind 리&와인드 - + Step backwards 돌아가기 - + Brightest solar level 가장 밝은 태양 수준 - + Darkest solar level 가장 어두운 태양 수준 - + Brightness %1 밝기 %1 - + Audio/&Video 오디오/&비디오 - + Frame size 프레임 크기 - + Toggle fullscreen 전체화면 전환 - + Lock aspect ratio 화면비 잠금 - + Frame&skip 프레임&건너뛰기 - + Mute 무음 - + FPS target FPS 대상 - + Take &screenshot 스크린샷 &찍기 - + F12 F12 - + Video layers 비디오 레이어 - + Audio channels 오디오 채널 - + &Tools &도구 - + View &logs... 로그 &보기... - + Game &overrides... 게임 &오버라이드... - + &Cheats... &치트.. - + + Create forwarder... + + + + Open debugger console... 디버거 콘솔 열기... - + Start &GDB server... GDB 서버 &시작... - + Settings... 설정... - + Select folder 폴더 선택 - + Couldn't Start 시작할 수 없음 - + Could not start game. 게임을 시작할 수 없습니다. - + Add folder to library... 라이브러리에 폴더 추가... - + Load state file... 상태 파일 로드... - + Save state file... 상태 파일 저장... - + Import GameShark Save... 게임샤크 저장 가져오기... - + Export GameShark Save... 게임샤크 저장 내보내기... - + Report bug... 버그를 제보하기... - + About... 정보... - + Solar sensor 태양광 센서 - + Increase solar level 태양광 레벨 증가 - + Decrease solar level 태양광 레벨 감소 - + Force integer scaling 정수 스케일링 강제 수행 - + Bilinear filtering 이중선형 필터링 - + Game Boy Printer... 게임 보이 프린터... - + Save games (%1) 게임 (%1) 저장 - + Select save game 저장 게임 선택 - + mGBA save state files (%1) mGBA 저장 상태 파일 (%1) - - + + Select save state 저장 상태 선택 - + Load alternate save game... 대체 저장 게임 로드... - + Load temporary save game... 임시 저장 게임 로드... - + BattleChip Gate... 배틀칩 게이트... - + %1× %1× - + Interframe blending 프레임간 조합 - + Native (59.7275) 실기 (59.7275) - + Record A/V... A/V 녹화... - + Record GIF/WebP/APNG... GIF/WebP/APNG 녹화... - + Adjust layer placement... 레이어 배치 조정... - + Game Pak sensors... 게임 팩 센서... - + Scripting... 스크립팅... - + View &palette... 팔레트 &보기... - + View &sprites... 스프라이트 &보기... - + View &tiles... 타일 &보기... - + View &map... 지도 &보기... - + &Frame inspector... 프레임 검사기 (&F)... - + View memory... 메모리 보기... - + Search memory... 메모리 검색... - + View &I/O registers... I/O 레지스터 &보기... - + Record debug video log... 디버그 비디오 로그 녹화... - + Stop debug video log 디버그 비디오 로그 중지 - + Exit fullscreen 전체화면 종료 - + GameShark Button (held) 게임샤크 버튼 (누름) - + Autofire 연사 - + Autofire A 연사 A - + Autofire B 연사 B - + Autofire L 연사 L - + Autofire R 연사 R - + Autofire Start 연사 시작 - + Autofire Select 연사 선택 - + Clear 지움 - + Autofire Up 연사 위쪽 - + Autofire Right 연사 오른쪽 - + Autofire Down 연사 아래쪽 - + Autofire Left 연사 왼쪽 @@ -6469,32 +6686,32 @@ Download size: %3 QObject - + %1 byte %1 바이트 - + %1 kiB %1 키비바이트 - + %1 MiB %1 메비바이트 - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-ms.ts b/src/platform/qt/ts/mgba-ms.ts index 55134457e..2e46c1d08 100644 --- a/src/platform/qt/ts/mgba-ms.ts +++ b/src/platform/qt/ts/mgba-ms.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + ROM Game Boy Advance (%1) + + + + Game Boy ROMs (%1) + ROM Game Boy (%1) + + + + All ROMs (%1) + Semua ROM (%1) + + + + %1 Video Logs (*.mvl) + %1 Log Video (*.mvl) + + QGBA::AboutScreen @@ -86,22 +109,22 @@ Download size: %3 QGBA::ApplicationUpdater - + Stable - + Development - + Unknown - + (None) @@ -394,12 +417,12 @@ Download size: %3 - + Failed to open snapshot file for reading: %1 Gagal membuka fail snapshot untuk baca: %1 - + Failed to open snapshot file for writing: %1 Gagal membuka fail snapshot untuk menulis: %1 @@ -407,17 +430,17 @@ Download size: %3 QGBA::CoreManager - + Failed to open game file: %1 Gagal membuka fail permainan: %1 - + Could not load game. Are you sure it's in the correct format? Gagal memuat permainan. Adakah ia dalam format betul? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -496,6 +519,195 @@ Download size: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Latar belakang + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + Pilih fail + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -587,7 +799,7 @@ Download size: %3 QGBA::GBAApp - + Enable Discord Rich Presence Dayakan Discord Rich Presence @@ -595,22 +807,22 @@ Download size: %3 QGBA::GBAKeyEditor - + Clear Button - + Clear Analog - + Refresh Segar Semula - + Set all @@ -744,148 +956,168 @@ Download size: %3 QGBA::GameBoy - - + + Autodetect Autokesan - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2904,7 +3136,7 @@ Download size: %3 Rosak - + Slot %1 Slot %1 @@ -4064,12 +4296,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive Arkib laporan pepijat - + ZIP archive (*.zip) Arkib ZIP (*.zip) @@ -4432,102 +4664,102 @@ Download size: %3 QGBA::SettingsView - - + + Qt Multimedia Multimedia Qt - + SDL SDL - + Software (Qt) Perisian (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (paksa versi 1.x) - + None Tiada - + None (Still Image) Tiada (Gambar Tenang) - + Keyboard Papan Kekunci - + Controllers Pengawal - + Shortcuts Pintas - - + + Shaders - + Select BIOS Pilih BIOS - + Select directory Pilih direktori - + (%1×%2) (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4853,37 +5085,37 @@ Download size: %3 - + Default color palette only Palet warna piawai sahaja - + SGB color palette if available Palet warna SGB jika ada - + GBC color palette if available Palet warna GBC jika ada - + SGB (preferred) or GBC color palette if available SGB (pilihan utama) atau palet warna GBC jika ada - + Game Boy Camera Game Boy Camera - + Driver: Pemacu: - + Source: Sumber: @@ -5676,100 +5908,80 @@ Download size: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - ROM Game Boy Advance (%1) - - - - Game Boy ROMs (%1) - ROM Game Boy (%1) - - - - All ROMs (%1) - Semua ROM (%1) - - - - %1 Video Logs (*.mvl) - %1 Log Video (*.mvl) - - - + Archives (%1) Arkib (%1) - - - + + + Select ROM Pilih ROM - + Select folder Pilih folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image Pilih gambar - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Fail gambar (*.png *.gif *.jpg *.jpeg);;Semua fail (*) - + GameShark saves (*.sps *.xps) Simpanan GameShark (*.sps *.xps) - + Select video log Pilih log video - + Video logs (*.mvl) Log video (*.mvl) - + Crash Nahas - + The game has crashed with the following error: %1 @@ -5778,684 +5990,689 @@ Download size: %3 %1 - + Couldn't Start Tidak dapat memula - + Could not start game. Permainan tidak dapat bermula. - + Unimplemented BIOS call Panggilan BIOS yg belum dilaksanakan - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Permainan ini menggunakan panggilan BIOS yang belum dilaksanakan. Sila pakai BIOS rasmi untuk pengalaman yang lebih baik. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Gagal mencipta peranti paparan yang sesuai, berbalik ke paparan perisian. Permainan mungkin menjadi lembap, terutamanya dengan tetingkap besar. - + Really make portable? Betulkah mahu buat jadi mudah alih? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Ini akan menetapkan pelagak untuk muat konfigurasi dari direktori yang sama dengan fail bolehlakunya. Teruskan? - + Restart needed Mula semula diperlukan - + Some changes will not take effect until the emulator is restarted. Beberapa perubahan tidak akan dilaksanakan sehingga pelagak dimula semula. - + - Player %1 of %2 - Pemain %1 dari %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &File - + Load &ROM... Muat %ROM... - + Load ROM in archive... Muat ROM daripada arkib... - + Add folder to library... Tambah folder ke perpustakaan... - + Save games (%1) Simpanan permainan (%1) - + Select save game Pilih simpanan permainan - + mGBA save state files (%1) mGBA fail keadaan tersimpan (%1) - - + + Select save state Pilih keadaan tersimpan - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) Fail gambar (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... Muat simpanan permainan alternatif... - + Load temporary save game... Muat simpanan permainan sementara... - + Load &patch... - + Boot BIOS But BIOS - + Replace ROM... Ganti ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... &Perihal ROM... - + Recent Terkini - + Make portable Buat jadi mudah alih - + &Load state &Muat keadaan - + Load state file... Muat fail keadaan... - + &Save state &Simpan keadaan - + Save state file... Simpan fail keadaan... - + Quick load - + Quick save - + Load recent Muat terkini - + Save recent Simpan terkini - + Undo load state Buat asal keadaan termuat - + Undo save state Buat asal keadaan tersimpan - - + + State &%1 Keadaan &%1 - + Load camera image... Muat gambar kamera... - + Convert save game... Tukar simpanan permainan... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games Simpanan permainan - + Import GameShark Save... Import Simpanan GameShark... - + Export GameShark Save... Eksport Simpanan GameShark... - + Automatically determine - + Use player %0 save game - + New multiplayer window Tetingkap multipemain baru - + Connect to Dolphin... Sambung ke Dolphin... - + Report bug... Laporkan pepijat... - + About... Perihal... - + E&xit &Keluar - + &Emulation Pe&lagak - + &Reset - + Sh&utdown &Matikan - + Yank game pak Alih keluar Game Pak - + &Pause &Jeda - + &Next frame Bingkai se&terusnya - + Fast forward (held) Mundar laju (pegang) - + &Fast forward Mundar &laju - + Fast forward speed Kelajuan mundar laju - + Unbounded Tidak terbatas - + %0x %0x - + Rewind (held) Putar balik (pegang) - + Re&wind Ma&ndir - + Step backwards Langkah belakang - + Solar sensor Pengesan suria - + Increase solar level Meningkatkan aras suria - + Decrease solar level Mengurangkan aras suria - + Brightest solar level Aras suria paling terang - + Darkest solar level Aras suria paling gelap - + Brightness %1 Kecerahan %1 - + Game Boy Printer... Pencetak Game Boy... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Audio/&Video - + Frame size Saiz bingkai - + %1× %1× - + Toggle fullscreen Togol skrinpenuh - + Lock aspect ratio Kekalkan nisbah aspek - + Force integer scaling Paksa skala integer - + Interframe blending Persebatian antarabingkai - + Bilinear filtering Penapisan bilinear - + Frame&skip Langkauan &bingkai - + Mute Senyap - + FPS target Sasaran FPS - + Native (59.7275) Asal (59.7275) - + Take &screenshot Ambil &cekupan skrin - + F12 F12 - + Record A/V... Rakam A/V... - + Record GIF/WebP/APNG... Rakam GIF/WebP/APNG... - + Video layers Lapisan video - + Audio channels Saluran audio - + Adjust layer placement... Melaras peletakan lapisan... - + &Tools &Alat - + View &logs... Lihat &log... - + Game &overrides... - + Game Pak sensors... Pengesan Game Pak... - + &Cheats... &Tipu... - + + Create forwarder... + + + + Settings... Tetapan... - + Open debugger console... Buka konsol penyahpepijat... - + Start &GDB server... Mula pelayan &GDB... - + Scripting... - + Game state views - + View &palette... Pelihat &palet... - + View &sprites... - + View &tiles... Pelihat &jubin... - + View &map... Pelihat pe&ta... - + &Frame inspector... Periksa &bingkai... - + View memory... Lihat ingatan... - + Search memory... Cari ingatan... - + View &I/O registers... Lihat daftar &I/O... - + Record debug video log... Rakam log video nyahpepijat... - + Stop debug video log Henti log video nyahpepijat - + Exit fullscreen Keluar skrinpenuh - + GameShark Button (held) Butang GameShark (pegang) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Kosongkan @@ -6463,32 +6680,32 @@ Download size: %3 QObject - + %1 byte %1 bait - + %1 kiB %1 kiB - + %1 MiB %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-nb_NO.ts b/src/platform/qt/ts/mgba-nb_NO.ts index 2dbaa2a6b..169a4bab3 100644 --- a/src/platform/qt/ts/mgba-nb_NO.ts +++ b/src/platform/qt/ts/mgba-nb_NO.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + QGBA::AboutScreen @@ -89,22 +112,22 @@ Nedlastningsstørrelse: %3 QGBA::ApplicationUpdater - + Stable - + Development - + Unknown Ukjent - + (None) @@ -397,12 +420,12 @@ Nedlastningsstørrelse: %3 - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -410,17 +433,17 @@ Nedlastningsstørrelse: %3 QGBA::CoreManager - + Failed to open game file: %1 Klarte ikke å åpne spillfil: %1 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -499,6 +522,195 @@ Nedlastningsstørrelse: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Bakgrunn + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -590,7 +802,7 @@ Nedlastningsstørrelse: %3 QGBA::GBAApp - + Enable Discord Rich Presence @@ -598,22 +810,22 @@ Nedlastningsstørrelse: %3 QGBA::GBAKeyEditor - + Clear Button - + Clear Analog - + Refresh Gjenoppfrisk - + Set all @@ -747,148 +959,168 @@ Nedlastningsstørrelse: %3 QGBA::GameBoy - - + + Autodetect - + Game Boy (DMG) Game Boy (DMG) - + Game Boy Pocket (MGB) Game Boy Pocket (MGB) - + Super Game Boy (SGB) Super Game Boy (SGB) - + Super Game Boy 2 (SGB) Super Game Boy 2 (SGB) - + Game Boy Color (CGB) Game Boy Color (CGB) - + Game Boy Advance (AGB) Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) Super Game Boy Color (SGB + CGB) - + ROM Only Kun ROM - + MBC1 MBC1 - + MBC2 MBC2 - + MBC3 MBC3 - + MBC3 + RTC MBC3 + RTC - + MBC5 MBC5 - + MBC5 + Rumble - + MBC6 MBC6 - + MBC7 (Tilt) - + MMM01 MMM01 - + HuC-1 HuC-1 - + HuC-3 HuC-3 - + Pocket Cam - + TAMA5 TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2907,7 +3139,7 @@ Nedlastningsstørrelse: %3 Skadet - + Slot %1 Plass %1 @@ -4067,12 +4299,12 @@ Nedlastningsstørrelse: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) ZIP-arkiv (*.zip) @@ -4435,95 +4667,95 @@ Nedlastningsstørrelse: %3 QGBA::SettingsView - - + + Qt Multimedia - + SDL SDL - + Software (Qt) Programvare (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) - + None Ingen - + None (Still Image) - + Keyboard Tastatur - + Controllers Kontrollere - + Shortcuts Snarveier - - + + Shaders Skyggeleggere - + Select BIOS Velg BIOS - + Select directory Velg mappe - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4531,7 +4763,7 @@ Nedlastningsstørrelse: %3 - + %n day(s) ago @@ -4858,37 +5090,37 @@ Nedlastningsstørrelse: %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5681,784 +5913,769 @@ Nedlastningsstørrelse: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - - - - - Game Boy ROMs (%1) - - - - - All ROMs (%1) - - - - - %1 Video Logs (*.mvl) - - - - + Archives (%1) Arkiv (%1) - - - + + + Select ROM Velg ROM - + Select folder Velg mappe - - + + Select save - + Select patch Velg feilfiks - + Patches (*.ips *.ups *.bps) Feilfikser (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image Velg bilde - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Bildefil (*.png *.gif *.jpg *.jpeg);;All filer (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash Krasj - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + + Create forwarder... + + + + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Tøm @@ -6466,32 +6683,32 @@ Nedlastningsstørrelse: %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA - + GB - + ? diff --git a/src/platform/qt/ts/mgba-pl.ts b/src/platform/qt/ts/mgba-pl.ts index 3acc435c0..3d764faae 100644 --- a/src/platform/qt/ts/mgba-pl.ts +++ b/src/platform/qt/ts/mgba-pl.ts @@ -5813,62 +5813,62 @@ Rozmiar pobierania: %3 Nie udało się utworzyć odpowiedniego urządzenia wyświetlającego, powracam do wyświetlania programowego. Gry mogą działać wolno, zwłaszcza w przypadku większych okien. - + Really make portable? Naprawdę stworzyć wersję portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? To sprawi, że emulator załaduje swoją konfigurację z tego samego katalogu, co plik wykonywalny. Czy chcesz kontynuować? - + Restart needed Wymagane ponowne uruchomienie - + Some changes will not take effect until the emulator is restarted. Niektóre zmiany nie zaczną obowiązywać, dopóki emulator nie zostanie ponownie uruchomiony. - + - Player %1 of %2 - Gracz %1 z %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 FPS) - %4 - + &File &Plik - + Load &ROM... Załaduj &ROM... - + Load ROM in archive... Załaduj ROM w archiwum... - + Add folder to library... Dodaj folder do biblioteki... @@ -5914,118 +5914,118 @@ Rozmiar pobierania: %3 %1 z %2 kart czytnika e-Reader zostało pomyślnie przekonwertowanych. - + Load alternate save game... Załaduj alternatywny zapis gry... - + Load temporary save game... Załaduj tymczasowy zapis gry... - + Load &patch... Wczytaj &poprawkę... - + Boot BIOS BIOS startowy - + Replace ROM... Wymień ROM... - + Scan e-Reader dotcodes... Skanuj kody kropkowe czytnika e-Reader... - + Convert e-Reader card image to raw... Konwertuj obraz karty czytnika e-Reader na surowy... - + ROM &info... &Informacje o pamięci ROM... - + Recent Ostatnie - + Make portable Stwórz wersję portable - + &Load state &Załaduj stan - + Load state file... Załaduj plik stanu… - + &Save state &Zapisz stan - + Save state file... Zapisz plik stanu... - + Quick load Szybkie załadowanie - + Quick save Szybki zapis - + Load recent Załaduj ostatnie - + Save recent Zapisz ostatnie - + Undo load state Cofnij załadowanie stanu - + Undo save state Cofnij zapisanie stanu - + State &%1 Stan &%1 - + Load camera image... Załaduj obraz do kamery... - + Convert save game... Konwertuj zapisaną grę... @@ -6035,437 +6035,437 @@ Rozmiar pobierania: %3 Zapisy GameShark (*.gsv *.sps *.xps) - + Reset needed Wymagane zresetowanie - + Some changes will not take effect until the game is reset. Niektóre zmiany nie zaczną obowiązywać, dopóki gra nie zostanie zresetowana. - + Save games Zapisy gry - + Import GameShark Save... Importuj Zapis GameShark... - + Export GameShark Save... Eksportuj Zapis GameShark... - + Automatically determine Wykryj automatycznie - + Use player %0 save game Użyj zapis gry gracza %0 - + New multiplayer window Nowe okno dla wielu graczy - + Connect to Dolphin... Połącz z Dolphinem... - + Report bug... Zgłoś błąd... - + About... O Aplikacji... - + E&xit Z&akończ - + &Emulation &Emulacja - + &Reset &Resetuj - + Sh&utdown Za&mknij - + Yank game pak Wyciągnij Game Pak - + &Pause &Pauza - + &Next frame &Następna klatka - + Fast forward (held) Przewijanie (przytrzymaj) - + &Fast forward &Przewijanie do przodu - + Fast forward speed Prędkość przewijania do przodu - + Unbounded Bez ograniczeń - + %0x %0x - + Rewind (held) Przewijanie (przytrzymaj) - + Re&wind Pr&zewijanie - + Step backwards Krok w tył - + Solar sensor Czujnik słoneczny - + Increase solar level Zwiększ poziom energii słonecznej - + Decrease solar level Zmniejsz poziom energii słonecznej - + Brightest solar level Najjaśniejszy poziom energii słonecznej - + Darkest solar level Najciemniejszy poziom energii słonecznej - + Brightness %1 Jasność %1 - + Game Boy Printer... Game Boy Printer... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Dźwięk/&Wideo - + Frame size Rozmiar klatki - + %1× %1× - + Toggle fullscreen Przełącz tryb pełnoekranowy - + Lock aspect ratio Zablokuj proporcje - + Force integer scaling Wymuś skalowanie całkowite - + Interframe blending Blendowanie międzyklatkowe - + Bilinear filtering Filtrowanie dwuliniowe - + Frame&skip Klatko&wanie - + Mute Wycisz - + FPS target Cel KL./S - + Native (59.7275) Natywny (59.7275) - + Take &screenshot Wykonaj &zrzut ekranu - + F12 F12 - + Record A/V... Nagraj A/W... - + Record GIF/WebP/APNG... Nagraj GIF/WebP/APNG... - + Video layers Warstwy wideo - + Audio channels Kanały audio - + Adjust layer placement... Dostosuj położenie warstw... - + &Tools &Narzędzia - + View &logs... Wyświetl &logi... - + Game &overrides... Nadpisania &ustawień gry... - + Game Pak sensors... Czujniki Game Pak... - + &Cheats... &Kody... - + Settings... Ustawienia... - + Open debugger console... Otwórz konsolę debugera... - + Start &GDB server... Uruchom serwer &GDB... - + Scripting... Skrypty... - + Game state views Widoki stanu gry - + View &palette... Wyświetl &paletę... - + View &sprites... Wyświetl &sprite'y... - + View &tiles... Wyświetl &kafelki... - + View &map... Wyświetl &mapę... - + &Frame inspector... Inspektor &klatek... - + View memory... Wyświetl pamięć... - + Search memory... Przeszukaj pamięć... - + View &I/O registers... Wyświetl rejestry &we/wy... - + Record debug video log... Nagraj dziennik wideo debugowania... - + Stop debug video log Zatrzymaj dziennik wideo debugowania - + Exit fullscreen Wyłączyć tryb pełnoekranowy - + GameShark Button (held) Przycisk GameShark (przytrzymany) - + Autofire Turbo - + Autofire A Turbo A - + Autofire B Turbo B - + Autofire L Turbo L - + Autofire R Turbo R - + Autofire Start Turbo Start - + Autofire Select Turbo Select - + Autofire Up Turbo Góra - + Autofire Right Turbo Prawo - + Autofire Down Turbo Dół - + Autofire Left Turbo Lewo - + Clear Wyczyść diff --git a/src/platform/qt/ts/mgba-pt_BR.ts b/src/platform/qt/ts/mgba-pt_BR.ts index 4bd9d228c..26e5f53fa 100644 --- a/src/platform/qt/ts/mgba-pt_BR.ts +++ b/src/platform/qt/ts/mgba-pt_BR.ts @@ -5801,137 +5801,137 @@ Tamanho do download: %3 Falhou em criar um dispositivo de exibição apropriado, voltando a exibição por software. Os jogos podem executar lentamente, especialmente com janelas maiores. - + Really make portable? Realmente tornar portátil? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Isto fará o emulador carregar sua configuração do mesmo diretório que o executável. Você quer continuar? - + Restart needed Reiniciar é necessário - + Some changes will not take effect until the emulator is restarted. Algumas mudanças não terão efeito até o emulador ser reiniciado. - + - Player %1 of %2 - Jogador %1 de %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Arquivo - + Load &ROM... Carregar &ROM... - + Load ROM in archive... Carregar ROM no arquivo compactado... - + Add folder to library... Adicionar pasta a biblioteca... - + Save games Saves dos jogos - + Automatically determine Determinar automaticamente - + Use player %0 save game Usar o save do jogo %0 do jogador - + Load &patch... Carregar &patch... - + Boot BIOS Dar Boot na BIOS - + Replace ROM... Substituir a ROM... - + ROM &info... Informações da &ROM... - + Recent Recentes - + Make portable Tornar portátil - + &Load state &Carregar state - + Report bug... Reportar bug... - + About... Sobre... - + Game Pak sensors... Sensores do Game Pak... - + Clear Limpar - + Load state file... Carregar arquivo do state... @@ -5977,73 +5977,73 @@ Tamanho do download: %3 %1 de %2 cartões do e-Reader convertidos com sucesso. - + Load alternate save game... Carregar save alternativo do jogo... - + Load temporary save game... Carregar save temporário do jogo... - + Convert e-Reader card image to raw... Converter imagem do cartão do e-Reader pro natural... - + &Save state &Salvar o state - + Save state file... Arquivo do save state... - + Quick load Carregamento rápido - + Quick save Salvamento rápido - + Load recent Carregar recentes - + Save recent Salvar recentes - + Undo load state Desfazer o carregamento do state - + Undo save state Desfazer o salvamento do state - + State &%1 State &%1 - + Load camera image... Carregar a imagem da câmera... - + Convert save game... Converter o save do jogo... @@ -6053,242 +6053,242 @@ Tamanho do download: %3 Saves do GameShark (*.gsv *.sps *.xps) - + Reset needed É necessário resetar - + Some changes will not take effect until the game is reset. Algumas mudanças não terão efeito até o jogo ser resetado. - + New multiplayer window Nova janela multi-jogador - + Connect to Dolphin... Conectar ao Dolphin... - + E&xit S&air - + &Emulation &Emulação - + &Reset &Resetar - + Sh&utdown De&sligar - + Yank game pak Arrancar o game pak - + &Pause &Pausar - + &Next frame &Próximo frame - + Fast forward (held) Avanço rápido (segurado) - + &Fast forward &Avanço rápido - + Fast forward speed Velocidade do avanço rápido - + Unbounded Ilimitado - + %0x %0x - + Rewind (held) Retroceder (segurado) - + Re&wind Re&troceder - + Step backwards Voltar um passo - + Solar sensor Sensor solar - + Increase solar level Aumentar nível solar - + Decrease solar level Diminuir nível solar - + Brightest solar level Nível solar mais brilhante - + Darkest solar level Nível solar mais escuro - + Brightness %1 Brilho %1 - + Audio/&Video Áudio/&Vídeo - + Frame size Tamanho do frame - + Toggle fullscreen Alternar tela cheia - + Lock aspect ratio Travar a proporção do aspecto - + Force integer scaling Forçar dimensionamento do inteiro - + Bilinear filtering Filtragem bilinear - + Frame&skip Frame&skip - + Mute Mudo - + FPS target FPS alvo - + Native (59.7275) Nativo (59,7275) - + Take &screenshot Tirar &screenshot - + F12 F12 - + Game Boy Printer... Impressora do Game Boy... - + BattleChip Gate... Portal do BattleChip... - + %1× %1× - + Interframe blending Mistura do interframe - + Record A/V... Gravar A/V... - + Video layers Camadas do vídeo - + Audio channels Canais de áudio - + Adjust layer placement... Ajustar posicionamento da camada... - + &Tools &Ferramentas - + View &logs... Visualizar &registros... - + Game &overrides... Substituições &do jogo... @@ -6303,167 +6303,167 @@ Tamanho do download: %3 Não pôde iniciar o jogo. - + Scan e-Reader dotcodes... Escanear dotcodes do e-Reader... - + Import GameShark Save... Importar Save do GameShark... - + Export GameShark Save... Exportar Save do GameShark... - + Record GIF/WebP/APNG... Gravar GIF/WebP/APNG... - + &Cheats... &Trapaças... - + Settings... Configurações... - + Open debugger console... Abrir console do debugger... - + Start &GDB server... Iniciar servidor do &GDB... - + Scripting... Scripting... - + Game state views Visualizações do estado do jogo - + View &palette... Visualizar &paleta... - + View &sprites... Visualizar &imagens móveis... - + View &tiles... Visualizar &ladrilhos... - + View &map... Visualizar &mapa... - + &Frame inspector... Inspetor dos &frames... - + View memory... Visualizar memória... - + Search memory... Procurar na memória... - + View &I/O registers... Visualizar registros de &E/S... - + Record debug video log... Gravar registro do vídeo de debug... - + Stop debug video log Parar o registro do vídeo de debug - + Exit fullscreen Sair da tela cheia - + GameShark Button (held) Botão do GameShark (segurado) - + Autofire Auto-disparar - + Autofire A Auto-disparar A - + Autofire B Auto-disparar B - + Autofire L Auto-disparar L - + Autofire R Auto-disparar R - + Autofire Start Auto-disparar Start - + Autofire Select Auto-disparar Select - + Autofire Up Auto-disparar Pra Cima - + Autofire Right Auto-disparar Direita - + Autofire Down Auto-disparar Pra Baixo - + Autofire Left Auto-disparar Esquerda diff --git a/src/platform/qt/ts/mgba-ru.ts b/src/platform/qt/ts/mgba-ru.ts index 828506be8..ecd21d9f7 100644 --- a/src/platform/qt/ts/mgba-ru.ts +++ b/src/platform/qt/ts/mgba-ru.ts @@ -5813,62 +5813,62 @@ Download size: %3 Не удалось создать устройство отображения, возврат к программному режиму. Игры могут идти медленнее, особенно в окнах больших размеров. - + Really make portable? Перейти в портативный режим? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? После этого эмулятор будет загружать конфигурацию из папки с исполняемым файлом. Продолжить? - + Restart needed Требуется перезапуск - + Some changes will not take effect until the emulator is restarted. Для применения некоторых изменений требуется перезапустить эмулятор. - + - Player %1 of %2 - Игрок %1 из %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File &Файл - + Load &ROM... Загрузить &ROM... - + Load ROM in archive... Загрузить игру из архива... - + Add folder to library... Добавить папку в библиотеку... @@ -5914,118 +5914,118 @@ Download size: %3 %1 из %2 карточек e-Reader успешно сконвертировано. - + Load alternate save game... Загрузить альтернативное сохранение... - + Load temporary save game... Загрузить временное сохранение... - + Load &patch... Загрузить &патч... - + Boot BIOS Загрузиться в BIOS - + Replace ROM... Заменить ROM... - + Scan e-Reader dotcodes... Сканировать dot-коды e-Reader... - + Convert e-Reader card image to raw... Конвертировать карту e-Reader в raw... - + ROM &info... Информация об &игре... - + Recent Недавние - + Make portable Портативный режим - + &Load state &Загрузить состояние - + Load state file... Загрузить состояние из файла... - + &Save state &Сохранить состояние - + Save state file... Сохранить состояние в файл... - + Quick load Быстрая загрузка - + Quick save Быстрое сохранение - + Load recent Загрузить недавнее - + Save recent Сохранить в недавнее - + Undo load state Отмена загрузки состояния - + Undo save state Отмена сохранения состояния - + State &%1 Слот &%1 - + Load camera image... Загрузить изображение с камеры... - + Convert save game... Конвертировать игровое сохранение... @@ -6035,437 +6035,437 @@ Download size: %3 Сохранения GameShark (*.gsv *.sps *.xps) - + Reset needed Необходима перезагрузка - + Some changes will not take effect until the game is reset. Некоторые изменения не войдут в силу, пока игра не перезагружена. - + Save games Сохранения - + Import GameShark Save... Импорт сохранения GameShark... - + Export GameShark Save... Экспорт сохранения GameShark... - + Automatically determine Определить автоматически - + Use player %0 save game Использовать сохранение игрока %0 - + New multiplayer window Новое окно мультиплеера - + Connect to Dolphin... Соединение с Dolphin... - + Report bug... Сообщить об ошибке... - + About... О программе... - + E&xit &Выход - + &Emulation &Эмуляция - + &Reset Перезагрузить (&R/&К) - + Sh&utdown Выключить (&U/&Г) - + Yank game pak Пнуть картридж - + &Pause Пау&за - + &Next frame Следующий кадр (&N/&Т) - + Fast forward (held) Перемотка (удержание) - + &Fast forward Перемотк&а - + Fast forward speed Скорость перемотки - + Unbounded Неограниченная - + %0x %0x - + Rewind (held) Обратная перемотка (удержание) - + Re&wind Обратная перемотка (&W/&Ц) - + Step backwards Шаг назад - + Solar sensor Датчик солнца - + Increase solar level Усилить солнечный свет - + Decrease solar level Ослабить солнечный свет - + Brightest solar level Ярчайшее солнце - + Darkest solar level Тусклейшее солнце - + Brightness %1 Яркость %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video Аудио/Видео (&V/&М) - + Frame size Размер кадра - + %1× %1× - + Toggle fullscreen Переключить полноэкранный режим - + Lock aspect ratio Зафиксировать соотношение сторон - + Force integer scaling Принудительное целочисленное масштабирование - + Interframe blending Межкадровое смешение - + Bilinear filtering Билинейная фильтрация - + Frame&skip Пропуск кадров (&S/&Ы) - + Mute Выключить звук - + FPS target Целевой FPS - + Native (59.7275) Родной (59.7275) - + Take &screenshot Снять скриншот (&S/&Ы) - + F12 F12 - + Record A/V... Записать аудио/видео... - + Record GIF/WebP/APNG... Записать GIF/WebP/APNG... - + Video layers Видеослои - + Audio channels Аудиоканалы - + Adjust layer placement... Настроить расположение слоёв... - + &Tools Инструм&енты - + View &logs... Посмотреть в журнал... (&L/&Д) - + Game &overrides... Переопределения для этой игры... (&О/&Щ) - + Game Pak sensors... Датчики в Game Pak... - + &Cheats... Читы... (&C/&С) - + Settings... Настройки... - + Open debugger console... Открыть консоль отладки... - + Start &GDB server... Запустить сервер &GDB... - + Scripting... Скрипты... - + Game state views Просмотр состояния игры - + View &palette... Просмотр палитры... (&P/&З) - + View &sprites... Просмотр спрайтов... (&S/&Ы) - + View &tiles... Просмотр тайлов... (&T/&Е) - + View &map... Просмотр карты... (&M/&Ь) - + &Frame inspector... Изучение фрейм&а... - + View memory... Просмотр памяти... - + Search memory... Поиск в памяти... - + View &I/O registers... Просмотр регистров &I/O... - + Record debug video log... Запись отладочного видеожурнала... - + Stop debug video log Закончить запись отладочного видеожурнала - + Exit fullscreen Выйти из полноэкранного режима - + GameShark Button (held) Кнопка GameShark (удерживается) - + Autofire Автострельба - + Autofire A A (автострельба) - + Autofire B B (автострельба) - + Autofire L L (автострельба) - + Autofire R R (автострельба) - + Autofire Start Start (автострельба) - + Autofire Select Select (автострельба) - + Autofire Up Вверх (автострельба) - + Autofire Right Вправо (автострельба) - + Autofire Down Вниз (автострельба) - + Autofire Left Влево (автострельба) - + Clear Очистить diff --git a/src/platform/qt/ts/mgba-sv.ts b/src/platform/qt/ts/mgba-sv.ts index 954b8ffc2..88d08dee1 100644 --- a/src/platform/qt/ts/mgba-sv.ts +++ b/src/platform/qt/ts/mgba-sv.ts @@ -1,6 +1,29 @@ - + + + QGBA + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Nedladdningsstorlek: %3 QGBA::ApplicationUpdater - + Stable Standard - + Development Utveckling - + Unknown Okänd - + (None) (Inga) @@ -316,7 +339,7 @@ Nedladdningsstorlek: %3 Remove - + Ta bort @@ -331,12 +354,12 @@ Nedladdningsstorlek: %3 Save - + Spara Load - + Ladda @@ -400,12 +423,12 @@ Nedladdningsstorlek: %3 - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -413,17 +436,17 @@ Nedladdningsstorlek: %3 QGBA::CoreManager - + Failed to open game file: %1 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -502,6 +525,195 @@ Nedladdningsstorlek: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Nedladdningsstorlek: %3 QGBA::GBAApp - + Enable Discord Rich Presence @@ -601,22 +813,22 @@ Nedladdningsstorlek: %3 QGBA::GBAKeyEditor - + Clear Button - + Clear Analog - + Refresh - + Set all @@ -750,148 +962,168 @@ Nedladdningsstorlek: %3 QGBA::GameBoy - - + + Autodetect - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2719,7 +2951,7 @@ Nedladdningsstorlek: %3 Unknown - + Okänd @@ -2737,13 +2969,13 @@ Nedladdningsstorlek: %3 Red - + Röd Blue - + Blå @@ -2910,7 +3142,7 @@ Nedladdningsstorlek: %3 - + Slot %1 @@ -3226,7 +3458,7 @@ Nedladdningsstorlek: %3 Load - + Ladda @@ -3279,7 +3511,7 @@ Nedladdningsstorlek: %3 Address - + Adress @@ -3514,7 +3746,7 @@ Nedladdningsstorlek: %3 Load - + Ladda @@ -3535,7 +3767,7 @@ Nedladdningsstorlek: %3 Address - + Adress @@ -3879,17 +4111,17 @@ Nedladdningsstorlek: %3 Red - + Röd Green - + Grön Blue - + Blå @@ -3932,7 +4164,7 @@ Nedladdningsstorlek: %3 0x%0 (%1) - + 0x%0 (%1) @@ -4070,12 +4302,12 @@ Nedladdningsstorlek: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -4097,7 +4329,7 @@ Nedladdningsstorlek: %3 Save - + Spara @@ -4438,105 +4670,107 @@ Nedladdningsstorlek: %3 QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago + - + %n day(s) ago + @@ -4859,37 +5093,37 @@ Nedladdningsstorlek: %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5222,7 +5456,7 @@ Nedladdningsstorlek: %3 Cheats - + Cheats @@ -5676,790 +5910,775 @@ Nedladdningsstorlek: %3 Show advanced - + Visa avancerat QGBA::Window - - Game Boy Advance ROMs (%1) - - - - - Game Boy ROMs (%1) - - - - - All ROMs (%1) - - - - - %1 Video Logs (*.mvl) - - - - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + + Create forwarder... + + + + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear @@ -6467,32 +6686,32 @@ Nedladdningsstorlek: %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA - + GB - + ? diff --git a/src/platform/qt/ts/mgba-template.ts b/src/platform/qt/ts/mgba-template.ts index e4b45de8c..6e39742bf 100644 --- a/src/platform/qt/ts/mgba-template.ts +++ b/src/platform/qt/ts/mgba-template.ts @@ -5801,62 +5801,62 @@ Download size: %3 - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... @@ -5902,118 +5902,118 @@ Download size: %3 - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - + State &%1 - + Load camera image... - + Convert save game... @@ -6023,437 +6023,437 @@ Download size: %3 - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear diff --git a/src/platform/qt/ts/mgba-tr.ts b/src/platform/qt/ts/mgba-tr.ts index 7d5a86f2e..e5f5c6074 100644 --- a/src/platform/qt/ts/mgba-tr.ts +++ b/src/platform/qt/ts/mgba-tr.ts @@ -5814,183 +5814,183 @@ Download size: %3 Uygun görüntü cihazı oluşturma başarısız, yazılım ekranına dönülüyor. Oyunlar özellikle daha büyük ekranlarda yavaş çalışabilir. - + Really make portable? Taşınabilir yapılsın mı? - + 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? - + Restart needed Yeniden başlatma gerekli - + 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. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... &ROM yükle... - + Load ROM in archive... ROM'u arşivden yükle ... - + Add folder to library... Kütüphaneye klasör ekle ... - + Save games Oyunları kaydet - + Automatically determine - + Use player %0 save game - + Load &patch... &Patch yükle... - + Boot BIOS BIOS boot et - + Replace ROM... ROM değişti... - + Game state views - + Convert e-Reader card image to raw... e-Okuyucu kart resimlerini rawa dönüştür... - + ROM &info... ROM &info... - + Recent Son kullanılanlar - + Make portable Portatif yap - + &Load state &Kaydedilmiş konum yükle - + Load state file... Kaydedilmiş konum dosyası yükle... - + &Save state &Konumu kaydet - + Save state file... Konum dosyasını kaydet... - + Quick load Hızlı Yükle - + Quick save Hızlı kaydet - + Load recent En son yükle - + Save recent Hızlı kaydet - + Undo load state Kaydedilen konum yüklemeyi geri al - + Undo save state Konum kaydetmeyi geri al - + State &%1 Konum &%1 - + Load camera image... Kamera resmini yükle ... - + Convert save game... Kayıtlı oyun dömnüştürülüyor... @@ -6000,237 +6000,237 @@ Download size: %3 - + Reset needed - + Some changes will not take effect until the game is reset. - + New multiplayer window Yeni çokoyunculu ekranı - + Connect to Dolphin... Dolphin'e Bağlan... - + Report bug... Hata rapor et... - + About... Hakkında... - + E&xit Çıkış - + &Emulation Emülasyon - + &Reset &Reset - + Sh&utdown Kapat - + Yank game pak - + &Pause &Durdur - + &Next frame &Sonraki kare - + Fast forward (held) İleriye sar(basılı tutun) - + &Fast forward &İleriye sar - + Fast forward speed İleriye sarma hızı - + Unbounded - + %0x - + Rewind (held) Geri sar (basılı tutun) - + Re&wind Geri sar - + Step backwards Geriye doğru adım - + Solar sensor - + Increase solar level Solar seviyesini arttır - + Decrease solar level Solar seviyesini düşür - + Brightest solar level En parlak solar seviyesi - + Darkest solar level En karanlık solar seviyesi - + Brightness %1 Parlaklık:%1 - + Game Boy Printer... Game Boy yazıcısı... - + BattleChip Gate... - + Audio/&Video Ses/&Video - + Frame size Çerçeve boyutu - + Toggle fullscreen Tamekranı aç/kapa - + Lock aspect ratio En boy oranını kilitle - + Force integer scaling Tamsayılı ölçeklendirmeyi zorla - + Bilinear filtering Bilinear filtreleme - + Frame&skip Kare atlama - + Mute Sessiz - + FPS target FPS hedefi - + Native (59.7275) - + Take &screenshot Ekran görüntüsü al - + F12 - + Video layers - + Audio channels Ses kanalları - + Adjust layer placement... Katman yerleşimini ayarlayın... - + &Tools &Araçlar - + View &logs... Kayıtları görüntüle... - + Game &overrides... & Oyunun üzerine yazılanlar... @@ -6266,197 +6266,197 @@ Download size: %3 Oyun başlatılamadı. - + Load alternate save game... Alternatif kayıtlı oyun yükle... - + Load temporary save game... Geçici kayıtlı oyunu yükle... - + Scan e-Reader dotcodes... e-Okuyucu noktakodları tara... - + Import GameShark Save... GameShark kaydını içeri aktar... - + Export GameShark Save... GameShark kaydını dışarı aktar... - + %1× %1× - + Interframe blending Kareler-arası Karıştırma - + Record A/V... A/V Kayıt... - + Record GIF/WebP/APNG... GIF/WebP/APNG Kayıt... - + Game Pak sensors... Oyun Kartuş sensörleri... - + &Cheats... &Hileler... - + Settings... Ayarlar... - + Open debugger console... Hata ayıklayıcı konsolunu aç ... - + Start &GDB server... &GDB sunucusunu başlat... - + Scripting... - + View &palette... &Renk Paletini gör... - + View &sprites... &Spriteları gör... - + View &tiles... &Desenleri gör... - + View &map... &Haritayı gör - + &Frame inspector... &Kare denetçisi... - + View memory... Hafıza gör... - + Search memory... Hafızada ara... - + View &I/O registers... &I/O kayıtlarını görüntüle - + Record debug video log... Hata ayıklama video günlüğünü kaydet... - + Stop debug video log Hata ayıklama video günlüğünü durdur - + Exit fullscreen Tam ekrandan çık - + GameShark Button (held) GameShark Butonu (basılı tutun) - + Autofire Otomatik basma - + Autofire A Otomatik basma A - + Autofire B Otomatik basma B - + Autofire L Otomatik basma L - + Autofire R Otomatik basma R - + Autofire Start Otomatik basma Start - + Autofire Select Otomatik basma Select - + Autofire Up Otomatik basma Up - + Autofire Right Otomatik basma Right - + Autofire Down Otomatik basma Down - + Autofire Left Otomatik basma Sol - + Clear Temizle diff --git a/src/platform/qt/ts/mgba-zh_CN.ts b/src/platform/qt/ts/mgba-zh_CN.ts index 8cd4ffa9f..6ef74e12e 100644 --- a/src/platform/qt/ts/mgba-zh_CN.ts +++ b/src/platform/qt/ts/mgba-zh_CN.ts @@ -5809,62 +5809,62 @@ Download size: %3 无法创建适合的显示设备,正在回滚到软件显示。游戏的运行速度(特别在大窗口的情况下)可能会变慢。 - + Really make portable? 确定进行程序便携化? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? 进行此操作后,模拟器将从其可执行文件所在目录中载入模拟器配置。您想继续吗? - + Restart needed 需要重新启动 - + Some changes will not take effect until the emulator is restarted. 更改将在模拟器下次重新启动时生效。 - + - Player %1 of %2 - 玩家 %1 共 %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File 文件(&F) - + Load &ROM... 载入 ROM(&R)... - + Load ROM in archive... 从压缩文件中载入 ROM... - + Add folder to library... 将文件夹添加到库中... @@ -5910,118 +5910,118 @@ Download size: %3 成功转换了 %1 张(共 %2 张)e-Reader 卡片。 - + Load alternate save game... 加载备用保存游戏... - + Load temporary save game... 加载临时保存游戏... - + Load &patch... 载入补丁(&P)... - + Boot BIOS 引导 BIOS - + Replace ROM... 替换 ROM... - + Scan e-Reader dotcodes... 扫描 e-Reader 点码... - + Convert e-Reader card image to raw... 将 e-Reader 卡片图像转换为原始数据... - + ROM &info... ROM 信息(&I)... - + Recent 最近打开 - + Make portable 程序便携化 - + &Load state 载入即时存档(&L) - + Load state file... 载入即时存档文件... - + &Save state 保存即时存档(&S) - + Save state file... 保存即时存档文件... - + Quick load 快速读档 - + Quick save 快速存档 - + Load recent 载入最近存档 - + Save recent 保存最近存档 - + Undo load state 撤消读档 - + Undo save state 撤消存档 - + State &%1 即时存档 (&%1) - + Load camera image... 载入相机图片... - + Convert save game... 转换保存游戏... @@ -6031,437 +6031,437 @@ Download size: %3 GameShark 存档 (*.gsv *.sps *.xps) - + Reset needed 需要重启 - + Some changes will not take effect until the game is reset. 某些改动需要重新启动才会生效。 - + Save games 游戏存档 - + Import GameShark Save... 导入 GameShark 存档... - + Export GameShark Save... 导出 GameShark 存档... - + Automatically determine 自动终止 - + Use player %0 save game 使用玩家 %0 存档 - + New multiplayer window 新建多人游戏窗口 - + Connect to Dolphin... 连接到 Dolphin... - + Report bug... 报告错误... - + About... 关于... - + E&xit 退出(&X) - + &Emulation 模拟(&E) - + &Reset 重置(&R) - + Sh&utdown 关机(&U) - + Yank game pak 快速抽出游戏卡带 - + &Pause 暂停(&P) - + &Next frame 下一帧(&N) - + Fast forward (held) 快进 (长按) - + &Fast forward 快进(&F) - + Fast forward speed 快进速度 - + Unbounded 不限制 - + %0x %0x - + Rewind (held) 倒带 (长按) - + Re&wind 倒带(&W) - + Step backwards 步退 - + Solar sensor 太阳光传感器 - + Increase solar level 增加太阳光等级 - + Decrease solar level 降低太阳光等级 - + Brightest solar level 太阳光等级为最亮 - + Darkest solar level 太阳光等级为最暗 - + Brightness %1 亮度 %1 - + Game Boy Printer... Game Boy 打印机... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video 音频/视频(&V) - + Frame size 画面大小 - + %1× %1× - + Toggle fullscreen 切换全屏 - + Lock aspect ratio 锁定纵横比 - + Force integer scaling 强制整数缩放 - + Interframe blending 帧间混合 - + Bilinear filtering 双线性过滤 - + Frame&skip 跳帧(&S) - + Mute 静音 - + FPS target 目标 FPS - + Native (59.7275) 原生 (59.7275) - + Take &screenshot 截图(&S) - + F12 F12 - + Record A/V... 录制音频/视频... - + Record GIF/WebP/APNG... 录制 GIF/WebP/APNG... - + Video layers 视频图层 - + Audio channels 音频声道 - + Adjust layer placement... 调整图层布局... - + &Tools 工具(&T) - + View &logs... 查看日志(&L)... - + Game &overrides... 覆写游戏(&O)... - + Game Pak sensors... 游戏卡带传感器... - + &Cheats... 作弊码(&C)... - + Settings... 设置... - + Open debugger console... 打开调试器控制台... - + Start &GDB server... 打开 GDB 服务器(&G)... - + Scripting... 脚本... - + Game state views 游戏状态视图 - + View &palette... 查看调色板(&P)... - + View &sprites... 查看精灵图(&S)... - + View &tiles... 查看图块(&T)... - + View &map... 查看映射(&M)... - + &Frame inspector... 帧检查器(&F)... - + View memory... 查看内存... - + Search memory... 搜索内存... - + View &I/O registers... 查看 I/O 寄存器(&I)... - + Record debug video log... 记录调试视频日志... - + Stop debug video log 停止记录调试视频日志 - + Exit fullscreen 退出全屏 - + GameShark Button (held) GameShark 键 (长按) - + Autofire 连发 - + Autofire A 连发 A - + Autofire B 连发 B - + Autofire L 连发 L - + Autofire R 连发 R - + Autofire Start 连发 Start - + Autofire Select 连发 Select - + Autofire Up 连发 上 - + Autofire Right 连发 右 - + Autofire Down 连发 下 - + Autofire Left 连发 左 - + Clear 清除 diff --git a/src/platform/qt/utils.cpp b/src/platform/qt/utils.cpp index 8b273cb6d..d6da29781 100644 --- a/src/platform/qt/utils.cpp +++ b/src/platform/qt/utils.cpp @@ -1,12 +1,17 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau +/* Copyright (c) 2013-2022 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 "utils.h" +#include #include +#include "VFileDevice.h" + +#include + namespace QGBA { QString niceSizeFormat(size_t filesize) { @@ -59,4 +64,69 @@ bool convertAddress(const QHostAddress* input, Address* output) { return true; } +QString romFilters(bool includeMvl) { + QStringList filters; + QStringList formats; + +#ifdef M_CORE_GBA + QStringList gbaFormats{ + "*.gba", +#if defined(USE_LIBZIP) || defined(USE_MINIZIP) + "*.zip", +#endif +#ifdef USE_LZMA + "*.7z", +#endif +#ifdef USE_ELF + "*.elf", +#endif + "*.agb", + "*.mb", + "*.rom", + "*.bin"}; + formats.append(gbaFormats); + filters.append(QCoreApplication::translate("QGBA", "Game Boy Advance ROMs (%1)", nullptr).arg(gbaFormats.join(QChar(' ')))); +#endif + +#ifdef M_CORE_GB + QStringList gbFormats{ + "*.gb", + "*.gbc", + "*.sgb", +#if defined(USE_LIBZIP) || defined(USE_MINIZIP) + "*.zip", +#endif +#ifdef USE_LZMA + "*.7z", +#endif + "*.rom", + "*.bin"}; + formats.append(gbFormats); + filters.append(QCoreApplication::translate("QGBA", "Game Boy ROMs (%1)", nullptr).arg(gbFormats.join(QChar(' ')))); +#endif + + formats.removeDuplicates(); + filters.prepend(QCoreApplication::translate("QGBA", "All ROMs (%1)", nullptr).arg(formats.join(QChar(' ')))); + if (includeMvl) { + filters.append(QCoreApplication::translate("QGBA", "%1 Video Logs (*.mvl)", nullptr).arg(projectName)); + } + return filters.join(";;"); +} + +bool extractMatchingFile(VDir* dir, std::function filter) { + for (VDirEntry* entry = dir->listNext(dir); entry; entry = dir->listNext(dir)) { + QString target = filter(entry); + if (target.isNull()) { + continue; + } + VFile* outfile = VFileOpen(target.toUtf8().constData(), O_WRONLY | O_TRUNC | O_CREAT); + VFile* infile = dir->openFile(dir, entry->name(entry), O_RDONLY); + VFileDevice::copyFile(infile, outfile); + infile->close(infile); + outfile->close(outfile); + return true; + } + return false; +} + } diff --git a/src/platform/qt/utils.h b/src/platform/qt/utils.h index ca9ef027f..72ce74c30 100644 --- a/src/platform/qt/utils.h +++ b/src/platform/qt/utils.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -14,6 +15,10 @@ #include #include +#include + +struct VDir; +struct VDirEntry; namespace QGBA { @@ -67,4 +72,7 @@ constexpr const T& clamp(const T& v, const T& lo, const T& hi) { } #endif +QString romFilters(bool includeMvl = false); +bool extractMatchingFile(VDir* dir, std::function filter); + } diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index 25b6f6eca..14996d5b0 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -82,6 +82,7 @@ static GLuint insizeLocation; static GLuint colorLocation; static GLuint tex; static GLuint oldTex; +static GLuint screenshotTex; static struct GUIFont* font; static color_t* frameBuffer; @@ -379,8 +380,6 @@ static void _gameUnloaded(struct mGUIRunner* runner) { static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded, bool blendTop) { glViewport(0, 1080 - vheight, vwidth, vheight); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glUseProgram(program); glBindVertexArray(vao); @@ -493,11 +492,16 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) { } if (interframeBlending) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, oldTex); _drawTex(runner, width, height, faded, false); glBindTexture(GL_TEXTURE_2D, tex); _drawTex(runner, width, height, faded, true); } else { + glDisable(GL_BLEND); + _drawTex(runner, width, height, faded, false); } @@ -523,10 +527,15 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) { static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) { glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, tex); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glBindTexture(GL_TEXTURE_2D, screenshotTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + runner->core->desiredVideoDimensions(runner->core, &width, &height); + glDisable(GL_BLEND); + bool wasPbo = usePbo; + usePbo = false; _drawTex(runner, width, height, faded, false); + usePbo = wasPbo; } static uint16_t _pollGameInput(struct mGUIRunner* runner) { @@ -711,6 +720,13 @@ static void glInit(void) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glGenTextures(1, &screenshotTex); + glBindTexture(GL_TEXTURE_2D, screenshotTex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glGenBuffers(1, &pbo); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); glBufferData(GL_PIXEL_UNPACK_BUFFER, 256 * 256 * 4, NULL, GL_STREAM_DRAW); @@ -790,6 +806,7 @@ static void glDeinit(void) { glDeleteFramebuffers(1, ©Fbo); glDeleteTextures(1, &tex); glDeleteTextures(1, &oldTex); + glDeleteTextures(1, &screenshotTex); glDeleteBuffers(1, &vbo); glDeleteProgram(program); glDeleteVertexArrays(1, &vao); @@ -1061,10 +1078,7 @@ int main(int argc, char* argv[]) { } if (argc > 1) { - size_t i; - for (i = 0; runner.keySources[i].id; ++i) { - mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config)); - } + mGUILoadInputMaps(&runner); mGUIRun(&runner, argv[1]); } else { mGUIRunloop(&runner); diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 36bb9abfc..cdfe00b2f 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -271,7 +271,7 @@ int main(int argc, char* argv[]) { memset(audioBuffer, 0, sizeof(audioBuffer)); #ifdef FIXED_ROM_BUFFER - romBufferSize = SIZE_CART0; + romBufferSize = GBA_SIZE_ROM0; romBuffer = SYS_GetArena2Lo(); SYS_SetArena2Lo((void*)((intptr_t) romBuffer + romBufferSize)); #endif @@ -653,10 +653,7 @@ int main(int argc, char* argv[]) { } if (argc > 1) { - size_t i; - for (i = 0; runner.keySources[i].id; ++i) { - mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config)); - } + mGUILoadInputMaps(&runner); mGUIRun(&runner, argv[1]); } else { mGUIRunloop(&runner); diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index 5cfb4ff41..d84659453 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -1,6 +1,7 @@ include(ExportDirectory) set(SOURCE_FILES context.c + input.c socket.c stdlib.c types.c) @@ -12,8 +13,10 @@ set(TEST_FILES if(USE_LUA) list(APPEND SOURCE_FILES engines/lua.c) list(APPEND TEST_FILES - test/stdlib.c - test/lua.c) + test/context.c + test/input.c + test/lua.c + test/stdlib.c) endif() source_group("Scripting" FILES ${SOURCE_FILES}) diff --git a/src/script/context.c b/src/script/context.c index fa1a0b4a2..bf5fcfa54 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -71,8 +71,8 @@ void mScriptContextInit(struct mScriptContext* context) { void mScriptContextDeinit(struct mScriptContext* context) { HashTableDeinit(&context->rootScope); - HashTableDeinit(&context->weakrefs); mScriptContextDrainPool(context); + HashTableDeinit(&context->weakrefs); mScriptListDeinit(&context->refPool); HashTableDeinit(&context->callbacks); TableDeinit(&context->callbackId); @@ -102,9 +102,12 @@ void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* void mScriptContextDrainPool(struct mScriptContext* context) { size_t i; for (i = 0; i < mScriptListSize(&context->refPool); ++i) { - struct mScriptValue* value = mScriptValueUnwrap(mScriptListGetPointer(&context->refPool, i)); - if (value) { + struct mScriptValue* value = mScriptListGetPointer(&context->refPool, i); + if (value->type->base == mSCRIPT_TYPE_WRAPPER) { + value = mScriptValueUnwrap(value); mScriptValueDeref(value); + } else if (value->type == mSCRIPT_TYPE_MS_WEAKREF) { + mScriptContextClearWeakref(context, value->value.u32); } } mScriptListClear(&context->refPool); @@ -201,7 +204,14 @@ void mScriptContextClearWeakref(struct mScriptContext* context, uint32_t weakref TableRemove(&context->weakrefs, weakref); } -void mScriptContextTriggerCallback(struct mScriptContext* context, const char* callback) { +void mScriptContextDisownWeakref(struct mScriptContext* context, uint32_t weakref) { + struct mScriptValue* poolEntry = mScriptListAppend(&context->refPool); + poolEntry->type = mSCRIPT_TYPE_MS_WEAKREF; + poolEntry->value.u32 = weakref; + poolEntry->refs = mSCRIPT_VALUE_UNREF; +} + +void mScriptContextTriggerCallback(struct mScriptContext* context, const char* callback, struct mScriptList* args) { struct mScriptValue* list = HashTableLookup(&context->callbacks, callback); if (!list) { return; @@ -214,6 +224,9 @@ void mScriptContextTriggerCallback(struct mScriptContext* context, const char* c continue; } mScriptFrameInit(&frame); + if (args) { + mScriptListCopy(&frame.arguments, args); + } if (fn->type->base == mSCRIPT_TYPE_WRAPPER) { fn = mScriptValueUnwrap(fn); } diff --git a/src/script/docgen.c b/src/script/docgen.c index 55d12eedc..65fcc87ce 100644 --- a/src/script/docgen.c +++ b/src/script/docgen.c @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include struct mScriptContext context; @@ -36,62 +38,6 @@ void explainValue(struct mScriptValue* value, const char* name, int level); void explainValueScoped(struct mScriptValue* value, const char* name, const char* scope, int level); void explainType(struct mScriptType* type, int level); -void addTypesFromTuple(const struct mScriptTypeTuple*); -void addTypesFromTable(struct Table*); - -void addType(const struct mScriptType* type) { - if (HashTableLookup(&types, type->name) || type->isConst) { - return; - } - HashTableInsert(&types, type->name, (struct mScriptType*) type); - switch (type->base) { - case mSCRIPT_TYPE_FUNCTION: - addTypesFromTuple(&type->details.function.parameters); - addTypesFromTuple(&type->details.function.returnType); - break; - case mSCRIPT_TYPE_OBJECT: - mScriptClassInit(type->details.cls); - if (type->details.cls->parent) { - addType(type->details.cls->parent); - } - addTypesFromTable(&type->details.cls->instanceMembers); - break; - case mSCRIPT_TYPE_OPAQUE: - case mSCRIPT_TYPE_WRAPPER: - if (type->details.type) { - addType(type->details.type); - } - case mSCRIPT_TYPE_VOID: - case mSCRIPT_TYPE_SINT: - case mSCRIPT_TYPE_UINT: - case mSCRIPT_TYPE_FLOAT: - case mSCRIPT_TYPE_STRING: - case mSCRIPT_TYPE_LIST: - case mSCRIPT_TYPE_TABLE: - case mSCRIPT_TYPE_WEAKREF: - // No subtypes - break; - } -} - -void addTypesFromTuple(const struct mScriptTypeTuple* tuple) { - size_t i; - for (i = 0; i < tuple->count; ++i) { - addType(tuple->entries[i]); - } -} - -void addTypesFromTable(struct Table* table) { - struct TableIterator iter; - if (!HashTableIteratorStart(table, &iter)) { - return; - } - do { - struct mScriptClassMember* member = HashTableIteratorGetValue(table, &iter); - addType(member->type); - } while(HashTableIteratorNext(table, &iter)); -} - void printchomp(const char* string, int level) { char indent[(level + 1) * 2 + 1]; memset(indent, ' ', sizeof(indent) - 1); @@ -265,7 +211,7 @@ void explainObject(struct mScriptValue* value, int level) { continue; } fprintf(out, "%s%s:\n", indent, details->info.member.name); - addType(details->info.member.type); + mScriptTypeAdd(&types, details->info.member.type); if (mScriptObjectGet(value, details->info.member.name, &member)) { struct mScriptValue* unwrappedMember; if (member.type->base == mSCRIPT_TYPE_WRAPPER) { @@ -288,7 +234,7 @@ void explainValueScoped(struct mScriptValue* value, const char* name, const char memset(indent, ' ', sizeof(indent) - 1); indent[sizeof(indent) - 1] = '\0'; value = mScriptContextAccessWeakref(&context, value); - addType(value->type); + mScriptTypeAdd(&types, value->type); fprintf(out, "%stype: %s\n", indent, value->type->name); const char* docstring = NULL; @@ -421,7 +367,7 @@ void explainCore(struct mCore* core) { mScriptContextAttachCore(&context, core); struct mScriptValue* emu = mScriptContextGetGlobal(&context, "emu"); - addType(emu->type); + mScriptTypeAdd(&types, emu->type); if (mScriptObjectGet(emu, "memory", &wrapper)) { struct mScriptValue* memory = mScriptValueUnwrap(&wrapper); @@ -434,7 +380,7 @@ void explainCore(struct mCore* core) { fprintf(out, " %s:\n", name->value.string->buffer); value = mScriptContextAccessWeakref(&context, value); - addType(value->type); + mScriptTypeAdd(&types, value->type); struct mScriptFrame frame; uint32_t baseVal; @@ -486,7 +432,7 @@ void initTypes(void) { size_t i; for (i = 0; baseTypes[i]; ++i) { - addType(baseTypes[i]); + mScriptTypeAdd(&types, baseTypes[i]); } } @@ -523,9 +469,11 @@ int main(int argc, char* argv[]) { mScriptContextInit(&context); mScriptContextAttachStdlib(&context); mScriptContextAttachSocket(&context); + mScriptContextAttachInput(&context); mScriptContextSetTextBufferFactory(&context, NULL, NULL); initTypes(); + mScriptContextGetInputTypes(&types); fputs("version:\n", out); fprintf(out, " string: \"%s\"\n", projectVersion); diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 37327ee54..db7a70075 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -693,6 +693,31 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v return true; } } + struct mScriptValue derefPtr; + if (value->type->base == mSCRIPT_TYPE_OPAQUE) { + if (!value->type->details.type) { + return false; + } + mScriptValueFollowPointer(value, &derefPtr); + switch (derefPtr.type->base) { + case mSCRIPT_TYPE_VOID: + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_FLOAT: + value = &derefPtr; + break; + case mSCRIPT_TYPE_OBJECT: + value = mScriptValueAlloc(derefPtr.type); + value->value.opaque = derefPtr.value.opaque; + weakref = mScriptContextSetWeakref(luaContext->d.context, value); + needsWeakref = true; + mScriptContextDisownWeakref(luaContext->d.context, weakref); + mScriptValueDeref(value); + break; + default: + return false; + } + } if (value->type == mSCRIPT_TYPE_MS_WEAKREF) { weakref = value->value.u32; value = mScriptContextAccessWeakref(luaContext->d.context, value); @@ -738,7 +763,19 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } break; case mSCRIPT_TYPE_STRING: - lua_pushlstring(luaContext->lua, value->value.string->buffer, value->value.string->size); + if (!value->value.string) { + lua_pushnil(luaContext->lua); + break; + } + if (value->type == mSCRIPT_TYPE_MS_STR) { + lua_pushlstring(luaContext->lua, value->value.string->buffer, value->value.string->size); + break; + } + if (value->type == mSCRIPT_TYPE_MS_CHARP) { + lua_pushstring(luaContext->lua, value->value.copaque); + break; + } + ok = false; break; case mSCRIPT_TYPE_LIST: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); @@ -769,6 +806,10 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v mScriptValueDeref(value); break; case mSCRIPT_TYPE_OBJECT: + if (!value->value.opaque) { + lua_pushnil(luaContext->lua); + break; + } newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); if (needsWeakref) { *newValue = mSCRIPT_MAKE(WEAKREF, weakref); diff --git a/src/script/input.c b/src/script/input.c new file mode 100644 index 000000000..fdb6593cb --- /dev/null +++ b/src/script/input.c @@ -0,0 +1,564 @@ +/* Copyright (c) 2013-2022 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +#include +#include +#include + +static const char* eventNames[mSCRIPT_EV_TYPE_MAX] = { + [mSCRIPT_EV_TYPE_KEY] = "key", + [mSCRIPT_EV_TYPE_MOUSE_BUTTON] = "mouseButton", + [mSCRIPT_EV_TYPE_MOUSE_MOVE] = "mouseMove", + [mSCRIPT_EV_TYPE_MOUSE_WHEEL] = "mouseWheel", + [mSCRIPT_EV_TYPE_GAMEPAD_BUTTON] = "gamepadButton", + [mSCRIPT_EV_TYPE_GAMEPAD_HAT] = "gamepadHat", + [mSCRIPT_EV_TYPE_TRIGGER] = "trigger", +}; + +static const struct mScriptType* eventTypes[mSCRIPT_EV_TYPE_MAX] = { + [mSCRIPT_EV_TYPE_KEY] = mSCRIPT_TYPE_MS_S(mScriptKeyEvent), + [mSCRIPT_EV_TYPE_MOUSE_BUTTON] = mSCRIPT_TYPE_MS_S(mScriptMouseButtonEvent), + [mSCRIPT_EV_TYPE_MOUSE_MOVE] = mSCRIPT_TYPE_MS_S(mScriptMouseMoveEvent), + [mSCRIPT_EV_TYPE_MOUSE_WHEEL] = mSCRIPT_TYPE_MS_S(mScriptMouseWheelEvent), + [mSCRIPT_EV_TYPE_GAMEPAD_BUTTON] = mSCRIPT_TYPE_MS_S(mScriptGamepadButtonEvent), + [mSCRIPT_EV_TYPE_GAMEPAD_HAT] = mSCRIPT_TYPE_MS_S(mScriptGamepadHatEvent), +}; + +struct mScriptInputContext { + uint64_t seq; + struct Table activeKeys; + struct mScriptGamepad* activeGamepad; +}; + +static void _mScriptInputDeinit(struct mScriptInputContext*); +static bool _mScriptInputIsKeyActive(const struct mScriptInputContext*, struct mScriptValue*); + +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_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, or the numerical Unicode codepoint") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptInputContext, isKeyActive) + mSCRIPT_DEFINE_DOCSTRING("The currently active gamepad, if any") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptInputContext, PCS(mScriptGamepad), activeGamepad) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "The base class for all event types. Different events have their own subclasses." + ) + mSCRIPT_DEFINE_DOCSTRING("The type of this event. See C.EV_TYPE for a list of possible types.") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptEvent, S32, type) + mSCRIPT_DEFINE_DOCSTRING("Sequence number of this event. This value increases monotinically.") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptEvent, U64, seq) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptKeyEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A keyboard key event.") + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("The state of the key, represented by a C.INPUT_STATE value") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptKeyEvent, U8, state) + mSCRIPT_DEFINE_DOCSTRING("A bitmask of current modifiers, represented by ORed C.KMOD values") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptKeyEvent, S16, modifiers) + mSCRIPT_DEFINE_DOCSTRING( + "The relevant key for this event. For most printable characters, this will be the Unicode " + "codepoint of the character. Some special values are present as C.KEY as well." + ) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptKeyEvent, S32, key) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptMouseButtonEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A mouse button event.") + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("Which mouse this event pertains to. Currently, this will always be 0.") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseButtonEvent, U8, mouse) + mSCRIPT_DEFINE_DOCSTRING("The state of the button, represented by a C.INPUT_STATE value") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseButtonEvent, U8, state) + mSCRIPT_DEFINE_DOCSTRING( + "Which mouse button this event pertains to. Symbolic names for primary (usually left), " + "secondary (usually right), and middle are in C.MOUSE_BUTTON, and further buttons " + "are numeric starting from 3." + ) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseButtonEvent, U8, button) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptMouseMoveEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A mouse movement event.") + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("Which mouse this event pertains to. Currently, this will always be 0.") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseMoveEvent, U8, mouse) + mSCRIPT_DEFINE_DOCSTRING( + "The x coordinate of the mouse in the context of game screen pixels. " + "This can be out of bounds of the game screen depending on the size of the window in question." + ) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseMoveEvent, S32, x) + mSCRIPT_DEFINE_DOCSTRING( + "The y coordinate of the mouse in the context of game screen pixels. " + "This can be out of bounds of the game screen depending on the size of the window in question." + ) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseMoveEvent, S32, y) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptMouseWheelEvent) + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("Which mouse this event pertains to. Currently, this will always be 0.") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseWheelEvent, U8, mouse) + mSCRIPT_DEFINE_DOCSTRING("The amount scrolled horizontally") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseWheelEvent, S32, x) + mSCRIPT_DEFINE_DOCSTRING("The amount scrolled vertically") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseWheelEvent, S32, y) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptGamepadButtonEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A gamead button event.") + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("The state of the button, represented by a C.INPUT_STATE value") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadButtonEvent, U8, state) + mSCRIPT_DEFINE_DOCSTRING("Which gamepad this event pertains to. Currently, this will always be 0.") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadButtonEvent, U8, pad) + mSCRIPT_DEFINE_DOCSTRING( + "Which button this event pertains to. There is currently no guaranteed button mapping, " + "and it might change between different controllers." + ) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadButtonEvent, U16, button) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptGamepadHatEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A gamepad POV hat event.") + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("Which gamepad this event pertains to. Currently, this will always be 0.") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, pad) + mSCRIPT_DEFINE_DOCSTRING("Which hat this event pertains to. For most gamepads this will be 0.") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, hat) + mSCRIPT_DEFINE_DOCSTRING("The current direction of the hat. See C.INPUT_DIR for possible values.") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, direction) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptGamepad) + mSCRIPT_DEFINE_DOCSTRING("The human-readable name of this gamepad") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, CHARP, visibleName) + mSCRIPT_DEFINE_DOCSTRING("The internal name of this gamepad, generally unique to the specific type of gamepad") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, CHARP, internalName) + mSCRIPT_DEFINE_DOCSTRING("An indexed list of the current values of each axis") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, axes) + mSCRIPT_DEFINE_DOCSTRING("An indexed list of the current values of each button") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, buttons) + mSCRIPT_DEFINE_DOCSTRING("An indexed list of the current values of POV hat") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, hats) +mSCRIPT_DEFINE_END; + +void mScriptContextAttachInput(struct mScriptContext* context) { + struct mScriptInputContext* inputContext = calloc(1, sizeof(*inputContext)); + struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptInputContext)); + value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + value->value.opaque = inputContext; + + inputContext->seq = 0; + TableInit(&inputContext->activeKeys, 0, NULL); + + mScriptContextSetGlobal(context, "input", value); + mScriptContextSetDocstring(context, "input", "Singleton instance of struct::mScriptInputContext"); + + mScriptContextExportConstants(context, "EV_TYPE", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, NONE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, KEY), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, MOUSE_BUTTON), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, MOUSE_MOVE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, MOUSE_WHEEL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, GAMEPAD_BUTTON), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, TRIGGER), + mSCRIPT_KV_SENTINEL + }); + + mScriptContextExportConstants(context, "INPUT_STATE", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_STATE, UP), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_STATE, DOWN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_STATE, HELD), + mSCRIPT_KV_SENTINEL + }); + + mScriptContextExportConstants(context, "INPUT_DIR", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, NONE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, NORTH), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, EAST), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, SOUTH), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, WEST), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, UP), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, RIGHT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, DOWN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, LEFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, NORTHEAST), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, NORTHWEST), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, SOUTHEAST), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, SOUTHWEST), + mSCRIPT_KV_SENTINEL + }); + + mScriptContextExportConstants(context, "KEY", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, NONE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, BACKSPACE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, TAB), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, ENTER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, ESCAPE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, DELETE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F1), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F2), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F3), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F4), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F5), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F6), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F7), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F8), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F9), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F10), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F11), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F12), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F13), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F14), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F15), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F16), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F17), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F18), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F19), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F20), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F21), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F22), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F23), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F24), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, UP), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, RIGHT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, DOWN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, LEFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, PAGE_UP), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, PAGE_DOWN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, HOME), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, END), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, INSERT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, BREAK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, CLEAR), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, PRINT_SCREEN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, SYSRQ), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, MENU), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, HELP), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, LSHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, RSHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, SHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, LCONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, RCONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, CONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, LALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, RALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, ALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, LSUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, RSUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, SUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, CAPS_LOCK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, NUM_LOCK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, SCROLL_LOCK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_0), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_1), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_2), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_3), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_4), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_5), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_6), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_7), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_8), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_9), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_PLUS), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_MINUS), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_MULTIPLY), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_DIVIDE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_COMMA), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_POINT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_ENTER), + mSCRIPT_KV_SENTINEL + }); + + mScriptContextExportConstants(context, "KMOD", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, NONE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, LSHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, RSHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, SHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, LCONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, RCONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, CONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, LALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, RALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, ALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, LSUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, RSUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, SUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, CAPS_LOCK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, NUM_LOCK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, SCROLL_LOCK), + mSCRIPT_KV_SENTINEL + }); + + mScriptContextExportConstants(context, "MOUSE_BUTTON", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_MOUSE_BUTTON, PRIMARY), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_MOUSE_BUTTON, SECONDARY), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_MOUSE_BUTTON, MIDDLE), + mSCRIPT_KV_SENTINEL + }); +} + +void _mScriptInputDeinit(struct mScriptInputContext* context) { + TableDeinit(&context->activeKeys); +} + +bool _mScriptInputIsKeyActive(const struct mScriptInputContext* context, struct mScriptValue* 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: + return false; + } + + void* down = TableLookup(&context->activeKeys, key); + return down != NULL; +} + +static bool _updateKeys(struct mScriptContext* context, struct mScriptKeyEvent* event) { + int offset = 0; + switch (event->state) { + case mSCRIPT_INPUT_STATE_UP: + offset = -1; + break; + case mSCRIPT_INPUT_STATE_DOWN: + offset = 1; + break; + default: + return true; + } + + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + intptr_t value = (intptr_t) TableLookup(&inputContext->activeKeys, event->key); + value += offset; + if (value < 1) { + TableRemove(&inputContext->activeKeys, event->key); + } else { + TableInsert(&inputContext->activeKeys, event->key, (void*) value); + } + if (offset < 0 && value > 0) { + return false; + } + if (offset > 0 && value != 1) { + event->state = mSCRIPT_INPUT_STATE_HELD; + } + return true; +} + +void mScriptContextFireEvent(struct mScriptContext* context, struct mScriptEvent* event) { + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return; + } + struct mScriptInputContext* inputContext = input->value.opaque; + + switch (event->type) { + case mSCRIPT_EV_TYPE_KEY: + if (!_updateKeys(context, (struct mScriptKeyEvent*) event)) { + return; + } + break; + case mSCRIPT_EV_TYPE_NONE: + return; + } + + struct mScriptList args; + mScriptListInit(&args, 1); + struct mScriptValue* value = mScriptListAppend(&args); + value->type = eventTypes[event->type]; + value->refs = mSCRIPT_VALUE_UNREF; + value->flags = 0; + value->value.opaque = event; + event->seq = inputContext->seq; + ++inputContext->seq; + mScriptContextTriggerCallback(context, eventNames[event->type], &args); + mScriptListDeinit(&args); +} + +int mScriptContextGamepadAttach(struct mScriptContext* context, struct mScriptGamepad* pad) { + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + if (inputContext->activeGamepad) { + return -1; + } + inputContext->activeGamepad = pad; + + return 0; +} + +bool mScriptContextGamepadDetach(struct mScriptContext* context, int pad) { + if (pad != 0) { + return false; + } + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + if (!inputContext->activeGamepad) { + return false; + } + inputContext->activeGamepad = NULL; + return true; +} + +struct mScriptGamepad* mScriptContextGamepadLookup(struct mScriptContext* context, int pad) { + if (pad != 0) { + return NULL; + } + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + return inputContext->activeGamepad; +} + +void mScriptGamepadInit(struct mScriptGamepad* gamepad) { + memset(gamepad, 0, sizeof(*gamepad)); + + mScriptListInit(&gamepad->axes, 8); + mScriptListInit(&gamepad->buttons, 1); + mScriptListInit(&gamepad->hats, 1); +} + +void mScriptGamepadDeinit(struct mScriptGamepad* gamepad) { + mScriptListDeinit(&gamepad->axes); + mScriptListDeinit(&gamepad->buttons); + mScriptListDeinit(&gamepad->hats); +} + +void mScriptGamepadSetAxisCount(struct mScriptGamepad* gamepad, unsigned count) { + if (count > UINT8_MAX) { + count = UINT8_MAX; + } + + unsigned oldSize = mScriptListSize(&gamepad->axes); + mScriptListResize(&gamepad->axes, (ssize_t) count - oldSize); + unsigned i; + for (i = oldSize; i < count; ++i) { + *mScriptListGetPointer(&gamepad->axes, i) = mSCRIPT_MAKE_S16(0); + } +} + +void mScriptGamepadSetButtonCount(struct mScriptGamepad* gamepad, unsigned count) { + if (count > UINT16_MAX) { + count = UINT16_MAX; + } + + unsigned oldSize = mScriptListSize(&gamepad->buttons); + mScriptListResize(&gamepad->buttons, (ssize_t) count - oldSize); + unsigned i; + for (i = oldSize; i < count; ++i) { + *mScriptListGetPointer(&gamepad->buttons, i) = mSCRIPT_MAKE_BOOL(false); + } +} + +void mScriptGamepadSetHatCount(struct mScriptGamepad* gamepad, unsigned count) { + if (count > UINT8_MAX) { + count = UINT8_MAX; + } + + unsigned oldSize = mScriptListSize(&gamepad->hats); + mScriptListResize(&gamepad->hats, (ssize_t) count - oldSize); + unsigned i; + for (i = oldSize; i < count; ++i) { + *mScriptListGetPointer(&gamepad->hats, i) = mSCRIPT_MAKE_U8(0); + } +} + +void mScriptGamepadSetAxis(struct mScriptGamepad* gamepad, unsigned id, int16_t value) { + if (id >= mScriptListSize(&gamepad->axes)) { + return; + } + + mScriptListGetPointer(&gamepad->axes, id)->value.s32 = value; +} + +void mScriptGamepadSetButton(struct mScriptGamepad* gamepad, unsigned id, bool down) { + if (id >= mScriptListSize(&gamepad->buttons)) { + return; + } + + mScriptListGetPointer(&gamepad->buttons, id)->value.u32 = down; +} + +void mScriptGamepadSetHat(struct mScriptGamepad* gamepad, unsigned id, int direction) { + if (id >= mScriptListSize(&gamepad->hats)) { + return; + } + + mScriptListGetPointer(&gamepad->hats, id)->value.u32 = direction; +} + +int16_t mScriptGamepadGetAxis(struct mScriptGamepad* gamepad, unsigned id) { + if (id >= mScriptListSize(&gamepad->axes)) { + return 0; + } + + return mScriptListGetPointer(&gamepad->axes, id)->value.s32; +} + +bool mScriptGamepadGetButton(struct mScriptGamepad* gamepad, unsigned id) { + if (id >= mScriptListSize(&gamepad->buttons)) { + return false; + } + + return mScriptListGetPointer(&gamepad->buttons, id)->value.u32; +} + +int mScriptGamepadGetHat(struct mScriptGamepad* gamepad, unsigned id) { + if (id >= mScriptListSize(&gamepad->hats)) { + return mSCRIPT_INPUT_DIR_NONE; + } + + return mScriptListGetPointer(&gamepad->hats, id)->value.u32; +} + +void mScriptContextGetInputTypes(struct Table* types) { + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptKeyEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptMouseMoveEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptMouseButtonEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptGamepadButtonEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptGamepadHatEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptGamepad)); +} diff --git a/src/script/test.h b/src/script/test.h new file mode 100644 index 000000000..f526bce30 --- /dev/null +++ b/src/script/test.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2013-2022 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_SCRIPT_TEST_H +#define M_SCRIPT_TEST_H + +#define LOAD_PROGRAM(PROG) \ + do { \ + struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ + assert_true(lua->load(lua, NULL, vf)); \ + vf->close(vf); \ + } while(0) + +#define TEST_VALUE(TYPE, NAME, VALUE) \ + do { \ + struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \ + struct mScriptValue* global = lua->getGlobal(lua, NAME); \ + assert_non_null(global); \ + assert_true(global->type->equal(global, &val)); \ + mScriptValueDeref(global); \ + } while(0) + +#define TEST_PROGRAM(PROG) \ + LOAD_PROGRAM(PROG); \ + assert_true(lua->run(lua)); \ + +#endif diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 476e24455..85afb627c 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -14,6 +14,9 @@ struct TestA { int32_t i2; int8_t b8; int16_t hUnaligned; + char str[6]; + struct mScriptValue table; + struct mScriptList list; int32_t (*ifn0)(struct TestA*); int32_t (*ifn1)(struct TestA*, int); void (*vfn0)(struct TestA*); @@ -103,6 +106,9 @@ mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S32, i2) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S8, b8) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S16, hUnaligned) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, CHARP, str) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, TABLE, table) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, LIST, list) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn0) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn1) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, icfn0) @@ -187,6 +193,27 @@ M_TEST_DEFINE(testALayout) { assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16); assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1); + member = HashTableLookup(&cls->instanceMembers, "str"); + assert_non_null(member); + assert_string_equal(member->name, "str"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_CHARP); + assert_int_equal(member->offset, &((struct TestA*) 0)->str); + + member = HashTableLookup(&cls->instanceMembers, "table"); + assert_non_null(member); + assert_string_equal(member->name, "table"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_TABLE); + assert_int_equal(member->offset, &((struct TestA*) 0)->table); + + member = HashTableLookup(&cls->instanceMembers, "list"); + assert_non_null(member); + assert_string_equal(member->name, "list"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_LIST); + assert_int_equal(member->offset, &((struct TestA*) 0)->list); + member = HashTableLookup(&cls->instanceMembers, "unknown"); assert_null(member); @@ -280,6 +307,14 @@ M_TEST_DEFINE(testAGet) { .hUnaligned = 4 }; + mScriptListInit(&s.list, 1); + *mScriptListAppend(&s.list) = mSCRIPT_MAKE_S32(5); + + s.table.type = mSCRIPT_TYPE_MS_TABLE; + s.table.type->alloc(&s.table); + + strcpy(s.str, "test"); + struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s); struct mScriptValue val; struct mScriptValue compare; @@ -300,8 +335,38 @@ M_TEST_DEFINE(testAGet) { assert_true(mScriptObjectGet(&sval, "hUnaligned", &val)); assert_true(compare.type->equal(&compare, &val)); + compare = mSCRIPT_MAKE_CHARP("test"); + assert_true(mScriptObjectGet(&sval, "str", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(5); + assert_true(mScriptObjectGet(&sval, "list", &val)); + assert_ptr_equal(val.type, mSCRIPT_TYPE_MS_LIST); + assert_int_equal(mScriptListSize(val.value.list), 1); + assert_true(compare.type->equal(&compare, mScriptListGetPointer(val.value.list, 0))); + + *mScriptListAppend(&s.list) = mSCRIPT_MAKE_S32(6); + compare = mSCRIPT_MAKE_S32(6); + assert_int_equal(mScriptListSize(val.value.list), 2); + assert_true(compare.type->equal(&compare, mScriptListGetPointer(val.value.list, 1))); + + struct mScriptValue* ival = &val; + assert_true(mScriptObjectGet(&sval, "table", &val)); + if (val.type->base == mSCRIPT_TYPE_WRAPPER) { + ival = mScriptValueUnwrap(&val); + } + assert_ptr_equal(ival->type, mSCRIPT_TYPE_MS_TABLE); + assert_int_equal(mScriptTableSize(ival), 0); + compare = mSCRIPT_MAKE_S32(7); + mScriptTableInsert(&s.table, &compare, &compare); + assert_int_equal(mScriptTableSize(&s.table), 1); + assert_int_equal(mScriptTableSize(ival), 1); + assert_false(mScriptObjectGet(&sval, "unknown", &val)); + mScriptListDeinit(&s.list); + mSCRIPT_TYPE_MS_TABLE->free(&s.table); + assert_true(cls->init); mScriptClassDeinit(cls); assert_false(cls->init); diff --git a/src/script/test/context.c b/src/script/test/context.c new file mode 100644 index 000000000..178dc25a7 --- /dev/null +++ b/src/script/test/context.c @@ -0,0 +1,103 @@ +/* Copyright (c) 2013-2023 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 +#include +#include + +M_TEST_DEFINE(weakrefBasic) { + struct mScriptContext context; + mScriptContextInit(&context); + + struct mScriptValue weakref = mSCRIPT_VAL(WEAKREF, 1); + struct mScriptValue fakeVal = mSCRIPT_S32(0x7E57CA5E); + struct mScriptValue* val; + + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_int_equal(context.nextWeakref, 1); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + assert_int_equal(mScriptContextSetWeakref(&context, &fakeVal), 1); + assert_int_equal(context.nextWeakref, 2); + assert_int_equal(TableSize(&context.weakrefs), 1); + val = mScriptContextAccessWeakref(&context, &weakref); + assert_non_null(val); + assert_int_equal(val->value.u32, 0x7E57CA5E); + + mScriptContextClearWeakref(&context, 1); + + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_int_equal(context.nextWeakref, 2); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(drainPool) { + struct mScriptContext context; + mScriptContextInit(&context); + + assert_int_equal(mScriptListSize(&context.refPool), 0); + + struct mScriptValue fakeVal = mSCRIPT_CHARP("foo"); + fakeVal.refs = 2; + + mScriptContextFillPool(&context, &fakeVal); + assert_int_equal(mScriptListSize(&context.refPool), 1); + assert_int_equal(fakeVal.refs, 2); + + mScriptContextDrainPool(&context); + assert_int_equal(mScriptListSize(&context.refPool), 0); + assert_int_equal(fakeVal.refs, 1); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(disownWeakref) { + struct mScriptContext context; + mScriptContextInit(&context); + + struct mScriptValue weakref = mSCRIPT_VAL(WEAKREF, 1); + struct mScriptValue fakeVal = mSCRIPT_S32(0x7E57CA5E); + struct mScriptValue* val; + + assert_int_equal(mScriptListSize(&context.refPool), 0); + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_int_equal(context.nextWeakref, 1); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + assert_int_equal(mScriptContextSetWeakref(&context, &fakeVal), 1); + assert_int_equal(TableSize(&context.weakrefs), 1); + assert_int_equal(context.nextWeakref, 2); + val = mScriptContextAccessWeakref(&context, &weakref); + assert_non_null(val); + assert_int_equal(val->value.u32, 0x7E57CA5E); + + mScriptContextDisownWeakref(&context, 1); + assert_int_equal(mScriptListSize(&context.refPool), 1); + assert_int_equal(TableSize(&context.weakrefs), 1); + val = mScriptContextAccessWeakref(&context, &weakref); + assert_non_null(val); + assert_int_equal(val->value.u32, 0x7E57CA5E); + + mScriptContextDrainPool(&context); + assert_int_equal(mScriptListSize(&context.refPool), 0); + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + mScriptContextDeinit(&context); +} + +M_TEST_SUITE_DEFINE(mScript, + cmocka_unit_test(weakrefBasic), + cmocka_unit_test(drainPool), + cmocka_unit_test(disownWeakref), +) diff --git a/src/script/test/input.c b/src/script/test/input.c new file mode 100644 index 000000000..de3ae0e0c --- /dev/null +++ b/src/script/test/input.c @@ -0,0 +1,157 @@ +/* Copyright (c) 2013-2022 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 +#include +#include +#include + +#include "script/test.h" + +#define SETUP_LUA \ + struct mScriptContext context; \ + mScriptContextInit(&context); \ + struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA); \ + mScriptContextAttachStdlib(&context); \ + mScriptContextAttachInput(&context) + +M_TEST_SUITE_SETUP(mScriptInput) { + if (mSCRIPT_ENGINE_LUA->init) { + mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_SUITE_TEARDOWN(mScriptInput) { + if (mSCRIPT_ENGINE_LUA->deinit) { + mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_DEFINE(members) { + SETUP_LUA; + + TEST_PROGRAM("assert(input)"); + TEST_PROGRAM("assert(input.seq == 0)"); + TEST_PROGRAM("assert(input.isKeyActive)"); + + mScriptContextDeinit(&context); +} +M_TEST_DEFINE(seq) { + SETUP_LUA; + + TEST_PROGRAM("assert(input.seq == 0)"); + + TEST_PROGRAM( + "seq = nil\n" + "function cb(ev)\n" + " seq = ev.seq\n" + "end\n" + "id = callbacks:add('key', cb)\n" + ); + + struct mScriptKeyEvent keyEvent = { + .d = { .type = mSCRIPT_EV_TYPE_KEY }, + .state = mSCRIPT_INPUT_STATE_DOWN, + }; + + mScriptContextFireEvent(&context, &keyEvent.d); + TEST_PROGRAM("assert(input.seq == 1)"); + TEST_PROGRAM("assert(seq == 0)"); + + mScriptContextFireEvent(&context, &keyEvent.d); + TEST_PROGRAM("assert(input.seq == 2)"); + TEST_PROGRAM("assert(seq == 1)"); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(fireKey) { + SETUP_LUA; + + TEST_PROGRAM("assert(not input:isKeyActive('a'))"); + + TEST_PROGRAM( + "activeKey = false\n" + "state = nil\n" + "function cb(ev)\n" + " assert(ev.type == C.EV_TYPE.KEY)\n" + " activeKey = string.char(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 = 'a' + }; + mScriptContextFireEvent(&context, &keyEvent.d); + + TEST_PROGRAM("assert(input:isKeyActive('a'))"); + TEST_PROGRAM("assert(activeKey == 'a')"); + TEST_PROGRAM("assert(state == C.INPUT_STATE.DOWN)"); + + keyEvent.state = mSCRIPT_INPUT_STATE_UP; + mScriptContextFireEvent(&context, &keyEvent.d); + + TEST_PROGRAM("assert(not input:isKeyActive('a'))"); + TEST_PROGRAM("assert(state == C.INPUT_STATE.UP)"); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(gamepadExport) { + SETUP_LUA; + + struct mScriptGamepad m_gamepad; + mScriptGamepadInit(&m_gamepad); + + TEST_PROGRAM("assert(not input.activeGamepad)"); + assert_int_equal(mScriptContextGamepadAttach(&context, &m_gamepad), 0); + TEST_PROGRAM("assert(input.activeGamepad)"); + + TEST_PROGRAM("assert(#input.activeGamepad.axes == 0)"); + TEST_PROGRAM("assert(#input.activeGamepad.buttons == 0)"); + TEST_PROGRAM("assert(#input.activeGamepad.hats == 0)"); + + mScriptGamepadSetAxisCount(&m_gamepad, 1); + TEST_PROGRAM("assert(#input.activeGamepad.axes == 1)"); + TEST_PROGRAM("assert(input.activeGamepad.axes[1] == 0)"); + mScriptGamepadSetAxis(&m_gamepad, 0, 123); + TEST_PROGRAM("assert(input.activeGamepad.axes[1] == 123)"); + + mScriptGamepadSetButtonCount(&m_gamepad, 1); + TEST_PROGRAM("assert(#input.activeGamepad.buttons == 1)"); + TEST_PROGRAM("assert(input.activeGamepad.buttons[1] == false)"); + mScriptGamepadSetButton(&m_gamepad, 0, true); + TEST_PROGRAM("assert(input.activeGamepad.buttons[1] == true)"); + + mScriptGamepadSetHatCount(&m_gamepad, 1); + TEST_PROGRAM("assert(#input.activeGamepad.hats == 1)"); + TEST_PROGRAM("assert(input.activeGamepad.hats[1] == C.INPUT_DIR.NONE)"); + mScriptGamepadSetHat(&m_gamepad, 0, mSCRIPT_INPUT_DIR_NORTHWEST); + TEST_PROGRAM("assert(input.activeGamepad.hats[1] == C.INPUT_DIR.NORTHWEST)"); + + mScriptContextGamepadDetach(&context, 0); + TEST_PROGRAM("assert(not input.activeGamepad)"); + + mScriptGamepadDeinit(&m_gamepad); + + mScriptContextDeinit(&context); +} + +M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptInput, + cmocka_unit_test(members), + cmocka_unit_test(seq), + cmocka_unit_test(fireKey), + cmocka_unit_test(gamepadExport), +) diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 2cc7fcbff..5731b0705 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -8,22 +8,13 @@ #include #include +#include "script/test.h" + #define SETUP_LUA \ struct mScriptContext context; \ mScriptContextInit(&context); \ struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA) -#define LOAD_PROGRAM(PROG) \ - do { \ - struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ - assert_true(lua->load(lua, NULL, vf)); \ - vf->close(vf); \ - } while(0) - -#define TEST_PROGRAM(PROG) \ - LOAD_PROGRAM(PROG); \ - assert_true(lua->run(lua)); \ - struct Test { int32_t i; int32_t (*ifn0)(struct Test*); @@ -31,6 +22,7 @@ struct Test { void (*vfn0)(struct Test*); void (*vfn1)(struct Test*, int); int32_t (*icfn0)(const struct Test*); + struct Test* next; }; static int identityInt(int in) { @@ -92,6 +84,7 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(Test, v1, testV1, 1, S32, b); mSCRIPT_DEFINE_STRUCT(Test) mSCRIPT_DEFINE_STRUCT_MEMBER(Test, S32, i) + mSCRIPT_DEFINE_STRUCT_MEMBER(Test, PS(Test), next) mSCRIPT_DEFINE_STRUCT_METHOD(Test, ifn0) mSCRIPT_DEFINE_STRUCT_METHOD(Test, ifn1) mSCRIPT_DEFINE_STRUCT_METHOD(Test, icfn0) @@ -380,6 +373,33 @@ M_TEST_DEFINE(callCFunc) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(globalNull) { + SETUP_LUA; + + struct Test s = {}; + struct mScriptValue* val; + struct mScriptValue a; + + LOAD_PROGRAM("assert(a)"); + + a = mSCRIPT_MAKE_CHARP("hello"); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_true(lua->run(lua)); + + a = mSCRIPT_MAKE_CHARP(NULL); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_false(lua->run(lua)); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_true(lua->run(lua)); + + a = mSCRIPT_MAKE_S(Test, NULL); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_false(lua->run(lua)); + + mScriptContextDeinit(&context); +} M_TEST_DEFINE(globalStructFieldGet) { SETUP_LUA; @@ -708,6 +728,42 @@ M_TEST_DEFINE(callList) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(linkedList) { + SETUP_LUA; + + struct Test first = { + .i = 1 + }; + struct Test second = { + .i = 2 + }; + struct mScriptValue a = mSCRIPT_MAKE_S(Test, &first); + + assert_true(lua->setGlobal(lua, "l", &a)); + TEST_PROGRAM("assert(l)"); + TEST_PROGRAM("assert(l.i == 1)"); + TEST_PROGRAM("assert(not l.next)"); + + first.next = &second; + TEST_PROGRAM("assert(l)"); + TEST_PROGRAM("assert(l.i == 1)"); + TEST_PROGRAM("assert(l.next)"); + TEST_PROGRAM("assert(l.next.i == 2)"); + TEST_PROGRAM("assert(not l.next.next)"); + + TEST_PROGRAM( + "n = l.next\n" + "function readN()\n" + " assert(n)\n" + " assert(n.i or not n.i)\n" + "end\n" + "assert(pcall(readN))\n"); + // The weakref stored in `n` gets pruned between executions to avoid stale pointers + TEST_PROGRAM("assert(not pcall(readN))"); + + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(create), cmocka_unit_test(loadGood), @@ -718,6 +774,7 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(rootScope), cmocka_unit_test(callLuaFunc), cmocka_unit_test(callCFunc), + cmocka_unit_test(globalNull), cmocka_unit_test(globalStructFieldGet), cmocka_unit_test(globalStructFieldSet), cmocka_unit_test(globalStructMethods), @@ -725,4 +782,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(tableLookup), cmocka_unit_test(tableIterate), cmocka_unit_test(callList), + cmocka_unit_test(linkedList) ) diff --git a/src/script/test/stdlib.c b/src/script/test/stdlib.c index d4c57605b..82a4c425b 100644 --- a/src/script/test/stdlib.c +++ b/src/script/test/stdlib.c @@ -10,32 +10,14 @@ #include #include +#include "script/test.h" + #define SETUP_LUA \ struct mScriptContext context; \ mScriptContextInit(&context); \ struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA); \ mScriptContextAttachStdlib(&context) -#define LOAD_PROGRAM(PROG) \ - do { \ - struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ - assert_true(lua->load(lua, NULL, vf)); \ - vf->close(vf); \ - } while(0) - -#define TEST_PROGRAM(PROG) \ - LOAD_PROGRAM(PROG); \ - assert_true(lua->run(lua)); \ - -#define TEST_VALUE(TYPE, NAME, VALUE) \ - do { \ - struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \ - struct mScriptValue* global = lua->getGlobal(lua, NAME); \ - assert_non_null(global); \ - assert_true(global->type->equal(global, &val)); \ - mScriptValueDeref(global); \ - } while(0) - M_TEST_SUITE_SETUP(mScriptStdlib) { if (mSCRIPT_ENGINE_LUA->init) { mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); @@ -100,15 +82,15 @@ M_TEST_DEFINE(callbacks) { TEST_VALUE(S32, "val", 0); - mScriptContextTriggerCallback(&context, "test"); + mScriptContextTriggerCallback(&context, "test", NULL); TEST_VALUE(S32, "val", 1); - mScriptContextTriggerCallback(&context, "test"); + mScriptContextTriggerCallback(&context, "test", NULL); TEST_VALUE(S32, "val", 2); TEST_PROGRAM("callbacks:remove(id)"); - mScriptContextTriggerCallback(&context, "test"); + mScriptContextTriggerCallback(&context, "test", NULL); TEST_VALUE(S32, "val", 2); mScriptContextDeinit(&context); diff --git a/src/script/test/types.c b/src/script/test/types.c index 6ad38563a..ae1df5ba0 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -76,6 +76,14 @@ static int isSequential(struct mScriptList* list) { return true; } +static bool isNullCharp(const char* arg) { + return !arg; +} + +static bool isNullStruct(struct Test* arg) { + return !arg; +} + mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32, ignored); mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, in); @@ -86,6 +94,8 @@ mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b); mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, a, S32, b); mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP, str); mSCRIPT_BIND_FUNCTION(boundIsSequential, S32, isSequential, 1, LIST, list); +mSCRIPT_BIND_FUNCTION(boundIsNullCharp, BOOL, isNullCharp, 1, CHARP, arg); +mSCRIPT_BIND_FUNCTION(boundIsNullStruct, BOOL, isNullStruct, 1, S(Test), arg); M_TEST_DEFINE(voidArgs) { struct mScriptFrame frame; @@ -1261,6 +1271,43 @@ M_TEST_DEFINE(invokeList) { mScriptListDeinit(&list); } +M_TEST_DEFINE(nullString) { + struct mScriptFrame frame; + bool res; + mScriptFrameInit(&frame); + + mSCRIPT_PUSH(&frame.arguments, CHARP, "hi"); + assert_true(mScriptInvoke(&boundIsNullCharp, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_false(res); + + mSCRIPT_PUSH(&frame.arguments, CHARP, NULL); + assert_true(mScriptInvoke(&boundIsNullCharp, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_true(res); + + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(nullStruct) { + struct mScriptFrame frame; + struct Test v = {}; + bool res; + mScriptFrameInit(&frame); + + mSCRIPT_PUSH(&frame.arguments, S(Test), &v); + assert_true(mScriptInvoke(&boundIsNullStruct, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_false(res); + + mSCRIPT_PUSH(&frame.arguments, S(Test), NULL); + assert_true(mScriptInvoke(&boundIsNullStruct, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_true(res); + + mScriptFrameDeinit(&frame); +} + M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(voidArgs), cmocka_unit_test(voidFunc), @@ -1295,4 +1342,6 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(stringIsHello), cmocka_unit_test(stringIsNotHello), cmocka_unit_test(invokeList), + cmocka_unit_test(nullString), + cmocka_unit_test(nullStruct), ) diff --git a/src/script/types.c b/src/script/types.c index b63fed4a5..0237cd4cb 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include #include @@ -853,6 +854,21 @@ const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* va return NULL; } +void mScriptValueFollowPointer(struct mScriptValue* ptr, struct mScriptValue* out) { + if (ptr->type->base != mSCRIPT_TYPE_OPAQUE || !ptr->type->details.type) { + return; + } + + out->value.opaque = *(void**) ptr->value.opaque; + if (out->value.opaque) { + out->type = ptr->type->details.type; + } else { + out->type = mSCRIPT_TYPE_MS_VOID; + } + out->refs = mSCRIPT_VALUE_UNREF; + out->flags = 0; +} + struct mScriptValue* mScriptStringCreateEmpty(size_t size) { struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); struct mScriptString* internal = val->value.opaque; @@ -1178,6 +1194,21 @@ static bool _accessRawMember(struct mScriptClassMember* member, void* raw, bool val->type = mSCRIPT_TYPE_MS_WRAPPER; val->value.table = raw; break; + case mSCRIPT_TYPE_STRING: + if (member->type == mSCRIPT_TYPE_MS_CHARP) { + val->refs = mSCRIPT_VALUE_UNREF; + val->flags = 0; + val->type = mSCRIPT_TYPE_MS_CHARP; + val->value.opaque = raw; + break; + } + return false; + case mSCRIPT_TYPE_LIST: + val->refs = mSCRIPT_VALUE_UNREF; + val->flags = 0; + val->type = mSCRIPT_TYPE_MS_LIST; + val->value.list = raw; + break; case mSCRIPT_TYPE_FUNCTION: val->refs = mSCRIPT_VALUE_UNREF; val->flags = 0; @@ -1494,3 +1525,56 @@ bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList } return true; } + +static void addTypesFromTuple(struct Table* types, const struct mScriptTypeTuple* tuple) { + size_t i; + for (i = 0; i < tuple->count; ++i) { + mScriptTypeAdd(types, tuple->entries[i]); + } +} + +static void addTypesFromTable(struct Table* types, struct Table* table) { + struct TableIterator iter; + if (!HashTableIteratorStart(table, &iter)) { + return; + } + do { + struct mScriptClassMember* member = HashTableIteratorGetValue(table, &iter); + mScriptTypeAdd(types, member->type); + } while(HashTableIteratorNext(table, &iter)); +} + +void mScriptTypeAdd(struct Table* types, const struct mScriptType* type) { + if (HashTableLookup(types, type->name) || type->isConst) { + return; + } + HashTableInsert(types, type->name, (struct mScriptType*) type); + switch (type->base) { + case mSCRIPT_TYPE_FUNCTION: + addTypesFromTuple(types, &type->details.function.parameters); + addTypesFromTuple(types, &type->details.function.returnType); + break; + case mSCRIPT_TYPE_OBJECT: + mScriptClassInit(type->details.cls); + if (type->details.cls->parent) { + mScriptTypeAdd(types, type->details.cls->parent); + } + addTypesFromTable(types, &type->details.cls->instanceMembers); + break; + case mSCRIPT_TYPE_OPAQUE: + case mSCRIPT_TYPE_WRAPPER: + if (type->details.type) { + mScriptTypeAdd(types, type->details.type); + } + case mSCRIPT_TYPE_VOID: + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_FLOAT: + case mSCRIPT_TYPE_STRING: + case mSCRIPT_TYPE_LIST: + case mSCRIPT_TYPE_TABLE: + case mSCRIPT_TYPE_WEAKREF: + // No subtypes + break; + } +} diff --git a/src/sm83/debugger/memory-debugger.c b/src/sm83/debugger/memory-debugger.c index e0a48553f..142ab92c7 100644 --- a/src/sm83/debugger/memory-debugger.c +++ b/src/sm83/debugger/memory-debugger.c @@ -47,7 +47,7 @@ static bool _checkWatchpoints(struct SM83Debugger* debugger, uint16_t address, s size_t i; for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) { watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i); - if (watchpoint->address == address && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address)) && watchpoint->type & type) { + if (watchpoint->type & type && address >= watchpoint->minAddress && address < watchpoint->maxAddress && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address))) { if (watchpoint->condition) { int32_t value; int segment; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 8d86f7b60..9246b20c4 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,24 +1,29 @@ include(ExportDirectory) -set(SOURCE_FILES +set(BASE_SOURCE_FILES circle-buffer.c configuration.c - convolve.c crc32.c - elf-read.c - export.c formatting.c gbk-table.c hash.c + string.c + table.c + vector.c + vfs.c) + +set(SOURCE_FILES + ${BASE_SOURCE_FILES} + convolve.c + elf-read.c + export.c patch.c patch-fast.c patch-ips.c patch-ups.c png-io.c ring-fifo.c - string.c - table.c - text-codec.c - vfs.c) + sfo.c + text-codec.c) set(GUI_FILES gui.c @@ -28,6 +33,7 @@ set(GUI_FILES gui/menu.c) set(TEST_FILES + test/sfo.c test/string-parser.c test/string-utf8.c test/table.c @@ -43,6 +49,7 @@ source_group("Utilities" FILES ${SOURCE_FILES}) source_group("GUI source" FILES ${GUI_FILES}) source_group("Utilities tests" FILES ${TEST_FILES}) +export_directory(UTIL_BASE BASE_SOURCE_FILES) export_directory(UTIL SOURCE_FILES) export_directory(GUI GUI_FILES) export_directory(UTIL_TEST TEST_FILES) diff --git a/src/util/sfo.c b/src/util/sfo.c new file mode 100644 index 000000000..90142897b --- /dev/null +++ b/src/util/sfo.c @@ -0,0 +1,228 @@ +/* Copyright (c) 2013-2022 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/. */ + +/* This code is loosely based on vita-mksfoex.c from the vitasdk + * Copyright (c) 2015 Sergi Granell + * Copyright (c) 2015 Danielle Church + * Used under the MIT license + * + * Which itself is based on mksfoex.c from the pspsdk + * Copyright (c) 2005 adresd + * Copyright (c) 2005 Marcus R. Brown + * Copyright (c) 2005 James Forshaw + * Copyright (c) 2005 John Kelley + * Copyright (c) 2005 Jesper Svennevid + * Used under the BSD 3-clause license +*/ + +#include +#include +#include + +#define PSF_MAGIC 0x46535000 +#define PSF_VERSION 0x00000101 + +struct SfoHeader { + uint32_t magic; + uint32_t version; + uint32_t keyofs; + uint32_t valofs; + uint32_t count; +}; + +struct SfoEntry { + uint16_t nameofs; + uint8_t alignment; + uint8_t type; + uint32_t valsize; + uint32_t totalsize; + uint32_t dataofs; +}; + +enum PSFType { + PSF_TYPE_BIN = 0, + PSF_TYPE_STR = 2, + PSF_TYPE_U32 = 4, +}; + +struct SfoEntryContainer { + const char* name; + enum PSFType type; + union { + const char* str; + uint32_t u32; + } data; + uint32_t size; +}; + +static struct SfoEntryContainer sfoDefaults[] = { + { "APP_VER", PSF_TYPE_STR, { .str = "00.00" } }, + { "ATTRIBUTE", PSF_TYPE_U32, { .u32 = 0x8000 } }, + { "ATTRIBUTE2", PSF_TYPE_U32, { .u32 = 0 } }, + { "ATTRIBUTE_MINOR", PSF_TYPE_U32, { .u32 = 0x10 } }, + { "BOOT_FILE", PSF_TYPE_STR, { .str = ""}, 0x20 }, + { "CATEGORY", PSF_TYPE_STR, { .str = "gd" } }, + { "CONTENT_ID", PSF_TYPE_STR, { .str = "" }, 0x30 }, + { "EBOOT_APP_MEMSIZE", PSF_TYPE_U32, { .u32 = 0 } }, + { "EBOOT_ATTRIBUTE", PSF_TYPE_U32, { .u32 = 0 } }, + { "EBOOT_PHY_MEMSIZE", PSF_TYPE_U32, { .u32 = 0 } }, + { "LAREA_TYPE", PSF_TYPE_U32, { .u32 = 0 } }, + { "NP_COMMUNICATION_ID", PSF_TYPE_STR, { .str = "" }, 0x10 }, + { "PARENTAL_LEVEL", PSF_TYPE_U32, { .u32 = 0 } }, + { "PSP2_DISP_VER", PSF_TYPE_STR, { .str = "00.000" } }, + { "PSP2_SYSTEM_VER", PSF_TYPE_U32, { .u32 = 0 } }, + { "STITLE", PSF_TYPE_STR, { .str = "Homebrew" }, 52 }, + { "TITLE", PSF_TYPE_STR, { .str = "Homebrew" }, 0x80 }, + { "TITLE_ID", PSF_TYPE_STR, { .str = "ABCD99999" } }, + { "VERSION", PSF_TYPE_STR, { .str = "00.00" } }, +}; + +bool SfoAddStrValue(struct Table* sfo, const char* name, const char* value) { + struct SfoEntryContainer* entry = HashTableLookup(sfo, name); + if (!entry) { + entry = calloc(1, sizeof(*entry)); + if (!entry) { + return false; + } + entry->name = name; + HashTableInsert(sfo, name, entry); + } + entry->type = PSF_TYPE_STR; + entry->data.str = value; + return true; +} + +bool SfoAddU32Value(struct Table* sfo, const char* name, uint32_t value) { + struct SfoEntryContainer* entry = HashTableLookup(sfo, name); + if (!entry) { + entry = calloc(1, sizeof(*entry)); + if (!entry) { + return false; + } + entry->name = name; + HashTableInsert(sfo, name, entry); + } + entry->type = PSF_TYPE_U32; + entry->data.u32 = value; + return true; +} + +bool SfoSetTitle(struct Table* sfo, const char* title) { + return SfoAddStrValue(sfo, "TITLE", title) && SfoAddStrValue(sfo, "STITLE", title); +} + +void SfoInit(struct Table* sfo) { + HashTableInit(sfo, 32, free); + + size_t i; + for (i = 0; i < sizeof(sfoDefaults) / sizeof(sfoDefaults[0]); ++i) { + struct SfoEntryContainer* entry = calloc(1, sizeof(*entry)); + memcpy(entry, &sfoDefaults[i], sizeof(*entry)); + HashTableInsert(sfo, entry->name, entry); + } +} + +#define ALIGN4(X) (((X) + 3) & ~3) + +static int _sfoSort(const void* a, const void* b) { + const struct SfoEntryContainer* ea = a; + const struct SfoEntryContainer* eb = b; + return strcmp(ea->name, eb->name); +} + +bool SfoWrite(struct Table* sfo, struct VFile* vf) { + struct SfoHeader header; + size_t count = HashTableSize(sfo); + STORE_32LE(PSF_MAGIC, 0, &header.magic); + STORE_32LE(PSF_VERSION, 0, &header.version); + STORE_32LE(count, 0, &header.count); + + struct TableIterator iter; + if (!TableIteratorStart(sfo, &iter)) { + return false; + } + + struct SfoEntryContainer* sortedEntries = calloc(count, sizeof(struct SfoEntryContainer)); + + uint32_t keysSize = 0; + uint32_t dataSize = 0; + size_t i = 0; + do { + memcpy(&sortedEntries[i], TableIteratorGetValue(sfo, &iter), sizeof(struct SfoEntryContainer)); + keysSize += strlen(sortedEntries[i].name) + 1; + if (!sortedEntries[i].size) { + switch (sortedEntries[i].type) { + case PSF_TYPE_STR: + sortedEntries[i].size = strlen(sortedEntries[i].data.str) + 1; + break; + case PSF_TYPE_U32: + sortedEntries[i].size = 4; + break; + } + } + dataSize += ALIGN4(sortedEntries[i].size); + ++i; + } while (TableIteratorNext(sfo, &iter)); + + keysSize = ALIGN4(keysSize); + + qsort(sortedEntries, count, sizeof(struct SfoEntryContainer), _sfoSort); + + uint32_t keysOffset = 0; + uint32_t dataOffset = 0; + + char* keys = calloc(1, keysSize); + char* data = calloc(1, dataSize); + + struct SfoEntry* entries = calloc(count, sizeof(struct SfoEntry)); + for (i = 0; i < count; ++i) { + STORE_16LE(keysOffset, 0, &entries[i].nameofs); + STORE_32LE(dataOffset, 0, &entries[i].dataofs); + entries[i].alignment = 4; + entries[i].type = sortedEntries[i].type; + + strcpy(&keys[keysOffset], sortedEntries[i].name); + keysOffset += strlen(sortedEntries[i].name) + 1; + + if (sortedEntries[i].type == PSF_TYPE_U32) { + STORE_32LE(4, 0, &entries[i].valsize); + STORE_32LE(4, 0, &entries[i].totalsize); + STORE_32LE(sortedEntries[i].data.u32, dataOffset, data); + dataOffset += 4; + } else { + STORE_32LE(ALIGN4(sortedEntries[i].size), 0, &entries[i].totalsize); + + memset(&data[dataOffset], 0, ALIGN4(sortedEntries[i].size)); + if (sortedEntries[i].data.str) { + STORE_32LE(strlen(sortedEntries[i].data.str) + 1, 0, &entries[i].valsize); + strncpy(&data[dataOffset], sortedEntries[i].data.str, sortedEntries[i].size); + } else { + STORE_32LE(sortedEntries[i].size, 0, &entries[i].valsize); + } + dataOffset += ALIGN4(sortedEntries[i].size); + } + } + + if (keysSize != ALIGN4(keysOffset) || dataSize != dataOffset) { + abort(); + } + + free(sortedEntries); + + STORE_32LE(count * sizeof(struct SfoEntry) + sizeof(header), 0, &header.keyofs); + STORE_32LE(count * sizeof(struct SfoEntry) + sizeof(header) + keysSize, 0, &header.valofs); + + vf->write(vf, &header, sizeof(header)); + vf->write(vf, entries, sizeof(entries[0]) * count); + vf->write(vf, keys, keysSize); + vf->write(vf, data, dataSize); + + free(entries); + free(keys); + free(data); + + return true; +} diff --git a/src/util/string.c b/src/util/string.c index 9e007dc2d..1a2ffe7a4 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -5,12 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include -#include - #include -DEFINE_VECTOR(StringList, char*); - #ifndef HAVE_STRNDUP char* strndup(const char* start, size_t len) { // This is suboptimal, but anything recent should have strndup diff --git a/src/util/test/sfo.c b/src/util/test/sfo.c new file mode 100644 index 000000000..54f923ae9 --- /dev/null +++ b/src/util/test/sfo.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2013-2022 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 +#include + +const char defaultMksfoex[] = { + 0x00, 0x50, 0x53, 0x46, 0x01, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, + 0x30, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, + 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x12, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x04, 0x04, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x04, 0x02, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x40, 0x00, 0x04, 0x02, + 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x4b, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x04, 0x04, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x04, 0x04, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0xad, 0x00, 0x04, 0x02, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x8c, 0x00, 0x00, 0x00, 0xbb, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x04, 0x02, + 0x09, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0xd2, 0x00, 0x04, 0x02, 0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xcc, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x04, 0x02, 0x0a, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0xe1, 0x00, 0x04, 0x02, + 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, + 0x41, 0x50, 0x50, 0x5f, 0x56, 0x45, 0x52, 0x00, 0x41, 0x54, 0x54, 0x52, + 0x49, 0x42, 0x55, 0x54, 0x45, 0x00, 0x41, 0x54, 0x54, 0x52, 0x49, 0x42, + 0x55, 0x54, 0x45, 0x32, 0x00, 0x41, 0x54, 0x54, 0x52, 0x49, 0x42, 0x55, + 0x54, 0x45, 0x5f, 0x4d, 0x49, 0x4e, 0x4f, 0x52, 0x00, 0x42, 0x4f, 0x4f, + 0x54, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x00, 0x43, 0x41, 0x54, 0x45, 0x47, + 0x4f, 0x52, 0x59, 0x00, 0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, + 0x49, 0x44, 0x00, 0x45, 0x42, 0x4f, 0x4f, 0x54, 0x5f, 0x41, 0x50, 0x50, + 0x5f, 0x4d, 0x45, 0x4d, 0x53, 0x49, 0x5a, 0x45, 0x00, 0x45, 0x42, 0x4f, + 0x4f, 0x54, 0x5f, 0x41, 0x54, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, + 0x00, 0x45, 0x42, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x48, 0x59, 0x5f, 0x4d, + 0x45, 0x4d, 0x53, 0x49, 0x5a, 0x45, 0x00, 0x4c, 0x41, 0x52, 0x45, 0x41, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x00, 0x4e, 0x50, 0x5f, 0x43, 0x4f, 0x4d, + 0x4d, 0x55, 0x4e, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, + 0x44, 0x00, 0x50, 0x41, 0x52, 0x45, 0x4e, 0x54, 0x41, 0x4c, 0x5f, 0x4c, + 0x45, 0x56, 0x45, 0x4c, 0x00, 0x50, 0x53, 0x50, 0x32, 0x5f, 0x44, 0x49, + 0x53, 0x50, 0x5f, 0x56, 0x45, 0x52, 0x00, 0x50, 0x53, 0x50, 0x32, 0x5f, + 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x56, 0x45, 0x52, 0x00, 0x53, + 0x54, 0x49, 0x54, 0x4c, 0x45, 0x00, 0x54, 0x49, 0x54, 0x4c, 0x45, 0x00, + 0x54, 0x49, 0x54, 0x4c, 0x45, 0x5f, 0x49, 0x44, 0x00, 0x56, 0x45, 0x52, + 0x53, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x2e, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x6f, 0x6d, 0x65, 0x62, 0x72, 0x65, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x6f, 0x6d, 0x65, + 0x62, 0x72, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x42, 0x43, 0x44, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x00, 0x00, 0x00, 0x30, 0x30, 0x2e, 0x30, 0x30, 0x00, 0x00, 0x00 +}; + +M_TEST_DEFINE(defaultSfo) { + struct VFile* vf = VFileMemChunk(NULL, 0); + struct Table sfo; + SfoInit(&sfo); + SfoWrite(&sfo, vf); + SfoDeinit(&sfo); + + assert_int_equal(vf->size(vf), sizeof(defaultMksfoex)); + void* buffer = vf->map(vf, sizeof(defaultMksfoex), MAP_READ); + assert_memory_equal(defaultMksfoex, buffer, sizeof(defaultMksfoex)); + + vf->unmap(vf, buffer, sizeof(defaultMksfoex)); + vf->close(vf); +} + +M_TEST_SUITE_DEFINE(Sfo, + cmocka_unit_test(defaultSfo), +) diff --git a/src/util/vector.c b/src/util/vector.c new file mode 100644 index 000000000..28750a1b4 --- /dev/null +++ b/src/util/vector.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2013-2023 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 + +DEFINE_VECTOR(IntList, int); +DEFINE_VECTOR(SInt8List, int8_t); +DEFINE_VECTOR(SInt16List, int16_t); +DEFINE_VECTOR(SInt32List, int32_t); +DEFINE_VECTOR(SIntPtrList, intptr_t); +DEFINE_VECTOR(UInt8List, uint8_t); +DEFINE_VECTOR(UInt16List, uint16_t); +DEFINE_VECTOR(UInt32List, uint32_t); +DEFINE_VECTOR(UIntPtrList, uintptr_t); +DEFINE_VECTOR(StringList, char*); diff --git a/src/util/vfs/vfs-zip.c b/src/util/vfs/vfs-zip.c index 2d353c566..2f8234b94 100644 --- a/src/util/vfs/vfs-zip.c +++ b/src/util/vfs/vfs-zip.c @@ -593,7 +593,11 @@ ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) { ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) { struct VFileZip* vfz = (struct VFileZip*) vf; - return zipWriteInFileInZip(vfz->z, buffer, size); + int res = zipWriteInFileInZip(vfz->z, buffer, size); + if (res != ZIP_OK) { + return res; + } + return size; } void* _vfzMap(struct VFile* vf, size_t size, int flags) { diff --git a/version.cmake b/version.cmake index e66142fbd..01509a37b 100644 --- a/version.cmake +++ b/version.cmake @@ -2,9 +2,9 @@ if(NOT PROJECT_NAME) set(PROJECT_NAME "mGBA") endif() set(LIB_VERSION_MAJOR 0) -set(LIB_VERSION_MINOR 10) +set(LIB_VERSION_MINOR 11) set(LIB_VERSION_PATCH 0) -set(LIB_VERSION_ABI 0.10) +set(LIB_VERSION_ABI 0.11) set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}) set(SUMMARY "${PROJECT_NAME} Game Boy Advance Emulator")