diff --git a/CHANGES b/CHANGES index dbd23e5d6..f67401895 100644 --- a/CHANGES +++ b/CHANGES @@ -8,179 +8,199 @@ Misc: - DS GX: Clean up and unify texture mapping 0.7.0: (Future) +Bugfixes: + - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) Misc: - GBA Timer: Use global cycles for timers + - GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722) + - All: Make FIXED_ROM_BUFFER an option instead of 3DS-only 0.6.0: (Future) Features: - - GBA: Support printing debug strings from inside a game - - GBA: Better cheat type autodetection - - GB: Tile viewer + - Library view - Sprite viewer - Debugging console - Improved memory viewer - - GB: LR35902/GB-Z80 disassembler - - Configuration of gamepad hats - - Qt: Spanish translation (by Kevin López) - - Add option for whether rewinding restores save games - - Qt: German translation (by Lothar Serra Mari) - - Savestates now contain any RTC override data + - Memory search - Command line ability to override configuration values - Add option to allow preloading the entire ROM before running - - GB: Video/audio channel enabling/disabling + - Add option for whether rewinding restores save games + - Savestates now contain any RTC override data - Add option to lock video to integer scaling - - Video log recording for testing and bug reporting - - Library view - - Debugger: Segment/bank support + - LR35902: Watchpoints + - LR35902/GB-Z80 disassembler + - GB: Tile viewer + - GB: Video/audio channel enabling/disabling - GB: Symbol table support - GB MBC: Add MBC1 multicart support + - GBA: Support printing debug strings from inside a game + - GBA: Better cheat type autodetection - Implement keypad interrupts - - LR35902: Watchpoints - - Memory search + - Configuration of gamepad hats + - Video log recording for testing and bug reporting + - Debugger: Segment/bank support - Debugger: Execution tracing + - Qt: German translation (by Lothar Serra Mari) + - Qt: Spanish translation (by Kevin López) - Qt: Italian translation (by theheroGAC) Bugfixes: - - LR35902: Fix core never exiting with certain event patterns - - GB Timer: Improve DIV reset behavior - - GBA Memory: Improve initial skipped BIOS state - - GBA BIOS: Implement BitUnPack - ARM7: Fix MLA/*MULL/*MLAL timing - - GBA: Fix multiboot ROM loading - - Libretro: Fix saving in GB games (fixes mgba.io/i/486) - - LR35902: Fix pc overflowing current region off-by-one - - GB MBC: Fix ROM bank overflows getting set to bank 0 - - Qt: Fix timing issues on high refresh rate monitors - - GBA Savedata: Fix savedata unmasking (fixes mgba.io/i/441) - - Util: Fix overflow when loading invalid UPS patches - - Tools: Fix recurring multiple times over the same library - - GBA I/O: Handle audio registers specially when deserializing - - Util: Fix highest-fd socket not being returned by SocketAccept - - Qt: Fix linking after some windows have been closed - - GBA Video: Fix wrong palette on 256-color sprites in OBJWIN - - Windows: Fix VDir.rewind - - SDL: Fix game crash check - - SDL: Fix race condition with audio thread when starting - - GB: Fix flickering when screen is strobed quickly - - FFmpeg: Fix overflow and general issues with audio encoding - - Qt: Fix crash when changing audio settings after a game is closed - - GBA BIOS: Fix ArcTan sign in HLE BIOS - - GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes mgba.io/i/689) - - GBA Video: Don't update background scanline params in mode 0 (fixes mgba.io/i/377) - - Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662) - Core: Fix crash with rewind if savestates shrink - - Test: Fix crash when loading invalid file - - GBA Hardware: Fix crash if a savestate lies about game hardware - - Test: Fix crash when fuzzing fails to load a file - - GBA: Fix multiboot loading resulting in too small WRAM - - Test: Don't rely on core for frames elapsed - - Test: Fix crash when loading invalid file - - GBA Hardware: Fix crash if a savestate lies about game hardware - - Test: Fix crash when fuzzing fails to load a file - - Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107) - - LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735) - - GB: Fix STAT blocking - - GB MBC: Fix swapping carts not detect new MBC - - GB Timer: Fix DIV batching if TAC changes - - GB Video: Reset renderer when loading state - - GBA BIOS: Fix INT_MIN/-1 crash - - GBA Savedata: Update and fix Sharkport importing (fixes mgba.io/i/658) - - OpenGL: Fix some shaders causing offset graphics - - Qt: Fix game unpausing after frame advancing and refocusing - - GB Timer: Fix sub-M-cycle DIV reset timing and edge triggering - Core: Fix interrupting a thread while on the thread (fixes mgba.io/i/692) - Core: Fix directory sets crashing on close if base isn't properly detached + - FFmpeg: Fix overflow and general issues with audio encoding + - GB: Fix flickering when screen is strobed quickly + - GB: Fix STAT blocking + - GB MBC: Fix ROM bank overflows getting set to bank 0 + - GB MBC: Fix swapping carts not detect new MBC + - GB Timer: Improve DIV reset behavior + - GB Timer: Fix DIV batching if TAC changes + - GB Video: Reset renderer when loading state + - GBA: Fix multiboot ROM loading + - GBA: Fix multiboot loading resulting in too small WRAM + - GBA BIOS: Implement BitUnPack + - GBA BIOS: Fix ArcTan sign in HLE BIOS + - GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes mgba.io/i/689) + - GBA BIOS: Fix INT_MIN/-1 crash + - GBA Hardware: Fix crash if a savestate lies about game hardware + - GBA I/O: Handle audio registers specially when deserializing + - GBA Memory: Improve initial skipped BIOS state + - GBA Savedata: Fix savedata unmasking (fixes mgba.io/i/441) + - GBA Savedata: Update and fix Sharkport importing (fixes mgba.io/i/658) + - GBA Video: Fix wrong palette on 256-color sprites in OBJWIN + - GBA Video: Don't update background scanline params in mode 0 (fixes mgba.io/i/377) + - Libretro: Fix saving in GB games (fixes mgba.io/i/486) + - LR35902: Fix core never exiting with certain event patterns + - LR35902: Fix pc overflowing current region off-by-one + - LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735) + - OpenGL: Fix some shaders causing offset graphics + - GB Timer: Fix sub-M-cycle DIV reset timing and edge triggering - Qt: Fix window icon being stretched - Qt: Fix data directory path + - Qt: Fix timing issues on high refresh rate monitors + - Qt: Fix linking after some windows have been closed + - Qt: Fix crash when changing audio settings after a game is closed + - Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662) + - Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107) + - Qt: Fix game unpausing after frame advancing and refocusing + - SDL: Fix game crash check + - SDL: Fix race condition with audio thread when starting + - SDL: Fix showing version number + - Test: Fix crash when loading invalid file + - Test: Fix crash when fuzzing fails to load a file + - Test: Don't rely on core for frames elapsed + - Test: Fix crash when loading invalid file + - Test: Fix crash when fuzzing fails to load a file + - Tools: Fix recurring multiple times over the same library + - Util: Fix overflow when loading invalid UPS patches + - Util: Fix highest-fd socket not being returned by SocketAccept + - Windows: Fix VDir.rewind Misc: - - SDL: Remove scancode key input - - GBA Video: Clean up unused timers - - Test: Add a basic test suite - - GBA Video: Allow multiple handles into the same tile cache - - VFS: Call msync when syncing mapped data - - GBA Video, GB Video: Colors are now fully scaled - - VFS: Allow truncating memory chunk VFiles - - Debugger: Modularize CLI debugger - - Core: Clean up some thread state checks - - Debugger: Make building with debugging aspects optional - - GBA Memory: Support for Mo Jie Qi Bing by Vast Fame (taizou) - - GBA Memory: Support reading/writing POSTFLG - - Util: Add size counting to Table - - Qt: Move last directory setting from qt.ini to config.ini + - All: Add C++ header guards + - All: Move time.h include to common.h - 3DS, PSP2, Wii: Last directory loaded is saved + - CMake: Add ability to just print version string + - Core: New, faster event timing subsystem + - Core: Clean up some thread state checks + - Core: Add generic checksum function + - Core: Cores can now have multiple sets of callbacks + - Core: Restore sleep callback + - Core: Move rewind diffing to its own thread + - Core: Ability to enumerate and modify video and audio channels + - Core: List memory segments in the core + - Core: Move savestate creation time to extdata + - Core: Config values can now be hexadecimal + - Core: Improved threading interrupted detection + - Debugger: Modularize CLI debugger + - Debugger: Make building with debugging aspects optional + - Debugger: Add functions for read- or write-only watchpoints + - Debugger: Make attaching a backend idempotent + - Debugger: Add mDebuggerRunFrame convenience function + - Feature: Move game database from flatfile to SQLite3 + - Feature: Support ImageMagick 7 + - Feature: Make -l option explicit + - FFmpeg: Return false if a file fails to open + - FFmpeg: Force MP4 files to YUV420P + - GB: Trust ROM header for number of SRAM banks (fixes mgba.io/i/726) + - GB: Reset with initial state of DIV register + - GB MBC: New MBC7 implementation - GB Audio: Simplify envelope code - GB Audio: Improve initial envelope samples - - Debugger: Add functions for read- or write-only watchpoints + - GB Audio: Start implementing "zombie" audio (fixes mgba.io/i/389) + - GB Video: Improved video timings + - GBA: Ignore invalid opcodes used by the Wii U VC emulator + - GBA, GB: ROM is now unloaded if a patch is applied - GBA DMA: Refactor DMA out of memory.c - GBA DMA: Move DMAs to using absolute timing - - All: Add C++ header guards - GBA I/O: Clear JOYSTAT RECV flag when reading JOY_RECV registers - GBA I/O: Set JOYSTAT TRANS flag when writing JOY_TRANS registers + - GBA Memory: Support for Mo Jie Qi Bing by Vast Fame (taizou) + - GBA Memory: Support reading/writing POSTFLG + - GBA Memory: Remove unused prefetch cruft + - GBA Timer: Improve accuracy of timers + - GBA Video: Clean up unused timers + - GBA Video: Allow multiple handles into the same tile cache + - GBA Video, GB Video: Colors are now fully scaled + - GBA Video: Optimize when BLD* registers are written frequently + - OpenGL: Add xBR-lv2 shader + - Qt: Move last directory setting from qt.ini to config.ini - Qt: Improved HiDPI support - Qt: Expose configuration directory - - Feature: Move game database from flatfile to SQLite3 - - GB Audio: Start implementing "zombie" audio (fixes mgba.io/i/389) - - VFS: Fix some minor VFile issues with FILEs - - Core: Add generic checksum function - - Feature: Support ImageMagick 7 - - All: Move time.h include to common.h - - CMake: Add ability to just print version string - Qt: Merge "Save" and "OK" buttons in shader options - - SDL: Automatically map controllers when plugged in - Qt: Automatically load controller profile when plugged in - - OpenGL: Add xBR-lv2 shader - - GBA, GB: ROM is now unloaded if a patch is applied - - Util: Add 8-bit PNG write support - Qt: Rename "Resample video" option to "Bilinear filtering" - - GBA Video: Optimize when BLD* registers are written frequently - - Core: Cores can now have multiple sets of callbacks - - GBA: Ignore invalid opcodes used by the Wii U VC emulator - Qt: Remove audio thread - Qt: Remove audio buffer sizing in AudioProcessorQt - Qt: Re-enable QtMultimedia on Windows - - FFmpeg: Return false if a file fails to open - - FFmpeg: Force MP4 files to YUV420P - Qt: Make "Mute" able to be bound to a key - - Core: Restore sleep callback - Qt: Add .gb/.gbc files to the extension list in Info.plist - - Feature: Make -l option explicit - - Core: Ability to enumerate and modify video and audio channels - - Debugger: Make attaching a backend idempotent + - Qt: Relax hard dependency on OpenGL + - Qt: Better highlight active key in control binding + - SDL: Remove scancode key input + - SDL: Automatically map controllers when plugged in + - Test: Add a basic test suite + - Util: Add size counting to Table + - Util: Add 8-bit PNG write support + - Util: Tune patch-fast extent sizes + - VFS: Call msync when syncing mapped data + - VFS: Allow truncating memory chunk VFiles + - VFS: Fix some minor VFile issues with FILEs - VFS: Optimize expanding in-memory files - VFS: Add VFileFIFO for operating on circle buffers - - Core: Move rewind diffing to its own thread - - Util: Tune patch-fast extent sizes - - Qt: Relax hard dependency on OpenGL - - GB Video: Improved video timings - - Core: List memory segments in the core - - Core: Move savestate creation time to extdata - - Debugger: Add mDebuggerRunFrame convenience function - - GBA Memory: Remove unused prefetch cruft - - GB: Trust ROM header for number of SRAM banks (fixes mgba.io/i/726) - - Core: Config values can now be hexadecimal - - GB: Reset with initial state of DIV register - - GB MBC: New MBC7 implementation - - Qt: Better highlight active key in control binding - - Core: Improved threading interrupted detection - - GBA Timer: Improve accuracy of timers - -0.6 beta 2: (Future) +Changes from beta 1: Features: - Qt: Italian translation (by theheroGAC) - Qt: Updated German translation Bugfixes: + - GB Audio: Fix incorrect channel 4 iteration + - GB Audio: Fix zombie mode bit masking + - GB Serialize: Fix timer serialization + - GB Video: Fix LYC regression + - GBA SIO: Improve SIO Normal dummy driver (fixes mgba.io/i/520) + - GBA Timer: Fix count-up timing overflowing timer 3 + - PSP2: Use custom localtime_r since newlib version is broken (fixes mgba.io/i/560) - Qt: Fix memory search close button (fixes mgba.io/i/769) - Qt: Fix window icon being stretched - Qt: Fix initial window size (fixes mgba.io/i/766) - Qt: Fix data directory path - Qt: Fix controls not saving on non-SDL builds - - GB Video: Fix LYC regression - Qt: Fix translation initialization (fixes mgba.io/i/776) - - PSP2: Use custom localtime_r since newlib version is broken (fixes mgba.io/i/560) + - Qt: Fix patch loading while a game is running + - Qt: Fix shader selector on Ubuntu (fixes mgba.io/i/767) + - Core: Fix rewinding getting out of sync (fixes mgba.io/i/791) + - Qt: Fix GL-less build + - Qt: Fix Software renderer not handling alpha bits properly Misc: - - Qt: Add language selector + - GB Serialize: Add MBC state serialization + - GBA Memory: Call crash callbacks regardless of if hard crash is enabled - GBA Timer: Improve accuracy of timers - - Qt: Minor test fixes - PSP2: Update toolchain to use vita.cmake + - Qt: Add language selector + - Qt: Minor test fixes + - Qt: Move shader settings into main settings window + - Qt: Dismiss game crashing/failing dialogs when a new game loads + - Qt: Properly ship Qt translations + - SDL: Remove writing back obtained samples (fixes mgba.io/i/768) 0.6 beta 1: (2017-06-29) - Initial beta for 0.6 diff --git a/CMakeLists.txt b/CMakeLists.txt index fd3a2b344..131b0a9aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core") set(M_CORE_GB ON CACHE BOOL "Build Game Boy core") set(M_CORE_DS ON CACHE BOOL "Build DS core") set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support") +set(ENABLE_SCRIPTING ON CACHE BOOL "Whether or not to enable scripting support") set(BUILD_QT ON CACHE BOOL "Build Qt frontend") set(BUILD_SDL ON CACHE BOOL "Build SDL frontend") set(BUILD_LIBRETRO OFF CACHE BOOL "Build libretro core") @@ -260,6 +261,10 @@ if(DEFINED 3DS OR DEFINED PSP2 OR DEFINED WII) set(USE_SQLITE3 OFF) endif() +if(DEFINED 3DS OR DEFINED WII) + add_definitions(-DFIXED_ROM_BUFFER) +endif() + if(NOT M_CORE_GBA) set(USE_GDB_STUB OFF) endif() @@ -354,6 +359,7 @@ endif() # Feature dependencies set(FEATURE_DEFINES) set(FEATURES) +set(ENABLES) if(CMAKE_SYSTEM_NAME MATCHES .*BSD) set(LIBEDIT_LIBRARIES -ledit) if (CMAKE_SYSTEM_NAME STREQUAL OpenBSD) @@ -396,6 +402,7 @@ find_feature(USE_MAGICK "MagickWand") find_feature(USE_EPOXY "epoxy") find_feature(USE_CMOCKA "cmocka") find_feature(USE_SQLITE3 "sqlite3") +find_feature(ENABLE_PYTHON "PythonLibs") # Features set(DEBUGGER_SRC @@ -613,6 +620,18 @@ if(USE_SQLITE3) list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/sqlite3/no-intro.c") endif() +if(ENABLE_SCRIPTING) + list(APPEND ENABLES SCRIPTING) + + if(BUILD_PYTHON) + find_program(PYTHON python) + include(FindPythonLibs) + list(APPEND DEPENDENCY_LIB ${PYTHON_LIBRARIES}) + include_directories(AFTER ${PYTHON_INCLUDE_DIRS}) + list(APPEND ENABLES PYTHON) + endif() +endif() + set(TEST_SRC ${CORE_TEST_SRC}) if(M_CORE_GB) add_definitions(-DM_CORE_GB) @@ -673,6 +692,10 @@ foreach(FEATURE IN LISTS FEATURES) list(APPEND FEATURE_DEFINES "USE_${FEATURE}") endforeach() +foreach(ENABLE IN LISTS ENABLES) + list(APPEND FEATURE_DEFINES "ENABLE_${ENABLE}") +endforeach() + source_group("Virtual files" FILES ${CORE_VFS_SRC} ${VFS_SRC}) source_group("Extra features" FILES ${FEATURE_SRC}) source_group("Third-party code" FILES ${THIRD_PARTY_SRC}) @@ -749,7 +772,7 @@ if(NOT SKIP_LIBRARY) target_link_libraries(${BINARY_NAME} ${DEBUGGER_LIB} ${DEPENDENCY_LIB} ${OS_LIB}) install(TARGETS ${BINARY_NAME} LIBRARY DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME} NAMELINK_SKIP ARCHIVE DESTINATION ${LIBDIR} RUNTIME DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME}) - if(UNIX AND NOT APPLE) + if(UNIX AND NOT APPLE AND NOT HAIKU) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/${BINARY_NAME}-16.png DESTINATION share/icons/hicolor/16x16/apps RENAME ${BINARY_NAME}.png COMPONENT lib${BINARY_NAME}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/${BINARY_NAME}-24.png DESTINATION share/icons/hicolor/24x24/apps RENAME ${BINARY_NAME}.png COMPONENT lib${BINARY_NAME}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/${BINARY_NAME}-32.png DESTINATION share/icons/hicolor/32x32/apps RENAME ${BINARY_NAME}.png COMPONENT lib${BINARY_NAME}) @@ -785,6 +808,11 @@ if(DISABLE_FRONTENDS) set(BUILD_QT OFF) endif() +if(BUILD_PYTHON) + enable_testing() + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/python ${CMAKE_CURRENT_BINARY_DIR}/python) +endif() + if(BUILD_LIBRETRO) file(GLOB RETRO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/libretro/*.c) add_library(${BINARY_NAME}_libretro SHARED ${CORE_SRC} ${RETRO_SRC}) @@ -857,11 +885,6 @@ if(BUILD_SUITE) add_test(${BINARY_NAME}-suite ${BINARY_NAME}-suite) endif() -if(BUILD_PYTHON) - enable_testing() - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/python ${CMAKE_CURRENT_BINARY_DIR}/python) -endif() - if(BUILD_EXAMPLE) add_executable(${BINARY_NAME}-example-server ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/example/client-server/server.c) target_link_libraries(${BINARY_NAME}-example-server ${BINARY_NAME}) @@ -877,7 +900,7 @@ if(BUILD_EXAMPLE) endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/core/flags.h.in ${CMAKE_CURRENT_BINARY_DIR}/flags.h) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/flags.h DESTINATION include/mgba COMPONENT lib${BINARY_NAME}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/flags.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mgba COMPONENT lib${BINARY_NAME}) # Packaging set(CPACK_PACKAGE_VERSION ${VERSION_STRING}) diff --git a/include/mgba/core/rewind.h b/include/mgba/core/rewind.h index e839209fc..052f2f45e 100644 --- a/include/mgba/core/rewind.h +++ b/include/mgba/core/rewind.h @@ -31,6 +31,7 @@ struct mCoreRewindContext { Thread thread; Condition cond; Mutex mutex; + bool ready; #endif }; diff --git a/include/mgba/core/scripting.h b/include/mgba/core/scripting.h new file mode 100644 index 000000000..ae1964186 --- /dev/null +++ b/include/mgba/core/scripting.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef M_SCRIPTING_H +#define M_SCRIPTING_H + +#include + +CXX_GUARD_START + +#ifdef USE_DEBUGGERS +#include +#endif + +struct mScriptBridge; +struct VFile; +struct mScriptEngine { + const char* (*name)(struct mScriptEngine*); + + bool (*init)(struct mScriptEngine*, struct mScriptBridge*); + void (*deinit)(struct mScriptEngine*); + bool (*isScript)(struct mScriptEngine*, const char* name, struct VFile* vf); + bool (*loadScript)(struct mScriptEngine*, const char* name, struct VFile* vf); + void (*run)(struct mScriptEngine*); + +#ifdef USE_DEBUGGERS + void (*debuggerEntered)(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); +#endif +}; + +struct mScriptBridge* mScriptBridgeCreate(void); +void mScriptBridgeDestroy(struct mScriptBridge*); + +void mScriptBridgeInstallEngine(struct mScriptBridge*, struct mScriptEngine*); + +#ifdef USE_DEBUGGERS +void mScriptBridgeSetDebugger(struct mScriptBridge*, struct mDebugger*); +struct mDebugger* mScriptBridgeGetDebugger(struct mScriptBridge*); +void mScriptBridgeDebuggerEntered(struct mScriptBridge*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); +#endif + +void mScriptBridgeRun(struct mScriptBridge*); +bool mScriptBridgeLoadScript(struct mScriptBridge*, const char* name); + +CXX_GUARD_END + +#endif diff --git a/include/mgba/core/thread.h b/include/mgba/core/thread.h index 3c7a168e9..d6e4566a3 100644 --- a/include/mgba/core/thread.h +++ b/include/mgba/core/thread.h @@ -11,15 +11,40 @@ CXX_GUARD_START #include -#include -#include -#include struct mCoreThread; struct mCore; typedef void (*ThreadCallback)(struct mCoreThread* threadContext); +struct mCoreThread; +struct mThreadLogger { + struct mLogger d; + struct mCoreThread* p; +}; + +struct mCoreThreadInternal; +struct mCoreThread { + // Input + struct mCore* core; + + struct mThreadLogger logger; + ThreadCallback startCallback; + ThreadCallback resetCallback; + ThreadCallback cleanCallback; + ThreadCallback frameCallback; + ThreadCallback sleepCallback; + void* userData; + void (*run)(struct mCoreThread*); + + struct mCoreThreadInternal* impl; +}; + +#ifndef OPAQUE_THREADING +#include +#include +#include + enum mCoreThreadState { THREAD_INITIALIZED = -1, THREAD_RUNNING = 0, @@ -37,17 +62,7 @@ enum mCoreThreadState { THREAD_CRASHED }; -struct mCoreThread; -struct mThreadLogger { - struct mLogger d; - struct mCoreThread* p; -}; - -struct mCoreThread { - // Input - struct mCore* core; - - // Threading state +struct mCoreThreadInternal { Thread thread; enum mCoreThreadState state; @@ -57,19 +72,12 @@ struct mCoreThread { int interruptDepth; bool frameWasOn; - struct mThreadLogger logger; - ThreadCallback startCallback; - ThreadCallback resetCallback; - ThreadCallback cleanCallback; - ThreadCallback frameCallback; - ThreadCallback sleepCallback; - void* userData; - void (*run)(struct mCoreThread*); - struct mCoreSync sync; struct mCoreRewindContext rewind; }; +#endif + bool mCoreThreadStart(struct mCoreThread* threadContext); bool mCoreThreadHasStarted(struct mCoreThread* threadContext); bool mCoreThreadHasExited(struct mCoreThread* threadContext); diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h index bef61c208..34535b9d4 100644 --- a/include/mgba/debugger/debugger.h +++ b/include/mgba/debugger/debugger.h @@ -92,6 +92,7 @@ struct mDebugger { struct mDebuggerPlatform* platform; enum mDebuggerState state; struct mCore* core; + struct mScriptBridge* bridge; void (*init)(struct mDebugger*); void (*deinit)(struct mDebugger*); diff --git a/include/mgba/internal/gb/mbc.h b/include/mgba/internal/gb/mbc.h index 477364967..96a27ddc9 100644 --- a/include/mgba/internal/gb/mbc.h +++ b/include/mgba/internal/gb/mbc.h @@ -18,6 +18,7 @@ struct GB; struct GBMemory; void GBMBCInit(struct GB* gb); void GBMBCSwitchBank(struct GB* gb, int bank); +void GBMBCSwitchBank0(struct GB* gb, int bank); void GBMBCSwitchSramBank(struct GB* gb, int bank); struct GBMBCRTCSaveBuffer { diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index c55f97600..dcdb400a7 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -145,7 +145,7 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * | 0x0017C - 0x0017D: HDMA remaining * | 0x0017E: DMA remaining * | 0x0017F - 0x00183: RTC registers - * | 0x00184 - 0x00193: MBC state (TODO) + * | 0x00184 - 0x00193: MBC state * | 0x00194 - 0x00195: Flags * | bit 0: SRAM accessable * | bit 1: RTC accessible @@ -331,18 +331,21 @@ struct GBSerializedState { union { struct { - uint32_t mode; + uint8_t mode; + uint8_t multicartStride; } mbc1; struct { uint64_t lastLatch; } rtc; struct { - int8_t machineState; - GBMBC7Field field; - int8_t address; + uint8_t state; + GBMBC7Field eeprom; + uint8_t address; + uint8_t access; + uint8_t latch; uint8_t srBits; - uint32_t sr; - GBSerializedMBC7Flags flags; + uint16_t sr; + uint32_t writable; } mbc7; struct { uint8_t reserved[16]; diff --git a/src/core/rewind.c b/src/core/rewind.c index 15ac69a95..f75229195 100644 --- a/src/core/rewind.c +++ b/src/core/rewind.c @@ -30,6 +30,7 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries, context->stateFlags = SAVESTATE_SAVEDATA; #ifndef DISABLE_THREADING context->onThread = onThread; + context->ready = false; if (onThread) { MutexInit(&context->mutex); ConditionInit(&context->cond); @@ -73,6 +74,7 @@ void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) { context->currentState = nextState; #ifndef DISABLE_THREADING if (context->onThread) { + context->ready = true; ConditionWake(&context->cond); MutexUnlock(&context->mutex); return; @@ -121,6 +123,12 @@ bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) } --context->size; + mCoreLoadStateNamed(core, context->previousState, context->stateFlags); + if (context->current == 0) { + context->current = mCoreRewindPatchesSize(&context->patchMemory); + } + --context->current; + struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current); size_t size2 = context->previousState->size(context->previousState); size_t size = context->currentState->size(context->currentState); @@ -129,18 +137,12 @@ bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) } void* current = context->currentState->map(context->currentState, size, MAP_READ); void* previous = context->previousState->map(context->previousState, size, MAP_WRITE); - patch->d.applyPatch(&patch->d, current, size, previous, size); + patch->d.applyPatch(&patch->d, previous, size, current, size); context->currentState->unmap(context->currentState, current, size); context->previousState->unmap(context->previousState, previous, size); - mCoreLoadStateNamed(core, context->previousState, context->stateFlags); struct VFile* nextState = context->previousState; context->previousState = context->currentState; context->currentState = nextState; - - if (context->current == 0) { - context->current = mCoreRewindPatchesSize(&context->patchMemory); - } - --context->current; #ifndef DISABLE_THREADING if (context->onThread) { MutexUnlock(&context->mutex); @@ -154,13 +156,14 @@ THREAD_ENTRY _rewindThread(void* context) { struct mCoreRewindContext* rewindContext = context; ThreadSetName("Rewind Diff Thread"); MutexLock(&rewindContext->mutex); - struct VFile* state = rewindContext->currentState; while (rewindContext->onThread) { - if (rewindContext->currentState != state) { - _rewindDiff(rewindContext); - state = rewindContext->currentState; + while (!rewindContext->ready && rewindContext->onThread) { + ConditionWait(&rewindContext->cond, &rewindContext->mutex); } - ConditionWait(&rewindContext->cond, &rewindContext->mutex); + if (rewindContext->ready) { + _rewindDiff(rewindContext); + } + rewindContext->ready = false; } MutexUnlock(&rewindContext->mutex); return 0; diff --git a/src/core/scripting.c b/src/core/scripting.c new file mode 100644 index 000000000..e176aa843 --- /dev/null +++ b/src/core/scripting.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +#include +#include + +struct mScriptBridge { + struct Table engines; + struct mDebugger* debugger; +}; + +struct mScriptInfo { + const char* name; + struct VFile* vf; + bool success; +}; + +static void _seDeinit(void* value) { + struct mScriptEngine* se = value; + se->deinit(se); +} + +static void _seTryLoad(const char* key, void* value, void* user) { + UNUSED(key); + struct mScriptEngine* se = value; + struct mScriptInfo* si = user; + if (!si->success && se->isScript(se, si->name, si->vf)) { + si->success = se->loadScript(se, si->name, si->vf); + } +} + +static void _seRun(const char* key, void* value, void* user) { + UNUSED(key); + UNUSED(user); + struct mScriptEngine* se = value; + se->run(se); +} + +#ifdef USE_DEBUGGERS +struct mScriptDebuggerEntry { + enum mDebuggerEntryReason reason; + struct mDebuggerEntryInfo* info; +}; + +static void _seDebuggerEnter(const char* key, void* value, void* user) { + UNUSED(key); + struct mScriptEngine* se = value; + struct mScriptDebuggerEntry* entry = user; + se->debuggerEntered(se, entry->reason, entry->info); +} +#endif + +struct mScriptBridge* mScriptBridgeCreate(void) { + struct mScriptBridge* sb = malloc(sizeof(*sb)); + HashTableInit(&sb->engines, 0, _seDeinit); + sb->debugger = NULL; + return sb; +} + +void mScriptBridgeDestroy(struct mScriptBridge* sb) { + HashTableDeinit(&sb->engines); + free(sb); +} + +void mScriptBridgeInstallEngine(struct mScriptBridge* sb, struct mScriptEngine* se) { + if (!se->init(se, sb)) { + return; + } + const char* name = se->name(se); + HashTableInsert(&sb->engines, name, se); +} + +#ifdef USE_DEBUGGERS +void mScriptBridgeSetDebugger(struct mScriptBridge* sb, struct mDebugger* debugger) { + sb->debugger = debugger; + debugger->bridge = sb; +} + +struct mDebugger* mScriptBridgeGetDebugger(struct mScriptBridge* sb) { + return sb->debugger; +} + +void mScriptBridgeDebuggerEntered(struct mScriptBridge* sb, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) { + struct mScriptDebuggerEntry entry = { + .reason = reason, + .info = info + }; + HashTableEnumerate(&sb->engines, _seDebuggerEnter, &entry); +} +#endif + +void mScriptBridgeRun(struct mScriptBridge* sb) { + HashTableEnumerate(&sb->engines, _seRun, NULL); +} + +bool mScriptBridgeLoadScript(struct mScriptBridge* sb, const char* name) { + struct VFile* vf = VFileOpen(name, O_RDONLY); + if (!vf) { + return false; + } + struct mScriptInfo info = { + .name = name, + .vf = vf, + .success = false + }; + HashTableEnumerate(&sb->engines, _seTryLoad, &info); + vf->close(vf); + return info.success; +} diff --git a/src/core/thread.c b/src/core/thread.c index 5ed87e9ca..16b22d25e 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -38,7 +38,7 @@ static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) { static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args); -static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadState newState, bool broadcast) { +static void _changeState(struct mCoreThreadInternal* threadContext, enum mCoreThreadState newState, bool broadcast) { MutexLock(&threadContext->stateMutex); threadContext->state = newState; if (broadcast) { @@ -47,13 +47,13 @@ static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadStat MutexUnlock(&threadContext->stateMutex); } -static void _waitOnInterrupt(struct mCoreThread* threadContext) { +static void _waitOnInterrupt(struct mCoreThreadInternal* threadContext) { while (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) { ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); } } -static void _waitUntilNotState(struct mCoreThread* threadContext, enum mCoreThreadState oldState) { +static void _waitUntilNotState(struct mCoreThreadInternal* threadContext, enum mCoreThreadState oldState) { MutexLock(&threadContext->sync.videoFrameMutex); bool videoFrameWait = threadContext->sync.videoFrameWait; threadContext->sync.videoFrameWait = false; @@ -81,7 +81,7 @@ static void _waitUntilNotState(struct mCoreThread* threadContext, enum mCoreThre MutexUnlock(&threadContext->sync.videoFrameMutex); } -static void _pauseThread(struct mCoreThread* threadContext) { +static void _pauseThread(struct mCoreThreadInternal* threadContext) { threadContext->state = THREAD_PAUSING; _waitUntilNotState(threadContext, THREAD_PAUSING); } @@ -92,11 +92,11 @@ void _frameStarted(void* context) { return; } if (thread->core->opts.rewindEnable && thread->core->opts.rewindBufferCapacity > 0) { - if (thread->state != THREAD_REWINDING) { - mCoreRewindAppend(&thread->rewind, thread->core); - } else if (thread->state == THREAD_REWINDING) { - if (!mCoreRewindRestore(&thread->rewind, thread->core)) { - mCoreRewindAppend(&thread->rewind, thread->core); + if (thread->impl->state != THREAD_REWINDING) { + mCoreRewindAppend(&thread->impl->rewind, thread->core); + } else if (thread->impl->state == THREAD_REWINDING) { + if (!mCoreRewindRestore(&thread->impl->rewind, thread->core)) { + mCoreRewindAppend(&thread->impl->rewind, thread->core); } } } @@ -117,7 +117,7 @@ void _crashed(void* context) { if (!thread) { return; } - _changeState(thread, THREAD_CRASHED, true); + _changeState(thread->impl, THREAD_CRASHED, true); } void _coreSleep(void* context) { @@ -157,7 +157,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { .context = threadContext }; core->addCoreCallbacks(core, &callbacks); - core->setSync(core, &threadContext->sync); + core->setSync(core, &threadContext->impl->sync); core->reset(core); struct mLogFilter filter; @@ -168,11 +168,11 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) { - mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity, true); - threadContext->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0; + mCoreRewindContextInit(&threadContext->impl->rewind, core->opts.rewindBufferCapacity, true); + threadContext->impl->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0; } - _changeState(threadContext, THREAD_RUNNING, true); + _changeState(threadContext->impl, THREAD_RUNNING, true); if (threadContext->startCallback) { threadContext->startCallback(threadContext); @@ -181,49 +181,50 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { threadContext->resetCallback(threadContext); } - while (threadContext->state < THREAD_EXITING) { + struct mCoreThreadInternal* impl = threadContext->impl; + while (impl->state < THREAD_EXITING) { #ifdef USE_DEBUGGERS struct mDebugger* debugger = core->debugger; if (debugger) { mDebuggerRun(debugger); if (debugger->state == DEBUGGER_SHUTDOWN) { - _changeState(threadContext, THREAD_EXITING, false); + _changeState(impl, THREAD_EXITING, false); } } else #endif { - while (threadContext->state <= THREAD_MAX_RUNNING) { + while (impl->state <= THREAD_MAX_RUNNING) { core->runLoop(core); } } int resetScheduled = 0; - MutexLock(&threadContext->stateMutex); - while (threadContext->state > THREAD_MAX_RUNNING && threadContext->state < THREAD_EXITING) { - if (threadContext->state == THREAD_PAUSING) { - threadContext->state = THREAD_PAUSED; - ConditionWake(&threadContext->stateCond); + MutexLock(&impl->stateMutex); + while (impl->state > THREAD_MAX_RUNNING && impl->state < THREAD_EXITING) { + if (impl->state == THREAD_PAUSING) { + impl->state = THREAD_PAUSED; + ConditionWake(&impl->stateCond); } - if (threadContext->state == THREAD_INTERRUPTING) { - threadContext->state = THREAD_INTERRUPTED; - ConditionWake(&threadContext->stateCond); + if (impl->state == THREAD_INTERRUPTING) { + impl->state = THREAD_INTERRUPTED; + ConditionWake(&impl->stateCond); } - if (threadContext->state == THREAD_RUN_ON) { + if (impl->state == THREAD_RUN_ON) { if (threadContext->run) { threadContext->run(threadContext); } - threadContext->state = threadContext->savedState; - ConditionWake(&threadContext->stateCond); + impl->state = impl->savedState; + ConditionWake(&impl->stateCond); } - if (threadContext->state == THREAD_RESETING) { - threadContext->state = THREAD_RUNNING; + if (impl->state == THREAD_RESETING) { + impl->state = THREAD_RUNNING; resetScheduled = 1; } - while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_WAITING) { - ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); + while (impl->state == THREAD_PAUSED || impl->state == THREAD_INTERRUPTED || impl->state == THREAD_WAITING) { + ConditionWait(&impl->stateCond, &impl->stateMutex); } } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&impl->stateMutex); if (resetScheduled) { core->reset(core); if (threadContext->resetCallback) { @@ -232,12 +233,12 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } } - while (threadContext->state < THREAD_SHUTDOWN) { - _changeState(threadContext, THREAD_SHUTDOWN, false); + while (impl->state < THREAD_SHUTDOWN) { + _changeState(impl, THREAD_SHUTDOWN, false); } if (core->opts.rewindEnable) { - mCoreRewindContextDeinit(&threadContext->rewind); + mCoreRewindContextDeinit(&impl->rewind); } if (threadContext->cleanCallback) { @@ -251,27 +252,28 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } bool mCoreThreadStart(struct mCoreThread* threadContext) { - threadContext->state = THREAD_INITIALIZED; + threadContext->impl = malloc(sizeof(*threadContext->impl)); + threadContext->impl->state = THREAD_INITIALIZED; threadContext->logger.p = threadContext; if (!threadContext->logger.d.log) { threadContext->logger.d.log = _mCoreLog; threadContext->logger.d.filter = NULL; } - if (!threadContext->sync.fpsTarget) { - threadContext->sync.fpsTarget = _defaultFPSTarget; + if (!threadContext->impl->sync.fpsTarget) { + threadContext->impl->sync.fpsTarget = _defaultFPSTarget; } - MutexInit(&threadContext->stateMutex); - ConditionInit(&threadContext->stateCond); + MutexInit(&threadContext->impl->stateMutex); + ConditionInit(&threadContext->impl->stateCond); - MutexInit(&threadContext->sync.videoFrameMutex); - ConditionInit(&threadContext->sync.videoFrameAvailableCond); - ConditionInit(&threadContext->sync.videoFrameRequiredCond); - MutexInit(&threadContext->sync.audioBufferMutex); - ConditionInit(&threadContext->sync.audioRequiredCond); + MutexInit(&threadContext->impl->sync.videoFrameMutex); + ConditionInit(&threadContext->impl->sync.videoFrameAvailableCond); + ConditionInit(&threadContext->impl->sync.videoFrameRequiredCond); + MutexInit(&threadContext->impl->sync.audioBufferMutex); + ConditionInit(&threadContext->impl->sync.audioRequiredCond); - threadContext->interruptDepth = 0; + threadContext->impl->interruptDepth = 0; #ifdef USE_PTHREADS sigset_t signals; @@ -281,271 +283,289 @@ bool mCoreThreadStart(struct mCoreThread* threadContext) { pthread_sigmask(SIG_BLOCK, &signals, 0); #endif - threadContext->sync.audioWait = threadContext->core->opts.audioSync; - threadContext->sync.videoFrameWait = threadContext->core->opts.videoSync; - threadContext->sync.fpsTarget = threadContext->core->opts.fpsTarget; + threadContext->impl->sync.audioWait = threadContext->core->opts.audioSync; + threadContext->impl->sync.videoFrameWait = threadContext->core->opts.videoSync; + threadContext->impl->sync.fpsTarget = threadContext->core->opts.fpsTarget; - MutexLock(&threadContext->stateMutex); - ThreadCreate(&threadContext->thread, _mCoreThreadRun, threadContext); - while (threadContext->state < THREAD_RUNNING) { - ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); + MutexLock(&threadContext->impl->stateMutex); + ThreadCreate(&threadContext->impl->thread, _mCoreThreadRun, threadContext); + while (threadContext->impl->state < THREAD_RUNNING) { + ConditionWait(&threadContext->impl->stateCond, &threadContext->impl->stateMutex); } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); return true; } bool mCoreThreadHasStarted(struct mCoreThread* threadContext) { + if (!threadContext->impl) { + return false; + } bool hasStarted; - MutexLock(&threadContext->stateMutex); - hasStarted = threadContext->state > THREAD_INITIALIZED; - MutexUnlock(&threadContext->stateMutex); + MutexLock(&threadContext->impl->stateMutex); + hasStarted = threadContext->impl->state > THREAD_INITIALIZED; + MutexUnlock(&threadContext->impl->stateMutex); return hasStarted; } bool mCoreThreadHasExited(struct mCoreThread* threadContext) { + if (!threadContext->impl) { + return false; + } bool hasExited; - MutexLock(&threadContext->stateMutex); - hasExited = threadContext->state > THREAD_EXITING; - MutexUnlock(&threadContext->stateMutex); + MutexLock(&threadContext->impl->stateMutex); + hasExited = threadContext->impl->state > THREAD_EXITING; + MutexUnlock(&threadContext->impl->stateMutex); return hasExited; } bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) { + if (!threadContext->impl) { + return false; + } bool hasExited; - MutexLock(&threadContext->stateMutex); - hasExited = threadContext->state == THREAD_CRASHED; - MutexUnlock(&threadContext->stateMutex); + MutexLock(&threadContext->impl->stateMutex); + hasExited = threadContext->impl->state == THREAD_CRASHED; + MutexUnlock(&threadContext->impl->stateMutex); return hasExited; } void mCoreThreadMarkCrashed(struct mCoreThread* threadContext) { - MutexLock(&threadContext->stateMutex); - threadContext->state = THREAD_CRASHED; - MutexUnlock(&threadContext->stateMutex); + MutexLock(&threadContext->impl->stateMutex); + threadContext->impl->state = THREAD_CRASHED; + MutexUnlock(&threadContext->impl->stateMutex); } void mCoreThreadEnd(struct mCoreThread* threadContext) { - MutexLock(&threadContext->stateMutex); - _waitOnInterrupt(threadContext); - threadContext->state = THREAD_EXITING; - ConditionWake(&threadContext->stateCond); - MutexUnlock(&threadContext->stateMutex); - MutexLock(&threadContext->sync.audioBufferMutex); - threadContext->sync.audioWait = 0; - ConditionWake(&threadContext->sync.audioRequiredCond); - MutexUnlock(&threadContext->sync.audioBufferMutex); + MutexLock(&threadContext->impl->stateMutex); + _waitOnInterrupt(threadContext->impl); + threadContext->impl->state = THREAD_EXITING; + ConditionWake(&threadContext->impl->stateCond); + MutexUnlock(&threadContext->impl->stateMutex); + MutexLock(&threadContext->impl->sync.audioBufferMutex); + threadContext->impl->sync.audioWait = 0; + ConditionWake(&threadContext->impl->sync.audioRequiredCond); + MutexUnlock(&threadContext->impl->sync.audioBufferMutex); - MutexLock(&threadContext->sync.videoFrameMutex); - threadContext->sync.videoFrameWait = false; - threadContext->sync.videoFrameOn = false; - ConditionWake(&threadContext->sync.videoFrameRequiredCond); - ConditionWake(&threadContext->sync.videoFrameAvailableCond); - MutexUnlock(&threadContext->sync.videoFrameMutex); + MutexLock(&threadContext->impl->sync.videoFrameMutex); + threadContext->impl->sync.videoFrameWait = false; + threadContext->impl->sync.videoFrameOn = false; + ConditionWake(&threadContext->impl->sync.videoFrameRequiredCond); + ConditionWake(&threadContext->impl->sync.videoFrameAvailableCond); + MutexUnlock(&threadContext->impl->sync.videoFrameMutex); } void mCoreThreadReset(struct mCoreThread* threadContext) { - MutexLock(&threadContext->stateMutex); - if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) { - threadContext->savedState = THREAD_RESETING; + MutexLock(&threadContext->impl->stateMutex); + if (threadContext->impl->state == THREAD_INTERRUPTED || threadContext->impl->state == THREAD_INTERRUPTING) { + threadContext->impl->savedState = THREAD_RESETING; } else { - threadContext->state = THREAD_RESETING; + threadContext->impl->state = THREAD_RESETING; } - ConditionWake(&threadContext->stateCond); - MutexUnlock(&threadContext->stateMutex); + ConditionWake(&threadContext->impl->stateCond); + MutexUnlock(&threadContext->impl->stateMutex); } void mCoreThreadJoin(struct mCoreThread* threadContext) { - ThreadJoin(threadContext->thread); + if (!threadContext->impl) { + return; + } + ThreadJoin(threadContext->impl->thread); - MutexDeinit(&threadContext->stateMutex); - ConditionDeinit(&threadContext->stateCond); + MutexDeinit(&threadContext->impl->stateMutex); + ConditionDeinit(&threadContext->impl->stateCond); - MutexDeinit(&threadContext->sync.videoFrameMutex); - ConditionWake(&threadContext->sync.videoFrameAvailableCond); - ConditionDeinit(&threadContext->sync.videoFrameAvailableCond); - ConditionWake(&threadContext->sync.videoFrameRequiredCond); - ConditionDeinit(&threadContext->sync.videoFrameRequiredCond); + MutexDeinit(&threadContext->impl->sync.videoFrameMutex); + ConditionWake(&threadContext->impl->sync.videoFrameAvailableCond); + ConditionDeinit(&threadContext->impl->sync.videoFrameAvailableCond); + ConditionWake(&threadContext->impl->sync.videoFrameRequiredCond); + ConditionDeinit(&threadContext->impl->sync.videoFrameRequiredCond); - ConditionWake(&threadContext->sync.audioRequiredCond); - ConditionDeinit(&threadContext->sync.audioRequiredCond); - MutexDeinit(&threadContext->sync.audioBufferMutex); + ConditionWake(&threadContext->impl->sync.audioRequiredCond); + ConditionDeinit(&threadContext->impl->sync.audioRequiredCond); + MutexDeinit(&threadContext->impl->sync.audioBufferMutex); + + free(threadContext->impl); + threadContext->impl = NULL; } bool mCoreThreadIsActive(struct mCoreThread* threadContext) { - return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING; + if (!threadContext->impl) { + return false; + } + return threadContext->impl->state >= THREAD_RUNNING && threadContext->impl->state < THREAD_EXITING; } void mCoreThreadInterrupt(struct mCoreThread* threadContext) { if (!threadContext) { return; } - MutexLock(&threadContext->stateMutex); - ++threadContext->interruptDepth; - if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) { - MutexUnlock(&threadContext->stateMutex); + MutexLock(&threadContext->impl->stateMutex); + ++threadContext->impl->interruptDepth; + if (threadContext->impl->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) { + MutexUnlock(&threadContext->impl->stateMutex); return; } - threadContext->savedState = threadContext->state; - _waitOnInterrupt(threadContext); - threadContext->state = THREAD_INTERRUPTING; - ConditionWake(&threadContext->stateCond); - _waitUntilNotState(threadContext, THREAD_INTERRUPTING); - MutexUnlock(&threadContext->stateMutex); + threadContext->impl->savedState = threadContext->impl->state; + _waitOnInterrupt(threadContext->impl); + threadContext->impl->state = THREAD_INTERRUPTING; + ConditionWake(&threadContext->impl->stateCond); + _waitUntilNotState(threadContext->impl, THREAD_INTERRUPTING); + MutexUnlock(&threadContext->impl->stateMutex); } void mCoreThreadInterruptFromThread(struct mCoreThread* threadContext) { if (!threadContext) { return; } - MutexLock(&threadContext->stateMutex); - ++threadContext->interruptDepth; - if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) { - if (threadContext->state == THREAD_INTERRUPTING) { - threadContext->state = THREAD_INTERRUPTED; + MutexLock(&threadContext->impl->stateMutex); + ++threadContext->impl->interruptDepth; + if (threadContext->impl->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) { + if (threadContext->impl->state == THREAD_INTERRUPTING) { + threadContext->impl->state = THREAD_INTERRUPTED; } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); return; } - threadContext->savedState = threadContext->state; - threadContext->state = THREAD_INTERRUPTED; - ConditionWake(&threadContext->stateCond); - MutexUnlock(&threadContext->stateMutex); + threadContext->impl->savedState = threadContext->impl->state; + threadContext->impl->state = THREAD_INTERRUPTING; + ConditionWake(&threadContext->impl->stateCond); + MutexUnlock(&threadContext->impl->stateMutex); } void mCoreThreadContinue(struct mCoreThread* threadContext) { if (!threadContext) { return; } - MutexLock(&threadContext->stateMutex); - --threadContext->interruptDepth; - if (threadContext->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) { - threadContext->state = threadContext->savedState; - ConditionWake(&threadContext->stateCond); + MutexLock(&threadContext->impl->stateMutex); + --threadContext->impl->interruptDepth; + if (threadContext->impl->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) { + threadContext->impl->state = threadContext->impl->savedState; + ConditionWake(&threadContext->impl->stateCond); } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); } void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) { - MutexLock(&threadContext->stateMutex); + MutexLock(&threadContext->impl->stateMutex); threadContext->run = run; - _waitOnInterrupt(threadContext); - threadContext->savedState = threadContext->state; - threadContext->state = THREAD_RUN_ON; - ConditionWake(&threadContext->stateCond); - _waitUntilNotState(threadContext, THREAD_RUN_ON); - MutexUnlock(&threadContext->stateMutex); + _waitOnInterrupt(threadContext->impl); + threadContext->impl->savedState = threadContext->impl->state; + threadContext->impl->state = THREAD_RUN_ON; + ConditionWake(&threadContext->impl->stateCond); + _waitUntilNotState(threadContext->impl, THREAD_RUN_ON); + MutexUnlock(&threadContext->impl->stateMutex); } void mCoreThreadPause(struct mCoreThread* threadContext) { - bool frameOn = threadContext->sync.videoFrameOn; - MutexLock(&threadContext->stateMutex); - _waitOnInterrupt(threadContext); - if (threadContext->state == THREAD_RUNNING) { - _pauseThread(threadContext); - threadContext->frameWasOn = frameOn; + bool frameOn = threadContext->impl->sync.videoFrameOn; + MutexLock(&threadContext->impl->stateMutex); + _waitOnInterrupt(threadContext->impl); + if (threadContext->impl->state == THREAD_RUNNING) { + _pauseThread(threadContext->impl); + threadContext->impl->frameWasOn = frameOn; frameOn = false; } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); - mCoreSyncSetVideoSync(&threadContext->sync, frameOn); + mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn); } void mCoreThreadUnpause(struct mCoreThread* threadContext) { - bool frameOn = threadContext->sync.videoFrameOn; - MutexLock(&threadContext->stateMutex); - _waitOnInterrupt(threadContext); - if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) { - threadContext->state = THREAD_RUNNING; - ConditionWake(&threadContext->stateCond); - frameOn = threadContext->frameWasOn; + bool frameOn = threadContext->impl->sync.videoFrameOn; + MutexLock(&threadContext->impl->stateMutex); + _waitOnInterrupt(threadContext->impl); + if (threadContext->impl->state == THREAD_PAUSED || threadContext->impl->state == THREAD_PAUSING) { + threadContext->impl->state = THREAD_RUNNING; + ConditionWake(&threadContext->impl->stateCond); + frameOn = threadContext->impl->frameWasOn; } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); - mCoreSyncSetVideoSync(&threadContext->sync, frameOn); + mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn); } bool mCoreThreadIsPaused(struct mCoreThread* threadContext) { bool isPaused; - MutexLock(&threadContext->stateMutex); - if (threadContext->interruptDepth) { - isPaused = threadContext->savedState == THREAD_PAUSED; + MutexLock(&threadContext->impl->stateMutex); + if (threadContext->impl->interruptDepth) { + isPaused = threadContext->impl->savedState == THREAD_PAUSED; } else { - isPaused = threadContext->state == THREAD_PAUSED; + isPaused = threadContext->impl->state == THREAD_PAUSED; } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); return isPaused; } void mCoreThreadTogglePause(struct mCoreThread* threadContext) { - bool frameOn = threadContext->sync.videoFrameOn; - MutexLock(&threadContext->stateMutex); - _waitOnInterrupt(threadContext); - if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) { - threadContext->state = THREAD_RUNNING; - ConditionWake(&threadContext->stateCond); - frameOn = threadContext->frameWasOn; - } else if (threadContext->state == THREAD_RUNNING) { - _pauseThread(threadContext); - threadContext->frameWasOn = frameOn; + bool frameOn = threadContext->impl->sync.videoFrameOn; + MutexLock(&threadContext->impl->stateMutex); + _waitOnInterrupt(threadContext->impl); + if (threadContext->impl->state == THREAD_PAUSED || threadContext->impl->state == THREAD_PAUSING) { + threadContext->impl->state = THREAD_RUNNING; + ConditionWake(&threadContext->impl->stateCond); + frameOn = threadContext->impl->frameWasOn; + } else if (threadContext->impl->state == THREAD_RUNNING) { + _pauseThread(threadContext->impl); + threadContext->impl->frameWasOn = frameOn; frameOn = false; } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); - mCoreSyncSetVideoSync(&threadContext->sync, frameOn); + mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn); } void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) { bool frameOn = true; - MutexLock(&threadContext->stateMutex); - if (threadContext->state == THREAD_RUNNING || (threadContext->interruptDepth && threadContext->savedState == THREAD_RUNNING)) { - threadContext->state = THREAD_PAUSING; + MutexLock(&threadContext->impl->stateMutex); + if (threadContext->impl->state == THREAD_RUNNING || (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_RUNNING)) { + threadContext->impl->state = THREAD_PAUSING; frameOn = false; } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); - mCoreSyncSetVideoSync(&threadContext->sync, frameOn); + mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn); } void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) { - MutexLock(&threadContext->stateMutex); - if (rewinding && (threadContext->state == THREAD_REWINDING || (threadContext->interruptDepth && threadContext->savedState == THREAD_REWINDING))) { - MutexUnlock(&threadContext->stateMutex); + MutexLock(&threadContext->impl->stateMutex); + if (rewinding && (threadContext->impl->state == THREAD_REWINDING || (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_REWINDING))) { + MutexUnlock(&threadContext->impl->stateMutex); return; } - if (!rewinding && ((!threadContext->interruptDepth && threadContext->state != THREAD_REWINDING) || (threadContext->interruptDepth && threadContext->savedState != THREAD_REWINDING))) { - MutexUnlock(&threadContext->stateMutex); + if (!rewinding && ((!threadContext->impl->interruptDepth && threadContext->impl->state != THREAD_REWINDING) || (threadContext->impl->interruptDepth && threadContext->impl->savedState != THREAD_REWINDING))) { + MutexUnlock(&threadContext->impl->stateMutex); return; } - _waitOnInterrupt(threadContext); - if (rewinding && threadContext->state == THREAD_RUNNING) { - threadContext->state = THREAD_REWINDING; + _waitOnInterrupt(threadContext->impl); + if (rewinding && threadContext->impl->state == THREAD_RUNNING) { + threadContext->impl->state = THREAD_REWINDING; } - if (!rewinding && threadContext->state == THREAD_REWINDING) { - threadContext->state = THREAD_RUNNING; + if (!rewinding && threadContext->impl->state == THREAD_REWINDING) { + threadContext->impl->state = THREAD_RUNNING; } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); } void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) { - MutexLock(&threadContext->stateMutex); - if (threadContext->interruptDepth && threadContext->savedState == THREAD_RUNNING) { - threadContext->savedState = THREAD_WAITING; - } else if (threadContext->state == THREAD_RUNNING) { - threadContext->state = THREAD_WAITING; + MutexLock(&threadContext->impl->stateMutex); + if (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_RUNNING) { + threadContext->impl->savedState = THREAD_WAITING; + } else if (threadContext->impl->state == THREAD_RUNNING) { + threadContext->impl->state = THREAD_WAITING; } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); } void mCoreThreadStopWaiting(struct mCoreThread* threadContext) { - MutexLock(&threadContext->stateMutex); - if (threadContext->interruptDepth && threadContext->savedState == THREAD_WAITING) { - threadContext->savedState = THREAD_RUNNING; - } else if (threadContext->state == THREAD_WAITING) { - threadContext->state = THREAD_RUNNING; - ConditionWake(&threadContext->stateCond); + MutexLock(&threadContext->impl->stateMutex); + if (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_WAITING) { + threadContext->impl->savedState = THREAD_RUNNING; + } else if (threadContext->impl->state == THREAD_WAITING) { + threadContext->impl->state = THREAD_RUNNING; + ConditionWake(&threadContext->impl->stateCond); } - MutexUnlock(&threadContext->stateMutex); + MutexUnlock(&threadContext->impl->stateMutex); } #ifdef USE_PTHREADS diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index cc911d8c7..c991f08ee 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -12,6 +12,10 @@ #include #include +#if ENABLE_SCRIPTING +#include +#endif + #if !defined(NDEBUG) && !defined(_WIN32) #include #endif @@ -51,6 +55,9 @@ static void _writeWord(struct CLIDebugger*, struct CLIDebugVector*); static void _dumpByte(struct CLIDebugger*, struct CLIDebugVector*); static void _dumpHalfword(struct CLIDebugger*, struct CLIDebugVector*); static void _dumpWord(struct CLIDebugger*, struct CLIDebugVector*); +#ifdef ENABLE_SCRIPTING +static void _source(struct CLIDebugger*, struct CLIDebugVector*); +#endif static struct CLIDebuggerCommandSummary _debuggerCommands[] = { { "b", _setBreakpoint, CLIDVParse, "Set a breakpoint" }, @@ -92,6 +99,9 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = { { "x/1", _dumpByte, CLIDVParse, "Examine bytes at a specified offset" }, { "x/2", _dumpHalfword, CLIDVParse, "Examine halfwords at a specified offset" }, { "x/4", _dumpWord, CLIDVParse, "Examine words at a specified offset" }, +#ifdef ENABLE_SCRIPTING + { "source", _source, CLIDVStringParse, "Load a script" }, +#endif #if !defined(NDEBUG) && !defined(_WIN32) { "!", _breakInto, 0, "Break into attached debugger (for developers)" }, #endif @@ -411,6 +421,20 @@ static void _dumpWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { } } +#ifdef ENABLE_SCRIPTING +static void _source(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + if (!dv) { + debugger->backend->printf(debugger->backend, "Needs a filename\n"); + return; + } + if (debugger->d.bridge && mScriptBridgeLoadScript(debugger->d.bridge, dv->charValue)) { + mScriptBridgeRun(debugger->d.bridge); + } else { + debugger->backend->printf(debugger->backend, "Failed to load script\n"); + } +} +#endif + static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { if (!dv || dv->type != CLIDV_INT_TYPE) { debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); diff --git a/src/debugger/debugger.c b/src/debugger/debugger.c index bac157f7c..79b06076a 100644 --- a/src/debugger/debugger.c +++ b/src/debugger/debugger.c @@ -13,6 +13,10 @@ #include #endif +#if ENABLE_SCRIPTING +#include +#endif + const uint32_t DEBUGGER_ID = 0xDEADBEEF; mLOG_DEFINE_CATEGORY(DEBUGGER, "Debugger", "core.debugger"); @@ -34,6 +38,7 @@ struct mDebugger* mDebuggerCreate(enum mDebuggerType type, struct mCore* core) { }; union DebugUnion* debugger = malloc(sizeof(union DebugUnion)); + memset(debugger, 0, sizeof(*debugger)); switch (type) { case DEBUGGER_CLI: @@ -109,6 +114,11 @@ void mDebuggerEnter(struct mDebugger* debugger, enum mDebuggerEntryReason reason if (debugger->platform->entered) { debugger->platform->entered(debugger->platform, reason, info); } +#ifdef ENABLE_SCRIPTING + if (debugger->bridge) { + mScriptBridgeDebuggerEntered(debugger->bridge, reason, info); + } +#endif } static void mDebuggerInit(void* cpu, struct mCPUComponent* component) { diff --git a/src/gb/audio.c b/src/gb/audio.c index 3b384cdf8..b8541b720 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -620,8 +620,9 @@ void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) { } } - *left = sampleLeft * (1 + audio->volumeLeft); - *right = sampleRight * (1 + audio->volumeRight); + int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : 0x1FC; + *left = (sampleLeft - dcOffset) * (1 + audio->volumeLeft); + *right = (sampleRight - dcOffset) * (1 + audio->volumeRight); } static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { @@ -702,6 +703,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value) { if (!envelope->stepTime) { // TODO: Improve "zombie" mode ++envelope->currentVolume; + envelope->currentVolume &= 0xF; } _updateEnvelopeDead(envelope); envelope->nextStep = envelope->stepTime; @@ -709,7 +711,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value) { } static void _updateSquareSample(struct GBAudioSquareChannel* ch) { - ch->sample = (ch->control.hi * 2 - 1) * ch->envelope.currentVolume * 0x8; + ch->sample = ch->control.hi * ch->envelope.currentVolume * 0x8; } static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) { @@ -860,8 +862,7 @@ static void _updateChannel3(struct mTiming* timing, void* user, uint32_t cyclesL ch->sample = bitsCarry >> 4; break; } - ch->sample -= 8; - ch->sample *= volume * 4; + ch->sample *= volume * 2; audio->ch3.readable = true; if (audio->style == GB_AUDIO_DMG) { mTimingDeschedule(audio->timing, &audio->ch3Fade); @@ -888,12 +889,12 @@ static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesL do { int lsb = ch->lfsr & 1; - ch->sample = lsb * 0x10 - 0x8; + ch->sample = lsb * 0x8; ch->sample *= ch->envelope.currentVolume; ch->lfsr >>= 1; ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8); cycles += baseCycles; - } while (cycles < audio->sampleInterval); + } while (cycles + baseCycles < audio->sampleInterval); mTimingSchedule(timing, &audio->ch4Event, cycles - cyclesLate); } diff --git a/src/gb/gb.c b/src/gb/gb.c index 7338701cc..e6f620ce1 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -42,7 +42,7 @@ static void GBStop(struct LR35902Core* cpu); static void _enableInterrupts(struct mTiming* timing, void* user, uint32_t cyclesLate); -#ifdef _3DS +#ifdef FIXED_ROM_BUFFER extern uint32_t* romBuffer; extern size_t romBufferSize; #endif @@ -109,7 +109,7 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf) { gb->pristineRomSize = vf->size(vf); vf->seek(vf, 0, SEEK_SET); gb->isPristine = true; -#ifdef _3DS +#ifdef FIXED_ROM_BUFFER if (gb->pristineRomSize <= romBufferSize) { gb->memory.rom = romBuffer; vf->read(vf, romBuffer, gb->pristineRomSize); @@ -277,7 +277,7 @@ void GBUnloadROM(struct GB* gb) { } if (gb->romVf) { -#ifndef _3DS +#ifndef FIXED_ROM_BUFFER gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize); #endif gb->romVf->close(gb->romVf); @@ -326,7 +326,7 @@ void GBApplyPatch(struct GB* gb, struct Patch* patch) { return; } if (gb->romVf) { -#ifndef _3DS +#ifndef FIXED_ROM_BUFFER gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize); #endif gb->romVf->close(gb->romVf); diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 4c8bee9a8..559e3b2f9 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -50,7 +50,7 @@ void GBMBCSwitchBank(struct GB* gb, int bank) { } } -static void _switchBank0(struct GB* gb, int bank) { +void GBMBCSwitchBank0(struct GB* gb, int bank) { size_t bankStart = bank * GB_SIZE_CART_BANK0 << gb->memory.mbcState.mbc1.multicartStride; if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) { mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); @@ -320,7 +320,7 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { case 0x2: bank &= 3; if (memory->mbcState.mbc1.mode) { - _switchBank0(gb, bank); + GBMBCSwitchBank0(gb, bank); GBMBCSwitchSramBank(gb, bank); } GBMBCSwitchBank(gb, (bank << memory->mbcState.mbc1.multicartStride) | (memory->currentBank & (stride - 1))); @@ -328,9 +328,9 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { case 0x3: memory->mbcState.mbc1.mode = value & 1; if (memory->mbcState.mbc1.mode) { - _switchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride); + GBMBCSwitchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride); } else { - _switchBank0(gb, 0); + GBMBCSwitchBank0(gb, 0); GBMBCSwitchSramBank(gb, 0); } break; diff --git a/src/gb/memory.c b/src/gb/memory.c index c424f5a92..f37f6ef73 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -629,6 +629,28 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { flags = GBSerializedMemoryFlagsSetIsHdma(flags, memory->isHdma); flags = GBSerializedMemoryFlagsSetActiveRtcReg(flags, memory->activeRtcReg); STORE_16LE(flags, 0, &state->memory.flags); + + switch (memory->mbcType) { + case GB_MBC1: + state->memory.mbc1.mode = memory->mbcState.mbc1.mode; + state->memory.mbc1.multicartStride = memory->mbcState.mbc1.multicartStride; + break; + case GB_MBC3_RTC: + STORE_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch); + break; + case GB_MBC7: + state->memory.mbc7.state = memory->mbcState.mbc7.state; + state->memory.mbc7.eeprom = memory->mbcState.mbc7.eeprom; + state->memory.mbc7.address = memory->mbcState.mbc7.address; + state->memory.mbc7.access = memory->mbcState.mbc7.access; + state->memory.mbc7.latch = memory->mbcState.mbc7.latch; + state->memory.mbc7.srBits = memory->mbcState.mbc7.srBits; + STORE_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr); + STORE_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable); + break; + default: + break; + } } void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { @@ -671,6 +693,32 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { memory->ime = GBSerializedMemoryFlagsGetIme(flags); memory->isHdma = GBSerializedMemoryFlagsGetIsHdma(flags); memory->activeRtcReg = GBSerializedMemoryFlagsGetActiveRtcReg(flags); + + switch (memory->mbcType) { + case GB_MBC1: + memory->mbcState.mbc1.mode = state->memory.mbc1.mode; + memory->mbcState.mbc1.multicartStride = state->memory.mbc1.multicartStride; + if (memory->mbcState.mbc1.mode) { + GBMBCSwitchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride); + } + break; + case GB_MBC3_RTC: + // TODO? + //LOAD_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch); + break; + case GB_MBC7: + memory->mbcState.mbc7.state = state->memory.mbc7.state; + memory->mbcState.mbc7.eeprom = state->memory.mbc7.eeprom; + memory->mbcState.mbc7.address = state->memory.mbc7.address & 0x7F; + memory->mbcState.mbc7.access = state->memory.mbc7.access; + memory->mbcState.mbc7.latch = state->memory.mbc7.latch; + memory->mbcState.mbc7.srBits = state->memory.mbc7.srBits; + LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr); + LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable); + break; + default: + break; + } } void _pristineCow(struct GB* gb) { diff --git a/src/gb/timer.c b/src/gb/timer.c index ad9e6c788..0e587410d 100644 --- a/src/gb/timer.c +++ b/src/gb/timer.c @@ -118,7 +118,7 @@ void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* sta STORE_32LE(timer->event.when - mTimingCurrentTime(&timer->p->timing), 0, &state->timer.nextEvent); STORE_32LE(timer->irq.when - mTimingCurrentTime(&timer->p->timing), 0, &state->timer.nextIRQ); GBSerializedTimerFlags flags = GBSerializedTimerFlagsSetIrqPending(0, mTimingIsScheduled(&timer->p->timing, &timer->irq)); - STORE_32LE(flags, 0, &state->timer.flags); + state->timer.flags = flags; } void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* state) { @@ -130,8 +130,7 @@ void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* s LOAD_32LE(when, 0, &state->timer.nextEvent); mTimingSchedule(&timer->p->timing, &timer->event, when); - GBSerializedTimerFlags flags; - LOAD_32LE(flags, 0, &state->timer.flags); + GBSerializedTimerFlags flags = state->timer.flags; if (GBSerializedTimerFlagsIsIrqPending(flags)) { LOAD_32LE(when, 0, &state->timer.nextIRQ); diff --git a/src/gba/gba.c b/src/gba/gba.c index 91a1bbe15..43c21b7de 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -42,7 +42,7 @@ static bool _setSoftwareBreakpoint(struct ARMDebugger*, uint32_t address, enum E static bool _clearSoftwareBreakpoint(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t opcode); -#ifdef _3DS +#ifdef FIXED_ROM_BUFFER extern uint32_t* romBuffer; extern size_t romBufferSize; #endif @@ -120,7 +120,7 @@ void GBAUnloadROM(struct GBA* gba) { } if (gba->romVf) { -#ifndef _3DS +#ifndef FIXED_ROM_BUFFER gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); #endif gba->romVf->close(gba->romVf); @@ -323,7 +323,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { gba->pristineRomSize = SIZE_CART0; } gba->isPristine = true; -#ifdef _3DS +#ifdef FIXED_ROM_BUFFER if (gba->pristineRomSize <= romBufferSize) { gba->memory.rom = romBuffer; vf->read(vf, romBuffer, gba->pristineRomSize); @@ -342,6 +342,16 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]); GBAVFameDetect(&gba->memory.vfame, gba->memory.rom, gba->memory.romSize); + 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); + memcpy(newRom, gba->memory.rom, gba->pristineRomSize); + gba->memory.rom = newRom; +#endif + gba->memory.romSize = SIZE_CART0; + gba->isPristine = false; + } // TODO: error check return true; } @@ -394,7 +404,7 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { return; } if (gba->romVf) { -#ifndef _3DS +#ifndef FIXED_ROM_BUFFER gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); #endif gba->romVf->close(gba->romVf); diff --git a/src/gba/io.c b/src/gba/io.c index 7ec88c399..49036f516 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -709,16 +709,16 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { switch (address) { // Reading this takes two cycles (1N+1I), so let's remove them preemptively case REG_TM0CNT_LO: - GBATimerUpdateRegister(gba, 0, 2); + GBATimerUpdateRegister(gba, 0, 4); break; case REG_TM1CNT_LO: - GBATimerUpdateRegister(gba, 1, 2); + GBATimerUpdateRegister(gba, 1, 4); break; case REG_TM2CNT_LO: - GBATimerUpdateRegister(gba, 2, 2); + GBATimerUpdateRegister(gba, 2, 4); break; case REG_TM3CNT_LO: - GBATimerUpdateRegister(gba, 3, 2); + GBATimerUpdateRegister(gba, 3, 4); break; case REG_KEYINPUT: diff --git a/src/gba/memory.c b/src/gba/memory.c index 7fe9d09f0..3bf7bc382 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -297,10 +297,8 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { memory->activeRegion = -1; cpu->memory.activeRegion = _deadbeef; cpu->memory.activeMask = 0; - if (gba->yankedRomSize || !gba->hardCrash) { - mLOG(GBA_MEM, GAME_ERROR, "Jumped to invalid address: %08X", address); - } else if (mCoreCallbacksListSize(&gba->coreCallbacks)) { - mLOG(GBA_MEM, GAME_ERROR, "Jumped to invalid address: %08X", address); + + if (!gba->yankedRomSize && mCoreCallbacksListSize(&gba->coreCallbacks)) { size_t c; for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c); @@ -308,6 +306,10 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { callbacks->coreCrashed(callbacks->context); } } + } + + if (gba->yankedRomSize || !gba->hardCrash) { + mLOG(GBA_MEM, GAME_ERROR, "Jumped to invalid address: %08X", address); } else { mLOG(GBA_MEM, FATAL, "Jumped to invalid address: %08X", address); } @@ -1551,7 +1553,7 @@ void _pristineCow(struct GBA* gba) { gba->cpu->memory.activeRegion = newRom; } if (gba->romVf) { -#ifndef _3DS +#ifndef FIXED_ROM_BUFFER gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize); #endif gba->romVf->close(gba->romVf); diff --git a/src/gba/sio.c b/src/gba/sio.c index 33a7c99d7..67b051303 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -155,11 +155,13 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { case SIO_NORMAL_8: case SIO_NORMAL_32: value |= 0x0004; - if ((value & 0x4080) == 0x4080) { - // TODO: Test this on hardware to see if this is correct - GBARaiseIRQ(sio->p, IRQ_SIO); + if ((value & 0x0081) == 0x0081) { + if (value & 0x4000) { + // TODO: Test this on hardware to see if this is correct + GBARaiseIRQ(sio->p, IRQ_SIO); + } + value &= ~0x0080; } - value &= ~0x0080; break; default: // TODO diff --git a/src/platform/python/CMakeLists.txt b/src/platform/python/CMakeLists.txt index ad0d7536a..2632eb081 100644 --- a/src/platform/python/CMakeLists.txt +++ b/src/platform/python/CMakeLists.txt @@ -6,12 +6,33 @@ foreach(DIR IN LISTS INCLUDE_DIRECTORIES) list(APPEND INCLUDE_FLAGS "-I${DIR}") endforeach() +include(FindPythonLibs) +list(APPEND DEPENDENCY_LIB ${PYTHON_LIBRARIES}) +include_directories(AFTER ${PYTHON_INCLUDE_DIRS}) + +file(GLOB PYTHON_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py) add_custom_command(OUTPUT build/lib/${BINARY_NAME}/__init__.py COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${BINARY_NAME} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup.py + DEPENDS ${PYTHON_HEADERS} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py) -add_custom_target(${BINARY_NAME}-py ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/build/lib/${BINARY_NAME}/__init__.py) +add_custom_command(OUTPUT lib.c + COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON} ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py + DEPENDS ${PYTHON_HEADERS} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py) + +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/lib.c PROPERTIES GENERATED ON) + +file(GLOB PYTHON_SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.c) +add_library(${BINARY_NAME}-pylib STATIC ${CMAKE_CURRENT_BINARY_DIR}/lib.c ${PYTHON_SRC}) +add_dependencies(${BINARY_NAME}-pylib ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py) +set_target_properties(${BINARY_NAME}-pylib PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_BINARY_DIR};${INCLUDE_DIRECTORIES}") +set_target_properties(${BINARY_NAME}-pylib PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}") +set(PYTHON_LIBRARY ${BINARY_NAME}-pylib PARENT_SCOPE) + +add_custom_target(${BINARY_NAME}-py ALL DEPENDS ${BINARY_NAME}-pylib ${CMAKE_CURRENT_BINARY_DIR}/build/lib/${BINARY_NAME}/__init__.py) diff --git a/src/platform/python/_builder.h b/src/platform/python/_builder.h index 7d7703bb5..0af3b4124 100644 --- a/src/platform/python/_builder.h +++ b/src/platform/python/_builder.h @@ -1,5 +1,6 @@ #define COMMON_H #define PNG_H +#define OPAQUE_THREADING #define _SYS_TIME_H #define _SYS_TIME_H_ #define _TIME_H @@ -30,9 +31,11 @@ void free(void*); #include #include #include +#include #include #define PYEXPORT extern "Python+C" +#include "platform/python/core.h" #include "platform/python/log.h" #include "platform/python/sio.h" #include "platform/python/vfs-py.h" @@ -53,3 +56,6 @@ void free(void*); #include #include #endif +#ifdef USE_DEBUGGERS +#include +#endif diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py index 2065dec40..bf77eeecb 100644 --- a/src/platform/python/_builder.py +++ b/src/platform/python/_builder.py @@ -18,12 +18,15 @@ cppflags.extend(["-I" + incdir, "-I" + srcdir, "-I" + bindir]) ffi.set_source("mgba._pylib", """ #include "flags.h" +#define OPAQUE_THREADING #include #include #include #include +#include #include #include +#include #include #include #include @@ -35,6 +38,7 @@ ffi.set_source("mgba._pylib", """ #include #define PYEXPORT +#include "platform/python/core.h" #include "platform/python/log.h" #include "platform/python/sio.h" #include "platform/python/vfs-py.h" @@ -43,7 +47,7 @@ ffi.set_source("mgba._pylib", """ extra_compile_args=cppflags, libraries=["medusa-emu"], library_dirs=[bindir], - sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "log.c", "sio.c"]]) + sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "core.c", "log.c", "sio.c"]]) preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "_builder.h")], universal_newlines=True) @@ -55,5 +59,59 @@ for line in preprocessed.splitlines(): lines.append(line) ffi.cdef('\n'.join(lines)) +preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "lib.h")], universal_newlines=True) + +lines = [] +for line in preprocessed.splitlines(): + line = line.strip() + if line.startswith('#'): + continue + lines.append(line) +ffi.embedding_api('\n'.join(lines)) + +ffi.embedding_init_code(""" + from mgba._pylib import ffi + debugger = None + pendingCode = [] + + @ffi.def_extern() + def mPythonSetDebugger(_debugger): + from mgba.debugger import NativeDebugger + global debugger + if debugger and debugger._native == _debugger: + return + debugger = _debugger and NativeDebugger(_debugger) + + @ffi.def_extern() + def mPythonLoadScript(name, vf): + from mgba.vfs import VFile + vf = VFile(vf) + name = ffi.string(name) + source = vf.readAll().decode('utf-8') + try: + code = compile(source, name, 'exec') + pendingCode.append(code) + except: + return False + return True + + @ffi.def_extern() + def mPythonRunPending(): + global pendingCode + for code in pendingCode: + exec(code) + pendingCode = [] + + @ffi.def_extern() + def mPythonDebuggerEntered(reason, info): + global debugger + if not debugger: + return + if info == ffi.NULL: + info = None + for cb in debugger._cbs: + cb(reason, info) +""") + if __name__ == "__main__": - ffi.compile() + ffi.emit_c_code("lib.c") diff --git a/src/platform/python/core.c b/src/platform/python/core.c new file mode 100644 index 000000000..dccd393d7 --- /dev/null +++ b/src/platform/python/core.c @@ -0,0 +1,19 @@ +/* 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 "core.h" + +#include + +struct mCoreCallbacks* mCorePythonCallbackCreate(void* pyobj) { + struct mCoreCallbacks* callbacks = malloc(sizeof(*callbacks)); + callbacks->videoFrameStarted = _mCorePythonCallbacksVideoFrameStarted; + callbacks->videoFrameEnded = _mCorePythonCallbacksVideoFrameEnded; + callbacks->coreCrashed = _mCorePythonCallbacksCoreCrashed; + callbacks->sleep = _mCorePythonCallbacksSleep; + + callbacks->context = pyobj; + return callbacks; +} diff --git a/src/platform/python/core.h b/src/platform/python/core.h new file mode 100644 index 000000000..ed4790917 --- /dev/null +++ b/src/platform/python/core.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +#include "pycommon.h" + +struct mCoreCallbacks* mCorePythonCallbackCreate(void* pyobj); + +PYEXPORT void _mCorePythonCallbacksVideoFrameStarted(void* user); +PYEXPORT void _mCorePythonCallbacksVideoFrameEnded(void* user); +PYEXPORT void _mCorePythonCallbacksCoreCrashed(void* user); +PYEXPORT void _mCorePythonCallbacksSleep(void* user); diff --git a/src/platform/python/engine.c b/src/platform/python/engine.c new file mode 100644 index 000000000..d91db2a8f --- /dev/null +++ b/src/platform/python/engine.c @@ -0,0 +1,103 @@ +/* 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 "engine.h" + +#include +#include +#include + +#ifdef USE_DEBUGGERS +#include +#endif + +#include "lib.h" + +static const char* mPythonScriptEngineName(struct mScriptEngine*); +static bool mPythonScriptEngineInit(struct mScriptEngine*, struct mScriptBridge*); +static void mPythonScriptEngineDeinit(struct mScriptEngine*); +static bool mPythonScriptEngineIsScript(struct mScriptEngine*, const char* name, struct VFile* vf); +static bool mPythonScriptEngineLoadScript(struct mScriptEngine*, const char* name, struct VFile* vf); +static void mPythonScriptEngineRun(struct mScriptEngine*); + +#ifdef USE_DEBUGGERS +static void mPythonScriptDebuggerEntered(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); +#endif + +struct mPythonScriptEngine { + struct mScriptEngine d; + struct mScriptBridge* sb; +}; + +struct mPythonScriptEngine* mPythonCreateScriptEngine(void) { + struct mPythonScriptEngine* engine = malloc(sizeof(*engine)); + engine->d.name = mPythonScriptEngineName; + engine->d.init = mPythonScriptEngineInit; + engine->d.deinit = mPythonScriptEngineDeinit; + engine->d.isScript = mPythonScriptEngineIsScript; + engine->d.loadScript = mPythonScriptEngineLoadScript; + engine->d.run = mPythonScriptEngineRun; +#ifdef USE_DEBUGGERS + engine->d.debuggerEntered = mPythonScriptDebuggerEntered; +#endif + engine->sb = NULL; + return engine; +} + +void mPythonSetup(struct mScriptBridge* sb) { + struct mPythonScriptEngine* se = mPythonCreateScriptEngine(); + mScriptBridgeInstallEngine(sb, &se->d); +} + +const char* mPythonScriptEngineName(struct mScriptEngine* se) { + UNUSED(se); + return "python"; +} + +bool mPythonScriptEngineInit(struct mScriptEngine* se, struct mScriptBridge* sb) { + struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se; + engine->sb = sb; + return true; +} + +void mPythonScriptEngineDeinit(struct mScriptEngine* se) { + struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se; + free(se); +} + +bool mPythonScriptEngineIsScript(struct mScriptEngine* se, const char* name, struct VFile* vf) { + UNUSED(se); + UNUSED(vf); + return endswith(name, ".py"); +} + +bool mPythonScriptEngineLoadScript(struct mScriptEngine* se, const char* name, struct VFile* vf) { + struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se; + return mPythonLoadScript(name, vf); +} + +void mPythonScriptEngineRun(struct mScriptEngine* se) { + struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se; + + struct mDebugger* debugger = mScriptBridgeGetDebugger(engine->sb); + if (debugger) { + mPythonSetDebugger(debugger); + } + + mPythonRunPending(); +} + +#ifdef USE_DEBUGGERS +void mPythonScriptDebuggerEntered(struct mScriptEngine* se, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) { + struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se; + + struct mDebugger* debugger = mScriptBridgeGetDebugger(engine->sb); + if (!debugger) { + return; + } + + mPythonDebuggerEntered(reason, info); +} +#endif diff --git a/src/platform/python/engine.h b/src/platform/python/engine.h new file mode 100644 index 000000000..e81e220c4 --- /dev/null +++ b/src/platform/python/engine.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef PYTHON_ENGINE_H +#define PYTHON_ENGINE_H + +#include + +CXX_GUARD_START + +struct mScriptBridge; +struct mPythonScriptEngine; +struct mPythonScriptEngine* mPythonCreateScriptEngine(void); +void mPythonSetup(struct mScriptBridge* sb); + +CXX_GUARD_END + +#endif diff --git a/src/platform/python/lib.h b/src/platform/python/lib.h new file mode 100644 index 000000000..d2b49d7e1 --- /dev/null +++ b/src/platform/python/lib.h @@ -0,0 +1,11 @@ +#include "flags.h" + +struct VFile; + +extern bool mPythonLoadScript(const char*, struct VFile*); +extern void mPythonRunPending(); + +#ifdef USE_DEBUGGERS +extern void mPythonSetDebugger(struct mDebugger*); +extern void mPythonDebuggerEntered(enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); +#endif diff --git a/src/platform/python/log.c b/src/platform/python/log.c index 53c8201e2..ed3d3f35b 100644 --- a/src/platform/python/log.c +++ b/src/platform/python/log.c @@ -3,14 +3,7 @@ * 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 - -struct mLoggerPy { - struct mLogger d; - void* pyobj; -}; - -void _pyLog(void* logger, int category, enum mLogLevel level, const char* message); +#include "log.h" static void _pyLogShim(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) { struct mLoggerPy* pylogger = (struct mLoggerPy*) logger; diff --git a/src/platform/python/log.h b/src/platform/python/log.h index 26ec70253..22df10090 100644 --- a/src/platform/python/log.h +++ b/src/platform/python/log.h @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include "pycommon.h" + struct mLoggerPy { struct mLogger d; void* pyobj; diff --git a/src/platform/python/mgba/core.py b/src/platform/python/mgba/core.py index 8d86e3127..9deb4000b 100644 --- a/src/platform/python/mgba/core.py +++ b/src/platform/python/mgba/core.py @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from ._pylib import ffi, lib -from . import tile +from . import tile, createCallback from cached_property import cached_property def find(path): @@ -38,10 +38,65 @@ def needsReset(f): return f(self, *args, **kwargs) return wrapper +def protected(f): + def wrapper(self, *args, **kwargs): + if self._protected: + raise RuntimeError("Core is protected") + return f(self, *args, **kwargs) + return wrapper + +@ffi.def_extern() +def _mCorePythonCallbacksVideoFrameStarted(user): + context = ffi.from_handle(user) + context._videoFrameStarted() + +@ffi.def_extern() +def _mCorePythonCallbacksVideoFrameEnded(user): + context = ffi.from_handle(user) + context._videoFrameEnded() + +@ffi.def_extern() +def _mCorePythonCallbacksCoreCrashed(user): + context = ffi.from_handle(user) + context._coreCrashed() + +@ffi.def_extern() +def _mCorePythonCallbacksSleep(user): + context = ffi.from_handle(user) + context._sleep() + +class CoreCallbacks(object): + def __init__(self): + self._handle = ffi.new_handle(self) + self.videoFrameStarted = [] + self.videoFrameEnded = [] + self.coreCrashed = [] + self.sleep = [] + self.context = lib.mCorePythonCallbackCreate(self._handle) + + def _videoFrameStarted(self): + for cb in self.videoFrameStarted: + cb() + + def _videoFrameEnded(self): + for cb in self.videoFrameEnded: + cb() + + def _coreCrashed(self): + for cb in self.coreCrashed: + cb() + + def _sleep(self): + for cb in self.sleep: + cb() + class Core(object): def __init__(self, native): self._core = native self._wasReset = False + self._protected = False + self._callbacks = CoreCallbacks() + self._core.addCoreCallbacks(self._core, self._callbacks.context) @cached_property def tiles(self): @@ -51,17 +106,22 @@ class Core(object): def _init(cls, native): core = ffi.gc(native, native.deinit) success = bool(core.init(core)) + lib.mCoreInitConfig(core, ffi.NULL) if not success: raise RuntimeError("Failed to initialize core") + return cls._detect(core) + + def _deinit(self): + self._core.deinit(self._core) + + @classmethod + def _detect(cls, core): if hasattr(cls, 'PLATFORM_GBA') and core.platform(core) == cls.PLATFORM_GBA: return GBA(core) if hasattr(cls, 'PLATFORM_GB') and core.platform(core) == cls.PLATFORM_GB: return GB(core) return Core(core) - def _deinit(self): - self._core.deinit(self._core) - def loadFile(self, path): return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8'))) @@ -103,10 +163,12 @@ class Core(object): self._wasReset = True @needsReset + @protected def runFrame(self): self._core.runFrame(self._core) @needsReset + @protected def runLoop(self): self._core.runLoop(self._core) @@ -132,26 +194,66 @@ class Core(object): def clearKeys(self, *args, **kwargs): self._core.clearKeys(self._core, self._keysToInt(*args, **kwargs)) + @property @needsReset def frameCounter(self): return self._core.frameCounter(self._core) + @property def frameCycles(self): return self._core.frameCycles(self._core) + @property def frequency(self): return self._core.frequency(self._core) - def getGameTitle(self): + @property + def gameTitle(self): title = ffi.new("char[16]") self._core.getGameTitle(self._core, title) return ffi.string(title, 16).decode("ascii") - def getGameCode(self): + @property + def gameCode(self): code = ffi.new("char[12]") self._core.getGameCode(self._core, code) return ffi.string(code, 12).decode("ascii") + def addFrameCallback(self, cb): + self._callbacks.videoFrameEnded.append(cb) + +class ICoreOwner(object): + def claim(self): + raise NotImplementedError + + def release(self): + raise NotImplementedError + + def __enter__(self): + self.core = self.claim() + self.core._protected = True + return self.core + + def __exit__(self, type, value, traceback): + self.core._protected = False + self.release() + +class IRunner(object): + def pause(self): + raise NotImplementedError + + def unpause(self): + raise NotImplementedError + + def useCore(self): + raise NotImplementedError + + def isRunning(self): + raise NotImplementedError + + def isPaused(self): + raise NotImplementedError + if hasattr(lib, 'PLATFORM_GBA'): from .gba import GBA Core.PLATFORM_GBA = lib.PLATFORM_GBA diff --git a/src/platform/python/mgba/debugger.py b/src/platform/python/mgba/debugger.py new file mode 100644 index 000000000..d6bf30e2e --- /dev/null +++ b/src/platform/python/mgba/debugger.py @@ -0,0 +1,80 @@ +# Copyright (c) 2013-2017 Jeffrey Pfau +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +from ._pylib import ffi, lib +from .core import IRunner, ICoreOwner, Core + +class DebuggerCoreOwner(ICoreOwner): + def __init__(self, debugger): + self.debugger = debugger + self.wasPaused = False + + def claim(self): + if self.debugger.isRunning(): + self.wasPaused = True + self.debugger.pause() + return self.debugger._core + + def release(self): + if self.wasPaused: + self.debugger.unpause() + +class NativeDebugger(IRunner): + WATCHPOINT_WRITE = lib.WATCHPOINT_WRITE + WATCHPOINT_READ = lib.WATCHPOINT_READ + WATCHPOINT_RW = lib.WATCHPOINT_RW + + BREAKPOINT_HARDWARE = lib.BREAKPOINT_HARDWARE + BREAKPOINT_SOFTWARE = lib.BREAKPOINT_SOFTWARE + + ENTER_MANUAL = lib.DEBUGGER_ENTER_MANUAL + ENTER_ATTACHED = lib.DEBUGGER_ENTER_ATTACHED + ENTER_BREAKPOINT = lib.DEBUGGER_ENTER_BREAKPOINT + ENTER_WATCHPOINT = lib.DEBUGGER_ENTER_WATCHPOINT + ENTER_ILLEGAL_OP = lib.DEBUGGER_ENTER_ILLEGAL_OP + + def __init__(self, native): + self._native = native + self._cbs = [] + self._core = Core._detect(native.core) + self._core._wasReset = True + + def pause(self): + lib.mDebuggerEnter(self._native, lib.DEBUGGER_ENTER_MANUAL, ffi.NULL) + + def unpause(self): + self._native.state = lib.DEBUGGER_RUNNING + + def isRunning(self): + return self._native.state == lib.DEBUGGER_RUNNING + + def isPaused(self): + return self._native.state in (lib.DEBUGGER_PAUSED, lib.DEBUGGER_CUSTOM) + + def useCore(self): + return DebuggerCoreOwner(self) + + def setBreakpoint(self, address): + if not self._native.platform.setBreakpoint: + raise RuntimeError("Platform does not support breakpoints") + self._native.platform.setBreakpoint(self._native.platform, address) + + def clearBreakpoint(self, address): + if not self._native.platform.setBreakpoint: + raise RuntimeError("Platform does not support breakpoints") + self._native.platform.clearBreakpoint(self._native.platform, address) + + def setWatchpoint(self, address): + if not self._native.platform.setWatchpoint: + raise RuntimeError("Platform does not support watchpoints") + self._native.platform.setWatchpoint(self._native.platform, address) + + def clearWatchpoint(self, address): + if not self._native.platform.clearWatchpoint: + raise RuntimeError("Platform does not support watchpoints") + self._native.platform.clearWatchpoint(self._native.platform, address) + + def addCallback(self, cb): + self._cbs.append(cb) diff --git a/src/platform/python/mgba/thread.py b/src/platform/python/mgba/thread.py new file mode 100644 index 000000000..09dafb40e --- /dev/null +++ b/src/platform/python/mgba/thread.py @@ -0,0 +1,58 @@ +# Copyright (c) 2013-2017 Jeffrey Pfau +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +from ._pylib import ffi, lib +from .core import IRunner, ICoreOwner, Core + +class ThreadCoreOwner(ICoreOwner): + def __init__(self, thread): + self.thread = thread + + def claim(self): + if not self.thread.isRunning(): + raise ValueError + lib.mCoreThreadInterrupt(self.thread._native) + return self.thread._core + + def release(self): + lib.mCoreThreadContinue(self.thread._native) + +class Thread(IRunner): + def __init__(self, native=None): + if native: + self._native = native + self._core = Core(native.core) + self._core._wasReset = lib.mCoreThreadHasStarted(self._native) + else: + self._native = ffi.new("struct mCoreThread*") + + def start(self, core): + if lib.mCoreThreadHasStarted(self._native): + raise ValueError + self._core = core + self._native.core = core._core + lib.mCoreThreadStart(self._native) + self._core._wasReset = lib.mCoreThreadHasStarted(self._native) + + def end(self): + if not lib.mCoreThreadHasStarted(self._native): + raise ValueError + lib.mCoreThreadEnd(self._native) + lib.mCoreThreadJoin(self._native) + + def pause(self): + lib.mCoreThreadPause(self._native) + + def unpause(self): + lib.mCoreThreadUnpause(self._native) + + def isRunning(self): + return bool(lib.mCoreThreadIsActive(self._native)) + + def isPaused(self): + return bool(lib.mCoreThreadIsPaused(self._native)) + + def useCore(self): + return ThreadCoreOwner(self) diff --git a/src/platform/python/mgba/vfs.py b/src/platform/python/mgba/vfs.py index 158c07639..48d1e6794 100644 --- a/src/platform/python/mgba/vfs.py +++ b/src/platform/python/mgba/vfs.py @@ -108,6 +108,13 @@ class VFile: def read(self, buffer, size): return self.handle.read(self.handle, buffer, size) + def readAll(self, size=0): + if not size: + size = self.size() + buffer = ffi.new("char[%i]" % size) + size = self.handle.read(self.handle, buffer, size) + return ffi.unpack(buffer, size) + def readline(self, buffer, size): return self.handle.readline(self.handle, buffer, size) diff --git a/src/platform/python/pycommon.h b/src/platform/python/pycommon.h new file mode 100644 index 000000000..29577e346 --- /dev/null +++ b/src/platform/python/pycommon.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef PYTHON_COMMON_H +#define PYTHON_COMMON_H + +#include + +#ifndef PYEXPORT +#define PYEXPORT extern +#endif + +#endif diff --git a/src/platform/python/vfs-py.c b/src/platform/python/vfs-py.c index f393832cd..1a90420b1 100644 --- a/src/platform/python/vfs-py.c +++ b/src/platform/python/vfs-py.c @@ -3,22 +3,7 @@ * 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 - -struct VFilePy { - struct VFile d; - void* fileobj; -}; - -bool _vfpClose(struct VFile* vf); -off_t _vfpSeek(struct VFile* vf, off_t offset, int whence); -ssize_t _vfpRead(struct VFile* vf, void* buffer, size_t size); -ssize_t _vfpWrite(struct VFile* vf, const void* buffer, size_t size); -void* _vfpMap(struct VFile* vf, size_t size, int flags); -void _vfpUnmap(struct VFile* vf, void* memory, size_t size); -void _vfpTruncate(struct VFile* vf, size_t size); -ssize_t _vfpSize(struct VFile* vf); -bool _vfpSync(struct VFile* vf, const void* buffer, size_t size); +#include "vfs-py.h" struct VFile* VFileFromPython(void* fileobj) { if (!fileobj) { diff --git a/src/platform/python/vfs-py.h b/src/platform/python/vfs-py.h index 6cc56dc4c..2f913a741 100644 --- a/src/platform/python/vfs-py.h +++ b/src/platform/python/vfs-py.h @@ -3,9 +3,10 @@ * 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 "pycommon.h" + struct VFilePy { struct VFile d; void* fileobj; diff --git a/src/platform/qt/AudioDevice.cpp b/src/platform/qt/AudioDevice.cpp index a74e67f50..af6bc6e01 100644 --- a/src/platform/qt/AudioDevice.cpp +++ b/src/platform/qt/AudioDevice.cpp @@ -26,13 +26,13 @@ void AudioDevice::setFormat(const QAudioFormat& format) { LOG(QT, INFO) << tr("Can't set format of context-less audio device"); return; } - double fauxClock = GBAAudioCalculateRatio(1, m_context->sync.fpsTarget, 1); - mCoreSyncLockAudio(&m_context->sync); + double fauxClock = GBAAudioCalculateRatio(1, m_context->impl->sync.fpsTarget, 1); + mCoreSyncLockAudio(&m_context->impl->sync); blip_set_rates(m_context->core->getAudioChannel(m_context->core, 0), m_context->core->frequency(m_context->core), format.sampleRate() * fauxClock); blip_set_rates(m_context->core->getAudioChannel(m_context->core, 1), m_context->core->frequency(m_context->core), format.sampleRate() * fauxClock); - mCoreSyncUnlockAudio(&m_context->sync); + mCoreSyncUnlockAudio(&m_context->impl->sync); } void AudioDevice::setInput(mCoreThread* input) { @@ -49,14 +49,14 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) { return 0; } - mCoreSyncLockAudio(&m_context->sync); + mCoreSyncLockAudio(&m_context->impl->sync); int available = blip_samples_avail(m_context->core->getAudioChannel(m_context->core, 0)); if (available > maxSize / sizeof(GBAStereoSample)) { available = maxSize / sizeof(GBAStereoSample); } blip_read_samples(m_context->core->getAudioChannel(m_context->core, 0), &reinterpret_cast(data)->left, available, true); blip_read_samples(m_context->core->getAudioChannel(m_context->core, 1), &reinterpret_cast(data)->right, available, true); - mCoreSyncConsumeAudio(&m_context->sync); + mCoreSyncConsumeAudio(&m_context->impl->sync); return available * sizeof(GBAStereoSample); } diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index fb4292b27..0e44cbd6b 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -214,7 +214,9 @@ if(NOT DEFINED DATADIR) set(DATADIR ${CMAKE_INSTALL_DATADIR}/${BINARY_NAME}) endif() endif() -install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/shaders DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) +if(BUILD_GL OR BUILD_GLES2) + install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/shaders DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) +endif() install(FILES ${CMAKE_SOURCE_DIR}/res/nointro.dat DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) if(NOT WIN32 AND NOT APPLE) list(APPEND QT_DEFINES DATADIR="${CMAKE_INSTALL_PREFIX}/${DATADIR}") @@ -230,8 +232,20 @@ if(Qt5LinguistTools_FOUND) else() qt5_add_translation(TRANSLATION_FILES ${TS_FILES}) endif() + set(QT_QM_FILES) + if(QT_STATIC) + get_target_property(QT_CORE_LOCATION Qt5::Core LOCATION) + get_filename_component(QT_CORE_LOCATION ${QT_CORE_LOCATION} DIRECTORY) + get_filename_component(QT_QM_LOCATION "${QT_CORE_LOCATION}/../translations" ABSOLUTE) + foreach(TS ${TS_FILES}) + get_filename_component(TS ${TS} NAME) + string(REGEX REPLACE "${BINARY_NAME}-(.*).ts$" "qtbase_\\1.qm" QT_QM "${TS}") + list(APPEND QT_QM_FILES "${QT_QM_LOCATION}/${QT_QM}") + endforeach() + list(APPEND TRANSLATION_FILES ${QT_QM_FILES}) + endif() add_custom_command(OUTPUT ${TRANSLATION_QRC} - COMMAND ${CMAKE_COMMAND} -DTRANSLATION_QRC:FILEPATH="${TRANSLATION_QRC}" -DQM_BASE="${CMAKE_CURRENT_BINARY_DIR}" -P "${CMAKE_CURRENT_SOURCE_DIR}/ts.cmake" + COMMAND ${CMAKE_COMMAND} -DTRANSLATION_QRC:FILEPATH="${TRANSLATION_QRC}" -DQM_BASE="${CMAKE_CURRENT_BINARY_DIR}" "-DTRANSLATION_FILES='${TRANSLATION_FILES}'" -P "${CMAKE_CURRENT_SOURCE_DIR}/ts.cmake" DEPENDS ${TRANSLATION_FILES}) qt5_add_resources(TRANSLATION_RESOURCES ${TRANSLATION_QRC}) list(APPEND RESOURCES ${TRANSLATION_RESOURCES}) @@ -250,7 +264,7 @@ target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE) install(TARGETS ${BINARY_NAME}-qt - RUNTIME DESTINATION bin COMPONENT ${BINARY_NAME}-qt + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt BUNDLE DESTINATION Applications COMPONENT ${BINARY_NAME}-qt) if(UNIX AND NOT APPLE) find_program(DESKTOP_FILE_INSTALL desktop-file-install) @@ -265,7 +279,6 @@ if(APPLE OR WIN32) set_target_properties(${BINARY_NAME}-qt PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) endif() if(APPLE) - message(STATUS ${CMAKE_SYSTEM_NAME}) if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") get_target_property(QTCOCOA Qt5::QCocoaIntegrationPlugin LOCATION) get_target_property(COREAUDIO Qt5::CoreAudioPlugin LOCATION) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 7aa18af04..6810488ed 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -69,7 +69,7 @@ void DisplayGL::startDrawing(mCoreThread* thread) { m_painter->moveToThread(m_drawThread); connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start); m_drawThread->start(); - mCoreSyncSetVideoSync(&m_context->sync, false); + mCoreSyncSetVideoSync(&m_context->impl->sync, false); lockAspectRatio(isAspectRatioLocked()); lockIntegerScaling(isIntegerScalingLocked()); @@ -338,9 +338,9 @@ void PainterGL::draw() { return; } - if (mCoreSyncWaitFrameStart(&m_context->sync) || !m_queue.isEmpty()) { + if (mCoreSyncWaitFrameStart(&m_context->impl->sync) || !m_queue.isEmpty()) { dequeue(); - mCoreSyncWaitFrameEnd(&m_context->sync); + mCoreSyncWaitFrameEnd(&m_context->impl->sync); m_painter.begin(m_gl->context()->device()); performDraw(); m_painter.end(); @@ -354,7 +354,7 @@ void PainterGL::draw() { m_delayTimer.restart(); } } else { - mCoreSyncWaitFrameEnd(&m_context->sync); + mCoreSyncWaitFrameEnd(&m_context->impl->sync); } if (!m_queue.isEmpty()) { QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection); diff --git a/src/platform/qt/DisplayQt.cpp b/src/platform/qt/DisplayQt.cpp index c905d071a..4f09a826e 100644 --- a/src/platform/qt/DisplayQt.cpp +++ b/src/platform/qt/DisplayQt.cpp @@ -51,7 +51,8 @@ void DisplayQt::framePosted(const uint32_t* buffer) { m_backing = QImage(reinterpret_cast(buffer), m_width, m_height, QImage::Format_RGB555); #endif #else - m_backing = QImage(reinterpret_cast(buffer), m_width, m_height, QImage::Format_RGB32); + m_backing = QImage(reinterpret_cast(buffer), m_width, m_height, QImage::Format_ARGB32); + m_backing = m_backing.convertToFormat(QImage::Format_RGB32); #endif } diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 6628468a2..f6403d8a9 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -79,7 +79,7 @@ GameController::GameController(QObject* parent) default: break; } - controller->m_fpsTarget = context->sync.fpsTarget; + controller->m_fpsTarget = context->impl->sync.fpsTarget; if (controller->m_override) { controller->m_override->identify(context->core); @@ -126,7 +126,6 @@ GameController::GameController(QObject* parent) if (controller->m_multiplayer) { controller->m_multiplayer->detachGame(controller); } - controller->m_patch = QString(); controller->clearOverride(); controller->endVideoLog(); @@ -300,8 +299,8 @@ void GameController::setConfig(const mCoreConfig* config) { if (isLoaded()) { Interrupter interrupter(this); mCoreLoadForeignConfig(m_threadContext.core, config); - m_audioSync = m_threadContext.sync.audioWait; - m_videoSync = m_threadContext.sync.videoFrameWait; + m_audioSync = m_threadContext.impl->sync.audioWait; + m_videoSync = m_threadContext.impl->sync.videoFrameWait; m_audioProcessor->setInput(&m_threadContext); } } @@ -411,13 +410,6 @@ void GameController::openGame(bool biosOnly) { m_pauseAfterFrame = false; - if (m_turbo) { - m_threadContext.sync.videoFrameWait = false; - m_threadContext.sync.audioWait = false; - } else { - m_threadContext.sync.videoFrameWait = m_videoSync; - m_threadContext.sync.audioWait = m_audioSync; - } m_threadContext.core->init(m_threadContext.core); mCoreInitConfig(m_threadContext.core, nullptr); @@ -474,6 +466,7 @@ void GameController::openGame(bool biosOnly) { m_threadContext.core->loadPatch(m_threadContext.core, patch); } patch->close(patch); + m_patch = QString(); } else { mCoreAutoloadPatch(m_threadContext.core); } @@ -483,6 +476,13 @@ void GameController::openGame(bool biosOnly) { if (!mCoreThreadStart(&m_threadContext)) { emit gameFailed(); } + if (m_turbo) { + m_threadContext.impl->sync.videoFrameWait = false; + m_threadContext.impl->sync.audioWait = false; + } else { + m_threadContext.impl->sync.videoFrameWait = m_videoSync; + m_threadContext.impl->sync.audioWait = m_audioSync; + } } void GameController::loadBIOS(int platform, const QString& path) { @@ -543,12 +543,10 @@ void GameController::replaceGame(const QString& path) { } void GameController::loadPatch(const QString& path) { + m_patch = path; if (m_gameOpen) { closeGame(); - m_patch = path; openGame(); - } else { - m_patch = path; } } @@ -713,14 +711,14 @@ void GameController::setRewind(bool enable, int capacity, bool rewindSave) { if (m_gameOpen) { Interrupter interrupter(this); if (m_threadContext.core->opts.rewindEnable && m_threadContext.core->opts.rewindBufferCapacity > 0) { - mCoreRewindContextDeinit(&m_threadContext.rewind); + mCoreRewindContextDeinit(&m_threadContext.impl->rewind); } m_threadContext.core->opts.rewindEnable = enable; m_threadContext.core->opts.rewindBufferCapacity = capacity; m_threadContext.core->opts.rewindSave = rewindSave; if (enable && capacity > 0) { - mCoreRewindContextInit(&m_threadContext.rewind, capacity, true); - m_threadContext.rewind.stateFlags = rewindSave ? SAVESTATE_SAVEDATA : 0; + mCoreRewindContextInit(&m_threadContext.impl->rewind, capacity, true); + m_threadContext.impl->rewind.stateFlags = rewindSave ? SAVESTATE_SAVEDATA : 0; } } } @@ -731,7 +729,7 @@ void GameController::rewind(int states) { states = INT_MAX; } for (int i = 0; i < states; ++i) { - if (!mCoreRewindRestore(&m_threadContext.rewind, m_threadContext.core)) { + if (!mCoreRewindRestore(&m_threadContext.impl->rewind, m_threadContext.core)) { break; } } @@ -873,8 +871,10 @@ void GameController::startAudio() { // Don't freeze! m_audioSync = false; m_videoSync = true; - m_threadContext.sync.audioWait = false; - m_threadContext.sync.videoFrameWait = true; + if (isLoaded()) { + m_threadContext.impl->sync.audioWait = false; + m_threadContext.impl->sync.videoFrameWait = true; + } } } @@ -895,9 +895,11 @@ void GameController::setVideoLayerEnabled(int layer, bool enable) { void GameController::setFPSTarget(float fps) { Interrupter interrupter(this); m_fpsTarget = fps; - m_threadContext.sync.fpsTarget = fps; - if (m_turbo && m_turboSpeed > 0) { - m_threadContext.sync.fpsTarget *= m_turboSpeed; + if (isLoaded()) { + m_threadContext.impl->sync.fpsTarget = fps; + if (m_turbo && m_turboSpeed > 0) { + m_threadContext.impl->sync.fpsTarget *= m_turboSpeed; + } } if (m_audioProcessor) { redoSamples(m_audioProcessor->getBufferSamples()); @@ -1015,22 +1017,25 @@ void GameController::setTurboSpeed(float ratio) { void GameController::enableTurbo() { Interrupter interrupter(this); + if (!isLoaded()) { + return; + } bool shouldRedoSamples = false; if (!m_turbo) { - shouldRedoSamples = m_threadContext.sync.fpsTarget != m_fpsTarget; - m_threadContext.sync.fpsTarget = m_fpsTarget; - m_threadContext.sync.audioWait = m_audioSync; - m_threadContext.sync.videoFrameWait = m_videoSync; + shouldRedoSamples = m_threadContext.impl->sync.fpsTarget != m_fpsTarget; + m_threadContext.impl->sync.fpsTarget = m_fpsTarget; + m_threadContext.impl->sync.audioWait = m_audioSync; + m_threadContext.impl->sync.videoFrameWait = m_videoSync; } else if (m_turboSpeed <= 0) { - shouldRedoSamples = m_threadContext.sync.fpsTarget != m_fpsTarget; - m_threadContext.sync.fpsTarget = m_fpsTarget; - m_threadContext.sync.audioWait = false; - m_threadContext.sync.videoFrameWait = false; + shouldRedoSamples = m_threadContext.impl->sync.fpsTarget != m_fpsTarget; + m_threadContext.impl->sync.fpsTarget = m_fpsTarget; + m_threadContext.impl->sync.audioWait = false; + m_threadContext.impl->sync.videoFrameWait = false; } else { - shouldRedoSamples = m_threadContext.sync.fpsTarget != m_fpsTarget * m_turboSpeed; - m_threadContext.sync.fpsTarget = m_fpsTarget * m_turboSpeed; - m_threadContext.sync.audioWait = true; - m_threadContext.sync.videoFrameWait = false; + shouldRedoSamples = m_threadContext.impl->sync.fpsTarget != m_fpsTarget * m_turboSpeed; + m_threadContext.impl->sync.fpsTarget = m_fpsTarget * m_turboSpeed; + m_threadContext.impl->sync.audioWait = true; + m_threadContext.impl->sync.videoFrameWait = false; } if (m_audioProcessor && shouldRedoSamples) { redoSamples(m_audioProcessor->getBufferSamples()); @@ -1040,24 +1045,30 @@ void GameController::enableTurbo() { void GameController::setSync(bool enable) { m_turbo = false; m_turboForced = false; - if (!enable) { - m_threadContext.sync.audioWait = false; - m_threadContext.sync.videoFrameWait = false; - } else { - m_threadContext.sync.audioWait = m_audioSync; - m_threadContext.sync.videoFrameWait = m_videoSync; + if (isLoaded()) { + if (!enable) { + m_threadContext.impl->sync.audioWait = false; + m_threadContext.impl->sync.videoFrameWait = false; + } else { + m_threadContext.impl->sync.audioWait = m_audioSync; + m_threadContext.impl->sync.videoFrameWait = m_videoSync; + } } m_sync = enable; } void GameController::setAudioSync(bool enable) { m_audioSync = enable; - m_threadContext.sync.audioWait = enable; + if (isLoaded()) { + m_threadContext.impl->sync.audioWait = enable; + } } void GameController::setVideoSync(bool enable) { m_videoSync = enable; - m_threadContext.sync.videoFrameWait = enable; + if (isLoaded()) { + m_threadContext.impl->sync.videoFrameWait = enable; + } } void GameController::setAVStream(mAVStream* stream) { diff --git a/src/platform/qt/GamepadAxisEvent.cpp b/src/platform/qt/GamepadAxisEvent.cpp index 833e82f3f..f35256097 100644 --- a/src/platform/qt/GamepadAxisEvent.cpp +++ b/src/platform/qt/GamepadAxisEvent.cpp @@ -20,7 +20,7 @@ GamepadAxisEvent::GamepadAxisEvent(int axis, Direction direction, bool isNew, in , m_key(GBA_KEY_NONE) { ignore(); - if (controller) { + if (controller && controller->map()) { m_key = static_cast(mInputMapAxis(controller->map(), type, axis, direction * INT_MAX)); } } diff --git a/src/platform/qt/GamepadButtonEvent.cpp b/src/platform/qt/GamepadButtonEvent.cpp index f789becb6..184dae591 100644 --- a/src/platform/qt/GamepadButtonEvent.cpp +++ b/src/platform/qt/GamepadButtonEvent.cpp @@ -19,7 +19,7 @@ GamepadButtonEvent::GamepadButtonEvent(QEvent::Type pressType, int button, int t , m_key(GBA_KEY_NONE) { ignore(); - if (controller) { + if (controller && controller->map()) { m_key = static_cast(mInputMapKey(controller->map(), type, button)); } } diff --git a/src/platform/qt/GamepadHatEvent.cpp b/src/platform/qt/GamepadHatEvent.cpp index 65c9a633c..6844b63c5 100644 --- a/src/platform/qt/GamepadHatEvent.cpp +++ b/src/platform/qt/GamepadHatEvent.cpp @@ -20,7 +20,7 @@ GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction di , m_key(GBA_KEY_NONE) { ignore(); - if (controller) { + if (controller && controller->map()) { m_key = static_cast(mInputMapHat(controller->map(), type, hatId, direction)); } } diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 666d8988d..4e148032c 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -10,6 +10,7 @@ #include "Display.h" #include "GBAApp.h" #include "InputController.h" +#include "ShaderSelector.h" #include "ShortcutView.h" #include @@ -158,7 +159,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC m_ui.languages->setItemData(0, QLocale("en")); QDir ts(":/translations/"); for (auto name : ts.entryList()) { - if (!name.endsWith(".qm")) { + if (!name.endsWith(".qm") || !name.startsWith(binaryName)) { continue; } QLocale locale(name.remove(QString("%0-").arg(binaryName)).remove(".qm")); @@ -180,6 +181,24 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC m_ui.tabs->addItem(tr("Shortcuts")); } +SettingsView::~SettingsView() { +#if defined(BUILD_GL) || defined(BUILD_GLES) + if (m_shader) { + m_ui.stackedWidget->removeWidget(m_shader); + m_shader->setParent(nullptr); + } +#endif +} + +void SettingsView::setShaderSelector(ShaderSelector* shaderSelector) { +#if defined(BUILD_GL) || defined(BUILD_GLES) + m_shader = shaderSelector; + m_ui.stackedWidget->addWidget(m_shader); + m_ui.tabs->addItem(tr("Shaders")); + connect(m_ui.buttonBox, &QDialogButtonBox::accepted, m_shader, &ShaderSelector::saved); +#endif +} + void SettingsView::selectBios(QLineEdit* bios) { QString filename = GBAApp::app()->getOpenFileName(this, tr("Select BIOS")); if (!filename.isEmpty()) { @@ -315,6 +334,8 @@ void SettingsView::reloadConfig() { loadSetting("showLibrary", m_ui.showLibrary); loadSetting("preload", m_ui.preload); + m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt()); + double fastForwardRatio = loadSetting("fastForwardRatio").toDouble(); if (fastForwardRatio <= 0) { m_ui.fastForwardUnbounded->setChecked(true); diff --git a/src/platform/qt/SettingsView.h b/src/platform/qt/SettingsView.h index b71d84796..d19962c5d 100644 --- a/src/platform/qt/SettingsView.h +++ b/src/platform/qt/SettingsView.h @@ -18,12 +18,16 @@ class ConfigController; class InputController; class InputIndex; class ShortcutView; +class ShaderSelector; class SettingsView : public QDialog { Q_OBJECT public: SettingsView(ConfigController* controller, InputController* inputController, QWidget* parent = nullptr); + ~SettingsView(); + + void setShaderSelector(ShaderSelector* shaderSelector); signals: void biosLoaded(int platform, const QString&); @@ -45,6 +49,7 @@ private: InputController* m_input; ShortcutView* m_shortcutView; ShortcutView* m_keyView; + ShaderSelector* m_shader = nullptr; void saveSetting(const char* key, const QAbstractButton*); void saveSetting(const char* key, const QComboBox*); diff --git a/src/platform/qt/ShaderSelector.cpp b/src/platform/qt/ShaderSelector.cpp index 9c3a9ce77..ae32e2145 100644 --- a/src/platform/qt/ShaderSelector.cpp +++ b/src/platform/qt/ShaderSelector.cpp @@ -21,6 +21,8 @@ #include #include "platform/video-backend.h" +#if defined(BUILD_GL) || defined(BUILD_GLES) + #if !defined(_WIN32) || defined(USE_EPOXY) #include "platform/opengl/gles2.h" #endif @@ -39,6 +41,9 @@ ShaderSelector::ShaderSelector(Display* display, ConfigController* config, QWidg connect(m_ui.load, &QAbstractButton::clicked, this, &ShaderSelector::selectShader); connect(m_ui.unload, &QAbstractButton::clicked, this, &ShaderSelector::clearShader); connect(m_ui.buttonBox, &QDialogButtonBox::clicked, this, &ShaderSelector::buttonPressed); + connect(this, &ShaderSelector::saved, [this]() { + m_config->setOption("shader", m_shaderPath); + }); } ShaderSelector::~ShaderSelector() { @@ -59,7 +64,7 @@ void ShaderSelector::clear() { void ShaderSelector::selectShader() { QString path(GBAApp::dataDir()); path += QLatin1String("/shaders"); - QFileDialog dialog(nullptr, tr("Load shader"), path, tr("%1 Shader (%.shader)").arg(projectName)); + QFileDialog dialog(nullptr, tr("Load shader"), path); dialog.setFileMode(QFileDialog::Directory); dialog.exec(); QStringList names = dialog.selectedFiles(); @@ -86,7 +91,6 @@ void ShaderSelector::clearShader() { m_display->clearShaders(); refreshShaders(); m_shaderPath = ""; - m_config->setOption("shader", nullptr); } void ShaderSelector::refreshShaders() { @@ -115,6 +119,10 @@ void ShaderSelector::refreshShaders() { disconnect(this, &ShaderSelector::reset, 0, 0); disconnect(this, &ShaderSelector::resetToDefault, 0, 0); + connect(this, &ShaderSelector::saved, [this]() { + m_config->setOption("shader", m_shaderPath); + }); + #if !defined(_WIN32) || defined(USE_EPOXY) if (m_shaders->preprocessShader) { m_ui.passes->addTab(makePage(static_cast(m_shaders->preprocessShader), "default", 0), tr("Preprocessing")); @@ -264,7 +272,6 @@ void ShaderSelector::buttonPressed(QAbstractButton* button) { emit reset(); break; case QDialogButtonBox::Ok: - m_config->setOption("shader", m_shaderPath); emit saved(); close(); break; @@ -275,3 +282,5 @@ void ShaderSelector::buttonPressed(QAbstractButton* button) { break; } } + +#endif diff --git a/src/platform/qt/ShaderSelector.h b/src/platform/qt/ShaderSelector.h index 76a166f85..299204181 100644 --- a/src/platform/qt/ShaderSelector.h +++ b/src/platform/qt/ShaderSelector.h @@ -6,6 +6,8 @@ #ifndef QGBA_SHADER_SELECTOR_H #define QGBA_SHADER_SELECTOR_H +#if defined(BUILD_GL) || defined(BUILD_GLES) + #include #include "ui_ShaderSelector.h" @@ -56,3 +58,5 @@ private: } #endif + +#endif diff --git a/src/platform/qt/ShaderSelector.ui b/src/platform/qt/ShaderSelector.ui index a2af540a2..5905e7a54 100644 --- a/src/platform/qt/ShaderSelector.ui +++ b/src/platform/qt/ShaderSelector.ui @@ -81,33 +81,36 @@ - - + + Unload Shader - + Load New Shader + + + + Qt::Horizontal + + + QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults + + + true + + + - - - - Qt::Horizontal - - - QDialogButtonBox::Ok|QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults - - - diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 58f4a949d..6b673bde4 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -100,7 +100,9 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) updateTitle(); m_display = Display::create(this); +#if defined(BUILD_GL) || defined(BUILD_GLES) m_shaderView = new ShaderSelector(m_display, m_config); +#endif m_logo.setDevicePixelRatio(m_screenWidget->devicePixelRatio()); m_logo = m_logo; // Free memory left over in old pixmap @@ -199,7 +201,6 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) connect(this, &Window::shutdown, m_display, &Display::stopDrawing); connect(this, &Window::shutdown, m_controller, &GameController::closeGame); connect(this, &Window::shutdown, m_logView, &QWidget::hide); - connect(this, &Window::shutdown, m_shaderView, &QWidget::hide); connect(this, &Window::audioBufferSamplesChanged, m_controller, &GameController::setAudioBufferSamples); connect(this, &Window::sampleRateChanged, m_controller, &GameController::setAudioSampleRate); connect(this, &Window::fpsTargetChanged, m_controller, &GameController::setFPSTarget); @@ -308,6 +309,7 @@ void Window::loadConfig() { enterFullScreen(); } +#if defined(BUILD_GL) || defined(BUILD_GLES) if (opts->shader) { struct VDir* shader = VDirOpen(opts->shader); if (shader) { @@ -316,6 +318,7 @@ void Window::loadConfig() { shader->close(shader); } } +#endif m_mruFiles = m_config->getMRU(); updateMRU(); @@ -509,6 +512,11 @@ void Window::exportSharkport() { void Window::openSettingsWindow() { SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController); +#if defined(BUILD_GL) || defined(BUILD_GLES) + if (m_display->supportsShaders()) { + settingsWindow->setShaderSelector(m_shaderView); + } +#endif connect(settingsWindow, &SettingsView::biosLoaded, m_controller, &GameController::loadBIOS); connect(settingsWindow, &SettingsView::audioDriverChanged, m_controller, &GameController::reloadAudioDriver); connect(settingsWindow, &SettingsView::displayDriverChanged, this, &Window::mustRestart); @@ -763,14 +771,9 @@ void Window::toggleFullScreen() { } void Window::gameStarted(mCoreThread* context, const QString& fname) { - MutexLock(&context->stateMutex); - if (context->state < THREAD_EXITING) { - emit startDrawing(context); - } else { - MutexUnlock(&context->stateMutex); + if (!mCoreThreadIsActive(context)) { return; } - MutexUnlock(&context->stateMutex); int platform = 1 << context->core->platform(context->core); #ifdef M_CORE_DS if ((platform & SUPPORT_DS) && (!m_config->getOption("useBios").toInt() || m_config->getOption("ds.bios7").isNull() || m_config->getOption("ds.bios9").isNull() || m_config->getOption("ds.firmware").isNull())) { @@ -783,6 +786,7 @@ void Window::gameStarted(mCoreThread* context, const QString& fname) { return; } #endif + emit startDrawing(context); for (QAction* action : m_gameActions) { action->setDisabled(false); } @@ -887,6 +891,7 @@ void Window::gameCrashed(const QString& errorMessage) { QMessageBox::Ok, this, Qt::Sheet); crash->setAttribute(Qt::WA_DeleteOnClose); crash->show(); + connect(m_controller, &GameController::gameStarted, crash, &QWidget::close); } void Window::gameFailed() { @@ -895,6 +900,7 @@ void Window::gameFailed() { QMessageBox::Ok, this, Qt::Sheet); fail->setAttribute(Qt::WA_DeleteOnClose); fail->show(); + connect(m_controller, &GameController::gameStarted, fail, &QWidget::close); } void Window::unimplementedBiosCall(int call) { @@ -1365,13 +1371,6 @@ void Window::setupMenu(QMenuBar* menubar) { } m_config->updateOption("frameskip"); - QAction* shaderView = new QAction(tr("Shader options..."), avMenu); - connect(shaderView, &QAction::triggered, m_shaderView, &QWidget::show); - if (!m_display->supportsShaders()) { - shaderView->setEnabled(false); - } - addControlledAction(avMenu, shaderView, "shaderSelector"); - avMenu->addSeparator(); ConfigOption* mute = m_config->addOption("mute"); diff --git a/src/platform/qt/input/InputController.cpp b/src/platform/qt/input/InputController.cpp index 437dda9ed..db7c2721e 100644 --- a/src/platform/qt/input/InputController.cpp +++ b/src/platform/qt/input/InputController.cpp @@ -375,6 +375,9 @@ void InputController::updateJoysticks() { } const mInputMap* InputController::map() { + if (!m_activeKeyInfo) { + return nullptr; + } return &m_inputMap; } diff --git a/src/platform/qt/input/InputModel.cpp b/src/platform/qt/input/InputModel.cpp index ccfecdeb2..7b4483f70 100644 --- a/src/platform/qt/input/InputModel.cpp +++ b/src/platform/qt/input/InputModel.cpp @@ -120,8 +120,14 @@ QVariant InputModel::headerData(int section, Qt::Orientation orientation, int ro QModelIndex InputModel::index(int row, int column, const QModelIndex& parent) const { if (parent.isValid()) { InputModelItem* p = static_cast(parent.internalPointer()); + if (row >= m_tree[p->obj].count()) { + return QModelIndex(); + } return createIndex(row, column, const_cast(&m_tree[p->obj][row])); } + if (row >= m_topLevelMenus.count()) { + return QModelIndex(); + } return createIndex(row, column, const_cast(&m_topLevelMenus[row])); } diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index 8b6de713d..d42fa6281 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -30,7 +30,7 @@ Q_IMPORT_PLUGIN(QWindowsAudioPlugin); using namespace QGBA; int main(int argc, char* argv[]) { -#ifdef BUILD_SDL +#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0) SDL_SetMainReady(); #endif @@ -58,6 +58,12 @@ int main(int argc, char* argv[]) { qtTranslator.load(locale, "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath)); application.installTranslator(&qtTranslator); +#ifdef QT_STATIC + QTranslator qtStaticTranslator; + qtStaticTranslator.load(locale, "qtbase", "_", ":/translations/"); + application.installTranslator(&qtStaticTranslator); +#endif + QTranslator langTranslator; langTranslator.load(locale, binaryName, "-", ":/translations/"); application.installTranslator(&langTranslator); diff --git a/src/platform/qt/ts.cmake b/src/platform/qt/ts.cmake index fdf58779a..b9cb4d781 100644 --- a/src/platform/qt/ts.cmake +++ b/src/platform/qt/ts.cmake @@ -1,7 +1,6 @@ -file(GLOB TRANSLATION_FILES "${QM_BASE}/*.qm") file(WRITE ${TRANSLATION_QRC} "\n\t\n") foreach(TS ${TRANSLATION_FILES}) get_filename_component(TS_BASE "${TS}" NAME) - file(APPEND ${TRANSLATION_QRC} "\t\t${TS_BASE}\n") + file(APPEND ${TRANSLATION_QRC} "\t\t${TS}\n") endforeach() file(APPEND ${TRANSLATION_QRC} "\t\n") diff --git a/src/platform/qt/ts/medusa-emu-de.ts b/src/platform/qt/ts/medusa-emu-de.ts index 5a12fb52c..2b500ac04 100644 --- a/src/platform/qt/ts/medusa-emu-de.ts +++ b/src/platform/qt/ts/medusa-emu-de.ts @@ -40,9 +40,9 @@ - © 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 + © 2013 – 2017 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. - © 2013 – 2016 Jeffrey Pfau, lizenziert unter der Mozilla Public License, Version 2.0 + © 2013 – 2017 Jeffrey Pfau, lizenziert unter der Mozilla Public License, Version 2.0 Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., Ltd. @@ -458,8 +458,8 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L MemorySearch - Form - Eingabemaske + Memory Search + Speicher durchsuchen @@ -1146,28 +1146,28 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L QGBA::GameController - - + + Failed to open game file: %1 Fehler beim Öffnen der Spieldatei: %1 - + Failed to open save file: %1 Fehler beim Öffnen der Speicherdatei: %1 - + 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 - + Failed to start audio processor Fehler beim Starten des Audio-Prozessors @@ -3285,7 +3285,7 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L Sh&utdown - B&eenden + Schli&eßen @@ -3935,7 +3935,7 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L Sample rate: - Sample-Rate: + Abtastrate: diff --git a/src/platform/qt/ts/medusa-emu-es.ts b/src/platform/qt/ts/medusa-emu-es.ts index 98f2c259c..6628e18a0 100644 --- a/src/platform/qt/ts/medusa-emu-es.ts +++ b/src/platform/qt/ts/medusa-emu-es.ts @@ -1,6 +1,6 @@ - + AboutScreen @@ -11,7 +11,12 @@ <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> - <a href="http://mgba.io/">Sitio web</a> • <a href="https://forums.mgba.io/">Foros / Soporte</a> • <a href="https://patreon.com/mgba">Donar</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Código fuente</a> + <a href="http://mgba.io/">Sitio web</a> • <a href="https://forums.mgba.io/">Foros / soporte</a> • <a href="https://patreon.com/mgba">Donar</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Código fuente</a> + + + + Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> + Rama: <tt>{gitBranch}</tt><br/>Revisión: <tt>{gitCommit}</tt> @@ -21,38 +26,34 @@ {projectName} would like to thank the following patrons from Patreon: - {projectName} desea agradecer a los siguientes mecenas de Patreon: + {projectName} desea agradecer a los siguientes patrocinadores desde Patreon: - - {projectName} is an open-source Game Boy/Game Boy Advance/DS emulator - - - - - {patrons} - {patrons} + + © 2013 – 2017 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 +Game Boy Advance is a registered trademark of Nintendo Co., Ltd. + © 2013 – 2017 Jeffrey Pfau, licenciado bajo la Mozilla Public License, versión 2.0 +Game Boy Advance es una marca registrada de Nintendo Co., Ltd. {projectVersion} {projectVersion} - - - © 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 -Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. - - {logo} {logo} - - Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> - Rama Git: <tt>{gitBranch}</tt><br/>Revisión: <tt>{gitCommit}</tt> + + {projectName} is an open-source Game Boy Advance emulator + {projectName} es un emulador de código abierto de Game Boy Advance + + + + {patrons} + {patrons} @@ -60,7 +61,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Open in archive... - Abrir dentro de archivo ... + Abrir desde contenedor... @@ -128,7 +129,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Remove - Eliminar + Quitar @@ -161,12 +162,12 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Enter command (try `help` for more info) - Ingresa un comando (prueba con 'help' para más información) + Ingresa un comando (intenta `help` para más información) Break - Entrar en depuración + Entrar a depuración @@ -189,7 +190,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Select File - Elegir archivo + Seleccionar archivo @@ -212,7 +213,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. I/O Viewer - Visor de E/S + Visor de I/O @@ -305,27 +306,27 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Name - Nombre + Nombre Location - Ubicación + Ubicación Platform - Plataforma + Plataforma Size - Tamaño + Tamaño CRC32 - CRC32 + CRC32 @@ -334,7 +335,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. %1 State - %1 captura de estado + %1 estado @@ -347,7 +348,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. No Save - Sin captura + Sin estado @@ -410,22 +411,22 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Debug - Depuración + Depuración (Debug) Stub - Auxiliar + Stub Info - Información + Información (Info) Warning - Advertencia + Advertencia (Warning) @@ -440,7 +441,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Game Error - Error del juego + Error de juego @@ -453,12 +454,111 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd.Máximo de líneas + + MemorySearch + + + Memory Search + Búsqueda en la memoria + + + + Address + Dirección + + + + Current Value + Valor actual + + + + + Type + Tipo + + + + Value + Valor + + + + Numeric + Numérico + + + + Text + Texto + + + + Width + Ancho + + + + 1 Byte (8-bit) + 1 byte (8 bits) + + + + 2 Bytes (16-bit) + 2 bytes (16 bits) + + + + 4 Bytes (32-bit) + 4 bytes (32 bits) + + + + Number type + Tipo de número + + + + Hexadecimal + Hexadecimal + + + + Decimal + Decimal + + + + Guess + Adivinar + + + + Search + Buscar + + + + Search Within + Buscar dentro + + + + Open in Memory Viewer + Abrir en el Visor de memoria + + + + Refresh + Actualizar + + MemoryView Memory - Memoría + Visor de memoria @@ -473,7 +573,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Set Alignment: - Alinear en: + Alinear a: @@ -490,6 +590,11 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd.4 Bytes 4 bytes + + + Unsigned Integer: + Entero sin signo: + Signed Integer: @@ -525,11 +630,6 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd.Load Cargar - - - Unsigned Integer: - Entero sin signo: - ObjView @@ -542,17 +642,17 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. × - × + x Magnification - Magnificación + Ampliación Export - + Esportar @@ -585,7 +685,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Double Size - Doble tamaño + Tamaño doble @@ -682,7 +782,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Game Overrides - Valores específicos por juego + Ajustes específicos por juego @@ -700,7 +800,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Realtime clock - Reloj de tiempo real + Reloj en tiempo real @@ -710,7 +810,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Tilt - Inclinación + Sensor de inclinación @@ -761,7 +861,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Game Boy Player features - Habilitar Game Boy Player + Características del Game Boy Player @@ -791,7 +891,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Memory bank controller - Controlador de banco de memoria + Controlador de bancos de memoria @@ -811,7 +911,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. MBC3 + RTC - MBC3 + Reloj + MBC3 + RTC @@ -821,7 +921,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. MBC5 + Rumble - MBC5 + Vibración + MBC5 + Rumble @@ -833,6 +933,11 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd.HuC-3 HuC-3 + + + Colors + Colores + PaletteView @@ -849,7 +954,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Objects - Objetos (OBJ) + Objetos @@ -881,12 +986,12 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. 16-bit value - Valor en 16 bits + Valor de 16 bits Hex code - Código hexadecimal + Código hex @@ -934,6 +1039,40 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd.0x%0 (%1) + + QGBA::AudioDevice + + + Can't set format of context-less audio device + No se puede establecer el formato de un dispositivo de audio sin contexto + + + + Audio device is missing its core + El dispositivo de audio no tiene núcleo + + + + Writing data to read-only audio device + Escribiendo datos a un dispositivo de audio de sólo lectura + + + + QGBA::AudioProcessorQt + + + Can't start an audio processor without input + No se puede iniciar un procesador de audio sin entrada + + + + QGBA::AudioProcessorSDL + + + Can't start an audio processor without input + No se puede iniciar un procesador de audio sin entrada + + QGBA::CheatsModel @@ -944,7 +1083,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Failed to open cheats file: %1 - Ocurrió un error al cargar el archivo de trucos: %1 + Error al abrir el archivo de trucos: %1 @@ -974,7 +1113,30 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Select cheats file - Elegir archivo de trucos + Seleccionar archivo de trucos + + + + QGBA::GBAKeyEditor + + + Clear Button + Limpiar botón + + + + Clear Analog + Limpiar análogo + + + + Refresh + Actualizar + + + + Set all + Configurar todo @@ -982,7 +1144,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Server settings - Ajustes del servidor + Configuración del servidor @@ -997,7 +1159,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Break - Entrar en depuración + Entrar a depuración @@ -1012,7 +1174,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Crash - Error + Error fatal @@ -1025,36 +1187,36 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Failed to open output GIF file: %1 - Error al abrir el archivo de salida GIF: %1 + Error al abrir el archivo GIF de salida: %1 Select output file - Elegir archivo de salida + Seleccionar archivo de salida Graphics Interchange Format (*.gif) - Formato de intercambio de gráficos (*.gif) + Graphics Interchange Format (*.gif) QGBA::GameController - - + + Failed to open game file: %1 Error al abrir el archivo del juego: %1 - + Failed to open save file: %1 Error al abrir el archivo de guardado: %1 Failed to open snapshot file for reading: %1 - Error al leer el archivo de captura: %1 + Error al leer del archivo de captura: %1 @@ -1062,7 +1224,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd.Error al escribir al archivo de captura: %1 - + Failed to start audio processor Error al iniciar el procesador de audio @@ -1082,27 +1244,27 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Mode 1: 2 tile layers + 1 rotated/scaled tile layer - Modo 1: 2 capas de tiles + 1 capa de tiles rotados/escalados + Modo 1: 2 capas de tiles + 1 capa de tiles con rotación/escalado Mode 2: 2 rotated/scaled tile layers - Modo 2: 2 capas de tiles rotados/escalados + Modo 2: 2 capas de tiles con rotación/escalado Mode 3: Full 15-bit bitmap - Modo 3: mapa de bits de 15 bits + Modo 3: Mapa de bits de 15 bits de tamaño completo Mode 4: Full 8-bit bitmap - Modo 4: mapa de bits de 8 bits + Modo 4: Mapa de bits de 8 bits de tamaño completo Mode 5: Small 15-bit bitmap - Modo 5: mapa de bits pequeño de 15 bits + Modo 5: Mapa de bits de 15 bits de tamaño pequeño @@ -1112,12 +1274,12 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Frame select - Selección de cuadros + Selección de cuadro Unlocked HBlank - HBlank sin bloqueo + HBlank desbloqueado @@ -1132,22 +1294,22 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Enable background 0 - Habilitar fondo 0 + Habilitar BG 0 Enable background 1 - Habilitar fondo 1 + Habilitar BG 1 Enable background 2 - Habilitar fondo 2 + Habilitar BG 2 Enable background 3 - Habilitar fondo 3 + Habilitar BG 3 @@ -1162,42 +1324,42 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Enable Window 1 - Habilitar Window 1 + Habilitar Windows 1 Enable OBJ Window - Habilitar Window OBJ + Habilitar Window de OBJ Currently in VBlank - En VBlank actualmente + En VBlank ahora Currently in HBlank - En HBlank actualmente + En HBlank ahora Currently in VCounter - En VCounter actualmente + En VCounter ahora Enable VBlank IRQ generation - Generar IRQ por cada VBlank + Habilitar generación IRQ en VBlank Enable HBlank IRQ generation - Generar IRQ por cada HBlank + Habilitar generación IRQ en HBlank Enable VCounter IRQ generation - Generar IRQ por cada VCounter + Habilitar generación IRQ en VCounter @@ -1223,7 +1385,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Tile data base (* 16kB) - Base de los tiles (* 16kB) + Dirección base de tiles (* 16kB) @@ -1231,7 +1393,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Enable mosaic - Mosaico (pixelar) + Habilitar mosaico @@ -1239,7 +1401,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Enable 256-color - 256 colores + Habilitar 256 colores @@ -1247,7 +1409,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Tile map base (* 2kB) - Base de asignación de tiles (* 2kB) + Dirección base de asignación de tiles (* 2kB) @@ -1255,13 +1417,13 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Background dimensions - Dimensiones del fondo + Dimensiones de BG Overflow wraps - Envolver al desbordar + Envolver en desbordamiento @@ -1313,7 +1475,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Integer part (bottom) - Parte entera (inferior) + Parte entera (función suelo) @@ -1321,7 +1483,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Integer part (top) - Parte entera (superior) + Parte entera (función techo) @@ -1350,142 +1512,142 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Window 0 enable BG 0 - Window 0 BG 0 + Window 0 habilitar BG 0 Window 0 enable BG 1 - Window 0 BG 1 + Window 0 habilitar BG 1 Window 0 enable BG 2 - Window 0 BG 2 + Window 0 habilitar BG 2 Window 0 enable BG 3 - Window 0 BG 3 + Window 0 habilitar BG 3 Window 0 enable OBJ - Window 0 OBJ + Window 0 habilitar OBJ Window 0 enable blend - Window 0 mezcla + Window 0 habilitar mezcla Window 1 enable BG 0 - Window 1 BG 0 + Window 1 habilitar BG 0 Window 1 enable BG 1 - Window 1 BG 1 + Window 1 habilitar BG 1 Window 1 enable BG 2 - Window 1 BG 2 + Window 1 habilitar BG 2 Window 1 enable BG 3 - Window 1 BG 3 + Window 1 habilitar BG 3 Window 1 enable OBJ - Window 1 OBJ + Window 1 habilitar OBJ Window 1 enable blend - Window 1 mezcla + Window 1 habilitar mezcla Outside window enable BG 0 - Outside window BG 0 + Window externa habilitar BG 0 Outside window enable BG 1 - Outside window BG 1 + Window externa habilitar BG 1 Outside window enable BG 2 - Outside window BG 2 + Window externa habilitar BG 2 Outside window enable BG 3 - Outside window BG 3 + Window externa habilitar BG 3 Outside window enable OBJ - Outside window OBJ + Window externa habilitar OBJ Outside window enable blend - Outside window mezcla + Outside window habilitar mezcla OBJ window enable BG 0 - OBJ window BG 0 + OBJ window habilitar BG 0 OBJ window enable BG 1 - OBJ window BG 1 + OBJ window habilitar BG 1 OBJ window enable BG 2 - OBJ window BG 2 + OBJ window habilitar BG 2 OBJ window enable BG 3 - OBJ window BG 3 + OBJ window habilitar BG 3 OBJ window enable OBJ - OBJ window OBJ + OBJ window habilitar OBJ OBJ window enable blend - OBJ window mezcla + OBJ window habilitar mezcla Background mosaic size vertical - Tamaño mosaico fondo vertical + Tamaño vertical mosaico BG Background mosaic size horizontal - Tamaño mosaico fondo horizontal + Tamaño horizontal mosaico BG Object mosaic size vertical - Tamaño mosaico objeto vertical + Tamaño vertical mosaico OBJ Object mosaic size horizontal - Tamaño mosaico objeto horizontal + Tamaño horizontal mosaico OBJ @@ -1530,7 +1692,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Additive blending - Mezcla aditiva + Aditivo @@ -1595,12 +1757,12 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Sweep subtract - Sustracción en barrido + Sustracción en barridos Sweep time (in 1/128s) - Tiempo de barrido (en 1/128seg) + Tiempo de barridos (in 1/128s) @@ -1608,7 +1770,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Sound length - Longitud del sonido + Largo del sonido @@ -1621,14 +1783,14 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Envelope step time - Tiempo paso envolvente + Tiempo de paso en envoltura Envelope increase - Aumento envolvente + Aumento en envoltura @@ -1642,7 +1804,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Sound frequency - Frecuencia del sonido + Frecuencia de sonido @@ -1650,7 +1812,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Timed - Cronometrado + Timed @@ -1663,7 +1825,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Double-size wave table - Tabla de ondas de doble tamaño + Tabla de ondas de tamaño doble @@ -1673,7 +1835,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Enable channel 3 - Canal 3 activo + Habilitar canal 3 @@ -1719,7 +1881,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Register stages - Etapas de registros + Etapas del registro @@ -1739,57 +1901,57 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. PSG volume right - Volumen pulso derecha + PSG volumen derecha PSG volume left - Volumen pulso izquierda + PSG volumen izquierda Enable channel 1 right - Canal 1 derecha + Habilitar canal 1 derecha Enable channel 2 right - Canal 2 derecha + Habilitar canal 2 derecha Enable channel 3 right - Canal 3 derecha + Habilitar canal 3 derecha Enable channel 4 right - Canal 4 derecha + Habilitar canal 4 derecha Enable channel 1 left - Canal 1 izquierda + Habilitar canal 1 izquierda Enable channel 2 left - Canal 2 izquierda + Habilitar canal 2 izquierda Enable channel 3 left - Canal 3 izquierda + Habilitar canal 3 izquierda Enable channel 4 left - Canal 4 izquierda + Habilitar canal 4 izquierda PSG master volume - Volumen maestro pulso + PSG volumen maestro @@ -1804,17 +1966,17 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Enable channel A right - Canal A derecha + Habilitar canal A derecha Enable channel A left - Canal A izquierda + Habilitar canal A izquierda Channel A timer - Temporizador canal A + Canal A temporizador @@ -1838,27 +2000,27 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Channel A reset - Reinic. canal A + Canal A reinicializar Enable channel B right - Canal B derecha + Habilitar canal B derecha Enable channel B left - Canal B izquierda + Habilitar canal B izquierda Channel B timer - Temporizador canal B + Canal B temporizador Channel B reset - Reinic. canal B + Canal B reinicializar @@ -1949,7 +2111,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Address (bottom) - Dirección (inferior) + Dirección (abajo) @@ -1961,7 +2123,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Address (top) - Dirección (superior) + Dirección (arriba) @@ -1969,7 +2131,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Word count - Contador de word + Tamaño palabra @@ -1977,7 +2139,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Destination offset - Compensación de destino + Desplazamiento de destino @@ -2029,7 +2191,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Source offset - Compensación de origen + Desplazamiento de origen @@ -2053,7 +2215,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Start timing - Inicio de temporizador + Comienzo del tiempo @@ -2113,7 +2275,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Audio FIFO - FIFO de audio + Audio FIFO @@ -2170,7 +2332,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Cascade - Cacada + Cascada @@ -2321,7 +2483,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Keypad - Teclera + Keypad @@ -2332,7 +2494,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. SRAM wait - Espera SRAM + SRAM wait @@ -2407,7 +2569,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Disable - Desactivar + Deshabilitar @@ -2435,37 +2597,6 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd.Habilitar IRQs - - QGBA::InputController - - - Autofire - Botones turbo - - - - Bindings - - - - - QGBA::InputModel - - - Action - Acción - - - - Keyboard - Teclado - - - - Gamepad - Mando - - QGBA::KeyEditor @@ -2480,12 +2611,12 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Load State - Cargar captura + Cargar estado Save State - Guardar captura + Guardar estado @@ -2495,10 +2626,10 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Corrupted - Corrompido + Dañado - + Slot %1 Espacio %1 @@ -2513,7 +2644,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. STUB - AUXILIAR + STUB @@ -2538,7 +2669,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. GAME ERROR - ERROR DEL JUEGO + ERROR DE JUEGO @@ -2605,59 +2736,82 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd.ISO-8859-1 + + QGBA::MemorySearch + + + (⅟%0×) + (⅟%0×) + + + + 1 byte%0 + 1 byte%0 + + + + 2 bytes%0 + 2 bytes%0 + + + + 4 bytes%0 + 4 bytes%0 + + QGBA::ObjView - - + + 0x%0 0x%0 - + Off No - + Normal Normal - + Trans Trans - + OBJWIN OBJWIN - + Invalid Inválido - - + + N/A n/d - + Export sprite - + Exportar sprite - + Portable Network Graphics (*.png) - + Portable Network Graphics (*.png) - + Failed to open output PNG file: %1 - + Error al abrir el archivo PNG de salida: %1 @@ -2692,12 +2846,12 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Windows PAL (*.pal);;Adobe Color Table (*.act) - Paleta de WIndows (*.pal);;Tabla de colores Adobe (*.act) + Windows PAL (*.pal);;Adobe Color Table (*.act) Failed to open output palette file: %1 - Error al abrir el archivo de salida de paleta: %1 + Error al abrir el archivo de paleta de salida: %1 @@ -2720,843 +2874,837 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. (no database present) - (no se encuentra la base de datos) + (no hay base de datos) 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) - - Bindings - + + Keyboard + Teclado - + + Controllers + Controladores + + + + Shortcuts + Atajos de teclado + + + + Shaders + Shaders + + + Select BIOS - Elegir BIOS + Seleccionar BIOS QGBA::ShaderSelector - + No shader active - No hay programa shader activo + No hay shader activo - + Load shader - Cargar programa shader + Cargar shader - - %1 Shader (%.shader) - Programa shader de %1 (%.shader) - - - + No shader loaded - No hay programa shader cargado + No hay shader cargado - + by %1 por %1 - + Preprocessing - preprocesamiento + Preproceso - + Pass %1 Paso %1 + + QGBA::ShortcutController + + + Action + Acción + + + + Keyboard + Teclado + + + + Gamepad + Mando + + QGBA::VideoView Failed to open output video file: %1 - Error al abrir el archivo de salida de video: %1 + Error al abrir el archivo de video de salida: %1 Native (%0x%1) - Nativo (%0x%1) + Native (%0x%1) - + Select output file - Elegir archivo de salida + Seleccionar archivo de salida QGBA::Window - + Game Boy Advance ROMs (%1) ROMs de Game Boy Advance (%1) - - DS ROMs (%1) - - - - + Game Boy ROMs (%1) ROMs de Game Boy (%1) - + All ROMs (%1) Todas las ROMs (%1) - + %1 Video Logs (*.mvl) - + Video-registros de %1 (*.mvl) - + Archives (%1) - Archivos (%1) + Contenedores (%1) - - - + + + Select ROM - Elegir ROM + Seleccionar ROM - + + Select folder + Seleccionar carpeta + + + Game Boy Advance save files (%1) Archivos de guardado de Game Boy Advance (%1) + + - - Select save - Elegir guardado + Seleccionar guardado - + Select patch - Elegir parche + Seleccionar parche - + Patches (*.ips *.ups *.bps) Parches (*.ips *.ups *.bps) - - + + GameShark saves (*.sps *.xps) Guardados de GameShark (*.sps *.xps) - + Select video log - + Seleccionar video-registro - + Video logs (*.mvl) - + Video-registros (*.mvl) - - BIOS required - - - - - DS support requires dumps of the BIOS and firmware. - - - - + Crash Error fatal - + The game has crashed with the following error: %1 - El juego dejó de funcionar inesperadamente debido a este error: + El juego ha fallado fatalmente por esta razón: %1 - + Couldn't Load No se pudo cargar - + 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? - + 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 hizo una llamada al BIOS que no está implementada. Usa el BIOS oficial para obtener la mejor experiencia. + Este juego utiliza una llamada al BIOS que no se ha implementado. Utiliza el BIOS oficial para obtener la mejor experiencia. - + Really make portable? - ¿Realmente hacer "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 su ejecutable. ¿Quieres continuar? + 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 tendrán efecto hasta que se reinicie el emulador. + 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 dentro de archivo... + Cargar ROM desde contenedor... - - Load temporary save... - Cargar guardado temporal... - - - - Load &patch... - Cargar &parche... - - - - Boot BIOS - Iniciar al BIOS - - - - Replace ROM... - Reemplazar ROM... - - - - ROM &info... - &Información de ROM... - - - - Recent - Reciente - - - - Make portable - Hacer "portable" - - - - &Load state - Cargar captura de estado (&L) - - - - F10 - F10 - - - - &Save state - Guardar captura de e&stado - - - - Shift+F10 - DO NOT TRANSLATE - Shift+F10 - - - - Quick load - Cargado rápido - - - - Quick save - Guardado rápido - - - - Load recent - Cargar reciente - - - - Save recent - Guardar reciente - - - - Undo load state - Deshacer cargar la captura de estado - - - - F11 - DO NOT TRANSLATE - F11 - - - - Undo save state - Deshacer guardar la captura de estado - - - - Shift+F11 - DO NOT TRANSLATE - Shift+F11 - - - - - State &%1 - Captura de estado &%1 - - - - F%1 - F%1 - - - - Shift+F%1 - DO NOT TRANSLATE - Shift+F%1 - - - - Import GameShark Save - Importar guardado de GameShark - - - - Export GameShark Save - Exportar guardado de GameShark - - - - New multiplayer window - Nueva ventana multijugador - - - - About - Acerca de - - - - E&xit - Salir (&X) - - - - &Emulation - &Emulación - - - - &Reset - &Reinicializar - - - - Ctrl+R - Ctrl+R - - - - Sh&utdown - Apagar (&U) - - - - Yank game pak - Arrancar el game pak de su ranura - - - - &Pause - &Pausar - - - - Ctrl+P - Ctrl+P - - - - &Next frame - Saltar al próximo cuadro (&N) - - - - Ctrl+N - Ctrl+N - - - - Fast forward (held) - Avance rápido (mantener) - - - - &Fast forward - Avance rápido (&F) - - - - Shift+Tab - Shift+Tab - - - - Fast forward speed - Velocidad de avance rápido - - - - Unbounded - Ilimitado - - - - %0x - %0x - - - - Rewind (held) - Retroceder (mantener) - - - - Re&wind - Retroceder (&W) - - - - ~ - ~ - - - - Step backwards - Paso hacia atrás - - - - Ctrl+B - Ctrl+B - - - - Sync to &video - Sincronizar a &video - - - - Sync to &audio - Sincronizar a &audio - - - - Solar sensor - Sensor solar - - - - Increase solar level - Aumentar nivel solar - - - - Decrease solar level - Disminuir nivel solar - - - - Brightest solar level - Nivel solar más brillante - - - - Darkest solar level - Nivel solar más oscuro - - - - Brightness %1 - Brillo %1 - - - - Audio/&Video - Audio/&Video - - - - Frame size - Tamaño del cuadro - - - - %1x - %1x - - - - Toggle fullscreen - Pantalla completa - - - - Lock aspect ratio - Bloquear relación de aspecto - - - - Force integer scaling - - - - - Frame&skip - &Salto de cuadros - - - - Shader options... - Opciones del programa shader... - - - - Mute - Silenciar - - - - FPS target - Objetivo de FPS - - - - 15 - 15 - - - - 30 - 30 - - - - 45 - 45 - - - - Native (59.7) - Nativo (59.7) - - - - 60 - 60 - - - - 90 - 90 - - - - 120 - 120 - - - - 240 - 240 - - - - Take &screenshot - Tomar pantallazo (&S) - - - - F12 - F12 - - - - Record output... - Grabar salida... - - - - Record GIF... - Grabar GIF... - - - - Record video log... - - - - - Stop video log - - - - - Video layers - Capas de video - - - - Audio channels - Canales de audio - - - - &Tools - Herramien&tas - - - - View &logs... - Ver registros... (&L) - - - - Game &overrides... - Val&ores específicos por juego... - - - - Game &Pak sensors... - Sensores en el Game &Pak... - - - - &Cheats... - Tru&cos... - - - - Open debugger console... - Abrir la consola de depuración... - - - - Start &GDB server... - Iniciar servidor &GDB... - - - - Settings... - Ajustes... - - - - Game Boy Advance - Game Boy Advance - - - - Game Boy - Game Boy - - - - DS - - - - - Select folder - Elegir carpeta - - - + Add folder to library... Agregar carpeta a la biblioteca... - - Bilinear filtering - + + Load temporary save... + Cargar guardado temporal... - + + Load &patch... + Cargar &parche... + + + + Boot BIOS + Arrancar BIOS + + + + Replace ROM... + Reemplazar ROM... + + + + ROM &info... + &Información de la ROM... + + + + Recent + Recientes + + + + Make portable + Hacer "portable" + + + + &Load state + Ca&rgar estado + + + + F10 + F10 + + + + &Save state + Guardar e&stado + + + + Shift+F10 + Shift+F10 + + + + Quick load + Cargado rápido + + + + Quick save + Guardado rápido + + + + Load recent + Cargar reciente + + + + Save recent + Guardar reciente + + + + Undo load state + Deshacer cargar estado + + + + F11 + F11 + + + + Undo save state + Deshacer guardar estado + + + + Shift+F11 + Shift+F11 + + + + + State &%1 + Estado &%1 + + + + F%1 + F%1 + + + + Shift+F%1 + Shift+F%1 + + + + Import GameShark Save + Importar guardado de GameShark + + + + Export GameShark Save + Exportar guardado de GameShark + + + + New multiplayer window + Nueva ventana multijugador + + + + About + Acerca de + + + + E&xit + Salir (&X) + + + + &Emulation + &Emulación + + + + &Reset + &Reinicializar + + + + Ctrl+R + Ctrl+R + + + + Sh&utdown + Apagar (&U) + + + + Yank game pak + Tirar del cartucho + + + + &Pause + &Pausar + + + + Ctrl+P + Ctrl+P + + + + &Next frame + Cuadro siguie&nte + + + + Ctrl+N + Ctrl+N + + + + Fast forward (held) + Avance rápido (mantener) + + + + &Fast forward + &Avance rápido + + + + Shift+Tab + Shift+Tab + + + + 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 + + + + Ctrl+B + Ctrl+B + + + + Sync to &video + Sincronizar a &video + + + + Sync to &audio + Sincronizar a au&dio + + + + 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/&video + + + + Frame size + Tamaño del cuadro + + + + %1x + %1x + + + + 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 + + + + 15 + 15 + + + + 30 + 30 + + + + 45 + 45 + + + + Native (59.7) + Nativo (59.7) + + + + 60 + 60 + + + + 90 + 90 + + + + 120 + Bilineal120 + + + + 240 + 240 + + + + Take &screenshot + Tomar pan&tallazo + + + + F12 + F12 + + + + Record output... + Grabar salida... + + + + Record GIF... + Grabar GIF... + + + + Record video log... + Grabar video-registro... + + + + Stop video log + Detener video-registro + + + + Video layers + Capas de video + + + + Audio channels + Canales de audio + + + + &Tools + Herramien&tas + + + + View &logs... + Ver re&gistros... + + + + Game &overrides... + Ajustes específic&os por juego... + + + + Game &Pak sensors... + Sensores del Game &Pak... + + + + &Cheats... + Tru&cos... + + + + Settings... + Ajustes... + + + + Open debugger console... + Abrir consola de depuración... + + + + Start &GDB server... + Iniciar servidor &GDB... + + + View &palette... Ver &paleta... - + View &sprites... Ver &sprites... - + View &tiles... Ver &tiles... - + View memory... Ver memoria... - - View &I/O registers... - Ver reg&istros E/S... + + Search memory... + Buscar memoria... - + + View &I/O registers... + Ver registros &I/O... + + + Exit fullscreen Salir de pantalla completa - + Autofire - Botones turbo + Disparo automático - + Autofire A - Turbo A + Disparo automático A - + Autofire B - Turbo B + Disparo automático B - + Autofire L - Turbo L + Disparo automático L - + Autofire R - Turbo R + Disparo automático R - + Autofire Start - Turbo Start + Disparo automático Start - + Autofire Select - Turbo Select + Disparo automático Select - + Autofire Up - Turbo Arriba + Disparo automático Arriba - + Autofire Right - Turbo Derecha + Disparo automático Derecha - + Autofire Down - Turbo Abajo + Disparo automático Abajo - + Autofire Left - Turbo Izquierda + Disparo automático Izquierda @@ -3564,17 +3712,17 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. GBA - GBA + GBA GB - GB + GB ? - ? + ? @@ -3607,7 +3755,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Game ID: - ID del juego: + ID de juego: @@ -3660,7 +3808,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Start time at - Contar desde el + Empezar desde esta hora @@ -3691,13 +3839,13 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Set Y - Config. Y + Ajustar Y Set X - Config. X + Ajustar X @@ -3720,7 +3868,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Audio/Video - Audio/Video + Audio/video @@ -3732,6 +3880,11 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd.Emulation Emulación + + + BIOS + BIOS + Paths @@ -3740,7 +3893,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Audio driver: - Controlador de audio: + Sistema de audio: @@ -3832,7 +3985,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Display driver: - Controlador de video: + Sistema de video: @@ -3846,7 +3999,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. - + frames cuadros @@ -3863,7 +4016,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Sync: - SIncronizar a: + Sincronizar con: @@ -3878,225 +4031,212 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Lock aspect ratio - Bloquear relación de aspecto - - - - Force integer scaling - - - - - List view - - - - - Tree view - - - - - Library: - Biblioteca: - - - - Show when no game open - Mostrar al no haber juego abierto - - - - Clear cache - Limpiar caché - - - - Fast forward speed: - Velocidad de avance rápido: - - - - - - - - - - - - - Browse - Examinar - - - - DS BIOS 7 file: - - - - - DS BIOS 9 file: - - - - - Use BIOS file if found - Usar archivo BIOS si hay - - - - Skip BIOS intro - Saltar pantalla de inicio de la BIOS - - - - × - × - - - - Unbounded - Ilimitado - - - - Suspend screensaver - No permitir protector de pantalla - - - - BIOS - BIOS - - - - Pause when inactive - Pausar al estar inactivo - - - - Run all - Ejecutar todos - - - - Remove known - Eliminar los conocidos - - - - Detect and remove - Detectar y eliminar - - - - Allow opposing input directions - Permitir direcciones opuestas - - - - - Screenshot - Pantallazo - - - - - Save data - Datos de guardado - - - - - Cheat codes - Trucos - - - - Enable rewind - Habilitar retroceso + Bloquear proporción de aspecto Bilinear filtering - + Filtro bilineal - + + Force integer scaling + Forzar escala a enteros + + + + Language + Idioma + + + + English + English + + + + Library: + Biblioteca: + + + + List view + Lista + + + + Tree view + Árbol + + + + Show when no game open + Mostrar cuando no haya un juego abierto + + + + Clear cache + Limpiar caché + + + + Allow opposing input directions + Permitir direcciones opuestas al mismo tiempo + + + + Suspend screensaver + Suspender protector de pantalla + + + + Pause when inactive + Pausar al no estar activo + + + + Fast forward speed: + Velocidad de avance rápido: + + + + × + × + + + + Unbounded + Sin límite + + + + Enable rewind + Habilitar el rebobinar + + + Rewind history: - Historial de retroceso: + Historial de rebobinado: - + Idle loops: Bucles inactivos: + + + Run all + Ejecutarlos todos + + Remove known + Eliminar los conocidos + + + + Detect and remove + Detectar y eliminar + + + Savestate extra data: - Datos extras en capturas de estado: + Guardar datos extra con el estado: + + + + + Screenshot + Pantallazo + + Save data + Datos de guardado + + + + + Cheat codes + Trucos + + + Load extra data: - Cargar datos extra: + Cargar datos extra con el estado: - + Rewind affects save data - + El rebobinar afecta los datos de guardado - + Preload entire ROM into memory - + Cargar ROM completa a la memoria - + GB BIOS file: - Archivo de BIOS GB: + Archivo BIOS GB: - + + + + + + + + Browse + Examinar + + + + Use BIOS file if found + Usar archivo BIOS si fue encontrado + + + + Skip BIOS intro + Saltar animación de entrada del BIOS + + + GBA BIOS file: - Archivo de BIOS GBA: + Archivo BIOS GBA: - + GBC BIOS file: - Archivo de BIOS GBC: + Archivo BIOS GBC: - - DS firmware file: - - - - + Save games - Guardados de juego + Datos de guardado - - - - + + + + Same directory as the ROM - Mismo directorio de la ROM + Al mismo directorio que la ROM - + Save states - Capturas de estado + Estados de guardado - + Screenshots Pantallazos - + Patches Parches @@ -4106,12 +4246,12 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Shaders - Programas shader + Shaders Active Shader: - Programa shader activo: + Shader activo: @@ -4131,12 +4271,12 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Unload Shader - Cerrar programa shader + Cerrar shader Load New Shader - Cargar nuevo p. shader + Cargar nuevo shader @@ -4144,7 +4284,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Edit Shortcuts - Editar accesos directos de teclado + Editar atajos de teclado @@ -4182,7 +4322,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Magnification - Magnificación + Ampliación @@ -4205,12 +4345,12 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Select File - Elegir archivo + Seleccionar archivo Presets - Ajustes predeterminados + Ajustes predefinidos @@ -4251,7 +4391,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Native - Nativo + Nativa @@ -4286,17 +4426,17 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. h.264 (NVENC) - + h.264 (NVENC) HEVC - + HEVC VP8 - + VP8 @@ -4336,7 +4476,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Bitrate (kbps) - Tasa de bits (kbps) + Tasa de bits (kbps) @@ -4366,7 +4506,7 @@ Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd. Lock aspect ratio - Bloquear relación de aspecto + Bloquear proporción de aspecto diff --git a/src/platform/qt/ts/mgba-it.ts b/src/platform/qt/ts/medusa-emu-it.ts similarity index 100% rename from src/platform/qt/ts/mgba-it.ts rename to src/platform/qt/ts/medusa-emu-it.ts diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 274f230e5..6a915592d 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -86,11 +86,17 @@ else() endif() endif() +if(ENABLE_SCRIPTING) + if(BUILD_PYTHON) + list(APPEND PLATFORM_LIBRARY "${PYTHON_LIBRARY}") + endif() +endif() + add_executable(${BINARY_NAME}-sdl WIN32 ${PLATFORM_SRC} ${MAIN_SRC}) set_target_properties(${BINARY_NAME}-sdl PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES}") target_link_libraries(${BINARY_NAME}-sdl ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) set_target_properties(${BINARY_NAME}-sdl PROPERTIES OUTPUT_NAME ${BINARY_NAME}) -install(TARGETS ${BINARY_NAME}-sdl DESTINATION bin COMPONENT ${BINARY_NAME}-sdl) +install(TARGETS ${BINARY_NAME}-sdl DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-sdl) if(UNIX) install(FILES ${CMAKE_SOURCE_DIR}/doc/${BINARY_NAME}.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-sdl) endif() diff --git a/src/platform/sdl/gl-sdl.c b/src/platform/sdl/gl-sdl.c index b723a495c..2919fc2b3 100644 --- a/src/platform/sdl/gl-sdl.c +++ b/src/platform/sdl/gl-sdl.c @@ -53,7 +53,7 @@ void mSDLGLRunloop(struct mSDLRenderer* renderer, void* user) { SDL_Event event; struct VideoBackend* v = &renderer->gl.d; - while (context->state < THREAD_EXITING) { + while (mCoreThreadIsActive(context)) { while (SDL_PollEvent(&event)) { mSDLHandleEvent(context, &renderer->player, &event); #if SDL_VERSION_ATLEAST(2, 0, 0) @@ -66,10 +66,10 @@ void mSDLGLRunloop(struct mSDLRenderer* renderer, void* user) { #endif } - if (mCoreSyncWaitFrameStart(&context->sync)) { + if (mCoreSyncWaitFrameStart(&context->impl->sync)) { v->postFrame(v, renderer->outputBuffer); } - mCoreSyncWaitFrameEnd(&context->sync); + mCoreSyncWaitFrameEnd(&context->impl->sync); v->drawFrame(v); v->swap(v); } diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 6c2306c09..521028d58 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -13,6 +13,13 @@ #ifdef USE_EDITLINE #include "feature/editline/cli-el-backend.h" #endif +#ifdef ENABLE_SCRIPTING +#include + +#ifdef ENABLE_PYTHON +#include "platform/python/engine.h" +#endif +#endif #include #include @@ -56,7 +63,7 @@ int main(int argc, char** argv) { initParserForGraphics(&subparser, &graphicsOpts); bool parsed = parseArguments(&args, argc, argv, &subparser); - if (!args.fname) { + if (!args.fname && !args.showVersion) { parsed = false; } if (!parsed || args.showHelp) { @@ -159,6 +166,13 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { return 1; } mCoreAutoloadSave(renderer->core); +#ifdef ENABLE_SCRIPTING + struct mScriptBridge* bridge = mScriptBridgeCreate(); +#ifdef ENABLE_PYTHON + mPythonSetup(bridge); +#endif +#endif + #ifdef USE_DEBUGGERS struct mDebugger* debugger = mDebuggerCreate(args->debuggerType, renderer->core); if (debugger) { @@ -171,6 +185,9 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { mDebuggerAttach(debugger, renderer->core); mDebuggerEnter(debugger, DEBUGGER_ENTER_MANUAL, NULL); } +#ifdef ENABLE_SCRIPTING + mScriptBridgeSetDebugger(bridge, debugger); +#endif #endif if (args->patch) { @@ -212,6 +229,11 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { printf("Could not run game. Are you sure the file exists and is a compatible game?\n"); } renderer->core->unloadROM(renderer->core); + +#ifdef ENABLE_SCRIPTING + mScriptBridgeDestroy(bridge); +#endif + return didFail; } diff --git a/src/platform/sdl/sdl-audio.c b/src/platform/sdl/sdl-audio.c index 6f9782669..8236fefde 100644 --- a/src/platform/sdl/sdl-audio.c +++ b/src/platform/sdl/sdl-audio.c @@ -40,12 +40,11 @@ bool mSDLInitAudio(struct mSDLAudio* context, struct mCoreThread* threadContext) mLOG(SDL_AUDIO, ERROR, "Could not open SDL sound system"); return false; } - context->samples = context->obtainedSpec.samples; context->core = 0; if (threadContext) { context->core = threadContext->core; - context->sync = &threadContext->sync; + context->sync = &threadContext->impl->sync; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_PauseAudioDevice(context->deviceId, 0); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 197c8443e..fb5075abc 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -416,7 +416,7 @@ static void _mSDLHandleKeypress(struct mCoreThread* context, struct mSDLPlayer* return; } if (event->keysym.sym == SDLK_TAB) { - context->sync.audioWait = event->type != SDL_KEYDOWN; + context->impl->sync.audioWait = event->type != SDL_KEYDOWN; return; } if (event->keysym.sym == SDLK_BACKQUOTE) { diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index f7561f018..632c20cc0 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #define GCN1_INPUT 0x47434E31 @@ -114,6 +115,9 @@ static bool frameLimiter = true; static int scaleFactor; static unsigned corew, coreh; +uint32_t* romBuffer; +size_t romBufferSize; + static void* framebuffer[2] = { 0, 0 }; static int whichFb = 0; @@ -242,6 +246,10 @@ int main(int argc, char* argv[]) { AUDIO_RegisterDMACallback(_audioDMA); memset(audioBuffer, 0, sizeof(audioBuffer)); +#ifdef FIXED_ROM_BUFFER + romBufferSize = SIZE_CART0; + romBuffer = anonymousMemoryMap(romBufferSize); +#endif #if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5) #error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5 @@ -510,6 +518,10 @@ int main(int argc, char* argv[]) { } mGUIDeinit(&runner); +#ifdef FIXED_ROM_BUFFER + mappedMemoryFree(romBuffer, romBufferSize); +#endif + free(fifo); free(texmem); free(rescaleTexmem);