all repos — mgba @ 1de4c8dc2e4243b07f7de5aef7aecc3a4b90f552

mGBA Game Boy Advance Emulator

Merge branch 'master' into feature/input-revamp
Vicki Pfau vi@endrift.com
Wed, 12 Jul 2017 18:53:49 -0700
commit

1de4c8dc2e4243b07f7de5aef7aecc3a4b90f552

parent

97e2004fd34cb5b0c377f8268e332609ac29be6f

62 files changed, 2328 insertions(+), 1201 deletions(-)

jump to
M CHANGESCHANGES

@@ -1,177 +1,197 @@

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 + - ARM7: Fix MLA/*MULL/*MLAL timing + - Core: Fix crash with rewind if savestates shrink + - 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 - - GBA Memory: Improve initial skipped BIOS state + - 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 - - ARM7: Fix MLA/*MULL/*MLAL timing - - GBA: Fix multiboot ROM loading + - 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 - - GB MBC: Fix ROM bank overflows getting set to bank 0 + - 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 - - 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 + - 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 - - 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 + - SDL: Fix showing version number - 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 - - Qt: Fix window icon being stretched - - Qt: Fix data directory path + - 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 + - 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 - - 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 - - 3DS, PSP2, Wii: Last directory loaded is saved + - 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 + - PSP2: Update toolchain to use vita.cmake + - Qt: Add language selector - Qt: Minor test fixes - - PSP2: Update toolchain to use vita.cmake + - 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
M CMakeLists.txtCMakeLists.txt

@@ -19,6 +19,7 @@ set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support")

set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core") set(M_CORE_GB ON CACHE BOOL "Build Game Boy 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")

@@ -253,6 +254,10 @@

if(DEFINED 3DS OR DEFINED PSP2 OR DEFINED WII) set(USE_DEBUGGERS OFF) set(USE_SQLITE3 OFF) +endif() + +if(DEFINED 3DS OR DEFINED WII) + add_definitions(-DFIXED_ROM_BUFFER) endif() if(NOT M_CORE_GBA)

@@ -349,6 +354,7 @@

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

@@ -391,6 +397,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

@@ -593,6 +600,18 @@ include_directories(AFTER ${SQLITE3_INCLUDE_DIRS})

list(APPEND DEPENDENCY_LIB ${SQLITE3_LIBRARIES}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libsqlite3-0") 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})

@@ -642,6 +661,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})

@@ -718,7 +741,7 @@ set_target_properties(${BINARY_NAME} PROPERTIES VERSION ${LIB_VERSION_STRING} SOVERSION ${LIB_VERSION_ABI} COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")

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/mgba-16.png DESTINATION share/icons/hicolor/16x16/apps RENAME mgba.png COMPONENT lib${BINARY_NAME}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/mgba-24.png DESTINATION share/icons/hicolor/24x24/apps RENAME mgba.png COMPONENT lib${BINARY_NAME}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/mgba-32.png DESTINATION share/icons/hicolor/32x32/apps RENAME mgba.png COMPONENT lib${BINARY_NAME})

@@ -754,6 +777,11 @@ set(BUILD_SDL OFF)

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

@@ -826,11 +854,6 @@ set_target_properties(${BINARY_NAME}-suite PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")

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

@@ -846,7 +869,7 @@ endif()

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})
M include/mgba/core/rewind.hinclude/mgba/core/rewind.h

@@ -31,6 +31,7 @@ bool onThread;

Thread thread; Condition cond; Mutex mutex; + bool ready; #endif };
A 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 <mgba-util/common.h> + +CXX_GUARD_START + +#ifdef USE_DEBUGGERS +#include <mgba/debugger/debugger.h> +#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
M include/mgba/core/thread.hinclude/mgba/core/thread.h

@@ -11,15 +11,40 @@

CXX_GUARD_START #include <mgba/core/log.h> -#include <mgba/core/rewind.h> -#include <mgba/core/sync.h> -#include <mgba-util/threading.h> 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 <mgba/core/rewind.h> +#include <mgba/core/sync.h> +#include <mgba-util/threading.h> + enum mCoreThreadState { THREAD_INITIALIZED = -1, THREAD_RUNNING = 0,

@@ -37,17 +62,7 @@ THREAD_SHUTDOWN,

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,18 +72,11 @@ enum mCoreThreadState savedState;

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);
M include/mgba/debugger/debugger.hinclude/mgba/debugger/debugger.h

@@ -92,6 +92,7 @@ struct mCPUComponent d;

struct mDebuggerPlatform* platform; enum mDebuggerState state; struct mCore* core; + struct mScriptBridge* bridge; void (*init)(struct mDebugger*); void (*deinit)(struct mDebugger*);
M include/mgba/internal/gb/mbc.hinclude/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 {
M include/mgba/internal/gb/serialize.hinclude/mgba/internal/gb/serialize.h

@@ -145,7 +145,7 @@ * | 0x0017A - 0x0017B: Next HDMA destination

* | 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 @@ uint8_t rtcRegs[5];

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];
M src/core/rewind.csrc/core/rewind.c

@@ -30,6 +30,7 @@ context->size = 0;

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 @@ context->previousState = context->currentState;

context->currentState = nextState; #ifndef DISABLE_THREADING if (context->onThread) { + context->ready = true; ConditionWake(&context->cond); MutexUnlock(&context->mutex); return;

@@ -121,6 +123,12 @@ return false;

} --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 @@ size = size2;

} void* current = context->currentState->map(context->currentState, size, MAP_READ); void* previous = context->previousState->map(context->previousState, size, MAP_WRITE); - patch->d.applyPatch(&patch->d, 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) { + while (!rewindContext->ready && rewindContext->onThread) { + ConditionWait(&rewindContext->cond, &rewindContext->mutex); + } + if (rewindContext->ready) { _rewindDiff(rewindContext); - state = rewindContext->currentState; } - ConditionWait(&rewindContext->cond, &rewindContext->mutex); + rewindContext->ready = false; } MutexUnlock(&rewindContext->mutex); return 0;
A 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 <mgba/core/scripting.h> + +#include <mgba-util/table.h> +#include <mgba-util/vfs.h> + +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; +}
M src/core/thread.csrc/core/thread.c

@@ -38,7 +38,7 @@ #endif

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 @@ }

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 @@ threadContext->sync.videoFrameWait = videoFrameWait;

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 @@ if (!thread) {

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 @@ struct mCoreThread* thread = context;

if (!thread) { return; } - _changeState(thread, THREAD_CRASHED, true); + _changeState(thread->impl, THREAD_CRASHED, true); } void _coreSleep(void* context) {

@@ -157,7 +157,7 @@ .sleep = _coreSleep,

.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 @@ mLogFilterLoad(threadContext->logger.d.filter, &core->config);

} 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 @@ if (threadContext->resetCallback) {

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 @@ }

} } - 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 @@ return 0;

} 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 @@ sigaddset(&signals, SIGTRAP);

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
M src/debugger/cli-debugger.csrc/debugger/cli-debugger.c

@@ -12,6 +12,10 @@ #include <mgba/core/version.h>

#include <mgba/internal/debugger/parser.h> #include <mgba-util/string.h> +#if ENABLE_SCRIPTING +#include <mgba/core/scripting.h> +#endif + #if !defined(NDEBUG) && !defined(_WIN32) #include <signal.h> #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 @@ { "watch/w", _setWriteWatchpoint, CLIDVParse, "Set a write watchpoint" },

{ "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

@@ -410,6 +420,20 @@ }

debugger->backend->printf(debugger->backend, "\n"); } } + +#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) {
M src/debugger/debugger.csrc/debugger/debugger.c

@@ -13,6 +13,10 @@ #ifdef USE_GDB_STUB

#include <mgba/internal/debugger/gdb-stub.h> #endif +#if ENABLE_SCRIPTING +#include <mgba/core/scripting.h> +#endif + const uint32_t DEBUGGER_ID = 0xDEADBEEF; mLOG_DEFINE_CATEGORY(DEBUGGER, "Debugger", "core.debugger");

@@ -34,6 +38,7 @@ #endif

}; union DebugUnion* debugger = malloc(sizeof(union DebugUnion)); + memset(debugger, 0, sizeof(*debugger)); switch (type) { case DEBUGGER_CLI:

@@ -109,6 +114,11 @@ debugger->state = DEBUGGER_PAUSED;

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) {
M src/gb/audio.csrc/gb/audio.c

@@ -620,8 +620,9 @@ sampleRight += audio->ch4.sample;

} } - *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 @@ envelope->initialVolume = GBAudioRegisterSweepGetInitialVolume(value);

if (!envelope->stepTime) { // TODO: Improve "zombie" mode ++envelope->currentVolume; + envelope->currentVolume &= 0xF; } _updateEnvelopeDead(envelope); envelope->nextStep = envelope->stepTime;

@@ -709,7 +711,7 @@ return (envelope->initialVolume || envelope->direction) && envelope->dead != 2;

} 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 @@ }

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 @@ int32_t cycles = 0;

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); }
M src/gb/gb.csrc/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 @@ gb->romVf = 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 @@ mappedMemoryFree(gb->memory.rom, GB_SIZE_CART_MAX);

} 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 @@ mappedMemoryFree(newRom, GB_SIZE_CART_MAX);

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);
M src/gb/mbc.csrc/gb/mbc.c

@@ -50,7 +50,7 @@ gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);

} } -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 @@ break;

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 @@ break;

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;
M src/gb/memory.csrc/gb/memory.c

@@ -629,6 +629,28 @@ flags = GBSerializedMemoryFlagsSetIme(flags, memory->ime);

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 @@ memory->rtcLatched = GBSerializedMemoryFlagsGetRtcLatched(flags);

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) {
M src/gb/timer.csrc/gb/timer.c

@@ -118,7 +118,7 @@ STORE_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);

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 @@ uint32_t when;

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);
M src/gba/gba.csrc/gba/gba.c

@@ -42,7 +42,7 @@ static bool _setSoftwareBreakpoint(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t* opcode);

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 @@ mappedMemoryFree(gba->memory.rom, SIZE_CART0);

} 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 @@ if (gba->pristineRomSize > SIZE_CART0) {

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 @@ gba->memory.mirroring = false;

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 @@ mappedMemoryFree(newRom, SIZE_CART0);

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);
M src/gba/io.csrc/gba/io.c

@@ -709,16 +709,16 @@

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:
M src/gba/memory.csrc/gba/memory.c

@@ -297,10 +297,8 @@ default:

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 @@ if (callbacks->coreCrashed) {

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 @@ if (gba->cpu->memory.activeRegion == gba->memory.rom) {

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);
M src/gba/sio.csrc/gba/sio.c

@@ -155,11 +155,13 @@ switch (sio->mode) {

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
M src/gba/timer.csrc/gba/timer.c

@@ -70,7 +70,7 @@ GBAAudioSampleFIFO(&gba->audio, 1, cyclesLate);

} } - if (timerId < 4) { + if (timerId < 3) { struct GBATimer* nextTimer = &gba->timers[timerId + 1]; if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled? ++gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)];

@@ -141,10 +141,6 @@ void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) {

struct GBATimer* currentTimer = &gba->timers[timer]; if (!GBATimerFlagsIsEnable(currentTimer->flags) || GBATimerFlagsIsCountUp(currentTimer->flags)) { return; - } - - if (gba->memory.lastPrefetchedPc > (uint32_t) gba->cpu->gprs[ARM_PC]) { - cyclesLate -= ((gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * gba->cpu->memory.activeSeqCycles16) / WORD_SIZE_THUMB; } int prescaleBits = GBATimerFlagsGetPrescaleBits(currentTimer->flags);
M src/platform/python/CMakeLists.txtsrc/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)
M src/platform/python/_builder.hsrc/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 @@

#include <mgba/core/core.h> #include <mgba/core/mem-search.h> #include <mgba/core/tile-cache.h> +#include <mgba/core/thread.h> #include <mgba/core/version.h> #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 @@ #include <mgba/internal/gb/gb.h>

#include <mgba/internal/gba/input.h> #include <mgba/internal/gb/renderers/tile-cache.h> #endif +#ifdef USE_DEBUGGERS +#include <mgba/debugger/debugger.h> +#endif
M src/platform/python/_builder.pysrc/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 <mgba-util/common.h> #include <mgba/core/core.h> #include <mgba/core/log.h> #include <mgba/core/mem-search.h> +#include <mgba/core/thread.h> #include <mgba/core/tile-cache.h> #include <mgba/core/version.h> +#include <mgba/debugger/debugger.h> #include <mgba/internal/arm/arm.h> #include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/input.h>

@@ -35,6 +38,7 @@ #include <mgba-util/png-io.h>

#include <mgba-util/vfs.h> #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 @@ """, include_dirs=[incdir, srcdir],

extra_compile_args=cppflags, libraries=["mgba"], 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 @@ continue

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")
A 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 <mgba/core/core.h> + +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; +}
A 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 <mgba-util/common.h> + +#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);
A 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 <mgba/core/scripting.h> +#include <mgba-util/string.h> +#include <mgba-util/vfs.h> + +#ifdef USE_DEBUGGERS +#include <mgba/debugger/debugger.h> +#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
A 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 <mgba-util/common.h> + +CXX_GUARD_START + +struct mScriptBridge; +struct mPythonScriptEngine; +struct mPythonScriptEngine* mPythonCreateScriptEngine(void); +void mPythonSetup(struct mScriptBridge* sb); + +CXX_GUARD_END + +#endif
A 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
M src/platform/python/log.csrc/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 <mgba/core/log.h> - -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;
M src/platform/python/log.hsrc/platform/python/log.h

@@ -5,6 +5,8 @@ * 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 <mgba/core/log.h> +#include "pycommon.h" + struct mLoggerPy { struct mLogger d; void* pyobj;
M src/platform/python/mgba/core.pysrc/platform/python/mgba/core.py

@@ -4,7 +4,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/. 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 @@ raise RuntimeError("Core must be reset first")

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 @@ @classmethod

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 @@ self._core.reset(self._core)

self._wasReset = True @needsReset + @protected def runFrame(self): self._core.runFrame(self._core) @needsReset + @protected def runLoop(self): self._core.runLoop(self._core)

@@ -132,25 +194,65 @@

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
A 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)
A 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)
M src/platform/python/mgba/vfs.pysrc/platform/python/mgba/vfs.py

@@ -108,6 +108,13 @@

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)
A 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 <mgba-util/common.h> + +#ifndef PYEXPORT +#define PYEXPORT extern +#endif + +#endif
M src/platform/python/vfs-py.csrc/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 <mgba-util/vfs.h> - -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) {
M src/platform/python/vfs-py.hsrc/platform/python/vfs-py.h

@@ -3,8 +3,9 @@ *

* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <mgba-util/vfs.h> -#include <mgba-util/vfs.h> +#include "pycommon.h" struct VFilePy { struct VFile d;
M src/platform/qt/AudioDevice.cppsrc/platform/qt/AudioDevice.cpp

@@ -26,13 +26,13 @@ if (!m_context || !mCoreThreadIsActive(m_context)) {

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 @@ LOG(QT, WARN) << tr("Audio device is missing its core");

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<GBAStereoSample*>(data)->left, available, true); blip_read_samples(m_context->core->getAudioChannel(m_context->core, 1), &reinterpret_cast<GBAStereoSample*>(data)->right, available, true); - mCoreSyncConsumeAudio(&m_context->sync); + mCoreSyncConsumeAudio(&m_context->impl->sync); return available * sizeof(GBAStereoSample); }
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -214,7 +214,9 @@ else()

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 @@ qt5_create_translation(TRANSLATION_FILES ${SOURCE_FILES} ${UI_FILES} ${TS_FILES} OPTIONS -locations absolute -no-obsolete)

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,12 +264,12 @@ target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES})

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) if(DESKTOP_FILE_INSTALL) - install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"\$ENV{DESTDIR}\${CMAKE_INSTALL_FULL_DATADIR}/applications/\")") + install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications/\")") endif() endif() if(UNIX)

@@ -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)
M src/platform/qt/DisplayGL.cppsrc/platform/qt/DisplayGL.cpp

@@ -69,7 +69,7 @@ m_gl->context()->moveToThread(m_drawThread);

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

@@ -333,9 +333,9 @@ if (m_queue.isEmpty() || !mCoreThreadIsActive(m_context)) {

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

@@ -349,7 +349,7 @@ }

m_delayTimer.restart(); } } else { - mCoreSyncWaitFrameEnd(&m_context->sync); + mCoreSyncWaitFrameEnd(&m_context->impl->sync); } if (!m_queue.isEmpty()) { QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
M src/platform/qt/DisplayQt.cppsrc/platform/qt/DisplayQt.cpp

@@ -50,7 +50,8 @@ #else

m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB555); #endif #else - m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB32); + m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_ARGB32); + m_backing = m_backing.convertToFormat(QImage::Format_RGB32); #endif }
M src/platform/qt/GameController.cppsrc/platform/qt/GameController.cpp

@@ -79,7 +79,7 @@ #endif

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 @@

if (controller->m_multiplayer) { controller->m_multiplayer->detachGame(controller); } - controller->m_patch = QString(); controller->clearOverride(); controller->endVideoLog();

@@ -300,8 +299,8 @@ m_config = 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 @@ }

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 @@ if (patch) {

m_threadContext.core->loadPatch(m_threadContext.core, patch); } patch->close(patch); + m_patch = QString(); } else { mCoreAutoloadPatch(m_threadContext.core); }

@@ -483,6 +476,13 @@

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 @@ mCoreLoadFile(m_threadContext.core, m_fname.toLocal8Bit().constData());

} void GameController::loadPatch(const QString& path) { + m_patch = path; if (m_gameOpen) { closeGame(); - m_patch = path; openGame(); - } else { - m_patch = path; } }

@@ -706,14 +704,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; } } }

@@ -724,7 +722,7 @@ if (!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; } }

@@ -852,8 +850,10 @@ LOG(QT, ERROR) << tr("Failed to start audio processor");

// 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; + } } }

@@ -874,9 +874,11 @@

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

@@ -994,22 +996,25 @@ }

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

@@ -1019,24 +1024,30 @@

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) {
M src/platform/qt/SettingsView.cppsrc/platform/qt/SettingsView.cpp

@@ -10,6 +10,7 @@ #include "ConfigController.h"

#include "Display.h" #include "GBAApp.h" #include "InputController.h" +#include "ShaderSelector.h" #include "ShortcutView.h" #include <mgba/core/serialize.h>

@@ -149,7 +150,7 @@

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

@@ -171,6 +172,24 @@ m_ui.stackedWidget->addWidget(m_shortcutView);

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()) {

@@ -299,6 +318,8 @@ loadSetting("screenshotPath", m_ui.screenshotPath);

loadSetting("patchPath", m_ui.patchPath); 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 src/platform/qt/SettingsView.hsrc/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 @@ ConfigController* m_controller;

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*);
M src/platform/qt/ShaderSelector.cppsrc/platform/qt/ShaderSelector.cpp

@@ -21,6 +21,8 @@ #include <mgba/core/version.h>

#include <mgba-util/vfs.h> #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 @@

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::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() {

@@ -114,6 +118,10 @@

disconnect(this, &ShaderSelector::saved, 0, 0); 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) {

@@ -264,7 +272,6 @@ case QDialogButtonBox::Reset:

emit reset(); break; case QDialogButtonBox::Ok: - m_config->setOption("shader", m_shaderPath); emit saved(); close(); break;

@@ -275,3 +282,5 @@ default:

break; } } + +#endif
M src/platform/qt/ShaderSelector.hsrc/platform/qt/ShaderSelector.h

@@ -6,6 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef QGBA_SHADER_SELECTOR_H #define QGBA_SHADER_SELECTOR_H +#if defined(BUILD_GL) || defined(BUILD_GLES) + #include <QDialog> #include "ui_ShaderSelector.h"

@@ -56,3 +58,5 @@

} #endif + +#endif
M src/platform/qt/ShaderSelector.uisrc/platform/qt/ShaderSelector.ui

@@ -81,32 +81,35 @@ </property>

</widget> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> <widget class="QPushButton" name="unload"> <property name="text"> <string>Unload Shader</string> </property> </widget> </item> - <item> + <item row="0" column="1"> <widget class="QPushButton" name="load"> <property name="text"> <string>Load New Shader</string> </property> </widget> </item> + <item row="1" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults</set> + </property> + <property name="centerButtons"> + <bool>true</bool> + </property> + </widget> + </item> </layout> - </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Ok|QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults</set> - </property> - </widget> </item> </layout> </widget>
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -79,7 +79,9 @@ m_controller->setInputController(&m_inputController);

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

@@ -178,7 +180,6 @@ connect(this, &Window::startDrawing, m_display, &Display::startDrawing, Qt::QueuedConnection);

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

@@ -284,6 +285,7 @@ if (opts->fullscreen) {

enterFullScreen(); } +#if defined(BUILD_GL) || defined(BUILD_GLES) if (opts->shader) { struct VDir* shader = VDirOpen(opts->shader); if (shader) {

@@ -292,6 +294,7 @@ m_shaderView->refreshShaders();

shader->close(shader); } } +#endif m_mruFiles = m_config->getMRU(); updateMRU();

@@ -469,6 +472,11 @@ }

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

@@ -691,14 +699,10 @@ }

} 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); + emit startDrawing(context); for (QAction* action : m_gameActions) { action->setDisabled(false); }

@@ -805,6 +809,7 @@ tr("The game has crashed with the following error:\n\n%1").arg(errorMessage),

QMessageBox::Ok, this, Qt::Sheet); crash->setAttribute(Qt::WA_DeleteOnClose); crash->show(); + connect(m_controller, &GameController::gameStarted, crash, &QWidget::close); } void Window::gameFailed() {

@@ -813,6 +818,7 @@ tr("Could not load game. Are you sure it's in the correct format?"),

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

@@ -1272,13 +1278,6 @@ for (int i = 0; i <= 10; ++i) {

skip->addValue(QString::number(i), i, skipMenu); } 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();
M src/platform/qt/main.cppsrc/platform/qt/main.cpp

@@ -30,7 +30,7 @@

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

@@ -57,6 +57,12 @@

QTranslator qtTranslator; 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/");
M src/platform/qt/ts.cmakesrc/platform/qt/ts.cmake

@@ -1,7 +1,6 @@

-file(GLOB TRANSLATION_FILES "${QM_BASE}/*.qm") file(WRITE ${TRANSLATION_QRC} "<RCC>\n\t<qresource prefix=\"/translations/\">\n") foreach(TS ${TRANSLATION_FILES}) get_filename_component(TS_BASE "${TS}" NAME) - file(APPEND ${TRANSLATION_QRC} "\t\t<file>${TS_BASE}</file>\n") + file(APPEND ${TRANSLATION_QRC} "\t\t<file alias=\"${TS_BASE}\">${TS}</file>\n") endforeach() file(APPEND ${TRANSLATION_QRC} "\t</qresource>\n</RCC>")
M src/platform/qt/ts/mgba-de.tssrc/platform/qt/ts/mgba-de.ts

@@ -24,13 +24,6 @@ <source>{projectName} would like to thank the following patrons from Patreon:</source>

<translation>{projectName} möchte den folgenden Unterstützern auf Patreon danken:</translation> </message> <message> - <location filename="../AboutScreen.ui" line="86"/> - <source>© 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 -Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source> - <translation>© 2013 – 2016 Jeffrey Pfau, lizenziert unter der Mozilla Public License, Version 2.0 -Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</translation> - </message> - <message> <location filename="../AboutScreen.ui" line="190"/> <source>{patrons}</source> <translation>{patrons}</translation>

@@ -39,6 +32,13 @@ <message>

<location filename="../AboutScreen.ui" line="106"/> <source>{projectVersion}</source> <translation>{projectVersion}</translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="86"/> + <source>© 2013 – 2017 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 +Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source> + <translation>© 2013 – 2017 Jeffrey Pfau, lizenziert unter der Mozilla Public License, Version 2.0 +Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</translation> </message> <message> <location filename="../AboutScreen.ui" line="155"/>

@@ -458,8 +458,8 @@ <context>

<name>MemorySearch</name> <message> <location filename="../MemorySearch.ui" line="20"/> - <source>Form</source> - <translation>Eingabemaske</translation> + <source>Memory Search</source> + <translation>Speicher durchsuchen</translation> </message> <message> <location filename="../MemorySearch.ui" line="45"/>

@@ -1169,28 +1169,28 @@ </context>

<context> <name>QGBA::GameController</name> <message> - <location filename="../GameController.cpp" line="351"/> - <location filename="../GameController.cpp" line="535"/> + <location filename="../GameController.cpp" line="352"/> + <location filename="../GameController.cpp" line="536"/> <source>Failed to open game file: %1</source> <translation>Fehler beim Öffnen der Spieldatei: %1</translation> </message> <message> - <location filename="../GameController.cpp" line="507"/> + <location filename="../GameController.cpp" line="508"/> <source>Failed to open save file: %1</source> <translation>Fehler beim Öffnen der Speicherdatei: %1</translation> </message> <message> - <location filename="../GameController.cpp" line="564"/> + <location filename="../GameController.cpp" line="565"/> <source>Failed to open snapshot file for reading: %1</source> <translation>Konnte Snapshot-Datei %1 nicht zum Lesen öffnen</translation> </message> <message> - <location filename="../GameController.cpp" line="584"/> + <location filename="../GameController.cpp" line="585"/> <source>Failed to open snapshot file for writing: %1</source> <translation>Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen</translation> </message> <message> - <location filename="../GameController.cpp" line="850"/> + <location filename="../GameController.cpp" line="851"/> <source>Failed to start audio processor</source> <translation>Fehler beim Starten des Audio-Prozessors</translation> </message>

@@ -3280,7 +3280,7 @@ </message>

<message> <location filename="../Window.cpp" line="1105"/> <source>Sh&amp;utdown</source> - <translation>B&amp;eenden</translation> + <translation>Schli&amp;eßen</translation> </message> <message> <location filename="../Window.cpp" line="1111"/>

@@ -3910,7 +3910,7 @@ </message>

<message> <location filename="../SettingsView.ui" line="173"/> <source>Sample rate:</source> - <translation>Sample-Rate:</translation> + <translation>Abtastrate:</translation> </message> <message> <location filename="../SettingsView.ui" line="185"/>
M src/platform/qt/ts/mgba-es.tssrc/platform/qt/ts/mgba-es.ts

@@ -1,6 +1,6 @@

<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> -<TS version="2.1" language="es" sourcelanguage="en_US"> +<TS version="2.1" language="es_419" sourcelanguage="en_US"> <context> <name>AboutScreen</name> <message>

@@ -11,7 +11,12 @@ </message>

<message> <location filename="../AboutScreen.ui" line="23"/> <source>&lt;a href=&quot;http://mgba.io/&quot;&gt;Website&lt;/a&gt; • &lt;a href=&quot;https://forums.mgba.io/&quot;&gt;Forums / Support&lt;/a&gt; • &lt;a href=&quot;https://patreon.com/mgba&quot;&gt;Donate&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/tree/{gitBranch}&quot;&gt;Source&lt;/a&gt;</source> - <translation>&lt;a href=&quot;http://mgba.io/&quot;&gt;Sitio web&lt;/a&gt; • &lt;a href=&quot;https://forums.mgba.io/&quot;&gt;Foros / Soporte&lt;/a&gt; • &lt;a href=&quot;https://patreon.com/mgba&quot;&gt;Donar&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/tree/{gitBranch}&quot;&gt;Código fuente&lt;/a&gt;</translation> + <translation>&lt;a href=&quot;http://mgba.io/&quot;&gt;Sitio web&lt;/a&gt; • &lt;a href=&quot;https://forums.mgba.io/&quot;&gt;Foros / soporte&lt;/a&gt; • &lt;a href=&quot;https://patreon.com/mgba&quot;&gt;Donar&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/tree/{gitBranch}&quot;&gt;Código fuente&lt;/a&gt;</translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="41"/> + <source>Branch: &lt;tt&gt;{gitBranch}&lt;/tt&gt;&lt;br/&gt;Revision: &lt;tt&gt;{gitCommit}&lt;/tt&gt;</source> + <translation>Rama: &lt;tt&gt;{gitBranch}&lt;/tt&gt;&lt;br/&gt;Revisión: &lt;tt&gt;{gitCommit}&lt;/tt&gt;</translation> </message> <message> <location filename="../AboutScreen.ui" line="58"/>

@@ -21,19 +26,14 @@ </message>

<message> <location filename="../AboutScreen.ui" line="68"/> <source>{projectName} would like to thank the following patrons from Patreon:</source> - <translation>{projectName} desea agradecer a los siguientes mecenas de Patreon:</translation> + <translation>{projectName} desea agradecer a los siguientes patrocinadores desde Patreon:</translation> </message> <message> <location filename="../AboutScreen.ui" line="86"/> - <source>© 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 + <source>© 2013 – 2017 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source> - <translation>© 2013 - 2016 Jeffrey Pfau, licenciado bajo la Licencia Pública de Mozilla, versión 2.0 + <translation>© 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.</translation> - </message> - <message> - <location filename="../AboutScreen.ui" line="190"/> - <source>{patrons}</source> - <translation>{patrons}</translation> </message> <message> <location filename="../AboutScreen.ui" line="106"/>

@@ -48,12 +48,12 @@ </message>

<message> <location filename="../AboutScreen.ui" line="177"/> <source>{projectName} is an open-source Game Boy Advance emulator</source> - <translation>{projectName} es un emulador de Game Boy Advance de código abierto</translation> + <translation>{projectName} es un emulador de código abierto de Game Boy Advance</translation> </message> <message> - <location filename="../AboutScreen.ui" line="41"/> - <source>Branch: &lt;tt&gt;{gitBranch}&lt;/tt&gt;&lt;br/&gt;Revision: &lt;tt&gt;{gitCommit}&lt;/tt&gt;</source> - <translation>Rama Git: &lt;tt&gt;{gitBranch}&lt;/tt&gt;&lt;br/&gt;Revisión: &lt;tt&gt;{gitCommit}&lt;/tt&gt;</translation> + <location filename="../AboutScreen.ui" line="190"/> + <source>{patrons}</source> + <translation>{patrons}</translation> </message> </context> <context>

@@ -61,7 +61,7 @@ <name>ArchiveInspector</name>

<message> <location filename="../ArchiveInspector.ui" line="14"/> <source>Open in archive...</source> - <translation>Abrir dentro de archivo ...</translation> + <translation>Abrir desde contenedor...</translation> </message> <message> <location filename="../ArchiveInspector.ui" line="20"/>

@@ -129,7 +129,7 @@ </message>

<message> <location filename="../CheatsView.ui" line="20"/> <source>Remove</source> - <translation>Eliminar</translation> + <translation>Quitar</translation> </message> <message> <location filename="../CheatsView.ui" line="34"/>

@@ -162,12 +162,12 @@ </message>

<message> <location filename="../DebuggerConsole.ui" line="25"/> <source>Enter command (try `help` for more info)</source> - <translation>Ingresa un comando (prueba con &apos;help&apos; para más información)</translation> + <translation>Ingresa un comando (intenta `help` para más información)</translation> </message> <message> <location filename="../DebuggerConsole.ui" line="32"/> <source>Break</source> - <translation>Entrar en depuración</translation> + <translation>Entrar a depuración</translation> </message> </context> <context>

@@ -190,7 +190,7 @@ </message>

<message> <location filename="../GIFView.ui" line="63"/> <source>Select File</source> - <translation>Elegir archivo</translation> + <translation>Seleccionar archivo</translation> </message> <message> <location filename="../GIFView.ui" line="101"/>

@@ -213,7 +213,7 @@ <name>IOViewer</name>

<message> <location filename="../IOViewer.ui" line="14"/> <source>I/O Viewer</source> - <translation>Visor de E/S</translation> + <translation>Visor de I/O</translation> </message> <message> <location filename="../IOViewer.ui" line="26"/>

@@ -302,11 +302,31 @@ <translation>B</translation>

</message> </context> <context> - <name>LibraryView</name> + <name>LibraryTree</name> <message> - <location filename="../LibraryView.ui" line="14"/> - <source>Library</source> - <translation>Biblioteca</translation> + <location filename="../library/LibraryTree.cpp" line="46"/> + <source>Name</source> + <translation>Nombre</translation> + </message> + <message> + <location filename="../library/LibraryTree.cpp" line="47"/> + <source>Location</source> + <translation>Ubicación</translation> + </message> + <message> + <location filename="../library/LibraryTree.cpp" line="48"/> + <source>Platform</source> + <translation>Plataforma</translation> + </message> + <message> + <location filename="../library/LibraryTree.cpp" line="49"/> + <source>Size</source> + <translation>Tamaño</translation> + </message> + <message> + <location filename="../library/LibraryTree.cpp" line="50"/> + <source>CRC32</source> + <translation>CRC32</translation> </message> </context> <context>

@@ -315,7 +335,7 @@ <message>

<location filename="../LoadSaveState.ui" line="14"/> <location filename="../LoadSaveState.ui" line="88"/> <source>%1 State</source> - <translation>%1 captura de estado</translation> + <translation>%1 estado</translation> </message> <message> <location filename="../LoadSaveState.ui" line="41"/>

@@ -328,7 +348,7 @@ <location filename="../LoadSaveState.ui" line="195"/>

<location filename="../LoadSaveState.ui" line="217"/> <location filename="../LoadSaveState.ui" line="239"/> <source>No Save</source> - <translation>Sin captura</translation> + <translation>Sin estado</translation> </message> <message> <location filename="../LoadSaveState.ui" line="50"/>

@@ -391,22 +411,22 @@ </message>

<message> <location filename="../LogView.ui" line="28"/> <source>Debug</source> - <translation>Depuración</translation> + <translation>Depuración (Debug)</translation> </message> <message> <location filename="../LogView.ui" line="38"/> <source>Stub</source> - <translation>Auxiliar</translation> + <translation>Stub</translation> </message> <message> <location filename="../LogView.ui" line="48"/> <source>Info</source> - <translation>Información</translation> + <translation>Información (Info)</translation> </message> <message> <location filename="../LogView.ui" line="58"/> <source>Warning</source> - <translation>Advertencia</translation> + <translation>Advertencia (Warning)</translation> </message> <message> <location filename="../LogView.ui" line="68"/>

@@ -421,7 +441,7 @@ </message>

<message> <location filename="../LogView.ui" line="95"/> <source>Game Error</source> - <translation>Error del juego</translation> + <translation>Error de juego</translation> </message> <message> <location filename="../LogView.ui" line="121"/>

@@ -435,11 +455,110 @@ <translation>Máximo de líneas</translation>

</message> </context> <context> + <name>MemorySearch</name> + <message> + <location filename="../MemorySearch.ui" line="20"/> + <source>Memory Search</source> + <translation>Búsqueda en la memoria</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="45"/> + <source>Address</source> + <translation>Dirección</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="50"/> + <source>Current Value</source> + <translation>Valor actual</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="55"/> + <location filename="../MemorySearch.ui" line="75"/> + <source>Type</source> + <translation>Tipo</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="65"/> + <source>Value</source> + <translation>Valor</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="82"/> + <source>Numeric</source> + <translation>Numérico</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="95"/> + <source>Text</source> + <translation>Texto</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="105"/> + <source>Width</source> + <translation>Ancho</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="112"/> + <source>1 Byte (8-bit)</source> + <translation>1 byte (8 bits)</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="122"/> + <source>2 Bytes (16-bit)</source> + <translation>2 bytes (16 bits)</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="132"/> + <source>4 Bytes (32-bit)</source> + <translation>4 bytes (32 bits)</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="145"/> + <source>Number type</source> + <translation>Tipo de número</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="152"/> + <source>Hexadecimal</source> + <translation>Hexadecimal</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="162"/> + <source>Decimal</source> + <translation>Decimal</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="169"/> + <source>Guess</source> + <translation>Adivinar</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="187"/> + <source>Search</source> + <translation>Buscar</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="194"/> + <source>Search Within</source> + <translation>Buscar dentro</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="201"/> + <source>Open in Memory Viewer</source> + <translation>Abrir en el Visor de memoria</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="208"/> + <source>Refresh</source> + <translation>Actualizar</translation> + </message> +</context> +<context> <name>MemoryView</name> <message> <location filename="../MemoryView.ui" line="14"/> <source>Memory</source> - <translation>Memoría</translation> + <translation>Visor de memoria</translation> </message> <message> <location filename="../MemoryView.ui" line="38"/>

@@ -454,7 +573,7 @@ </message>

<message> <location filename="../MemoryView.ui" line="81"/> <source>Set Alignment:</source> - <translation>Alinear en:</translation> + <translation>Alinear a:</translation> </message> <message> <location filename="../MemoryView.ui" line="101"/>

@@ -472,6 +591,11 @@ <source>4 Bytes</source>

<translation>4 bytes</translation> </message> <message> + <location filename="../MemoryView.ui" line="181"/> + <source>Unsigned Integer:</source> + <translation>Entero sin signo:</translation> + </message> + <message> <location filename="../MemoryView.ui" line="199"/> <source>Signed Integer:</source> <translation>Entero con signo:</translation>

@@ -506,11 +630,6 @@ <location filename="../MemoryView.ui" line="278"/>

<source>Load</source> <translation>Cargar</translation> </message> - <message> - <location filename="../MemoryView.ui" line="181"/> - <source>Unsigned Integer:</source> - <translation>Entero sin signo:</translation> - </message> </context> <context> <name>ObjView</name>

@@ -523,17 +642,17 @@ <message>

<location filename="../ObjView.ui" line="56"/> <location filename="../ObjView.ui" line="506"/> <source>×</source> - <translation>×</translation> + <translation>x</translation> </message> <message> <location filename="../ObjView.ui" line="69"/> <source>Magnification</source> - <translation>Magnificación</translation> + <translation>Ampliación</translation> </message> <message> <location filename="../ObjView.ui" line="76"/> <source>Export</source> - <translation type="unfinished"></translation> + <translation>Esportar</translation> </message> <message> <location filename="../ObjView.ui" line="85"/>

@@ -566,7 +685,7 @@ </message>

<message> <location filename="../ObjView.ui" line="158"/> <source>Double Size</source> - <translation>Doble tamaño</translation> + <translation>Tamaño doble</translation> </message> <message> <location filename="../ObjView.ui" line="184"/>

@@ -663,7 +782,7 @@ <name>OverrideView</name>

<message> <location filename="../OverrideView.ui" line="20"/> <source>Game Overrides</source> - <translation>Valores específicos por juego</translation> + <translation>Ajustes específicos por juego</translation> </message> <message> <location filename="../OverrideView.ui" line="30"/>

@@ -681,7 +800,7 @@ </message>

<message> <location filename="../OverrideView.ui" line="55"/> <source>Realtime clock</source> - <translation>Reloj de tiempo real</translation> + <translation>Reloj en tiempo real</translation> </message> <message> <location filename="../OverrideView.ui" line="65"/>

@@ -691,7 +810,7 @@ </message>

<message> <location filename="../OverrideView.ui" line="75"/> <source>Tilt</source> - <translation>Inclinación</translation> + <translation>Sensor de inclinación</translation> </message> <message> <location filename="../OverrideView.ui" line="85"/>

@@ -742,7 +861,7 @@ </message>

<message> <location filename="../OverrideView.ui" line="195"/> <source>Game Boy Player features</source> - <translation>Habilitar Game Boy Player</translation> + <translation>Características del Game Boy Player</translation> </message> <message> <location filename="../OverrideView.ui" line="234"/>

@@ -772,7 +891,7 @@ </message>

<message> <location filename="../OverrideView.ui" line="271"/> <source>Memory bank controller</source> - <translation>Controlador de banco de memoria</translation> + <translation>Controlador de bancos de memoria</translation> </message> <message> <location filename="../OverrideView.ui" line="289"/>

@@ -792,7 +911,7 @@ </message>

<message> <location filename="../OverrideView.ui" line="304"/> <source>MBC3 + RTC</source> - <translation>MBC3 + Reloj</translation> + <translation>MBC3 + RTC</translation> </message> <message> <location filename="../OverrideView.ui" line="309"/>

@@ -802,7 +921,7 @@ </message>

<message> <location filename="../OverrideView.ui" line="314"/> <source>MBC5 + Rumble</source> - <translation>MBC5 + Vibración</translation> + <translation>MBC5 + Rumble</translation> </message> <message> <location filename="../OverrideView.ui" line="319"/>

@@ -814,6 +933,11 @@ <location filename="../OverrideView.ui" line="324"/>

<source>HuC-3</source> <translation>HuC-3</translation> </message> + <message> + <location filename="../OverrideView.ui" line="332"/> + <source>Colors</source> + <translation>Colores</translation> + </message> </context> <context> <name>PaletteView</name>

@@ -830,7 +954,7 @@ </message>

<message> <location filename="../PaletteView.ui" line="94"/> <source>Objects</source> - <translation>Objetos (OBJ)</translation> + <translation>Objetos</translation> </message> <message> <location filename="../PaletteView.ui" line="153"/>

@@ -862,12 +986,12 @@ </message>

<message> <location filename="../PaletteView.ui" line="244"/> <source>16-bit value</source> - <translation>Valor en 16 bits</translation> + <translation>Valor de 16 bits</translation> </message> <message> <location filename="../PaletteView.ui" line="251"/> <source>Hex code</source> - <translation>Código hexadecimal</translation> + <translation>Código hex</translation> </message> <message> <location filename="../PaletteView.ui" line="258"/>

@@ -903,19 +1027,53 @@ </context>

<context> <name>QGBA::AssetTile</name> <message> - <location filename="../AssetTile.cpp" line="107"/> + <location filename="../AssetTile.cpp" line="103"/> <source>%0%1%2</source> <translation>%0%1%2</translation> </message> <message> - <location filename="../AssetTile.cpp" line="136"/> - <location filename="../AssetTile.cpp" line="137"/> - <location filename="../AssetTile.cpp" line="138"/> + <location filename="../AssetTile.cpp" line="132"/> + <location filename="../AssetTile.cpp" line="133"/> + <location filename="../AssetTile.cpp" line="134"/> <source>0x%0 (%1)</source> <translation>0x%0 (%1)</translation> </message> </context> <context> + <name>QGBA::AudioDevice</name> + <message> + <location filename="../AudioDevice.cpp" line="26"/> + <source>Can&apos;t set format of context-less audio device</source> + <translation>No se puede establecer el formato de un dispositivo de audio sin contexto</translation> + </message> + <message> + <location filename="../AudioDevice.cpp" line="48"/> + <source>Audio device is missing its core</source> + <translation>El dispositivo de audio no tiene núcleo</translation> + </message> + <message> + <location filename="../AudioDevice.cpp" line="64"/> + <source>Writing data to read-only audio device</source> + <translation>Escribiendo datos a un dispositivo de audio de sólo lectura</translation> + </message> +</context> +<context> + <name>QGBA::AudioProcessorQt</name> + <message> + <location filename="../AudioProcessorQt.cpp" line="35"/> + <source>Can&apos;t start an audio processor without input</source> + <translation>No se puede iniciar un procesador de audio sin entrada</translation> + </message> +</context> +<context> + <name>QGBA::AudioProcessorSDL</name> + <message> + <location filename="../AudioProcessorSDL.cpp" line="33"/> + <source>Can&apos;t start an audio processor without input</source> + <translation>No se puede iniciar un procesador de audio sin entrada</translation> + </message> +</context> +<context> <name>QGBA::CheatsModel</name> <message> <location filename="../CheatsModel.cpp" line="54"/>

@@ -925,7 +1083,7 @@ </message>

<message> <location filename="../CheatsModel.cpp" line="209"/> <source>Failed to open cheats file: %1</source> - <translation>Ocurrió un error al cargar el archivo de trucos: %1</translation> + <translation>Error al abrir el archivo de trucos: %1</translation> </message> </context> <context>

@@ -955,7 +1113,7 @@ <message>

<location filename="../CheatsView.cpp" line="112"/> <location filename="../CheatsView.cpp" line="119"/> <source>Select cheats file</source> - <translation>Elegir archivo de trucos</translation> + <translation>Seleccionar archivo de trucos</translation> </message> </context> <context>

@@ -963,12 +1121,12 @@ <name>QGBA::GBAKeyEditor</name>

<message> <location filename="../GBAKeyEditor.cpp" line="68"/> <source>Clear Button</source> - <translation>Limpiar botones</translation> + <translation>Limpiar botón</translation> </message> <message> <location filename="../GBAKeyEditor.cpp" line="80"/> <source>Clear Analog</source> - <translation>Limpiar análogos</translation> + <translation>Limpiar análogo</translation> </message> <message> <location filename="../GBAKeyEditor.cpp" line="91"/>

@@ -986,7 +1144,7 @@ <name>QGBA::GDBWindow</name>

<message> <location filename="../GDBWindow.cpp" line="28"/> <source>Server settings</source> - <translation>Ajustes del servidor</translation> + <translation>Configuración del servidor</translation> </message> <message> <location filename="../GDBWindow.cpp" line="34"/>

@@ -1001,7 +1159,7 @@ </message>

<message> <location filename="../GDBWindow.cpp" line="55"/> <source>Break</source> - <translation>Entrar en depuración</translation> + <translation>Entrar a depuración</translation> </message> <message> <location filename="../GDBWindow.cpp" line="104"/>

@@ -1016,7 +1174,7 @@ </message>

<message> <location filename="../GDBWindow.cpp" line="122"/> <source>Crash</source> - <translation>Error</translation> + <translation>Error fatal</translation> </message> <message> <location filename="../GDBWindow.cpp" line="122"/>

@@ -1027,46 +1185,46 @@ </context>

<context> <name>QGBA::GIFView</name> <message> - <location filename="../GIFView.cpp" line="45"/> + <location filename="../GIFView.cpp" line="46"/> <source>Failed to open output GIF file: %1</source> - <translation>Error al abrir el archivo de salida GIF: %1</translation> + <translation>Error al abrir el archivo GIF de salida: %1</translation> </message> <message> - <location filename="../GIFView.cpp" line="63"/> + <location filename="../GIFView.cpp" line="64"/> <source>Select output file</source> - <translation>Elegir archivo de salida</translation> + <translation>Seleccionar archivo de salida</translation> </message> <message> - <location filename="../GIFView.cpp" line="63"/> + <location filename="../GIFView.cpp" line="64"/> <source>Graphics Interchange Format (*.gif)</source> - <translation>Formato de intercambio de gráficos (*.gif)</translation> + <translation>Graphics Interchange Format (*.gif)</translation> </message> </context> <context> <name>QGBA::GameController</name> <message> - <location filename="../GameController.cpp" line="397"/> - <location filename="../GameController.cpp" line="572"/> + <location filename="../GameController.cpp" line="351"/> + <location filename="../GameController.cpp" line="536"/> <source>Failed to open game file: %1</source> <translation>Error al abrir el archivo del juego: %1</translation> </message> <message> - <location filename="../GameController.cpp" line="544"/> + <location filename="../GameController.cpp" line="508"/> <source>Failed to open save file: %1</source> <translation>Error al abrir el archivo de guardado: %1</translation> </message> <message> - <location filename="../GameController.cpp" line="601"/> + <location filename="../GameController.cpp" line="563"/> <source>Failed to open snapshot file for reading: %1</source> - <translation>Error al leer el archivo de captura: %1</translation> + <translation>Error al leer del archivo de captura: %1</translation> </message> <message> - <location filename="../GameController.cpp" line="621"/> + <location filename="../GameController.cpp" line="583"/> <source>Failed to open snapshot file for writing: %1</source> <translation>Error al escribir al archivo de captura: %1</translation> </message> <message> - <location filename="../GameController.cpp" line="916"/> + <location filename="../GameController.cpp" line="849"/> <source>Failed to start audio processor</source> <translation>Error al iniciar el procesador de audio</translation> </message>

@@ -1086,27 +1244,27 @@ </message>

<message> <location filename="../IOViewer.cpp" line="32"/> <source>Mode 1: 2 tile layers + 1 rotated/scaled tile layer</source> - <translation>Modo 1: 2 capas de tiles + 1 capa de tiles rotados/escalados</translation> + <translation>Modo 1: 2 capas de tiles + 1 capa de tiles con rotación/escalado</translation> </message> <message> <location filename="../IOViewer.cpp" line="33"/> <source>Mode 2: 2 rotated/scaled tile layers</source> - <translation>Modo 2: 2 capas de tiles rotados/escalados</translation> + <translation>Modo 2: 2 capas de tiles con rotación/escalado</translation> </message> <message> <location filename="../IOViewer.cpp" line="34"/> <source>Mode 3: Full 15-bit bitmap</source> - <translation>Modo 3: mapa de bits de 15 bits</translation> + <translation>Modo 3: Mapa de bits de 15 bits de tamaño completo</translation> </message> <message> <location filename="../IOViewer.cpp" line="35"/> <source>Mode 4: Full 8-bit bitmap</source> - <translation>Modo 4: mapa de bits de 8 bits</translation> + <translation>Modo 4: Mapa de bits de 8 bits de tamaño completo</translation> </message> <message> <location filename="../IOViewer.cpp" line="36"/> <source>Mode 5: Small 15-bit bitmap</source> - <translation>Modo 5: mapa de bits pequeño de 15 bits</translation> + <translation>Modo 5: Mapa de bits de 15 bits de tamaño pequeño</translation> </message> <message> <location filename="../IOViewer.cpp" line="40"/>

@@ -1116,12 +1274,12 @@ </message>

<message> <location filename="../IOViewer.cpp" line="41"/> <source>Frame select</source> - <translation>Selección de cuadros</translation> + <translation>Selección de cuadro</translation> </message> <message> <location filename="../IOViewer.cpp" line="42"/> <source>Unlocked HBlank</source> - <translation>HBlank sin bloqueo</translation> + <translation>HBlank desbloqueado</translation> </message> <message> <location filename="../IOViewer.cpp" line="43"/>

@@ -1136,22 +1294,22 @@ </message>

<message> <location filename="../IOViewer.cpp" line="45"/> <source>Enable background 0</source> - <translation>Habilitar fondo 0</translation> + <translation>Habilitar BG 0</translation> </message> <message> <location filename="../IOViewer.cpp" line="46"/> <source>Enable background 1</source> - <translation>Habilitar fondo 1</translation> + <translation>Habilitar BG 1</translation> </message> <message> <location filename="../IOViewer.cpp" line="47"/> <source>Enable background 2</source> - <translation>Habilitar fondo 2</translation> + <translation>Habilitar BG 2</translation> </message> <message> <location filename="../IOViewer.cpp" line="48"/> <source>Enable background 3</source> - <translation>Habilitar fondo 3</translation> + <translation>Habilitar BG 3</translation> </message> <message> <location filename="../IOViewer.cpp" line="49"/>

@@ -1166,42 +1324,42 @@ </message>

<message> <location filename="../IOViewer.cpp" line="51"/> <source>Enable Window 1</source> - <translation>Habilitar Window 1</translation> + <translation>Habilitar Windows 1</translation> </message> <message> <location filename="../IOViewer.cpp" line="52"/> <source>Enable OBJ Window</source> - <translation>Habilitar Window OBJ</translation> + <translation>Habilitar Window de OBJ</translation> </message> <message> <location filename="../IOViewer.cpp" line="58"/> <source>Currently in VBlank</source> - <translation>En VBlank actualmente</translation> + <translation>En VBlank ahora</translation> </message> <message> <location filename="../IOViewer.cpp" line="59"/> <source>Currently in HBlank</source> - <translation>En HBlank actualmente</translation> + <translation>En HBlank ahora</translation> </message> <message> <location filename="../IOViewer.cpp" line="60"/> <source>Currently in VCounter</source> - <translation>En VCounter actualmente</translation> + <translation>En VCounter ahora</translation> </message> <message> <location filename="../IOViewer.cpp" line="61"/> <source>Enable VBlank IRQ generation</source> - <translation>Generar IRQ por cada VBlank</translation> + <translation>Habilitar generación IRQ en VBlank</translation> </message> <message> <location filename="../IOViewer.cpp" line="62"/> <source>Enable HBlank IRQ generation</source> - <translation>Generar IRQ por cada HBlank</translation> + <translation>Habilitar generación IRQ en HBlank</translation> </message> <message> <location filename="../IOViewer.cpp" line="63"/> <source>Enable VCounter IRQ generation</source> - <translation>Generar IRQ por cada VCounter</translation> + <translation>Habilitar generación IRQ en VCounter</translation> </message> <message> <location filename="../IOViewer.cpp" line="64"/>

@@ -1227,7 +1385,7 @@ <location filename="../IOViewer.cpp" line="82"/>

<location filename="../IOViewer.cpp" line="91"/> <location filename="../IOViewer.cpp" line="101"/> <source>Tile data base (* 16kB)</source> - <translation>Base de los tiles (* 16kB)</translation> + <translation>Dirección base de tiles (* 16kB)</translation> </message> <message> <location filename="../IOViewer.cpp" line="74"/>

@@ -1235,7 +1393,7 @@ <location filename="../IOViewer.cpp" line="83"/>

<location filename="../IOViewer.cpp" line="92"/> <location filename="../IOViewer.cpp" line="102"/> <source>Enable mosaic</source> - <translation>Mosaico (pixelar)</translation> + <translation>Habilitar mosaico</translation> </message> <message> <location filename="../IOViewer.cpp" line="75"/>

@@ -1243,7 +1401,7 @@ <location filename="../IOViewer.cpp" line="84"/>

<location filename="../IOViewer.cpp" line="93"/> <location filename="../IOViewer.cpp" line="103"/> <source>Enable 256-color</source> - <translation>256 colores</translation> + <translation>Habilitar 256 colores</translation> </message> <message> <location filename="../IOViewer.cpp" line="76"/>

@@ -1251,7 +1409,7 @@ <location filename="../IOViewer.cpp" line="85"/>

<location filename="../IOViewer.cpp" line="94"/> <location filename="../IOViewer.cpp" line="104"/> <source>Tile map base (* 2kB)</source> - <translation>Base de asignación de tiles (* 2kB)</translation> + <translation>Dirección base de asignación de tiles (* 2kB)</translation> </message> <message> <location filename="../IOViewer.cpp" line="77"/>

@@ -1259,13 +1417,13 @@ <location filename="../IOViewer.cpp" line="86"/>

<location filename="../IOViewer.cpp" line="96"/> <location filename="../IOViewer.cpp" line="106"/> <source>Background dimensions</source> - <translation>Dimensiones del fondo</translation> + <translation>Dimensiones de BG</translation> </message> <message> <location filename="../IOViewer.cpp" line="95"/> <location filename="../IOViewer.cpp" line="105"/> <source>Overflow wraps</source> - <translation>Envolver al desbordar</translation> + <translation>Envolver en desbordamiento</translation> </message> <message> <location filename="../IOViewer.cpp" line="110"/>

@@ -1317,7 +1475,7 @@ <location filename="../IOViewer.cpp" line="172"/>

<location filename="../IOViewer.cpp" line="201"/> <location filename="../IOViewer.cpp" line="210"/> <source>Integer part (bottom)</source> - <translation>Parte entera (inferior)</translation> + <translation>Parte entera (función suelo)</translation> </message> <message> <location filename="../IOViewer.cpp" line="167"/>

@@ -1325,7 +1483,7 @@ <location filename="../IOViewer.cpp" line="176"/>

<location filename="../IOViewer.cpp" line="205"/> <location filename="../IOViewer.cpp" line="214"/> <source>Integer part (top)</source> - <translation>Parte entera (superior)</translation> + <translation>Parte entera (función techo)</translation> </message> <message> <location filename="../IOViewer.cpp" line="218"/>

@@ -1354,142 +1512,142 @@ </message>

<message> <location filename="../IOViewer.cpp" line="238"/> <source>Window 0 enable BG 0</source> - <translation>Window 0 BG 0</translation> + <translation>Window 0 habilitar BG 0</translation> </message> <message> <location filename="../IOViewer.cpp" line="239"/> <source>Window 0 enable BG 1</source> - <translation>Window 0 BG 1</translation> + <translation>Window 0 habilitar BG 1</translation> </message> <message> <location filename="../IOViewer.cpp" line="240"/> <source>Window 0 enable BG 2</source> - <translation>Window 0 BG 2</translation> + <translation>Window 0 habilitar BG 2</translation> </message> <message> <location filename="../IOViewer.cpp" line="241"/> <source>Window 0 enable BG 3</source> - <translation>Window 0 BG 3</translation> + <translation>Window 0 habilitar BG 3</translation> </message> <message> <location filename="../IOViewer.cpp" line="242"/> <source>Window 0 enable OBJ</source> - <translation>Window 0 OBJ</translation> + <translation>Window 0 habilitar OBJ</translation> </message> <message> <location filename="../IOViewer.cpp" line="243"/> <source>Window 0 enable blend</source> - <translation>Window 0 mezcla</translation> + <translation>Window 0 habilitar mezcla</translation> </message> <message> <location filename="../IOViewer.cpp" line="244"/> <source>Window 1 enable BG 0</source> - <translation>Window 1 BG 0</translation> + <translation>Window 1 habilitar BG 0</translation> </message> <message> <location filename="../IOViewer.cpp" line="245"/> <source>Window 1 enable BG 1</source> - <translation>Window 1 BG 1</translation> + <translation>Window 1 habilitar BG 1</translation> </message> <message> <location filename="../IOViewer.cpp" line="246"/> <source>Window 1 enable BG 2</source> - <translation>Window 1 BG 2</translation> + <translation>Window 1 habilitar BG 2</translation> </message> <message> <location filename="../IOViewer.cpp" line="247"/> <source>Window 1 enable BG 3</source> - <translation>Window 1 BG 3</translation> + <translation>Window 1 habilitar BG 3</translation> </message> <message> <location filename="../IOViewer.cpp" line="248"/> <source>Window 1 enable OBJ</source> - <translation>Window 1 OBJ</translation> + <translation>Window 1 habilitar OBJ</translation> </message> <message> <location filename="../IOViewer.cpp" line="249"/> <source>Window 1 enable blend</source> - <translation>Window 1 mezcla</translation> + <translation>Window 1 habilitar mezcla</translation> </message> <message> <location filename="../IOViewer.cpp" line="253"/> <source>Outside window enable BG 0</source> - <translation>Outside window BG 0</translation> + <translation>Window externa habilitar BG 0</translation> </message> <message> <location filename="../IOViewer.cpp" line="254"/> <source>Outside window enable BG 1</source> - <translation>Outside window BG 1</translation> + <translation>Window externa habilitar BG 1</translation> </message> <message> <location filename="../IOViewer.cpp" line="255"/> <source>Outside window enable BG 2</source> - <translation>Outside window BG 2</translation> + <translation>Window externa habilitar BG 2</translation> </message> <message> <location filename="../IOViewer.cpp" line="256"/> <source>Outside window enable BG 3</source> - <translation>Outside window BG 3</translation> + <translation>Window externa habilitar BG 3</translation> </message> <message> <location filename="../IOViewer.cpp" line="257"/> <source>Outside window enable OBJ</source> - <translation>Outside window OBJ</translation> + <translation>Window externa habilitar OBJ</translation> </message> <message> <location filename="../IOViewer.cpp" line="258"/> <source>Outside window enable blend</source> - <translation>Outside window mezcla</translation> + <translation>Outside window habilitar mezcla</translation> </message> <message> <location filename="../IOViewer.cpp" line="259"/> <source>OBJ window enable BG 0</source> - <translation>OBJ window BG 0</translation> + <translation>OBJ window habilitar BG 0</translation> </message> <message> <location filename="../IOViewer.cpp" line="260"/> <source>OBJ window enable BG 1</source> - <translation>OBJ window BG 1</translation> + <translation>OBJ window habilitar BG 1</translation> </message> <message> <location filename="../IOViewer.cpp" line="261"/> <source>OBJ window enable BG 2</source> - <translation>OBJ window BG 2</translation> + <translation>OBJ window habilitar BG 2</translation> </message> <message> <location filename="../IOViewer.cpp" line="262"/> <source>OBJ window enable BG 3</source> - <translation>OBJ window BG 3</translation> + <translation>OBJ window habilitar BG 3</translation> </message> <message> <location filename="../IOViewer.cpp" line="263"/> <source>OBJ window enable OBJ</source> - <translation>OBJ window OBJ</translation> + <translation>OBJ window habilitar OBJ</translation> </message> <message> <location filename="../IOViewer.cpp" line="264"/> <source>OBJ window enable blend</source> - <translation>OBJ window mezcla</translation> + <translation>OBJ window habilitar mezcla</translation> </message> <message> <location filename="../IOViewer.cpp" line="268"/> <source>Background mosaic size vertical</source> - <translation>Tamaño mosaico fondo vertical</translation> + <translation>Tamaño vertical mosaico BG</translation> </message> <message> <location filename="../IOViewer.cpp" line="269"/> <source>Background mosaic size horizontal</source> - <translation>Tamaño mosaico fondo horizontal</translation> + <translation>Tamaño horizontal mosaico BG</translation> </message> <message> <location filename="../IOViewer.cpp" line="270"/> <source>Object mosaic size vertical</source> - <translation>Tamaño mosaico objeto vertical</translation> + <translation>Tamaño vertical mosaico OBJ</translation> </message> <message> <location filename="../IOViewer.cpp" line="271"/> <source>Object mosaic size horizontal</source> - <translation>Tamaño mosaico objeto horizontal</translation> + <translation>Tamaño horizontal mosaico OBJ</translation> </message> <message> <location filename="../IOViewer.cpp" line="277"/>

@@ -1534,7 +1692,7 @@ </message>

<message> <location filename="../IOViewer.cpp" line="285"/> <source>Additive blending</source> - <translation>Mezcla aditiva</translation> + <translation>Aditivo</translation> </message> <message> <location filename="../IOViewer.cpp" line="286"/>

@@ -1599,12 +1757,12 @@ </message>

<message> <location filename="../IOViewer.cpp" line="318"/> <source>Sweep subtract</source> - <translation>Sustracción en barrido</translation> + <translation>Sustracción en barridos</translation> </message> <message> <location filename="../IOViewer.cpp" line="319"/> <source>Sweep time (in 1/128s)</source> - <translation>Tiempo de barrido (en 1/128seg)</translation> + <translation>Tiempo de barridos (in 1/128s)</translation> </message> <message> <location filename="../IOViewer.cpp" line="323"/>

@@ -1612,7 +1770,7 @@ <location filename="../IOViewer.cpp" line="339"/>

<location filename="../IOViewer.cpp" line="363"/> <location filename="../IOViewer.cpp" line="385"/> <source>Sound length</source> - <translation>Longitud del sonido</translation> + <translation>Largo del sonido</translation> </message> <message> <location filename="../IOViewer.cpp" line="324"/>

@@ -1625,14 +1783,14 @@ <location filename="../IOViewer.cpp" line="325"/>

<location filename="../IOViewer.cpp" line="341"/> <location filename="../IOViewer.cpp" line="386"/> <source>Envelope step time</source> - <translation>Tiempo paso envolvente</translation> + <translation>Tiempo de paso en envoltura</translation> </message> <message> <location filename="../IOViewer.cpp" line="326"/> <location filename="../IOViewer.cpp" line="342"/> <location filename="../IOViewer.cpp" line="387"/> <source>Envelope increase</source> - <translation>Aumento envolvente</translation> + <translation>Aumento en envoltura</translation> </message> <message> <location filename="../IOViewer.cpp" line="327"/>

@@ -1646,7 +1804,7 @@ <location filename="../IOViewer.cpp" line="331"/>

<location filename="../IOViewer.cpp" line="349"/> <location filename="../IOViewer.cpp" line="377"/> <source>Sound frequency</source> - <translation>Frecuencia del sonido</translation> + <translation>Frecuencia de sonido</translation> </message> <message> <location filename="../IOViewer.cpp" line="332"/>

@@ -1654,7 +1812,7 @@ <location filename="../IOViewer.cpp" line="350"/>

<location filename="../IOViewer.cpp" line="378"/> <location filename="../IOViewer.cpp" line="400"/> <source>Timed</source> - <translation>Cronometrado</translation> + <translation>Timed</translation> </message> <message> <location filename="../IOViewer.cpp" line="333"/>

@@ -1667,7 +1825,7 @@ </message>

<message> <location filename="../IOViewer.cpp" line="357"/> <source>Double-size wave table</source> - <translation>Tabla de ondas de doble tamaño</translation> + <translation>Tabla de ondas de tamaño doble</translation> </message> <message> <location filename="../IOViewer.cpp" line="358"/>

@@ -1677,7 +1835,7 @@ </message>

<message> <location filename="../IOViewer.cpp" line="359"/> <source>Enable channel 3</source> - <translation>Canal 3 activo</translation> + <translation>Habilitar canal 3</translation> </message> <message> <location filename="../IOViewer.cpp" line="364"/>

@@ -1723,7 +1881,7 @@ </message>

<message> <location filename="../IOViewer.cpp" line="395"/> <source>Register stages</source> - <translation>Etapas de registros</translation> + <translation>Etapas del registro</translation> </message> <message> <location filename="../IOViewer.cpp" line="396"/>

@@ -1743,57 +1901,57 @@ </message>

<message> <location filename="../IOViewer.cpp" line="407"/> <source>PSG volume right</source> - <translation>Volumen pulso derecha</translation> + <translation>PSG volumen derecha</translation> </message> <message> <location filename="../IOViewer.cpp" line="408"/> <source>PSG volume left</source> - <translation>Volumen pulso izquierda</translation> + <translation>PSG volumen izquierda</translation> </message> <message> <location filename="../IOViewer.cpp" line="409"/> <source>Enable channel 1 right</source> - <translation>Canal 1 derecha</translation> + <translation>Habilitar canal 1 derecha</translation> </message> <message> <location filename="../IOViewer.cpp" line="410"/> <source>Enable channel 2 right</source> - <translation>Canal 2 derecha</translation> + <translation>Habilitar canal 2 derecha</translation> </message> <message> <location filename="../IOViewer.cpp" line="411"/> <source>Enable channel 3 right</source> - <translation>Canal 3 derecha</translation> + <translation>Habilitar canal 3 derecha</translation> </message> <message> <location filename="../IOViewer.cpp" line="412"/> <source>Enable channel 4 right</source> - <translation>Canal 4 derecha</translation> + <translation>Habilitar canal 4 derecha</translation> </message> <message> <location filename="../IOViewer.cpp" line="413"/> <source>Enable channel 1 left</source> - <translation>Canal 1 izquierda</translation> + <translation>Habilitar canal 1 izquierda</translation> </message> <message> <location filename="../IOViewer.cpp" line="414"/> <source>Enable channel 2 left</source> - <translation>Canal 2 izquierda</translation> + <translation>Habilitar canal 2 izquierda</translation> </message> <message> <location filename="../IOViewer.cpp" line="415"/> <source>Enable channel 3 left</source> - <translation>Canal 3 izquierda</translation> + <translation>Habilitar canal 3 izquierda</translation> </message> <message> <location filename="../IOViewer.cpp" line="416"/> <source>Enable channel 4 left</source> - <translation>Canal 4 izquierda</translation> + <translation>Habilitar canal 4 izquierda</translation> </message> <message> <location filename="../IOViewer.cpp" line="420"/> <source>PSG master volume</source> - <translation>Volumen maestro pulso</translation> + <translation>PSG volumen maestro</translation> </message> <message> <location filename="../IOViewer.cpp" line="426"/>

@@ -1808,17 +1966,17 @@ </message>

<message> <location filename="../IOViewer.cpp" line="428"/> <source>Enable channel A right</source> - <translation>Canal A derecha</translation> + <translation>Habilitar canal A derecha</translation> </message> <message> <location filename="../IOViewer.cpp" line="429"/> <source>Enable channel A left</source> - <translation>Canal A izquierda</translation> + <translation>Habilitar canal A izquierda</translation> </message> <message> <location filename="../IOViewer.cpp" line="430"/> <source>Channel A timer</source> - <translation>Temporizador canal A</translation> + <translation>Canal A temporizador</translation> </message> <message> <location filename="../IOViewer.cpp" line="431"/>

@@ -1842,27 +2000,27 @@ </message>

<message> <location filename="../IOViewer.cpp" line="434"/> <source>Channel A reset</source> - <translation>Reinic. canal A</translation> + <translation>Canal A reinicializar</translation> </message> <message> <location filename="../IOViewer.cpp" line="435"/> <source>Enable channel B right</source> - <translation>Canal B derecha</translation> + <translation>Habilitar canal B derecha</translation> </message> <message> <location filename="../IOViewer.cpp" line="436"/> <source>Enable channel B left</source> - <translation>Canal B izquierda</translation> + <translation>Habilitar canal B izquierda</translation> </message> <message> <location filename="../IOViewer.cpp" line="437"/> <source>Channel B timer</source> - <translation>Temporizador canal B</translation> + <translation>Canal B temporizador</translation> </message> <message> <location filename="../IOViewer.cpp" line="441"/> <source>Channel B reset</source> - <translation>Reinic. canal B</translation> + <translation>Canal B reinicializar</translation> </message> <message> <location filename="../IOViewer.cpp" line="445"/>

@@ -1953,7 +2111,7 @@ <location filename="../IOViewer.cpp" line="648"/>

<location filename="../IOViewer.cpp" line="685"/> <location filename="../IOViewer.cpp" line="693"/> <source>Address (bottom)</source> - <translation>Dirección (inferior)</translation> + <translation>Dirección (abajo)</translation> </message> <message> <location filename="../IOViewer.cpp" line="554"/>

@@ -1965,7 +2123,7 @@ <location filename="../IOViewer.cpp" line="652"/>

<location filename="../IOViewer.cpp" line="689"/> <location filename="../IOViewer.cpp" line="697"/> <source>Address (top)</source> - <translation>Dirección (superior)</translation> + <translation>Dirección (arriba)</translation> </message> <message> <location filename="../IOViewer.cpp" line="566"/>

@@ -1973,7 +2131,7 @@ <location filename="../IOViewer.cpp" line="611"/>

<location filename="../IOViewer.cpp" line="656"/> <location filename="../IOViewer.cpp" line="701"/> <source>Word count</source> - <translation>Contador de word</translation> + <translation>Tamaño palabra</translation> </message> <message> <location filename="../IOViewer.cpp" line="570"/>

@@ -1981,7 +2139,7 @@ <location filename="../IOViewer.cpp" line="615"/>

<location filename="../IOViewer.cpp" line="660"/> <location filename="../IOViewer.cpp" line="705"/> <source>Destination offset</source> - <translation>Compensación de destino</translation> + <translation>Desplazamiento de destino</translation> </message> <message> <location filename="../IOViewer.cpp" line="571"/>

@@ -2033,7 +2191,7 @@ <location filename="../IOViewer.cpp" line="621"/>

<location filename="../IOViewer.cpp" line="666"/> <location filename="../IOViewer.cpp" line="711"/> <source>Source offset</source> - <translation>Compensación de origen</translation> + <translation>Desplazamiento de origen</translation> </message> <message> <location filename="../IOViewer.cpp" line="582"/>

@@ -2057,7 +2215,7 @@ <location filename="../IOViewer.cpp" line="629"/>

<location filename="../IOViewer.cpp" line="674"/> <location filename="../IOViewer.cpp" line="720"/> <source>Start timing</source> - <translation>Inicio de temporizador</translation> + <translation>Comienzo del tiempo</translation> </message> <message> <location filename="../IOViewer.cpp" line="585"/>

@@ -2117,7 +2275,7 @@ <location filename="../IOViewer.cpp" line="633"/>

<location filename="../IOViewer.cpp" line="678"/> <location filename="../IOViewer.cpp" line="724"/> <source>Audio FIFO</source> - <translation>FIFO de audio</translation> + <translation>Audio FIFO</translation> </message> <message> <location filename="../IOViewer.cpp" line="715"/>

@@ -2174,7 +2332,7 @@ <location filename="../IOViewer.cpp" line="788"/>

<location filename="../IOViewer.cpp" line="804"/> <location filename="../IOViewer.cpp" line="820"/> <source>Cascade</source> - <translation>Cacada</translation> + <translation>Cascada</translation> </message> <message> <location filename="../IOViewer.cpp" line="858"/>

@@ -2325,7 +2483,7 @@ <message>

<location filename="../IOViewer.cpp" line="951"/> <location filename="../IOViewer.cpp" line="968"/> <source>Keypad</source> - <translation>Teclera</translation> + <translation>Keypad</translation> </message> <message> <location filename="../IOViewer.cpp" line="952"/>

@@ -2336,7 +2494,7 @@ </message>

<message> <location filename="../IOViewer.cpp" line="973"/> <source>SRAM wait</source> - <translation>Espera SRAM</translation> + <translation>SRAM wait</translation> </message> <message> <location filename="../IOViewer.cpp" line="974"/>

@@ -2411,7 +2569,7 @@ </message>

<message> <location filename="../IOViewer.cpp" line="1010"/> <source>Disable</source> - <translation>Desactivar</translation> + <translation>Deshabilitar</translation> </message> <message> <location filename="../IOViewer.cpp" line="1011"/>

@@ -2442,71 +2600,23 @@ </context>

<context> <name>QGBA::KeyEditor</name> <message> - <location filename="../KeyEditor.cpp" line="37"/> - <location filename="../KeyEditor.cpp" line="211"/> + <location filename="../KeyEditor.cpp" line="33"/> + <location filename="../KeyEditor.cpp" line="207"/> <source>---</source> <translation>---</translation> </message> </context> <context> - <name>QGBA::LibraryModel</name> - <message> - <location filename="../LibraryModel.cpp" line="24"/> - <source>Name</source> - <translation>Nombre</translation> - </message> - <message> - <location filename="../LibraryModel.cpp" line="33"/> - <source>Filename</source> - <translation>Nombre de archivo</translation> - </message> - <message> - <location filename="../LibraryModel.cpp" line="39"/> - <source>Size</source> - <translation>Tamaño</translation> - </message> - <message> - <location filename="../LibraryModel.cpp" line="56"/> - <source>Platform</source> - <translation>Plataforma</translation> - </message> - <message> - <location filename="../LibraryModel.cpp" line="62"/> - <source>GBA</source> - <translation>GBA</translation> - </message> - <message> - <location filename="../LibraryModel.cpp" line="66"/> - <source>GB</source> - <translation>GB</translation> - </message> - <message> - <location filename="../LibraryModel.cpp" line="69"/> - <source>?</source> - <translation>?</translation> - </message> - <message> - <location filename="../LibraryModel.cpp" line="74"/> - <source>Location</source> - <translation>Ubicación</translation> - </message> - <message> - <location filename="../LibraryModel.cpp" line="80"/> - <source>CRC32</source> - <translation>CRC32</translation> - </message> -</context> -<context> <name>QGBA::LoadSaveState</name> <message> <location filename="../LoadSaveState.cpp" line="68"/> <source>Load State</source> - <translation>Cargar captura</translation> + <translation>Cargar estado</translation> </message> <message> <location filename="../LoadSaveState.cpp" line="68"/> <source>Save State</source> - <translation>Guardar captura</translation> + <translation>Guardar estado</translation> </message> <message> <location filename="../LoadSaveState.cpp" line="177"/>

@@ -2516,10 +2626,10 @@ </message>

<message> <location filename="../LoadSaveState.cpp" line="186"/> <source>Corrupted</source> - <translation>Corrompido</translation> + <translation>Dañado</translation> </message> <message> - <location filename="../LoadSaveState.cpp" line="209"/> + <location filename="../LoadSaveState.cpp" line="215"/> <source>Slot %1</source> <translation>Espacio %1</translation> </message>

@@ -2534,7 +2644,7 @@ </message>

<message> <location filename="../LogController.cpp" line="59"/> <source>STUB</source> - <translation>AUXILIAR</translation> + <translation>STUB</translation> </message> <message> <location filename="../LogController.cpp" line="61"/>

@@ -2559,126 +2669,149 @@ </message>

<message> <location filename="../LogController.cpp" line="69"/> <source>GAME ERROR</source> - <translation>ERROR DEL JUEGO</translation> + <translation>ERROR DE JUEGO</translation> </message> </context> <context> <name>QGBA::MemoryModel</name> <message> - <location filename="../MemoryModel.cpp" line="50"/> + <location filename="../MemoryModel.cpp" line="44"/> <source>Copy selection</source> <translation>Copiar selección</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="55"/> + <location filename="../MemoryModel.cpp" line="49"/> <source>Save selection</source> <translation>Guardar selección</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="60"/> + <location filename="../MemoryModel.cpp" line="54"/> <source>Paste</source> <translation>Pegar</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="65"/> + <location filename="../MemoryModel.cpp" line="59"/> <source>Load</source> <translation>Cargar</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="97"/> - <location filename="../MemoryModel.cpp" line="162"/> + <location filename="../MemoryModel.cpp" line="91"/> + <location filename="../MemoryModel.cpp" line="156"/> <source>All</source> <translation>Todo</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="142"/> + <location filename="../MemoryModel.cpp" line="136"/> <source>Load TBL</source> <translation>Cargar TBL</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="202"/> + <location filename="../MemoryModel.cpp" line="196"/> <source>Save selected memory</source> <translation>Guardar memoria seleccionada</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="208"/> + <location filename="../MemoryModel.cpp" line="202"/> <source>Failed to open output file: %1</source> <translation>Error al abrir el archivo de salida: %1</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="216"/> + <location filename="../MemoryModel.cpp" line="210"/> <source>Load memory</source> <translation>Cargar memoria</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="222"/> + <location filename="../MemoryModel.cpp" line="216"/> <source>Failed to open input file: %1</source> <translation>Error al abrir el archivo de entrada: %1</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="338"/> + <location filename="../MemoryModel.cpp" line="332"/> <source>TBL</source> <translation>TBL</translation> </message> <message> - <location filename="../MemoryModel.cpp" line="338"/> + <location filename="../MemoryModel.cpp" line="332"/> <source>ISO-8859-1</source> <translation>ISO-8859-1</translation> </message> </context> <context> + <name>QGBA::MemorySearch</name> + <message> + <location filename="../MemorySearch.cpp" line="188"/> + <source> (⅟%0×)</source> + <translation> (⅟%0×)</translation> + </message> + <message> + <location filename="../MemorySearch.cpp" line="192"/> + <source>1 byte%0</source> + <translation>1 byte%0</translation> + </message> + <message> + <location filename="../MemorySearch.cpp" line="195"/> + <source>2 bytes%0</source> + <translation>2 bytes%0</translation> + </message> + <message> + <location filename="../MemorySearch.cpp" line="199"/> + <source>4 bytes%0</source> + <translation>4 bytes%0</translation> + </message> +</context> +<context> <name>QGBA::ObjView</name> <message> - <location filename="../ObjView.cpp" line="141"/> - <location filename="../ObjView.cpp" line="233"/> + <location filename="../ObjView.cpp" line="143"/> + <location filename="../ObjView.cpp" line="236"/> <source>0x%0</source> <translation>0x%0</translation> </message> <message> - <location filename="../ObjView.cpp" line="152"/> + <location filename="../ObjView.cpp" line="154"/> <source>Off</source> <translation>No</translation> </message> <message> - <location filename="../ObjView.cpp" line="157"/> + <location filename="../ObjView.cpp" line="159"/> <source>Normal</source> <translation>Normal</translation> </message> <message> - <location filename="../ObjView.cpp" line="160"/> + <location filename="../ObjView.cpp" line="162"/> <source>Trans</source> <translation>Trans</translation> </message> <message> - <location filename="../ObjView.cpp" line="163"/> + <location filename="../ObjView.cpp" line="165"/> <source>OBJWIN</source> <translation>OBJWIN</translation> </message> <message> - <location filename="../ObjView.cpp" line="166"/> + <location filename="../ObjView.cpp" line="168"/> <source>Invalid</source> <translation>Inválido</translation> </message> <message> - <location filename="../ObjView.cpp" line="240"/> - <location filename="../ObjView.cpp" line="241"/> + <location filename="../ObjView.cpp" line="243"/> + <location filename="../ObjView.cpp" line="244"/> <source>N/A</source> <translation>n/d</translation> </message> <message> - <location filename="../ObjView.cpp" line="247"/> + <location filename="../ObjView.cpp" line="251"/> <source>Export sprite</source> - <translation type="unfinished"></translation> + <translation>Exportar sprite</translation> </message> <message> - <location filename="../ObjView.cpp" line="248"/> + <location filename="../ObjView.cpp" line="252"/> <source>Portable Network Graphics (*.png)</source> - <translation type="unfinished"></translation> + <translation>Portable Network Graphics (*.png)</translation> </message> <message> <location filename="../ObjView.cpp" line="255"/> <source>Failed to open output PNG file: %1</source> - <translation type="unfinished"></translation> + <translation>Error al abrir el archivo PNG de salida: %1</translation> </message> </context> <context>

@@ -2713,12 +2846,12 @@ </message>

<message> <location filename="../PaletteView.cpp" line="138"/> <source>Windows PAL (*.pal);;Adobe Color Table (*.act)</source> - <translation>Paleta de WIndows (*.pal);;Tabla de colores Adobe (*.act)</translation> + <translation>Windows PAL (*.pal);;Adobe Color Table (*.act)</translation> </message> <message> - <location filename="../PaletteView.cpp" line="145"/> + <location filename="../PaletteView.cpp" line="141"/> <source>Failed to open output palette file: %1</source> - <translation>Error al abrir el archivo de salida de paleta: %1</translation> + <translation>Error al abrir el archivo de paleta de salida: %1</translation> </message> </context> <context>

@@ -2741,91 +2874,91 @@ </message>

<message> <location filename="../ROMInfo.cpp" line="83"/> <source>(no database present)</source> - <translation>(no se encuentra la base de datos)</translation> + <translation>(no hay base de datos)</translation> </message> </context> <context> <name>QGBA::SettingsView</name> <message> - <location filename="../SettingsView.cpp" line="96"/> + <location filename="../SettingsView.cpp" line="99"/> <source>Qt Multimedia</source> <translation>Qt Multimedia</translation> </message> <message> - <location filename="../SettingsView.cpp" line="103"/> + <location filename="../SettingsView.cpp" line="106"/> <source>SDL</source> <translation>SDL</translation> </message> <message> - <location filename="../SettingsView.cpp" line="111"/> + <location filename="../SettingsView.cpp" line="114"/> <source>Software (Qt)</source> <translation>Software (Qt)</translation> </message> <message> - <location filename="../SettingsView.cpp" line="117"/> + <location filename="../SettingsView.cpp" line="120"/> <source>OpenGL</source> <translation>OpenGL</translation> </message> <message> - <location filename="../SettingsView.cpp" line="124"/> + <location filename="../SettingsView.cpp" line="127"/> <source>OpenGL (force version 1.x)</source> <translation>OpenGL (forzar versión 1.x)</translation> </message> <message> - <location filename="../SettingsView.cpp" line="142"/> + <location filename="../SettingsView.cpp" line="145"/> <source>Keyboard</source> <translation>Teclado</translation> </message> <message> - <location filename="../SettingsView.cpp" line="151"/> + <location filename="../SettingsView.cpp" line="154"/> <source>Controllers</source> - <translation>Mandos</translation> + <translation>Controladores</translation> </message> <message> - <location filename="../SettingsView.cpp" line="170"/> + <location filename="../SettingsView.cpp" line="186"/> <source>Shortcuts</source> - <translation>Accesos directos</translation> + <translation>Atajos de teclado</translation> </message> <message> - <location filename="../SettingsView.cpp" line="174"/> + <location filename="../SettingsView.cpp" line="202"/> + <source>Shaders</source> + <translation>Shaders</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="208"/> <source>Select BIOS</source> - <translation>Elegir BIOS</translation> + <translation>Seleccionar BIOS</translation> </message> </context> <context> <name>QGBA::ShaderSelector</name> <message> - <location filename="../ShaderSelector.cpp" line="50"/> + <location filename="../ShaderSelector.cpp" line="54"/> <source>No shader active</source> - <translation>No hay programa shader activo</translation> + <translation>No hay shader activo</translation> </message> <message> - <location filename="../ShaderSelector.cpp" line="63"/> + <location filename="../ShaderSelector.cpp" line="67"/> <source>Load shader</source> - <translation>Cargar programa shader</translation> + <translation>Cargar shader</translation> </message> <message> - <location filename="../ShaderSelector.cpp" line="63"/> - <source>%1 Shader (%.shader)</source> - <translation>Programa shader de %1 (%.shader)</translation> - </message> - <message> - <location filename="../ShaderSelector.cpp" line="102"/> + <location filename="../ShaderSelector.cpp" line="105"/> <source>No shader loaded</source> - <translation>No hay programa shader cargado</translation> + <translation>No hay shader cargado</translation> </message> <message> - <location filename="../ShaderSelector.cpp" line="110"/> + <location filename="../ShaderSelector.cpp" line="113"/> <source>by %1</source> <translation>por %1</translation> </message> <message> - <location filename="../ShaderSelector.cpp" line="121"/> + <location filename="../ShaderSelector.cpp" line="128"/> <source>Preprocessing</source> - <translation>preprocesamiento</translation> + <translation>Preproceso</translation> </message> <message> - <location filename="../ShaderSelector.cpp" line="128"/> + <location filename="../ShaderSelector.cpp" line="135"/> <source>Pass %1</source> <translation>Paso %1</translation> </message>

@@ -2833,17 +2966,17 @@ </context>

<context> <name>QGBA::ShortcutController</name> <message> - <location filename="../ShortcutController.cpp" line="67"/> + <location filename="../ShortcutController.cpp" line="64"/> <source>Action</source> <translation>Acción</translation> </message> <message> - <location filename="../ShortcutController.cpp" line="69"/> + <location filename="../ShortcutController.cpp" line="66"/> <source>Keyboard</source> <translation>Teclado</translation> </message> <message> - <location filename="../ShortcutController.cpp" line="71"/> + <location filename="../ShortcutController.cpp" line="68"/> <source>Gamepad</source> <translation>Mando</translation> </message>

@@ -2851,726 +2984,745 @@ </context>

<context> <name>QGBA::VideoView</name> <message> - <location filename="../VideoView.cpp" line="208"/> + <location filename="../VideoView.cpp" line="203"/> <source>Failed to open output video file: %1</source> - <translation>Error al abrir el archivo de salida de video: %1</translation> + <translation>Error al abrir el archivo de video de salida: %1</translation> </message> <message> - <location filename="../VideoView.cpp" line="226"/> + <location filename="../VideoView.cpp" line="221"/> <source>Native (%0x%1)</source> - <translation>Nativo (%0x%1)</translation> + <translation>Native (%0x%1)</translation> </message> <message> - <location filename="../VideoView.cpp" line="241"/> + <location filename="../VideoView.cpp" line="236"/> <source>Select output file</source> - <translation>Elegir archivo de salida</translation> + <translation>Seleccionar archivo de salida</translation> </message> </context> <context> <name>QGBA::Window</name> <message> - <location filename="../Window.cpp" line="340"/> + <location filename="../Window.cpp" line="332"/> <source>Game Boy Advance ROMs (%1)</source> <translation>ROMs de Game Boy Advance (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="356"/> + <location filename="../Window.cpp" line="348"/> <source>Game Boy ROMs (%1)</source> <translation>ROMs de Game Boy (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="360"/> + <location filename="../Window.cpp" line="352"/> <source>All ROMs (%1)</source> <translation>Todas las ROMs (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="375"/> + <location filename="../Window.cpp" line="353"/> + <source>%1 Video Logs (*.mvl)</source> + <translation>Video-registros de %1 (*.mvl)</translation> + </message> + <message> + <location filename="../Window.cpp" line="368"/> <source>Archives (%1)</source> - <translation>Archivos (%1)</translation> + <translation>Contenedores (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="380"/> - <location filename="../Window.cpp" line="388"/> - <location filename="../Window.cpp" line="415"/> + <location filename="../Window.cpp" line="373"/> + <location filename="../Window.cpp" line="381"/> + <location filename="../Window.cpp" line="408"/> <source>Select ROM</source> - <translation>Elegir ROM</translation> + <translation>Seleccionar ROM</translation> + </message> + <message> + <location filename="../Window.cpp" line="399"/> + <source>Select folder</source> + <translation>Seleccionar carpeta</translation> </message> <message> - <location filename="../Window.cpp" line="423"/> + <location filename="../Window.cpp" line="416"/> <source>Game Boy Advance save files (%1)</source> <translation>Archivos de guardado de Game Boy Advance (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="424"/> - <location filename="../Window.cpp" line="457"/> - <location filename="../Window.cpp" line="464"/> + <location filename="../Window.cpp" line="417"/> + <location filename="../Window.cpp" line="451"/> + <location filename="../Window.cpp" line="458"/> <source>Select save</source> - <translation>Elegir guardado</translation> + <translation>Seleccionar guardado</translation> </message> <message> - <location filename="../Window.cpp" line="444"/> + <location filename="../Window.cpp" line="437"/> <source>Select patch</source> - <translation>Elegir parche</translation> + <translation>Seleccionar parche</translation> </message> <message> - <location filename="../Window.cpp" line="444"/> + <location filename="../Window.cpp" line="437"/> <source>Patches (*.ips *.ups *.bps)</source> <translation>Parches (*.ips *.ups *.bps)</translation> </message> <message> - <location filename="../Window.cpp" line="457"/> - <location filename="../Window.cpp" line="464"/> + <location filename="../Window.cpp" line="451"/> + <location filename="../Window.cpp" line="458"/> <source>GameShark saves (*.sps *.xps)</source> <translation>Guardados de GameShark (*.sps *.xps)</translation> </message> <message> - <location filename="../Window.cpp" line="782"/> + <location filename="../Window.cpp" line="486"/> + <source>Select video log</source> + <translation>Seleccionar video-registro</translation> + </message> + <message> + <location filename="../Window.cpp" line="486"/> + <source>Video logs (*.mvl)</source> + <translation>Video-registros (*.mvl)</translation> + </message> + <message> + <location filename="../Window.cpp" line="828"/> <source>Crash</source> <translation>Error fatal</translation> </message> <message> - <location filename="../Window.cpp" line="783"/> + <location filename="../Window.cpp" line="829"/> <source>The game has crashed with the following error: %1</source> - <translation>El juego dejó de funcionar inesperadamente debido a este error: + <translation>El juego ha fallado fatalmente por esta razón: %1</translation> </message> <message> - <location filename="../Window.cpp" line="790"/> + <location filename="../Window.cpp" line="837"/> <source>Couldn&apos;t Load</source> <translation>No se pudo cargar</translation> </message> <message> - <location filename="../Window.cpp" line="791"/> + <location filename="../Window.cpp" line="838"/> <source>Could not load game. Are you sure it&apos;s in the correct format?</source> <translation>No se pudo cargar el juego. ¿Estás seguro de que está en el formato correcto?</translation> </message> <message> - <location filename="../Window.cpp" line="804"/> + <location filename="../Window.cpp" line="852"/> <source>Unimplemented BIOS call</source> <translation>Llamada a BIOS no implementada</translation> </message> <message> - <location filename="../Window.cpp" line="805"/> + <location filename="../Window.cpp" line="853"/> <source>This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience.</source> - <translation>Este juego hizo una llamada al BIOS que no está implementada. Usa el BIOS oficial para obtener la mejor experiencia.</translation> + <translation>Este juego utiliza una llamada al BIOS que no se ha implementado. Utiliza el BIOS oficial para obtener la mejor experiencia.</translation> </message> <message> - <location filename="../Window.cpp" line="812"/> + <location filename="../Window.cpp" line="860"/> <source>Really make portable?</source> - <translation>¿Realmente hacer &quot;portable&quot;?</translation> + <translation>¿Hacer &quot;portable&quot;?</translation> </message> <message> - <location filename="../Window.cpp" line="813"/> + <location filename="../Window.cpp" line="861"/> <source>This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?</source> - <translation>Esto hará que el emulador cargue su configuración desde el mismo directorio que su ejecutable. ¿Quieres continuar?</translation> + <translation>Esto hará que el emulador cargue su configuración desde el mismo directorio que el ejecutable. ¿Quieres continuar?</translation> </message> <message> - <location filename="../Window.cpp" line="821"/> + <location filename="../Window.cpp" line="869"/> <source>Restart needed</source> <translation>Reinicio necesario</translation> </message> <message> - <location filename="../Window.cpp" line="822"/> + <location filename="../Window.cpp" line="870"/> <source>Some changes will not take effect until the emulator is restarted.</source> - <translation>Algunos cambios no tendrán efecto hasta que se reinicie el emulador.</translation> + <translation>Algunos cambios no surtirán efecto hasta que se reinicie el emulador.</translation> </message> <message> - <location filename="../Window.cpp" line="869"/> + <location filename="../Window.cpp" line="917"/> <source> - Player %1 of %2</source> <translation> - Jugador %1 de %2</translation> </message> <message> - <location filename="../Window.cpp" line="880"/> + <location filename="../Window.cpp" line="928"/> <source>%1 - %2</source> <translation>%1 - %2</translation> </message> <message> - <location filename="../Window.cpp" line="882"/> + <location filename="../Window.cpp" line="930"/> <source>%1 - %2 - %3</source> <translation>%1 - %2 - %3</translation> </message> <message> - <location filename="../Window.cpp" line="884"/> + <location filename="../Window.cpp" line="932"/> <source>%1 - %2 (%3 fps) - %4</source> <translation>%1 - %2 (%3 fps) - %4</translation> </message> <message> - <location filename="../Window.cpp" line="916"/> + <location filename="../Window.cpp" line="964"/> <source>&amp;File</source> <translation>&amp;Archivo</translation> </message> <message> - <location filename="../Window.cpp" line="919"/> + <location filename="../Window.cpp" line="967"/> <source>Load &amp;ROM...</source> <translation>Cargar &amp;ROM...</translation> </message> <message> - <location filename="../Window.cpp" line="922"/> + <location filename="../Window.cpp" line="970"/> <source>Load ROM in archive...</source> - <translation>Cargar ROM dentro de archivo...</translation> + <translation>Cargar ROM desde contenedor...</translation> + </message> + <message> + <location filename="../Window.cpp" line="972"/> + <source>Add folder to library...</source> + <translation>Agregar carpeta a la biblioteca...</translation> </message> <message> - <location filename="../Window.cpp" line="928"/> + <location filename="../Window.cpp" line="976"/> <source>Load temporary save...</source> <translation>Cargar guardado temporal...</translation> </message> <message> - <location filename="../Window.cpp" line="933"/> + <location filename="../Window.cpp" line="981"/> <source>Load &amp;patch...</source> <translation>Cargar &amp;parche...</translation> </message> <message> - <location filename="../Window.cpp" line="935"/> + <location filename="../Window.cpp" line="983"/> <source>Boot BIOS</source> - <translation>Iniciar al BIOS</translation> + <translation>Arrancar BIOS</translation> </message> <message> - <location filename="../Window.cpp" line="942"/> + <location filename="../Window.cpp" line="990"/> <source>Replace ROM...</source> <translation>Reemplazar ROM...</translation> </message> <message> - <location filename="../Window.cpp" line="944"/> + <location filename="../Window.cpp" line="992"/> <source>ROM &amp;info...</source> - <translation>&amp;Información de ROM...</translation> + <translation>&amp;Información de la ROM...</translation> </message> <message> - <location filename="../Window.cpp" line="949"/> + <location filename="../Window.cpp" line="997"/> <source>Recent</source> - <translation>Reciente</translation> + <translation>Recientes</translation> </message> <message> - <location filename="../Window.cpp" line="953"/> + <location filename="../Window.cpp" line="1001"/> <source>Make portable</source> <translation>Hacer &quot;portable&quot;</translation> </message> <message> - <location filename="../Window.cpp" line="957"/> + <location filename="../Window.cpp" line="1005"/> <source>&amp;Load state</source> - <translation>Cargar captura de estado (&amp;L)</translation> + <translation>Ca&amp;rgar estado</translation> </message> <message> - <location filename="../Window.cpp" line="958"/> + <location filename="../Window.cpp" line="1006"/> <source>F10</source> <translation>F10</translation> </message> <message> - <location filename="../Window.cpp" line="964"/> + <location filename="../Window.cpp" line="1012"/> <source>&amp;Save state</source> - <translation>Guardar captura de e&amp;stado</translation> + <translation>Guardar e&amp;stado</translation> </message> <message> - <location filename="../Window.cpp" line="965"/> + <location filename="../Window.cpp" line="1013"/> <source>Shift+F10</source> - <translatorcomment>DO NOT TRANSLATE</translatorcomment> <translation>Shift+F10</translation> </message> <message> - <location filename="../Window.cpp" line="971"/> + <location filename="../Window.cpp" line="1019"/> <source>Quick load</source> <translation>Cargado rápido</translation> </message> <message> - <location filename="../Window.cpp" line="972"/> + <location filename="../Window.cpp" line="1020"/> <source>Quick save</source> <translation>Guardado rápido</translation> </message> <message> - <location filename="../Window.cpp" line="976"/> + <location filename="../Window.cpp" line="1024"/> <source>Load recent</source> <translation>Cargar reciente</translation> </message> <message> - <location filename="../Window.cpp" line="982"/> + <location filename="../Window.cpp" line="1030"/> <source>Save recent</source> <translation>Guardar reciente</translation> </message> <message> - <location filename="../Window.cpp" line="991"/> + <location filename="../Window.cpp" line="1039"/> <source>Undo load state</source> - <translation>Deshacer cargar la captura de estado</translation> + <translation>Deshacer cargar estado</translation> </message> <message> - <location filename="../Window.cpp" line="992"/> + <location filename="../Window.cpp" line="1040"/> <source>F11</source> - <translatorcomment>DO NOT TRANSLATE</translatorcomment> <translation>F11</translation> </message> <message> - <location filename="../Window.cpp" line="998"/> + <location filename="../Window.cpp" line="1046"/> <source>Undo save state</source> - <translation>Deshacer guardar la captura de estado</translation> + <translation>Deshacer guardar estado</translation> </message> <message> - <location filename="../Window.cpp" line="999"/> + <location filename="../Window.cpp" line="1047"/> <source>Shift+F11</source> - <translatorcomment>DO NOT TRANSLATE</translatorcomment> <translation>Shift+F11</translation> </message> <message> - <location filename="../Window.cpp" line="1010"/> - <location filename="../Window.cpp" line="1017"/> + <location filename="../Window.cpp" line="1058"/> + <location filename="../Window.cpp" line="1065"/> <source>State &amp;%1</source> - <translation>Captura de estado &amp;%1</translation> + <translation>Estado &amp;%1</translation> </message> <message> - <location filename="../Window.cpp" line="1011"/> + <location filename="../Window.cpp" line="1059"/> <source>F%1</source> <translation>F%1</translation> </message> <message> - <location filename="../Window.cpp" line="1018"/> + <location filename="../Window.cpp" line="1066"/> <source>Shift+F%1</source> - <translatorcomment>DO NOT TRANSLATE</translatorcomment> <translation>Shift+F%1</translation> </message> <message> - <location filename="../Window.cpp" line="1027"/> + <location filename="../Window.cpp" line="1075"/> <source>Import GameShark Save</source> <translation>Importar guardado de GameShark</translation> </message> <message> - <location filename="../Window.cpp" line="1033"/> + <location filename="../Window.cpp" line="1081"/> <source>Export GameShark Save</source> <translation>Exportar guardado de GameShark</translation> </message> <message> - <location filename="../Window.cpp" line="1041"/> + <location filename="../Window.cpp" line="1089"/> <source>New multiplayer window</source> <translation>Nueva ventana multijugador</translation> </message> <message> - <location filename="../Window.cpp" line="1051"/> + <location filename="../Window.cpp" line="1099"/> <source>About</source> <translation>Acerca de</translation> </message> <message> - <location filename="../Window.cpp" line="1056"/> + <location filename="../Window.cpp" line="1104"/> <source>E&amp;xit</source> <translation>Salir (&amp;X)</translation> </message> <message> - <location filename="../Window.cpp" line="1059"/> + <location filename="../Window.cpp" line="1107"/> <source>&amp;Emulation</source> <translation>&amp;Emulación</translation> </message> <message> - <location filename="../Window.cpp" line="1061"/> + <location filename="../Window.cpp" line="1109"/> <source>&amp;Reset</source> <translation>&amp;Reinicializar</translation> </message> <message> - <location filename="../Window.cpp" line="1062"/> + <location filename="../Window.cpp" line="1110"/> <source>Ctrl+R</source> <translation>Ctrl+R</translation> </message> <message> - <location filename="../Window.cpp" line="1067"/> + <location filename="../Window.cpp" line="1115"/> <source>Sh&amp;utdown</source> <translation>Apagar (&amp;U)</translation> </message> <message> - <location filename="../Window.cpp" line="1073"/> + <location filename="../Window.cpp" line="1121"/> <source>Yank game pak</source> - <translation>Arrancar el game pak de su ranura</translation> + <translation>Tirar del cartucho</translation> </message> <message> - <location filename="../Window.cpp" line="1081"/> + <location filename="../Window.cpp" line="1129"/> <source>&amp;Pause</source> <translation>&amp;Pausar</translation> </message> <message> - <location filename="../Window.cpp" line="1084"/> + <location filename="../Window.cpp" line="1132"/> <source>Ctrl+P</source> <translation>Ctrl+P</translation> </message> <message> - <location filename="../Window.cpp" line="1093"/> + <location filename="../Window.cpp" line="1141"/> <source>&amp;Next frame</source> - <translation>Saltar al próximo cuadro (&amp;N)</translation> + <translation>Cuadro siguie&amp;nte</translation> </message> <message> - <location filename="../Window.cpp" line="1094"/> + <location filename="../Window.cpp" line="1142"/> <source>Ctrl+N</source> <translation>Ctrl+N</translation> </message> <message> - <location filename="../Window.cpp" line="1105"/> + <location filename="../Window.cpp" line="1153"/> <source>Fast forward (held)</source> <translation>Avance rápido (mantener)</translation> </message> <message> - <location filename="../Window.cpp" line="1107"/> + <location filename="../Window.cpp" line="1155"/> <source>&amp;Fast forward</source> - <translation>Avance rápido (&amp;F)</translation> + <translation>&amp;Avance rápido</translation> </message> <message> - <location filename="../Window.cpp" line="1110"/> + <location filename="../Window.cpp" line="1158"/> <source>Shift+Tab</source> <translation>Shift+Tab</translation> </message> <message> - <location filename="../Window.cpp" line="1114"/> + <location filename="../Window.cpp" line="1162"/> <source>Fast forward speed</source> <translation>Velocidad de avance rápido</translation> </message> <message> - <location filename="../Window.cpp" line="1119"/> + <location filename="../Window.cpp" line="1167"/> <source>Unbounded</source> - <translation>Ilimitado</translation> + <translation>Sin límite</translation> </message> <message> - <location filename="../Window.cpp" line="1123"/> + <location filename="../Window.cpp" line="1171"/> <source>%0x</source> <translation>%0x</translation> </message> <message> - <location filename="../Window.cpp" line="1131"/> + <location filename="../Window.cpp" line="1179"/> <source>Rewind (held)</source> - <translation>Retroceder (mantener)</translation> + <translation>Rebobinar (mantener)</translation> </message> <message> - <location filename="../Window.cpp" line="1133"/> + <location filename="../Window.cpp" line="1181"/> <source>Re&amp;wind</source> - <translation>Retroceder (&amp;W)</translation> + <translation>Re&amp;bobinar</translation> </message> <message> - <location filename="../Window.cpp" line="1134"/> + <location filename="../Window.cpp" line="1182"/> <source>~</source> <translation>~</translation> </message> <message> - <location filename="../Window.cpp" line="1140"/> + <location filename="../Window.cpp" line="1188"/> <source>Step backwards</source> <translation>Paso hacia atrás</translation> </message> <message> - <location filename="../Window.cpp" line="1141"/> + <location filename="../Window.cpp" line="1189"/> <source>Ctrl+B</source> <translation>Ctrl+B</translation> </message> <message> - <location filename="../Window.cpp" line="1150"/> + <location filename="../Window.cpp" line="1198"/> <source>Sync to &amp;video</source> <translation>Sincronizar a &amp;video</translation> </message> <message> - <location filename="../Window.cpp" line="1157"/> + <location filename="../Window.cpp" line="1205"/> <source>Sync to &amp;audio</source> - <translation>Sincronizar a &amp;audio</translation> + <translation>Sincronizar a au&amp;dio</translation> </message> <message> - <location filename="../Window.cpp" line="1165"/> + <location filename="../Window.cpp" line="1213"/> <source>Solar sensor</source> <translation>Sensor solar</translation> </message> <message> - <location filename="../Window.cpp" line="1167"/> + <location filename="../Window.cpp" line="1215"/> <source>Increase solar level</source> - <translation>Aumentar nivel solar</translation> + <translation>Subir nivel</translation> </message> <message> - <location filename="../Window.cpp" line="1171"/> + <location filename="../Window.cpp" line="1219"/> <source>Decrease solar level</source> - <translation>Disminuir nivel solar</translation> + <translation>Bajar nivel</translation> </message> <message> - <location filename="../Window.cpp" line="1175"/> + <location filename="../Window.cpp" line="1223"/> <source>Brightest solar level</source> - <translation>Nivel solar más brillante</translation> + <translation>Más claro</translation> </message> <message> - <location filename="../Window.cpp" line="1179"/> + <location filename="../Window.cpp" line="1227"/> <source>Darkest solar level</source> - <translation>Nivel solar más oscuro</translation> + <translation>Más oscuro</translation> </message> <message> - <location filename="../Window.cpp" line="1185"/> + <location filename="../Window.cpp" line="1233"/> <source>Brightness %1</source> <translation>Brillo %1</translation> </message> <message> - <location filename="../Window.cpp" line="1192"/> + <location filename="../Window.cpp" line="1240"/> <source>Audio/&amp;Video</source> - <translation>Audio/&amp;Video</translation> + <translation>Audio/&amp;video</translation> </message> <message> - <location filename="../Window.cpp" line="1194"/> + <location filename="../Window.cpp" line="1242"/> <source>Frame size</source> <translation>Tamaño del cuadro</translation> </message> <message> - <location filename="../Window.cpp" line="1197"/> + <location filename="../Window.cpp" line="1245"/> <source>%1x</source> <translation>%1x</translation> </message> <message> - <location filename="../Window.cpp" line="1225"/> + <location filename="../Window.cpp" line="1273"/> <source>Toggle fullscreen</source> <translation>Pantalla completa</translation> </message> <message> - <location filename="../Window.cpp" line="1228"/> + <location filename="../Window.cpp" line="1276"/> <source>Lock aspect ratio</source> - <translation>Bloquear relación de aspecto</translation> + <translation>Bloquear proporción de aspecto</translation> + </message> + <message> + <location filename="../Window.cpp" line="1283"/> + <source>Force integer scaling</source> + <translation>Forzar escala a enteros</translation> + </message> + <message> + <location filename="../Window.cpp" line="1293"/> + <source>Bilinear filtering</source> + <translation>Filtro bilineal</translation> </message> <message> - <location filename="../Window.cpp" line="1241"/> + <location filename="../Window.cpp" line="1299"/> <source>Frame&amp;skip</source> <translation>&amp;Salto de cuadros</translation> </message> <message> - <location filename="../Window.cpp" line="1251"/> - <source>Shader options...</source> - <translation>Opciones del programa shader...</translation> - </message> - <message> - <location filename="../Window.cpp" line="1261"/> + <location filename="../Window.cpp" line="1312"/> <source>Mute</source> <translation>Silenciar</translation> </message> <message> - <location filename="../Window.cpp" line="1267"/> + <location filename="../Window.cpp" line="1319"/> <source>FPS target</source> <translation>Objetivo de FPS</translation> </message> <message> - <location filename="../Window.cpp" line="1272"/> + <location filename="../Window.cpp" line="1324"/> <source>15</source> <translation>15</translation> </message> <message> - <location filename="../Window.cpp" line="1273"/> + <location filename="../Window.cpp" line="1325"/> <source>30</source> <translation>30</translation> </message> <message> - <location filename="../Window.cpp" line="1274"/> + <location filename="../Window.cpp" line="1326"/> <source>45</source> <translation>45</translation> </message> <message> - <location filename="../Window.cpp" line="1275"/> + <location filename="../Window.cpp" line="1327"/> <source>Native (59.7)</source> <translation>Nativo (59.7)</translation> </message> <message> - <location filename="../Window.cpp" line="1276"/> + <location filename="../Window.cpp" line="1328"/> <source>60</source> <translation>60</translation> </message> <message> - <location filename="../Window.cpp" line="1277"/> + <location filename="../Window.cpp" line="1329"/> <source>90</source> <translation>90</translation> </message> <message> - <location filename="../Window.cpp" line="1278"/> + <location filename="../Window.cpp" line="1330"/> <source>120</source> - <translation>120</translation> + <translation>Bilineal120</translation> </message> <message> - <location filename="../Window.cpp" line="1279"/> + <location filename="../Window.cpp" line="1331"/> <source>240</source> <translation>240</translation> </message> <message> - <location filename="../Window.cpp" line="1287"/> + <location filename="../Window.cpp" line="1339"/> <source>Take &amp;screenshot</source> - <translation>Tomar pantallazo (&amp;S)</translation> + <translation>Tomar pan&amp;tallazo</translation> </message> <message> - <location filename="../Window.cpp" line="1288"/> + <location filename="../Window.cpp" line="1340"/> <source>F12</source> <translation>F12</translation> </message> <message> - <location filename="../Window.cpp" line="1295"/> + <location filename="../Window.cpp" line="1347"/> <source>Record output...</source> <translation>Grabar salida...</translation> </message> <message> - <location filename="../Window.cpp" line="1302"/> + <location filename="../Window.cpp" line="1354"/> <source>Record GIF...</source> <translation>Grabar GIF...</translation> </message> <message> - <location filename="../Window.cpp" line="1308"/> - <source>Video layers</source> - <translation>Capas de video</translation> + <location filename="../Window.cpp" line="1359"/> + <source>Record video log...</source> + <translation>Grabar video-registro...</translation> </message> <message> - <location filename="../Window.cpp" line="1312"/> - <source>Background %0</source> - <translation>Fondo %0</translation> + <location filename="../Window.cpp" line="1364"/> + <source>Stop video log</source> + <translation>Detener video-registro</translation> </message> <message> - <location filename="../Window.cpp" line="1319"/> - <source>OBJ (sprites)</source> - <translation>OBJ (sprites)</translation> + <location filename="../Window.cpp" line="1370"/> + <source>Video layers</source> + <translation>Capas de video</translation> </message> <message> - <location filename="../Window.cpp" line="1325"/> + <location filename="../Window.cpp" line="1373"/> <source>Audio channels</source> <translation>Canales de audio</translation> </message> <message> - <location filename="../Window.cpp" line="1329"/> - <source>Channel %0</source> - <translation>Canal %0</translation> - </message> - <message> - <location filename="../Window.cpp" line="1336"/> - <source>Channel A</source> - <translation>Canal A</translation> - </message> - <message> - <location filename="../Window.cpp" line="1342"/> - <source>Channel B</source> - <translation>Canal B</translation> - </message> - <message> - <location filename="../Window.cpp" line="1348"/> + <location filename="../Window.cpp" line="1376"/> <source>&amp;Tools</source> <translation>Herramien&amp;tas</translation> </message> <message> - <location filename="../Window.cpp" line="1350"/> + <location filename="../Window.cpp" line="1378"/> <source>View &amp;logs...</source> - <translation>Ver registros... (&amp;L)</translation> + <translation>Ver re&amp;gistros...</translation> </message> <message> - <location filename="../Window.cpp" line="1354"/> + <location filename="../Window.cpp" line="1382"/> <source>Game &amp;overrides...</source> - <translation>Val&amp;ores específicos por juego...</translation> + <translation>Ajustes específic&amp;os por juego...</translation> </message> <message> - <location filename="../Window.cpp" line="1358"/> + <location filename="../Window.cpp" line="1386"/> <source>Game &amp;Pak sensors...</source> - <translation>Sensores en el Game &amp;Pak...</translation> + <translation>Sensores del Game &amp;Pak...</translation> </message> <message> - <location filename="../Window.cpp" line="1362"/> + <location filename="../Window.cpp" line="1390"/> <source>&amp;Cheats...</source> <translation>Tru&amp;cos...</translation> </message> <message> - <location filename="../Window.cpp" line="1374"/> - <source>Open debugger console...</source> - <translation>Abrir la consola de depuración...</translation> - </message> - <message> - <location filename="../Window.cpp" line="1380"/> - <source>Start &amp;GDB server...</source> - <translation>Iniciar servidor &amp;GDB...</translation> - </message> - <message> - <location filename="../Window.cpp" line="1368"/> + <location filename="../Window.cpp" line="1396"/> <source>Settings...</source> <translation>Ajustes...</translation> </message> <message> - <location filename="../Window.cpp" line="406"/> - <source>Select folder</source> - <translation>Elegir carpeta</translation> - </message> - <message> - <location filename="../Window.cpp" line="924"/> - <source>Add folder to library...</source> - <translation>Agregar carpeta a la biblioteca...</translation> + <location filename="../Window.cpp" line="1402"/> + <source>Open debugger console...</source> + <translation>Abrir consola de depuración...</translation> </message> <message> - <location filename="../Window.cpp" line="1235"/> - <source>Bilinear filtering</source> - <translation type="unfinished"></translation> + <location filename="../Window.cpp" line="1408"/> + <source>Start &amp;GDB server...</source> + <translation>Iniciar servidor &amp;GDB...</translation> </message> <message> - <location filename="../Window.cpp" line="1387"/> + <location filename="../Window.cpp" line="1415"/> <source>View &amp;palette...</source> <translation>Ver &amp;paleta...</translation> </message> <message> - <location filename="../Window.cpp" line="1392"/> + <location filename="../Window.cpp" line="1420"/> <source>View &amp;sprites...</source> <translation>Ver &amp;sprites...</translation> </message> <message> - <location filename="../Window.cpp" line="1397"/> + <location filename="../Window.cpp" line="1425"/> <source>View &amp;tiles...</source> <translation>Ver &amp;tiles...</translation> </message> <message> - <location filename="../Window.cpp" line="1402"/> + <location filename="../Window.cpp" line="1430"/> <source>View memory...</source> <translation>Ver memoria...</translation> </message> <message> - <location filename="../Window.cpp" line="1408"/> + <location filename="../Window.cpp" line="1435"/> + <source>Search memory...</source> + <translation>Buscar memoria...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1441"/> <source>View &amp;I/O registers...</source> - <translation>Ver reg&amp;istros E/S...</translation> + <translation>Ver registros &amp;I/O...</translation> </message> <message> - <location filename="../Window.cpp" line="1470"/> + <location filename="../Window.cpp" line="1511"/> <source>Exit fullscreen</source> <translation>Salir de pantalla completa</translation> </message> <message> - <location filename="../Window.cpp" line="1475"/> + <location filename="../Window.cpp" line="1516"/> <source>Autofire</source> - <translation>Botones turbo</translation> + <translation>Disparo automático</translation> </message> <message> - <location filename="../Window.cpp" line="1482"/> + <location filename="../Window.cpp" line="1523"/> <source>Autofire A</source> - <translation>Turbo A</translation> + <translation>Disparo automático A</translation> </message> <message> - <location filename="../Window.cpp" line="1488"/> + <location filename="../Window.cpp" line="1529"/> <source>Autofire B</source> - <translation>Turbo B</translation> + <translation>Disparo automático B</translation> </message> <message> - <location filename="../Window.cpp" line="1494"/> + <location filename="../Window.cpp" line="1535"/> <source>Autofire L</source> - <translation>Turbo L</translation> + <translation>Disparo automático L</translation> </message> <message> - <location filename="../Window.cpp" line="1500"/> + <location filename="../Window.cpp" line="1541"/> <source>Autofire R</source> - <translation>Turbo R</translation> + <translation>Disparo automático R</translation> </message> <message> - <location filename="../Window.cpp" line="1506"/> + <location filename="../Window.cpp" line="1547"/> <source>Autofire Start</source> - <translation>Turbo Start</translation> + <translation>Disparo automático Start</translation> </message> <message> - <location filename="../Window.cpp" line="1512"/> + <location filename="../Window.cpp" line="1553"/> <source>Autofire Select</source> - <translation>Turbo Select</translation> + <translation>Disparo automático Select</translation> </message> <message> - <location filename="../Window.cpp" line="1518"/> + <location filename="../Window.cpp" line="1559"/> <source>Autofire Up</source> - <translation>Turbo Arriba</translation> + <translation>Disparo automático Arriba</translation> </message> <message> - <location filename="../Window.cpp" line="1524"/> + <location filename="../Window.cpp" line="1565"/> <source>Autofire Right</source> - <translation>Turbo Derecha</translation> + <translation>Disparo automático Derecha</translation> </message> <message> - <location filename="../Window.cpp" line="1530"/> + <location filename="../Window.cpp" line="1571"/> <source>Autofire Down</source> - <translation>Turbo Abajo</translation> + <translation>Disparo automático Abajo</translation> </message> <message> - <location filename="../Window.cpp" line="1536"/> + <location filename="../Window.cpp" line="1577"/> <source>Autofire Left</source> - <translation>Turbo Izquierda</translation> + <translation>Disparo automático Izquierda</translation> + </message> +</context> +<context> + <name>QObject</name> + <message> + <location filename="../utils.cpp" line="29"/> + <source>GBA</source> + <translation>GBA</translation> + </message> + <message> + <location filename="../utils.cpp" line="33"/> + <source>GB</source> + <translation>GB</translation> + </message> + <message> + <location filename="../utils.cpp" line="36"/> + <source>?</source> + <translation>?</translation> </message> </context> <context>

@@ -3603,7 +3755,7 @@ </message>

<message> <location filename="../ROMInfo.ui" line="63"/> <source>Game ID:</source> - <translation>ID del juego:</translation> + <translation>ID de juego:</translation> </message> <message> <location filename="../ROMInfo.ui" line="70"/>

@@ -3656,7 +3808,7 @@ </message>

<message> <location filename="../SensorView.ui" line="60"/> <source>Start time at</source> - <translation>Contar desde el</translation> + <translation>Empezar desde esta hora</translation> </message> <message> <location filename="../SensorView.ui" line="70"/>

@@ -3687,13 +3839,13 @@ <message>

<location filename="../SensorView.ui" line="161"/> <location filename="../SensorView.ui" line="250"/> <source>Set Y</source> - <translation>Config. Y</translation> + <translation>Ajustar Y</translation> </message> <message> <location filename="../SensorView.ui" line="171"/> <location filename="../SensorView.ui" line="260"/> <source>Set X</source> - <translation>Config. X</translation> + <translation>Ajustar X</translation> </message> <message> <location filename="../SensorView.ui" line="242"/>

@@ -3716,7 +3868,7 @@ </message>

<message> <location filename="../SettingsView.ui" line="45"/> <source>Audio/Video</source> - <translation>Audio/Video</translation> + <translation>Audio/video</translation> </message> <message> <location filename="../SettingsView.ui" line="50"/>

@@ -3729,6 +3881,11 @@ <source>Emulation</source>

<translation>Emulación</translation> </message> <message> + <location filename="../SettingsView.ui" line="60"/> + <source>BIOS</source> + <translation>BIOS</translation> + </message> + <message> <location filename="../SettingsView.ui" line="65"/> <source>Paths</source> <translation>Rutas</translation>

@@ -3736,7 +3893,7 @@ </message>

<message> <location filename="../SettingsView.ui" line="90"/> <source>Audio driver:</source> - <translation>Controlador de audio:</translation> + <translation>Sistema de audio:</translation> </message> <message> <location filename="../SettingsView.ui" line="107"/>

@@ -3828,7 +3985,7 @@ </message>

<message> <location filename="../SettingsView.ui" line="271"/> <source>Display driver:</source> - <translation>Controlador de video:</translation> + <translation>Sistema de video:</translation> </message> <message> <location filename="../SettingsView.ui" line="288"/>

@@ -3842,7 +3999,7 @@ <translation>Saltar cada</translation>

</message> <message> <location filename="../SettingsView.ui" line="307"/> - <location filename="../SettingsView.ui" line="532"/> + <location filename="../SettingsView.ui" line="576"/> <source>frames</source> <translation>cuadros</translation> </message>

@@ -3859,7 +4016,7 @@ </message>

<message> <location filename="../SettingsView.ui" line="354"/> <source>Sync:</source> - <translation>SIncronizar a:</translation> + <translation>Sincronizar con:</translation> </message> <message> <location filename="../SettingsView.ui" line="363"/>

@@ -3874,187 +4031,212 @@ </message>

<message> <location filename="../SettingsView.ui" line="379"/> <source>Lock aspect ratio</source> - <translation>Bloquear relación de aspecto</translation> + <translation>Bloquear proporción de aspecto</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="386"/> + <source>Bilinear filtering</source> + <translation>Filtro bilineal</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="393"/> + <source>Force integer scaling</source> + <translation>Forzar escala a enteros</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="404"/> + <source>Language</source> + <translation>Idioma</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="412"/> + <source>English</source> + <translation>English</translation> </message> <message> - <location filename="../SettingsView.ui" line="421"/> + <location filename="../SettingsView.ui" line="420"/> <source>Library:</source> <translation>Biblioteca:</translation> </message> <message> <location filename="../SettingsView.ui" line="428"/> + <source>List view</source> + <translation>Lista</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="433"/> + <source>Tree view</source> + <translation>Árbol</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="441"/> <source>Show when no game open</source> - <translation>Mostrar al no haber juego abierto</translation> + <translation>Mostrar cuando no haya un juego abierto</translation> </message> <message> - <location filename="../SettingsView.ui" line="445"/> + <location filename="../SettingsView.ui" line="451"/> <source>Clear cache</source> <translation>Limpiar caché</translation> </message> <message> - <location filename="../SettingsView.ui" line="459"/> - <source>Fast forward speed:</source> - <translation>Velocidad de avance rápido:</translation> + <location filename="../SettingsView.ui" line="465"/> + <source>Allow opposing input directions</source> + <translation>Permitir direcciones opuestas al mismo tiempo</translation> </message> <message> - <location filename="../SettingsView.ui" line="689"/> - <location filename="../SettingsView.ui" line="727"/> - <location filename="../SettingsView.ui" line="762"/> - <location filename="../SettingsView.ui" line="803"/> - <location filename="../SettingsView.ui" line="851"/> - <location filename="../SettingsView.ui" line="899"/> - <location filename="../SettingsView.ui" line="947"/> - <source>Browse</source> - <translation>Examinar</translation> + <location filename="../SettingsView.ui" line="472"/> + <source>Suspend screensaver</source> + <translation>Suspender protector de pantalla</translation> </message> <message> - <location filename="../SettingsView.ui" line="698"/> - <source>Use BIOS file if found</source> - <translation>Usar archivo BIOS si hay</translation> + <location filename="../SettingsView.ui" line="482"/> + <source>Pause when inactive</source> + <translation>Pausar al no estar activo</translation> </message> <message> - <location filename="../SettingsView.ui" line="708"/> - <source>Skip BIOS intro</source> - <translation>Saltar pantalla de inicio de la BIOS</translation> + <location filename="../SettingsView.ui" line="503"/> + <source>Fast forward speed:</source> + <translation>Velocidad de avance rápido:</translation> </message> <message> - <location filename="../SettingsView.ui" line="471"/> + <location filename="../SettingsView.ui" line="515"/> <source>×</source> <translation>×</translation> </message> <message> - <location filename="../SettingsView.ui" line="490"/> + <location filename="../SettingsView.ui" line="534"/> <source>Unbounded</source> - <translation>Ilimitado</translation> + <translation>Sin límite</translation> </message> <message> - <location filename="../SettingsView.ui" line="404"/> - <source>Suspend screensaver</source> - <translation>No permitir protector de pantalla</translation> + <location filename="../SettingsView.ui" line="553"/> + <source>Enable rewind</source> + <translation>Habilitar el rebobinar</translation> </message> <message> - <location filename="../SettingsView.ui" line="60"/> - <source>BIOS</source> - <translation>BIOS</translation> + <location filename="../SettingsView.ui" line="560"/> + <source>Rewind history:</source> + <translation>Historial de rebobinado:</translation> </message> <message> - <location filename="../SettingsView.ui" line="414"/> - <source>Pause when inactive</source> - <translation>Pausar al estar inactivo</translation> + <location filename="../SettingsView.ui" line="592"/> + <source>Idle loops:</source> + <translation>Bucles inactivos:</translation> </message> <message> - <location filename="../SettingsView.ui" line="556"/> + <location filename="../SettingsView.ui" line="600"/> <source>Run all</source> - <translation>Ejecutar todos</translation> + <translation>Ejecutarlos todos</translation> </message> <message> - <location filename="../SettingsView.ui" line="561"/> + <location filename="../SettingsView.ui" line="605"/> <source>Remove known</source> <translation>Eliminar los conocidos</translation> </message> <message> - <location filename="../SettingsView.ui" line="566"/> + <location filename="../SettingsView.ui" line="610"/> <source>Detect and remove</source> <translation>Detectar y eliminar</translation> </message> <message> - <location filename="../SettingsView.ui" line="397"/> - <source>Allow opposing input directions</source> - <translation>Permitir direcciones opuestas</translation> + <location filename="../SettingsView.ui" line="625"/> + <source>Savestate extra data:</source> + <translation>Guardar datos extra con el estado:</translation> </message> <message> - <location filename="../SettingsView.ui" line="588"/> - <location filename="../SettingsView.ui" line="625"/> + <location filename="../SettingsView.ui" line="632"/> + <location filename="../SettingsView.ui" line="669"/> <source>Screenshot</source> <translation>Pantallazo</translation> </message> <message> - <location filename="../SettingsView.ui" line="598"/> - <location filename="../SettingsView.ui" line="635"/> + <location filename="../SettingsView.ui" line="642"/> + <location filename="../SettingsView.ui" line="679"/> <source>Save data</source> <translation>Datos de guardado</translation> </message> <message> - <location filename="../SettingsView.ui" line="608"/> - <location filename="../SettingsView.ui" line="642"/> + <location filename="../SettingsView.ui" line="652"/> + <location filename="../SettingsView.ui" line="686"/> <source>Cheat codes</source> <translation>Trucos</translation> </message> <message> - <location filename="../SettingsView.ui" line="509"/> - <source>Enable rewind</source> - <translation>Habilitar retroceso</translation> + <location filename="../SettingsView.ui" line="662"/> + <source>Load extra data:</source> + <translation>Cargar datos extra con el estado:</translation> </message> <message> - <location filename="../SettingsView.ui" line="386"/> - <source>Bilinear filtering</source> - <translation type="unfinished"></translation> + <location filename="../SettingsView.ui" line="700"/> + <source>Rewind affects save data</source> + <translation>El rebobinar afecta los datos de guardado</translation> </message> <message> - <location filename="../SettingsView.ui" line="516"/> - <source>Rewind history:</source> - <translation>Historial de retroceso:</translation> + <location filename="../SettingsView.ui" line="710"/> + <source>Preload entire ROM into memory</source> + <translation>Cargar ROM completa a la memoria</translation> </message> <message> - <location filename="../SettingsView.ui" line="548"/> - <source>Idle loops:</source> - <translation>Bucles inactivos:</translation> + <location filename="../SettingsView.ui" line="721"/> + <source>GB BIOS file:</source> + <translation>Archivo BIOS GB:</translation> </message> <message> - <location filename="../SettingsView.ui" line="581"/> - <source>Savestate extra data:</source> - <translation>Datos extras en capturas de estado:</translation> + <location filename="../SettingsView.ui" line="740"/> + <location filename="../SettingsView.ui" line="778"/> + <location filename="../SettingsView.ui" line="813"/> + <location filename="../SettingsView.ui" line="854"/> + <location filename="../SettingsView.ui" line="902"/> + <location filename="../SettingsView.ui" line="950"/> + <location filename="../SettingsView.ui" line="998"/> + <source>Browse</source> + <translation>Examinar</translation> </message> <message> - <location filename="../SettingsView.ui" line="618"/> - <source>Load extra data:</source> - <translation>Cargar datos extra:</translation> + <location filename="../SettingsView.ui" line="749"/> + <source>Use BIOS file if found</source> + <translation>Usar archivo BIOS si fue encontrado</translation> </message> <message> - <location filename="../SettingsView.ui" line="656"/> - <source>Rewind affects save data</source> - <translation type="unfinished"></translation> + <location filename="../SettingsView.ui" line="759"/> + <source>Skip BIOS intro</source> + <translation>Saltar animación de entrada del BIOS</translation> </message> <message> - <location filename="../SettingsView.ui" line="670"/> - <source>GB BIOS file:</source> - <translation>Archivo de BIOS GB:</translation> - </message> - <message> - <location filename="../SettingsView.ui" line="736"/> + <location filename="../SettingsView.ui" line="787"/> <source>GBA BIOS file:</source> - <translation>Archivo de BIOS GBA:</translation> + <translation>Archivo BIOS GBA:</translation> </message> <message> - <location filename="../SettingsView.ui" line="743"/> + <location filename="../SettingsView.ui" line="794"/> <source>GBC BIOS file:</source> - <translation>Archivo de BIOS GBC:</translation> + <translation>Archivo BIOS GBC:</translation> </message> <message> - <location filename="../SettingsView.ui" line="778"/> + <location filename="../SettingsView.ui" line="829"/> <source>Save games</source> - <translation>Guardados de juego</translation> + <translation>Datos de guardado</translation> </message> <message> - <location filename="../SettingsView.ui" line="812"/> - <location filename="../SettingsView.ui" line="860"/> - <location filename="../SettingsView.ui" line="908"/> - <location filename="../SettingsView.ui" line="956"/> + <location filename="../SettingsView.ui" line="863"/> + <location filename="../SettingsView.ui" line="911"/> + <location filename="../SettingsView.ui" line="959"/> + <location filename="../SettingsView.ui" line="1007"/> <source>Same directory as the ROM</source> - <translation>Mismo directorio de la ROM</translation> + <translation>Al mismo directorio que la ROM</translation> </message> <message> - <location filename="../SettingsView.ui" line="826"/> + <location filename="../SettingsView.ui" line="877"/> <source>Save states</source> - <translation>Capturas de estado</translation> + <translation>Estados de guardado</translation> </message> <message> - <location filename="../SettingsView.ui" line="874"/> + <location filename="../SettingsView.ui" line="925"/> <source>Screenshots</source> <translation>Pantallazos</translation> </message> <message> - <location filename="../SettingsView.ui" line="922"/> + <location filename="../SettingsView.ui" line="973"/> <source>Patches</source> <translation>Parches</translation> </message>

@@ -4064,12 +4246,12 @@ <name>ShaderSelector</name>

<message> <location filename="../ShaderSelector.ui" line="14"/> <source>Shaders</source> - <translation>Programas shader</translation> + <translation>Shaders</translation> </message> <message> <location filename="../ShaderSelector.ui" line="28"/> <source>Active Shader:</source> - <translation>Programa shader activo:</translation> + <translation>Shader activo:</translation> </message> <message> <location filename="../ShaderSelector.ui" line="35"/>

@@ -4089,12 +4271,12 @@ </message>

<message> <location filename="../ShaderSelector.ui" line="88"/> <source>Unload Shader</source> - <translation>Cerrar programa shader</translation> + <translation>Cerrar shader</translation> </message> <message> <location filename="../ShaderSelector.ui" line="95"/> <source>Load New Shader</source> - <translation>Cargar nuevo p. shader</translation> + <translation>Cargar nuevo shader</translation> </message> </context> <context>

@@ -4102,7 +4284,7 @@ <name>ShortcutView</name>

<message> <location filename="../ShortcutView.ui" line="14"/> <source>Edit Shortcuts</source> - <translation>Editar accesos directos de teclado</translation> + <translation>Editar atajos de teclado</translation> </message> <message> <location filename="../ShortcutView.ui" line="29"/>

@@ -4140,7 +4322,7 @@ </message>

<message> <location filename="../TileView.ui" line="83"/> <source>Magnification</source> - <translation>Magnificación</translation> + <translation>Ampliación</translation> </message> </context> <context>

@@ -4163,12 +4345,12 @@ </message>

<message> <location filename="../VideoView.ui" line="69"/> <source>Select File</source> - <translation>Elegir archivo</translation> + <translation>Seleccionar archivo</translation> </message> <message> <location filename="../VideoView.ui" line="101"/> <source>Presets</source> - <translation>Ajustes predeterminados</translation> + <translation>Ajustes predefinidos</translation> </message> <message> <location filename="../VideoView.ui" line="109"/>

@@ -4209,7 +4391,7 @@ </message>

<message> <location filename="../VideoView.ui" line="189"/> <source>Native</source> - <translation>Nativo</translation> + <translation>Nativa</translation> </message> <message> <location filename="../VideoView.ui" line="222"/>

@@ -4243,86 +4425,91 @@ <translation>h.264</translation>

</message> <message> <location filename="../VideoView.ui" line="269"/> - <source>VP8</source> - <translation></translation> + <source>h.264 (NVENC)</source> + <translation>h.264 (NVENC)</translation> </message> <message> <location filename="../VideoView.ui" line="274"/> - <source>Xvid</source> - <translation>Xvid</translation> + <source>HEVC</source> + <translation>HEVC</translation> </message> <message> <location filename="../VideoView.ui" line="279"/> + <source>VP8</source> + <translation>VP8</translation> + </message> + <message> + <location filename="../VideoView.ui" line="284"/> <source>FFV1</source> <translation>FFV1</translation> </message> <message> - <location filename="../VideoView.ui" line="291"/> + <location filename="../VideoView.ui" line="296"/> <source>FLAC</source> <translation>FLAC</translation> </message> <message> - <location filename="../VideoView.ui" line="296"/> + <location filename="../VideoView.ui" line="301"/> <source>Opus</source> <translation>Opus</translation> </message> <message> - <location filename="../VideoView.ui" line="301"/> + <location filename="../VideoView.ui" line="306"/> <source>Vorbis</source> <translation>Vorbis</translation> </message> <message> - <location filename="../VideoView.ui" line="306"/> + <location filename="../VideoView.ui" line="311"/> <source>MP3</source> <translation>MP3</translation> </message> <message> - <location filename="../VideoView.ui" line="311"/> + <location filename="../VideoView.ui" line="316"/> <source>AAC</source> <translation>AAC</translation> </message> <message> - <location filename="../VideoView.ui" line="316"/> + <location filename="../VideoView.ui" line="321"/> <source>Uncompressed</source> <translation>Sin comprimir</translation> </message> <message> - <location filename="../VideoView.ui" line="327"/> + <location filename="../VideoView.ui" line="332"/> <source> Bitrate (kbps)</source> - <translation>Tasa de bits (kbps)</translation> + <translation> Tasa de bits (kbps)</translation> </message> <message> - <location filename="../VideoView.ui" line="333"/> + <location filename="../VideoView.ui" line="338"/> <source>VBR </source> <translation>VBR </translation> </message> <message> - <location filename="../VideoView.ui" line="381"/> + <location filename="../VideoView.ui" line="386"/> <source>ABR</source> <translation>ABR</translation> </message> <message> - <location filename="../VideoView.ui" line="397"/> + <location filename="../VideoView.ui" line="402"/> <source>Dimensions</source> <translation>Dimensiones</translation> </message> <message> - <location filename="../VideoView.ui" line="403"/> + <location filename="../VideoView.ui" line="408"/> <source>:</source> <translation>:</translation> </message> <message> - <location filename="../VideoView.ui" line="413"/> + <location filename="../VideoView.ui" line="418"/> <source>×</source> <translation>×</translation> </message> <message> - <location filename="../VideoView.ui" line="463"/> + <location filename="../VideoView.ui" line="468"/> <source>Lock aspect ratio</source> - <translation>Bloquear relación de aspecto</translation> + <translation>Bloquear proporción de aspecto</translation> </message> <message> - <location filename="../VideoView.ui" line="478"/> + <location filename="../VideoView.ui" line="483"/> <source>Show advanced</source> <translation>Mostrar ajustes avanzados</translation> </message>
M src/platform/sdl/CMakeLists.txtsrc/platform/sdl/CMakeLists.txt

@@ -86,11 +86,17 @@ list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sw-sdl.c)

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/mgba.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-sdl) endif()
M src/platform/sdl/gl-sdl.csrc/platform/sdl/gl-sdl.c

@@ -53,7 +53,7 @@ struct mCoreThread* context = 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 @@ }

#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); }
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -13,6 +13,13 @@ #endif

#ifdef USE_EDITLINE #include "feature/editline/cli-el-backend.h" #endif +#ifdef ENABLE_SCRIPTING +#include <mgba/core/scripting.h> + +#ifdef ENABLE_PYTHON +#include "platform/python/engine.h" +#endif +#endif #include <mgba/core/core.h> #include <mgba/core/config.h>

@@ -56,7 +63,7 @@ struct mSubParser subparser;

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 @@ if (!mCoreLoadFile(renderer->core, args->fname)) {

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 @@ #endif

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 @@ } else {

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; }
M src/platform/sdl/sdl-audio.csrc/platform/sdl/sdl-audio.c

@@ -40,12 +40,11 @@ #endif

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);
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -416,7 +416,7 @@ }

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) {
M src/platform/wii/main.csrc/platform/wii/main.c

@@ -24,6 +24,7 @@ #include <mgba-util/gui.h>

#include <mgba-util/gui/file-select.h> #include <mgba-util/gui/font.h> #include <mgba-util/gui/menu.h> +#include <mgba-util/memory.h> #include <mgba-util/vfs.h> #define GCN1_INPUT 0x47434E31

@@ -113,6 +114,9 @@ static uint32_t referenceRetraceCount;

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 @@ AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);

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

@@ -509,6 +517,10 @@ } else {

mGUIRunloop(&runner); } mGUIDeinit(&runner); + +#ifdef FIXED_ROM_BUFFER + mappedMemoryFree(romBuffer, romBufferSize); +#endif free(fifo); free(texmem);
M version.cmakeversion.cmake

@@ -2,9 +2,9 @@ if(NOT PROJECT_NAME)

set(PROJECT_NAME "mGBA") endif() set(LIB_VERSION_MAJOR 0) -set(LIB_VERSION_MINOR 6) +set(LIB_VERSION_MINOR 7) set(LIB_VERSION_PATCH 0) -set(LIB_VERSION_ABI 0.6) +set(LIB_VERSION_ABI 0.7) set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}) set(SUMMARY "${PROJECT_NAME} Game Boy Advance Emulator")