diff --git a/CHANGES b/CHANGES index ff795586a..3855918da 100644 --- a/CHANGES +++ b/CHANGES @@ -21,12 +21,19 @@ Emulation fixes: - GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378) - GB Audio: Fix channel 1/2 reseting edge cases (fixes mgba.io/i/1925) - GB Audio: Properly apply per-model audio differences + - GB Audio: Revamp channel rendering + - GB Audio: Fix APU re-enable timing glitch + - GB I/O: Fix writing to WAVE RAM behavior (fixes mgba.io/i/1334) - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) + - GB MBC: Fix edge case with Pocket Cam register accesses (fixes mgba.io/i/2557) - GB Serialize: Fix loading MBC1 states that affect bank 0 (fixes mgba.io/i/2402) + - GB SIO: Fix bidirectional transfer starting (fixes mgba.io/i/2290) - GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339) - GBA: Improve timing when not booting from BIOS - GBA: Fix expected entry point for multiboot ELFs (fixes mgba.io/i/2450) - GBA: Fix booting multiboot ROMs with no JOY entrypoint + - GBA Audio: Adjust PSG sampling rate with SOUNDBIAS + - GBA Audio: Sample FIFOs at SOUNDBIAS-set frequency - GBA BIOS: Work around IRQ handling hiccup in Mario & Luigi (fixes mgba.io/i/1059) - GBA BIOS: Initial HLE timing estimation of UnLz77 functions (fixes mgba.io/i/2141) - GBA DMA: Fix DMA source direction bits being cleared (fixes mgba.io/i/2410) @@ -38,20 +45,29 @@ Emulation fixes: - GBA Video: Fix Hblank timing (fixes mgba.io/i/2131, mgba.io/i/2310) - GBA Video: Fix rare crash in modes 3-5 - GBA Video: Fix sprites with mid-frame palette changes in GL (fixes mgba.io/i/2476) + - GBA Video: Fix OBJ tile wrapping with 2D char mapping (fixes mgba.io/i/2443) + - GBA Video: Fix horizontal lines in GL when charbase is changed (fixes mgba.io/i/1631) + - GBA Video: Fix sprite layer priority updating in GL Other fixes: + - ARM: Disassemble Thumb mov pseudo-instruction properly - Core: Don't attempt to restore rewind diffs past start of rewind - Core: Fix the runloop resuming after a game has crashed (fixes mgba.io/i/2451) - Core: Fix crash if library can't be opened + - Debugger: Fix crash with extremely long CLI strings - FFmpeg: Fix crash when encoding audio with some containers - FFmpeg: Fix GIF recording (fixes mgba.io/i/2393) - GB: Fix temporary saves + - GB: Fix replacing the ROM crashing when accessing ROM base - GB, GBA: Save writeback-pending masked saves on unload (fixes mgba.io/i/2396) - mGUI: Fix FPS counter after closing menu - Qt: Fix some hangs when using the debugger console - Qt: Fix crash when clicking past last tile in viewer + - Qt: Fix preloading for ROM replacing + - Qt: Fix screen not displaying on Wayland (fixes mgba.io/i/2190) - VFS: Failed file mapping should return NULL on POSIX Misc: - Core: Suspend runloop when a core crashes + - Core: Add wallclock offset RTC type - Debugger: Save and restore CLI history - Debugger: GDB now works while the game is paused - Debugger: Add command to load external symbol file (fixes mgba.io/i/2480) @@ -60,6 +76,7 @@ Misc: - GBA: Automatically skip BIOS if ROM has invalid logo - GBA: Refine multiboot detection (fixes mgba.io/i/2192) - GBA Cheats: Implement "never" type codes (closes mgba.io/i/915) + - GBA Memory: Implement adjustable EWRAM waitstates (closes mgba.io/i/1276) - GBA DMA: Enhanced logging (closes mgba.io/i/2454) - GBA Video: Implement layer placement for OpenGL renderer (fixes mgba.io/i/1962) - GBA Video: Fix highlighting for sprites with mid-frame palette changes @@ -79,6 +96,7 @@ Misc: - Qt: Add e-Card passing to the command line (closes mgba.io/i/2474) - Qt: Boot both a multiboot image and ROM with CLI args (closes mgba.io/i/1941) - Qt: Improve cheat parsing (fixes mgba.io/i/2297) + - SDL: Support exposing an axis directly as the gyro value (closes mgba.io/i/2531) - Windows: Attach to console if present - Vita: Add bilinear filtering option (closes mgba.io/i/344) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3afbaadc1..fce26578a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,9 @@ endif() mark_as_advanced(DISTBUILD) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBDIR}") +if(${CMAKE_INSTALL_PREFIX} STREQUAL "/usr") + set(CMAKE_SKIP_RPATH ON) +endif() if (NOT DEFINED MANDIR) set(MANDIR ${CMAKE_INSTALL_MANDIR}) @@ -498,7 +501,11 @@ if(USE_EDITLINE) list(APPEND FEATURES EDITLINE) include_directories(AFTER ${LIBEDIT_INCLUDE_DIRS}) link_directories(${LIBEDIT_LIBRARY_DIRS}) - set(DEBUGGER_LIB ${LIBEDIT_LIBRARIES}) + if(BUILD_STATIC) + set(DEBUGGER_LIB ${LIBEDIT_STATIC_LIBRARIES}) + else() + set(DEBUGGER_LIB ${LIBEDIT_LIBRARIES}) + endif() set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libedit2") list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/editline/cli-el-backend.c") else() @@ -947,14 +954,17 @@ if(BUILD_OPENEMU) install(TARGETS ${BINARY_NAME}-openemu LIBRARY DESTINATION ${OE_LIBDIR} COMPONENT ${BINARY_NAME}.oecoreplugin NAMELINK_SKIP) endif() -if(BUILD_QT AND (WIN32 OR APPLE)) +if(BUILD_QT AND (WIN32 OR APPLE OR CMAKE_SYSTEM_NAME STREQUAL "Linux")) set(BUILD_UPDATER ON) endif() if(BUILD_UPDATER) - add_executable(updater-stub WIN32 ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater-main.c) - target_link_libraries(updater-stub ${OS_LIB} ${PLATFORM_LIBRARY} ${BINARY_NAME}) - set_target_properties(updater-stub PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FUNCTION_DEFINES}") + add_executable(updater-stub WIN32 ${CORE_VFS_SRC} ${OS_SRC} ${UTIL_SRC} ${THIRD_PARTY_SRC} + ${CMAKE_CURRENT_SOURCE_DIR}/src/core/config.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater-main.c) + target_link_libraries(updater-stub ${OS_LIB} ${PLATFORM_LIBRARY}) + set_target_properties(updater-stub PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FUNCTION_DEFINES};BUILD_STATIC") if(MSVC) set_target_properties(updater-stub PROPERTIES LINK_FLAGS /ENTRY:mainCRTStartup) else() diff --git a/README.md b/README.md index f027584a9..56c3003a7 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ The recommended way to build for most platforms is to use Docker. Several Docker To use a Docker image to build mGBA, simply run the following command while in the root of an mGBA checkout: - docker run --rm -t -v $PWD:/home/mgba/src mgba/windows:w32 + docker run --rm -t -v ${PWD}:/home/mgba/src mgba/windows:w32 This will produce a `build-win32` directory with the build products. Replace `mgba/windows:w32` with another Docker image for other platforms, which will produce a corresponding other directory. The following Docker images available on Docker Hub: diff --git a/cinema/gb/blargg/cgb_sound/07-len sweep period sync/baseline_0000.png b/cinema/gb/blargg/cgb_sound/07-len sweep period sync/baseline_0000.png new file mode 100644 index 000000000..c5a976f9b Binary files /dev/null and b/cinema/gb/blargg/cgb_sound/07-len sweep period sync/baseline_0000.png differ diff --git a/cinema/gb/blargg/cgb_sound/07-len sweep period sync/config.ini b/cinema/gb/blargg/cgb_sound/07-len sweep period sync/config.ini deleted file mode 100644 index 7ddee425b..000000000 --- a/cinema/gb/blargg/cgb_sound/07-len sweep period sync/config.ini +++ /dev/null @@ -1,2 +0,0 @@ -[testinfo] -fail=1 diff --git a/cinema/gb/blargg/cgb_sound/07-len sweep period sync/xbaseline_0000.png b/cinema/gb/blargg/cgb_sound/07-len sweep period sync/xbaseline_0000.png deleted file mode 100644 index ae283b3b6..000000000 Binary files a/cinema/gb/blargg/cgb_sound/07-len sweep period sync/xbaseline_0000.png and /dev/null differ diff --git a/cinema/gb/blargg/cgb_sound/08-len ctr during power/baseline_0000.png b/cinema/gb/blargg/cgb_sound/08-len ctr during power/baseline_0000.png new file mode 100644 index 000000000..ac38f491b Binary files /dev/null and b/cinema/gb/blargg/cgb_sound/08-len ctr during power/baseline_0000.png differ diff --git a/cinema/gb/blargg/cgb_sound/08-len ctr during power/config.ini b/cinema/gb/blargg/cgb_sound/08-len ctr during power/config.ini deleted file mode 100644 index 7ddee425b..000000000 --- a/cinema/gb/blargg/cgb_sound/08-len ctr during power/config.ini +++ /dev/null @@ -1,2 +0,0 @@ -[testinfo] -fail=1 diff --git a/cinema/gb/blargg/cgb_sound/08-len ctr during power/xbaseline_0000.png b/cinema/gb/blargg/cgb_sound/08-len ctr during power/xbaseline_0000.png deleted file mode 100644 index 4dbe59509..000000000 Binary files a/cinema/gb/blargg/cgb_sound/08-len ctr during power/xbaseline_0000.png and /dev/null differ diff --git a/cinema/gb/blargg/cgb_sound/12-wave/baseline_0000.png b/cinema/gb/blargg/cgb_sound/12-wave/baseline_0000.png new file mode 100644 index 000000000..960eda9ce Binary files /dev/null and b/cinema/gb/blargg/cgb_sound/12-wave/baseline_0000.png differ diff --git a/cinema/gb/blargg/cgb_sound/12-wave/config.ini b/cinema/gb/blargg/cgb_sound/12-wave/config.ini deleted file mode 100644 index 7ddee425b..000000000 --- a/cinema/gb/blargg/cgb_sound/12-wave/config.ini +++ /dev/null @@ -1,2 +0,0 @@ -[testinfo] -fail=1 diff --git a/cinema/gb/blargg/cgb_sound/12-wave/xbaseline_0000.png b/cinema/gb/blargg/cgb_sound/12-wave/xbaseline_0000.png deleted file mode 100644 index f0af8856d..000000000 Binary files a/cinema/gb/blargg/cgb_sound/12-wave/xbaseline_0000.png and /dev/null differ diff --git a/cinema/gb/blargg/dmg_sound/07-len sweep period sync/baseline_0000.png b/cinema/gb/blargg/dmg_sound/07-len sweep period sync/baseline_0000.png new file mode 100644 index 000000000..c5a976f9b Binary files /dev/null and b/cinema/gb/blargg/dmg_sound/07-len sweep period sync/baseline_0000.png differ diff --git a/cinema/gb/blargg/dmg_sound/07-len sweep period sync/config.ini b/cinema/gb/blargg/dmg_sound/07-len sweep period sync/config.ini deleted file mode 100644 index 7ddee425b..000000000 --- a/cinema/gb/blargg/dmg_sound/07-len sweep period sync/config.ini +++ /dev/null @@ -1,2 +0,0 @@ -[testinfo] -fail=1 diff --git a/cinema/gb/blargg/dmg_sound/07-len sweep period sync/xbaseline_0000.png b/cinema/gb/blargg/dmg_sound/07-len sweep period sync/xbaseline_0000.png deleted file mode 100644 index ae283b3b6..000000000 Binary files a/cinema/gb/blargg/dmg_sound/07-len sweep period sync/xbaseline_0000.png and /dev/null differ diff --git a/cinema/gb/blargg/dmg_sound/08-len ctr during power/baseline_0000.png b/cinema/gb/blargg/dmg_sound/08-len ctr during power/baseline_0000.png new file mode 100644 index 000000000..034aeec3c Binary files /dev/null and b/cinema/gb/blargg/dmg_sound/08-len ctr during power/baseline_0000.png differ diff --git a/cinema/gb/blargg/dmg_sound/08-len ctr during power/config.ini b/cinema/gb/blargg/dmg_sound/08-len ctr during power/config.ini deleted file mode 100644 index 7ddee425b..000000000 --- a/cinema/gb/blargg/dmg_sound/08-len ctr during power/config.ini +++ /dev/null @@ -1,2 +0,0 @@ -[testinfo] -fail=1 diff --git a/cinema/gb/blargg/dmg_sound/08-len ctr during power/xbaseline_0000.png b/cinema/gb/blargg/dmg_sound/08-len ctr during power/xbaseline_0000.png deleted file mode 100644 index 588e334ea..000000000 Binary files a/cinema/gb/blargg/dmg_sound/08-len ctr during power/xbaseline_0000.png and /dev/null differ diff --git a/cinema/gb/blargg/halt_bug/baseline_0000.png b/cinema/gb/blargg/halt_bug/baseline_0000.png new file mode 100644 index 000000000..213cc9a96 Binary files /dev/null and b/cinema/gb/blargg/halt_bug/baseline_0000.png differ diff --git a/cinema/gb/blargg/halt_bug/test.gb b/cinema/gb/blargg/halt_bug/test.gb new file mode 100644 index 000000000..38e36625d Binary files /dev/null and b/cinema/gb/blargg/halt_bug/test.gb differ diff --git a/cinema/gb/blargg/interrupt_time/baseline_0000.png b/cinema/gb/blargg/interrupt_time/baseline_0000.png new file mode 100644 index 000000000..307ef3fff Binary files /dev/null and b/cinema/gb/blargg/interrupt_time/baseline_0000.png differ diff --git a/cinema/gb/blargg/interrupt_time/test.gb b/cinema/gb/blargg/interrupt_time/test.gb new file mode 100644 index 000000000..1b17845e3 Binary files /dev/null and b/cinema/gb/blargg/interrupt_time/test.gb differ diff --git a/cinema/gb/blargg/mem_timing/01-read_timing/baseline_0000.png b/cinema/gb/blargg/mem_timing/01-read_timing/baseline_0000.png new file mode 100644 index 000000000..bdbca9c06 Binary files /dev/null and b/cinema/gb/blargg/mem_timing/01-read_timing/baseline_0000.png differ diff --git a/cinema/gb/blargg/mem_timing/01-read_timing/test.gb b/cinema/gb/blargg/mem_timing/01-read_timing/test.gb new file mode 100644 index 000000000..660298b1e Binary files /dev/null and b/cinema/gb/blargg/mem_timing/01-read_timing/test.gb differ diff --git a/cinema/gb/blargg/mem_timing/02-write_timing/baseline_0000.png b/cinema/gb/blargg/mem_timing/02-write_timing/baseline_0000.png new file mode 100644 index 000000000..023d35895 Binary files /dev/null and b/cinema/gb/blargg/mem_timing/02-write_timing/baseline_0000.png differ diff --git a/cinema/gb/blargg/mem_timing/02-write_timing/test.gb b/cinema/gb/blargg/mem_timing/02-write_timing/test.gb new file mode 100644 index 000000000..e92f55333 Binary files /dev/null and b/cinema/gb/blargg/mem_timing/02-write_timing/test.gb differ diff --git a/cinema/gb/blargg/mem_timing/03-modify_timing/baseline_0000.png b/cinema/gb/blargg/mem_timing/03-modify_timing/baseline_0000.png new file mode 100644 index 000000000..3deadc0bd Binary files /dev/null and b/cinema/gb/blargg/mem_timing/03-modify_timing/baseline_0000.png differ diff --git a/cinema/gb/blargg/mem_timing/03-modify_timing/test.gb b/cinema/gb/blargg/mem_timing/03-modify_timing/test.gb new file mode 100644 index 000000000..7579c4271 Binary files /dev/null and b/cinema/gb/blargg/mem_timing/03-modify_timing/test.gb differ diff --git a/cinema/gba/obj/2d-wrap/baseline_0000.png b/cinema/gba/obj/2d-wrap/baseline_0000.png new file mode 100644 index 000000000..c0866f0a8 Binary files /dev/null and b/cinema/gba/obj/2d-wrap/baseline_0000.png differ diff --git a/cinema/gba/obj/2d-wrap/config.ini b/cinema/gba/obj/2d-wrap/config.ini new file mode 100644 index 000000000..108616db9 --- /dev/null +++ b/cinema/gba/obj/2d-wrap/config.ini @@ -0,0 +1,3 @@ +[testinfo] +skip=1 +frames=1 diff --git a/cinema/gba/obj/2d-wrap/test.gba b/cinema/gba/obj/2d-wrap/test.gba new file mode 100644 index 000000000..3b5998ff3 Binary files /dev/null and b/cinema/gba/obj/2d-wrap/test.gba differ diff --git a/include/mgba-util/vector.h b/include/mgba-util/vector.h index ba412d7da..a4ec71b65 100644 --- a/include/mgba-util/vector.h +++ b/include/mgba-util/vector.h @@ -98,6 +98,7 @@ CXX_GUARD_START } \ DECLARE_VECTOR(StringList, char*); +DECLARE_VECTOR(IntList, int); CXX_GUARD_END diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index b1440f2fe..7d72c7148 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -181,6 +181,8 @@ bool mCoreAutoloadSave(struct mCore* core); bool mCoreAutoloadPatch(struct mCore* core); bool mCoreAutoloadCheats(struct mCore* core); +bool mCoreLoadSaveFile(struct mCore* core, const char* path, bool temporary); + bool mCoreSaveState(struct mCore* core, int slot, int flags); bool mCoreLoadState(struct mCore* core, int slot, int flags); struct VFile* mCoreGetState(struct mCore* core, int slot, bool write); diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index a480db6e5..e2625045f 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -194,6 +194,11 @@ struct mAVStream { void (*postAudioBuffer)(struct mAVStream*, struct blip_t* left, struct blip_t* right); }; +struct mStereoSample { + int16_t left; + int16_t right; +}; + struct mKeyCallback { uint16_t (*readKeys)(struct mKeyCallback*); bool requireOpposingDirections; @@ -234,6 +239,7 @@ enum mRTCGenericType { RTC_NO_OVERRIDE, RTC_FIXED, RTC_FAKE_EPOCH, + RTC_WALLCLOCK_OFFSET, RTC_CUSTOM_START = 0x1000 }; diff --git a/include/mgba/core/map-cache.h b/include/mgba/core/map-cache.h index 2bd5934e8..349fc1663 100644 --- a/include/mgba/core/map-cache.h +++ b/include/mgba/core/map-cache.h @@ -23,6 +23,7 @@ DECL_BITS(mMapCacheSystemInfo, TilesWide, 8, 4); DECL_BITS(mMapCacheSystemInfo, TilesHigh, 12, 4); DECL_BITS(mMapCacheSystemInfo, MacroTileSize, 16, 7); DECL_BITS(mMapCacheSystemInfo, MapAlign, 23, 2); +DECL_BITS(mMapCacheSystemInfo, WriteAlign, 25, 2); DECL_BITFIELD(mMapCacheEntryFlags, uint16_t); DECL_BITS(mMapCacheEntryFlags, PaletteId, 0, 4); diff --git a/include/mgba/internal/arm/decoder.h b/include/mgba/internal/arm/decoder.h index 61226f8c4..c0efb22e8 100644 --- a/include/mgba/internal/arm/decoder.h +++ b/include/mgba/internal/arm/decoder.h @@ -223,7 +223,7 @@ uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegis #ifdef USE_DEBUGGERS struct mDebuggerSymbols; -int ARMDisassemble(struct ARMInstructionInfo* info, struct ARMCore* core, const struct mDebuggerSymbols* symbols, uint32_t pc, char* buffer, int blen); +int ARMDisassemble(const struct ARMInstructionInfo* info, struct ARMCore* core, const struct mDebuggerSymbols* symbols, uint32_t pc, char* buffer, int blen); #endif CXX_GUARD_END diff --git a/include/mgba/internal/debugger/parser.h b/include/mgba/internal/debugger/parser.h index 37f194ddb..0b8f09d4c 100644 --- a/include/mgba/internal/debugger/parser.h +++ b/include/mgba/internal/debugger/parser.h @@ -43,7 +43,7 @@ enum Operation { struct Token { enum TokenType { - TOKEN_ERROR_TYPE, + TOKEN_ERROR_TYPE = 0, TOKEN_UINT_TYPE, TOKEN_IDENTIFIER_TYPE, TOKEN_OPERATOR_TYPE, @@ -60,8 +60,10 @@ struct Token { struct ParseTree { struct Token token; + struct ParseTree* p; struct ParseTree* lhs; struct ParseTree* rhs; + int precedence; }; size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol); diff --git a/include/mgba/internal/gb/audio.h b/include/mgba/internal/gb/audio.h index 4f4fdef32..3667145e1 100644 --- a/include/mgba/internal/gb/audio.h +++ b/include/mgba/internal/gb/audio.h @@ -87,7 +87,6 @@ struct GBAudioSquareControl { int frequency; int length; bool stop; - int hi; }; struct GBAudioSweep { @@ -104,6 +103,8 @@ struct GBAudioSquareChannel { struct GBAudioSweep sweep; struct GBAudioEnvelope envelope; struct GBAudioSquareControl control; + int32_t lastUpdate; + uint8_t index; int8_t sample; }; @@ -112,6 +113,7 @@ struct GBAudioWaveChannel { bool bank; bool enable; + int8_t sample; unsigned length; int volume; @@ -124,7 +126,7 @@ struct GBAudioWaveChannel { uint32_t wavedata32[8]; uint8_t wavedata8[16]; }; - int8_t sample; + int32_t nextUpdate; }; struct GBAudioNoiseChannel { @@ -194,11 +196,6 @@ struct GBAudio { enum GBAudioStyle style; struct mTimingEvent frameEvent; - struct mTimingEvent ch1Event; - struct mTimingEvent ch2Event; - struct mTimingEvent ch3Event; - struct mTimingEvent ch3Fade; - struct mTimingEvent ch4Event; struct mTimingEvent sampleEvent; bool enable; @@ -239,8 +236,8 @@ void GBAudioWriteNR50(struct GBAudio* audio, uint8_t); void GBAudioWriteNR51(struct GBAudio* audio, uint8_t); void GBAudioWriteNR52(struct GBAudio* audio, uint8_t); +void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels); void GBAudioUpdateFrame(struct GBAudio* audio); -void GBAudioUpdateChannel4(struct GBAudio* audio); void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right); diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 6973d4e57..11bdf7dc3 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -19,7 +19,7 @@ extern MGBA_EXPORT const uint32_t GBSavestateVersion; mLOG_DECLARE_CATEGORY(GB_STATE); /* Savestate format: - * 0x00000 - 0x00003: Version Magic (0x01000002) + * 0x00000 - 0x00003: Version Magic (0x00400003) * 0x00004 - 0x00007: ROM CRC32 * 0x00008: Game Boy model * 0x00009 - 0x0000B: Reserved (leave zero) @@ -56,20 +56,23 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * | bits 0 - 6: Remaining length * | bits 7 - 9: Next step * | bits 10 - 20: Shadow frequency register - * | bits 21 - 31: Reserved + * | bits 21 - 23: Duty index + * | bits 24 - 31: Reserved * | 0x0004C - 0x0004F: Next frame - * | 0x00050 - 0x00053: Next channel 3 fade + * | 0x00050 - 0x00053: Reserved * | 0x00054 - 0x00057: Sweep state * | bits 0 - 2: Timesteps * | bits 3 - 31: Reserved - * | 0x00058 - 0x0005B: Next event + * | 0x00058 - 0x0005B: Last update * 0x0005C - 0x0006B: Audio channel 2 state * | 0x0005C - 0x0005F: Envelepe timing * | bits 0 - 2: Remaining length * | bits 3 - 5: Next step - * | bits 6 - 31: Reserved + * | bits 6 - 20: Reserved + * | bits 21 - 23: Duty index + * | bits 24 - 31: Reserved * | 0x00060 - 0x00067: Reserved - * | 0x00068 - 0x0006B: Next event + * | 0x00068 - 0x0006B: Last update * 0x0006C - 0x00093: Audio channel 3 state * | 0x0006C - 0x0008B: Wave banks * | 0x0008C - 0x0008D: Remaining length @@ -208,7 +211,7 @@ DECL_BITFIELD(GBSerializedAudioEnvelope, uint32_t); DECL_BITS(GBSerializedAudioEnvelope, Length, 0, 7); DECL_BITS(GBSerializedAudioEnvelope, NextStep, 7, 3); DECL_BITS(GBSerializedAudioEnvelope, Frequency, 10, 11); - +DECL_BITS(GBSerializedAudioEnvelope, DutyIndex, 21, 3); DECL_BITFIELD(GBSerializedAudioSweep, uint32_t); DECL_BITS(GBSerializedAudioSweep, Time, 0, 3); @@ -217,14 +220,14 @@ struct GBSerializedPSGState { struct { GBSerializedAudioEnvelope envelope; int32_t nextFrame; - int32_t nextCh3Fade; + int32_t reserved; GBSerializedAudioSweep sweep; - uint32_t nextEvent; + uint32_t lastUpdate; } ch1; struct { GBSerializedAudioEnvelope envelope; int32_t reserved[2]; - int32_t nextEvent; + uint32_t lastUpdate; } ch2; struct { uint32_t wavebanks[8]; diff --git a/include/mgba/internal/gba/audio.h b/include/mgba/internal/gba/audio.h index c9fab3ae1..8b212dd6a 100644 --- a/include/mgba/internal/gba/audio.h +++ b/include/mgba/internal/gba/audio.h @@ -11,11 +11,13 @@ CXX_GUARD_START #include +#include #include #include #include #define GBA_AUDIO_FIFO_SIZE 8 +#define GBA_MAX_SAMPLES 16 #define MP2K_MAGIC 0x68736D53 #define MP2K_MAX_SOUND_CHANNELS 12 @@ -34,7 +36,7 @@ struct GBAAudioFIFO { uint32_t internalSample; int internalRemaining; int dmaSource; - int8_t sample; + int8_t samples[GBA_MAX_SAMPLES]; }; DECL_BITFIELD(GBARegisterSOUNDCNT_HI, uint16_t); @@ -78,14 +80,16 @@ struct GBAAudio { bool enable; size_t samples; - unsigned sampleRate; - GBARegisterSOUNDBIAS soundbias; struct GBAAudioMixer* mixer; bool externalMixing; int32_t sampleInterval; + int32_t lastSample; + int sampleIndex; + struct mStereoSample currentSamples[GBA_MAX_SAMPLES]; + bool forceDisableChA; bool forceDisableChB; int masterVolume; @@ -93,11 +97,6 @@ struct GBAAudio { struct mTimingEvent sampleEvent; }; -struct GBAStereoSample { - int16_t left; - int16_t right; -}; - struct GBAMP2kADSR { uint8_t attack; uint8_t decay; @@ -277,7 +276,7 @@ struct GBAAudioMixer { double tempo; double frame; - struct GBAStereoSample last; + struct mStereoSample last; }; void GBAAudioInit(struct GBAAudio* audio, size_t samples); @@ -308,6 +307,8 @@ uint32_t GBAAudioReadWaveRAM(struct GBAAudio* audio, int address); uint32_t GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value); void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles); +void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp); + struct GBASerializedState; void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state); void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state); diff --git a/include/mgba/internal/gba/io.h b/include/mgba/internal/gba/io.h index 9875061f3..fdd480037 100644 --- a/include/mgba/internal/gba/io.h +++ b/include/mgba/internal/gba/io.h @@ -154,6 +154,13 @@ enum GBAIORegisters { REG_POSTFLG = 0x300, REG_HALTCNT = 0x301, + REG_EXWAITCNT_LO = 0x800, + REG_EXWAITCNT_HI = 0x802, + + REG_INTERNAL_EXWAITCNT_LO = 0x210, + REG_INTERNAL_EXWAITCNT_HI = 0x212, + REG_INTERNAL_MAX = 0x214, + REG_DEBUG_STRING = 0xFFF600, REG_DEBUG_FLAGS = 0xFFF700, REG_DEBUG_ENABLE = 0xFFF780, diff --git a/include/mgba/internal/gba/memory.h b/include/mgba/internal/gba/memory.h index 59b22b457..93d565a16 100644 --- a/include/mgba/internal/gba/memory.h +++ b/include/mgba/internal/gba/memory.h @@ -172,6 +172,7 @@ uint32_t GBAStoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum int* cycleCounter); void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters); +void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters); struct GBASerializedState; void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state); diff --git a/include/mgba/internal/gba/renderers/gl.h b/include/mgba/internal/gba/renderers/gl.h index 1a972ada8..9e022f6f5 100644 --- a/include/mgba/internal/gba/renderers/gl.h +++ b/include/mgba/internal/gba/renderers/gl.h @@ -49,6 +49,7 @@ struct GBAVideoGLBackground { int enabled; unsigned priority; uint32_t charBase; + uint32_t oldCharBase; int mosaic; int multipalette; uint32_t screenBase; @@ -99,10 +100,12 @@ enum { GBA_GL_BG_TRANSFORM, GBA_GL_BG_RANGE, GBA_GL_BG_MOSAIC, + GBA_GL_BG_OLDCHARBASE, GBA_GL_OBJ_VRAM = 2, GBA_GL_OBJ_PALETTE, GBA_GL_OBJ_CHARBASE, + GBA_GL_OBJ_TILE, GBA_GL_OBJ_STRIDE, GBA_GL_OBJ_LOCALPALETTE, GBA_GL_OBJ_INFLAGS, diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 203c4fb0c..07223ea3f 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -20,7 +20,7 @@ extern MGBA_EXPORT const uint32_t GBASavestateVersion; mLOG_DECLARE_CATEGORY(GBA_STATE); /* Savestate format: - * 0x00000 - 0x00003: Version Magic (0x01000004) + * 0x00000 - 0x00003: Version Magic (0x01000006) * 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS) * 0x00008 - 0x0000B: ROM CRC32 * 0x0000C - 0x0000F: Master cycles @@ -39,20 +39,23 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bits 0 - 6: Remaining length * | bits 7 - 9: Next step * | bits 10 - 20: Shadow frequency register - * | bits 21 - 31: Reserved + * | bits 21 - 23: Duty index + * | bits 24 - 31: Reserved * | 0x00134 - 0x00137: Next frame - * | 0x00138 - 0x0013B: Next channel 3 fade + * | 0x00138 - 0x0013B: Reserved * | 0x0013C - 0x0013F: Sweep state * | bits 0 - 2: Timesteps * | bits 3 - 7: Reserved - * | 0x00140 - 0x00143: Next event + * | 0x00140 - 0x00143: Last update * 0x00144 - 0x00153: Audio channel 2 state * | 0x00144 - 0x00147: Envelepe timing * | bits 0 - 2: Remaining length * | bits 3 - 5: Next step - * | bits 6 - 31: Reserved + * | bits 6 - 20: Reserved + * | bits 21 - 23: Duty index + * | bits 24 - 31: Reserved * | 0x00148 - 0x0014F: Reserved - * | 0x00150 - 0x00153: Next event + * | 0x00150 - 0x00153: Last update * 0x00154 - 0x0017B: Audio channel 3 state * | 0x00154 - 0x00173: Wave banks * | 0x00174 - 0x00175: Remaining length @@ -68,7 +71,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | 0x00188 - 0x0018B: Next event * 0x0018C - 0x001AB: Audio FIFO 1 * 0x001AC - 0x001CB: Audio FIFO 2 - * 0x001CC - 0x001DF: Audio miscellaneous state + * 0x001CC - 0x001EF: Audio miscellaneous state * | 0x001CC - 0x001CF: Channel A internal audio samples * | 0x001D0 - 0x001D3: Channel B internal audio samples * | 0x001D4 - 0x001D7: Next sample @@ -101,9 +104,13 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bit 3: Is channel 3's memory readable? * | bit 4: Skip frame * | bits 5 - 7: Reserved - * 0x001E0 - 0x001FF: Video miscellaneous state - * | 0x001E0 - 0x001E3: Next event - * | 0x001E4 - 0x001F7: Reserved + * | 0x001E0 - 0x001E3: Last sample + * | 0x001E4 - 0x001E7: Additional audio flags + * | bits 0 - 3: Current sample index + * | 0x001E8 - 0x001EF: Reserved + * 0x001F0 - 0x001FF: Video miscellaneous state + * | 0x001F0 - 0x001F3: Reserved + * | 0x001F4 - 0x001F7: Next event * | 0x001F8 - 0x001FB: Miscellaneous flags * | 0x001FC - 0x001FF: Frame counter * 0x00200 - 0x00213: Timer 0 @@ -221,7 +228,11 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * 0x00320 - 0x00323: Next IRQ event * 0x00324 - 0x00327: Interruptable BIOS stall cycles * 0x00328 - 0x00367: Matrix memory mapping table - * 0x00368 - 0x003FF: Reserved (leave zero) + * 0x00368 - 0x0036F: Reserved (leave zero) + * 0x00370 - 0x0037F: Audio FIFO A samples + * 0x00380 - 0x0038F: Audio FIFO B samples + * 0x00390 - 0x003CF: Audio rendered samples + * 0x003D0 - 0x003FF: Reserved (leave zero) * 0x00400 - 0x007FF: I/O memory * 0x00800 - 0x00BFF: Palette * 0x00C00 - 0x00FFF: OAM @@ -237,6 +248,9 @@ DECL_BITS(GBASerializedAudioFlags, FIFOSamplesB, 2, 3); // Yay legacy? DECL_BITS(GBASerializedAudioFlags, FIFOInternalSamplesA, 5, 2); DECL_BITS(GBASerializedAudioFlags, FIFOSamplesA, 7, 3); +DECL_BITFIELD(GBASerializedAudioFlags2, uint32_t); +DECL_BITS(GBASerializedAudioFlags2, SampleIndex, 0, 4); + DECL_BITFIELD(GBASerializedVideoFlags, uint32_t); DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2); @@ -297,11 +311,14 @@ struct GBASerializedState { int8_t sampleB; GBASerializedAudioFlags gbaFlags; GBSerializedAudioFlags flags; + int32_t lastSample; + GBASerializedAudioFlags2 gbaFlags2; + int32_t reserved[2]; } audio; struct { + int32_t reserved; int32_t nextEvent; - int32_t reserved[5]; GBASerializedVideoFlags flags; uint32_t frameCounter; } video; @@ -378,8 +395,16 @@ struct GBASerializedState { int32_t biosStall; uint32_t matrixMappings[16]; + uint32_t reservedMatrix[2]; - uint32_t reserved[38]; + struct { + int8_t chA[16]; + int8_t chB[16]; + } samples; + + struct mStereoSample currentSamples[16]; + + uint32_t reserved[12]; uint16_t io[SIZE_IO >> 1]; uint16_t pram[SIZE_PALETTE_RAM >> 1]; diff --git a/include/mgba/internal/gba/sio.h b/include/mgba/internal/gba/sio.h index e0fca7812..260772f55 100644 --- a/include/mgba/internal/gba/sio.h +++ b/include/mgba/internal/gba/sio.h @@ -21,7 +21,7 @@ extern const int GBASIOCyclesPerTransfer[4][MAX_GBAS]; mLOG_DECLARE_CATEGORY(GBA_SIO); enum { - RCNT_INITIAL = 0x8000 + RCNT_INITIAL = -0x8000 }; enum { diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 8633e0ed4..753c356ba 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -14,8 +14,9 @@ CXX_GUARD_START #include #include +#define mSCRIPT_KV_PAIR(KEY, VALUE) { #KEY, VALUE } #define mSCRIPT_CONSTANT_PAIR(NS, CONST) { #CONST, mScriptValueCreateFromSInt(NS ## _ ## CONST) } -#define mSCRIPT_CONSTANT_SENTINEL { NULL, NULL } +#define mSCRIPT_KV_SENTINEL { NULL, NULL } struct mScriptFrame; struct mScriptFunction; @@ -28,7 +29,10 @@ struct mScriptContext { struct Table weakrefs; uint32_t nextWeakref; struct Table callbacks; + struct Table callbackId; + uint32_t nextCallbackId; struct mScriptValue* constants; + struct Table docstrings; }; struct mScriptEngine2 { @@ -80,9 +84,14 @@ void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref); void mScriptContextAttachStdlib(struct mScriptContext* context); void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants); +void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value); void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback); -void mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value); +uint32_t mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value); +void mScriptContextRemoveCallback(struct mScriptContext*, uint32_t cbid); + +void mScriptContextSetDocstring(struct mScriptContext*, const char* key, const char* docstring); +const char* mScriptContextGetDocstring(struct mScriptContext*, const char* key); struct VFile; bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf); diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index 79b9ed611..e350b4d5d 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -334,7 +334,7 @@ CXX_GUARD_START mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAU(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAULTS(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \ static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX] = { \ @@ -459,6 +459,7 @@ CXX_GUARD_START #define mSCRIPT_MAKE_S64(VALUE) mSCRIPT_MAKE(S64, VALUE) #define mSCRIPT_MAKE_U64(VALUE) mSCRIPT_MAKE(U64, VALUE) #define mSCRIPT_MAKE_F64(VALUE) mSCRIPT_MAKE(F64, VALUE) +#define mSCRIPT_MAKE_BOOL(VALUE) mSCRIPT_MAKE(BOOL, VALUE) #define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(CHARP, VALUE) #define mSCRIPT_MAKE_S(STRUCT, VALUE) mSCRIPT_MAKE(S(STRUCT), VALUE) #define mSCRIPT_MAKE_CS(STRUCT, VALUE) mSCRIPT_MAKE(CS(STRUCT), VALUE) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index fd7dbe5d8..267f6b775 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -27,6 +27,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_S64 int64_t #define mSCRIPT_TYPE_C_U64 uint64_t #define mSCRIPT_TYPE_C_F64 double +#define mSCRIPT_TYPE_C_BOOL bool #define mSCRIPT_TYPE_C_STR struct mScriptString* #define mSCRIPT_TYPE_C_CHARP const char* #define mSCRIPT_TYPE_C_PTR void* @@ -41,6 +42,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_PS(X) void #define mSCRIPT_TYPE_C_PCS(X) void #define mSCRIPT_TYPE_C_WSTR struct mScriptValue* +#define mSCRIPT_TYPE_C_WLIST struct mScriptValue* #define mSCRIPT_TYPE_C_W(X) struct mScriptValue* #define mSCRIPT_TYPE_C_CW(X) const struct mScriptValue* @@ -54,6 +56,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_S64 s64 #define mSCRIPT_TYPE_FIELD_U64 u64 #define mSCRIPT_TYPE_FIELD_F64 f64 +#define mSCRIPT_TYPE_FIELD_BOOL u32 #define mSCRIPT_TYPE_FIELD_STR string #define mSCRIPT_TYPE_FIELD_CHARP copaque #define mSCRIPT_TYPE_FIELD_PTR opaque @@ -67,6 +70,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_PS(STRUCT) opaque #define mSCRIPT_TYPE_FIELD_PCS(STRUCT) copaque #define mSCRIPT_TYPE_FIELD_WSTR opaque +#define mSCRIPT_TYPE_FIELD_WLIST opaque #define mSCRIPT_TYPE_FIELD_W(TYPE) opaque #define mSCRIPT_TYPE_FIELD_CW(TYPE) opaque @@ -80,6 +84,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_S64 (&mSTSInt64) #define mSCRIPT_TYPE_MS_U64 (&mSTUInt64) #define mSCRIPT_TYPE_MS_F64 (&mSTFloat64) +#define mSCRIPT_TYPE_MS_BOOL (&mSTBool) #define mSCRIPT_TYPE_MS_STR (&mSTString) #define mSCRIPT_TYPE_MS_CHARP (&mSTCharPtr) #define mSCRIPT_TYPE_MS_LIST (&mSTList) @@ -92,6 +97,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_PS(STRUCT) (&mSTStructPtr_ ## STRUCT) #define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructConstPtr_ ## STRUCT) #define mSCRIPT_TYPE_MS_WSTR (&mSTStringWrapper) +#define mSCRIPT_TYPE_MS_WLIST (&mSTListWrapper) #define mSCRIPT_TYPE_MS_W(TYPE) (&mSTWrapper_ ## TYPE) #define mSCRIPT_TYPE_MS_CW(TYPE) (&mSTWrapperConst_ ## TYPE) @@ -106,8 +112,10 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_U64(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U64, TYPE) #define mSCRIPT_TYPE_CMP_S64(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S64, TYPE) #define mSCRIPT_TYPE_CMP_F64(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_F64, TYPE) +#define mSCRIPT_TYPE_CMP_BOOL(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_BOOL, TYPE) #define mSCRIPT_TYPE_CMP_STR(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE) #define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE) +#define mSCRIPT_TYPE_CMP_LIST(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_LIST, TYPE) #define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE) #define mSCRIPT_TYPE_CMP_WRAPPER(TYPE) (true) #define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME @@ -115,6 +123,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1) #define mSCRIPT_TYPE_CMP_WSTR(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE) || mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE)) +#define mSCRIPT_TYPE_CMP_WLIST(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_LIST, TYPE)) enum mScriptTypeBase { mSCRIPT_TYPE_VOID = 0, @@ -160,6 +169,7 @@ extern const struct mScriptType mSTFloat32; extern const struct mScriptType mSTSInt64; extern const struct mScriptType mSTUInt64; extern const struct mScriptType mSTFloat64; +extern const struct mScriptType mSTBool; extern const struct mScriptType mSTString; extern const struct mScriptType mSTCharPtr; extern const struct mScriptType mSTList; @@ -167,6 +177,9 @@ extern const struct mScriptType mSTTable; extern const struct mScriptType mSTWrapper; extern const struct mScriptType mSTWeakref; extern const struct mScriptType mSTStringWrapper; +extern const struct mScriptType mSTListWrapper; + +extern struct mScriptValue mScriptValueNull; struct mScriptType; struct mScriptValue { @@ -321,6 +334,7 @@ bool mScriptPopF32(struct mScriptList* list, float* out); bool mScriptPopS64(struct mScriptList* list, int64_t* out); bool mScriptPopU64(struct mScriptList* list, uint64_t* out); bool mScriptPopF64(struct mScriptList* list, double* out); +bool mScriptPopBool(struct mScriptList* list, bool* out); bool mScriptPopPointer(struct mScriptList* list, void** out); bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output); diff --git a/res/gb-icon-128.png b/res/gb-icon-128.png new file mode 100644 index 000000000..4aef63996 Binary files /dev/null and b/res/gb-icon-128.png differ diff --git a/res/gb-icon-256.png b/res/gb-icon-256.png new file mode 100644 index 000000000..872eae6f0 Binary files /dev/null and b/res/gb-icon-256.png differ diff --git a/res/gb-icon.svg b/res/gb-icon.svg new file mode 100644 index 000000000..422bc236e --- /dev/null +++ b/res/gb-icon.svg @@ -0,0 +1,2 @@ + + diff --git a/res/gba-icon-128.png b/res/gba-icon-128.png new file mode 100644 index 000000000..53d17aba2 Binary files /dev/null and b/res/gba-icon-128.png differ diff --git a/res/gba-icon-256.png b/res/gba-icon-256.png new file mode 100644 index 000000000..bd7fc64ad Binary files /dev/null and b/res/gba-icon-256.png differ diff --git a/res/gba-icon.svg b/res/gba-icon.svg new file mode 100644 index 000000000..7649e5bd9 --- /dev/null +++ b/res/gba-icon.svg @@ -0,0 +1,2 @@ + + diff --git a/res/gbc-icon-128.png b/res/gbc-icon-128.png new file mode 100644 index 000000000..f22938d16 Binary files /dev/null and b/res/gbc-icon-128.png differ diff --git a/res/gbc-icon-256.png b/res/gbc-icon-256.png new file mode 100644 index 000000000..37659abab Binary files /dev/null and b/res/gbc-icon-256.png differ diff --git a/res/gbc-icon.svg b/res/gbc-icon.svg new file mode 100644 index 000000000..6286714bf --- /dev/null +++ b/res/gbc-icon.svg @@ -0,0 +1,2 @@ + + diff --git a/res/scripts/pokemon.lua b/res/scripts/pokemon.lua index 09cd472ef..7ebe54bb8 100644 --- a/res/scripts/pokemon.lua +++ b/res/scripts/pokemon.lua @@ -68,22 +68,16 @@ local GBAGameEn = Game:new{ local Generation1En = GBGameEn:new{ _boxMonSize=33, _partyMonSize=44, - _readBoxMon=readBoxMonGen1, - _readPartyMon=readPartyMonGen1, } local Generation2En = GBGameEn:new{ _boxMonSize=32, _partyMonSize=48, - _readBoxMon=readBoxMonGen2, - _readPartyMon=readPartyMonGen2, } local Generation3En = GBAGameEn:new{ _boxMonSize=80, _partyMonSize=100, - _readBoxMon=readBoxMonGen3, - _readPartyMon=readPartyMonGen3, } GBGameEn._charmap = { [0]= @@ -475,7 +469,6 @@ gameCrc32 = { [0x9f7fdd53] = gameRBEn, -- Red [0xd6da8a1a] = gameRBEn, -- Blue [0x7d527d62] = gameYellowEn, - [0x3358e30a] = gameCrystal, -- Crystal rev 1 [0x84ee4776] = gameFireRedEnR1, [0xdaffecec] = gameLeafGreenEnR1, } @@ -506,10 +499,21 @@ function detectGame() console:error("Unknown game!") else console:log("Found game: " .. game.name) + if not partyBuffer then + partyBuffer = console:createBuffer("Party") + end end end +function updateBuffer() + if not game or not partyBuffer then + return + end + printPartyStatus(game, partyBuffer) +end + callbacks:add("start", detectGame) +callbacks:add("frame", updateBuffer) if emu then detectGame() end diff --git a/src/arm/debugger/debugger.c b/src/arm/debugger/debugger.c index 99fd4ffb4..afc1e0c49 100644 --- a/src/arm/debugger/debugger.c +++ b/src/arm/debugger/debugger.c @@ -190,14 +190,12 @@ static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointLis static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) { if (breakpoint->d.condition) { parseFree(breakpoint->d.condition); - free(breakpoint->d.condition); } } static void _destroyWatchpoint(struct mWatchpoint* watchpoint) { if (watchpoint->condition) { parseFree(watchpoint->condition); - free(watchpoint->condition); } } diff --git a/src/arm/decoder.c b/src/arm/decoder.c index 86583fa2d..e75023ba1 100644 --- a/src/arm/decoder.c +++ b/src/arm/decoder.c @@ -190,6 +190,9 @@ static int _decodeMemory(struct ARMMemoryAccess memory, struct ARMCore* cpu, con case 4: value = cpu->memory.load32(cpu, addrBase, NULL); break; + default: + // Should never be reached + abort(); } const char* label = NULL; if (symbols) { @@ -378,10 +381,11 @@ static const char* _armAccessTypeStrings[] = { "" }; -int ARMDisassemble(struct ARMInstructionInfo* info, struct ARMCore* cpu, const struct mDebuggerSymbols* symbols, uint32_t pc, char* buffer, int blen) { +int ARMDisassemble(const struct ARMInstructionInfo* info, struct ARMCore* cpu, const struct mDebuggerSymbols* symbols, uint32_t pc, char* buffer, int blen) { const char* mnemonic = _armMnemonicStrings[info->mnemonic]; int written; int total = 0; + bool skip3 = false; const char* cond = ""; if (info->condition != ARM_CONDITION_AL && info->condition < ARM_CONDITION_NV) { cond = _armConditions[info->condition]; @@ -398,6 +402,11 @@ int ARMDisassemble(struct ARMInstructionInfo* info, struct ARMCore* cpu, const s flags = _armAccessTypeStrings[info->memory.width]; break; case ARM_MN_ADD: + if ((info->operandFormat & (ARM_OPERAND_3 | ARM_OPERAND_4)) == ARM_OPERAND_IMMEDIATE_3 && info->op3.immediate == 0 && info->execMode == MODE_THUMB) { + skip3 = true; + mnemonic = "mov"; + } + // Fall through case ARM_MN_ADC: case ARM_MN_AND: case ARM_MN_ASR: @@ -406,7 +415,6 @@ int ARMDisassemble(struct ARMInstructionInfo* info, struct ARMCore* cpu, const s case ARM_MN_LSL: case ARM_MN_LSR: case ARM_MN_MLA: - case ARM_MN_MOV: case ARM_MN_MUL: case ARM_MN_MVN: case ARM_MN_ORR: @@ -497,26 +505,28 @@ int ARMDisassemble(struct ARMInstructionInfo* info, struct ARMCore* cpu, const s written = _decodeShift(info->op2, false, buffer, blen); ADVANCE(written); } - if (info->operandFormat & ARM_OPERAND_3) { - strlcpy(buffer, ", ", blen); - ADVANCE(2); - } - if (info->operandFormat & ARM_OPERAND_IMMEDIATE_3) { - written = snprintf(buffer, blen, "#%i", info->op3.immediate); - ADVANCE(written); - } else if (info->operandFormat & ARM_OPERAND_MEMORY_3) { - written = _decodeMemory(info->memory, cpu, symbols, pc, buffer, blen); - ADVANCE(written); - } else if (info->operandFormat & ARM_OPERAND_REGISTER_3) { - written = _decodeRegister(info->op3.reg, buffer, blen); - ADVANCE(written); - } - if (info->operandFormat & ARM_OPERAND_SHIFT_REGISTER_3) { - written = _decodeShift(info->op3, true, buffer, blen); - ADVANCE(written); - } else if (info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_3) { - written = _decodeShift(info->op3, false, buffer, blen); - ADVANCE(written); + if (!skip3) { + if (info->operandFormat & ARM_OPERAND_3) { + strlcpy(buffer, ", ", blen); + ADVANCE(2); + } + if (info->operandFormat & ARM_OPERAND_IMMEDIATE_3) { + written = snprintf(buffer, blen, "#%i", info->op3.immediate); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_MEMORY_3) { + written = _decodeMemory(info->memory, cpu, symbols, pc, buffer, blen); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_REGISTER_3) { + written = _decodeRegister(info->op3.reg, buffer, blen); + ADVANCE(written); + } + if (info->operandFormat & ARM_OPERAND_SHIFT_REGISTER_3) { + written = _decodeShift(info->op3, true, buffer, blen); + ADVANCE(written); + } else if (info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_3) { + written = _decodeShift(info->op3, false, buffer, blen); + ADVANCE(written); + } } if (info->operandFormat & ARM_OPERAND_4) { strlcpy(buffer, ", ", blen); diff --git a/src/core/config.c b/src/core/config.c index 4308b50ba..6bfcb8e8b 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -242,7 +242,7 @@ void mCoreConfigDirectory(char* out, size_t outLength) { CreateDirectoryW(wpath, NULL); if (PATH_SEP[0] != '\\') { WCHAR* pathSep; - for (pathSep = wpath; pathSep = wcschr(pathSep, L'\\');) { + for (pathSep = wpath; (pathSep = wcschr(pathSep, L'\\'));) { pathSep[0] = PATH_SEP[0]; } } @@ -284,7 +284,7 @@ void mCoreConfigPortablePath(char* out, size_t outLength) { PathRemoveFileSpecW(wpath); if (PATH_SEP[0] != '\\') { WCHAR* pathSep; - for (pathSep = wpath; pathSep = wcschr(pathSep, L'\\');) { + for (pathSep = wpath; (pathSep = wcschr(pathSep, L'\\'));) { pathSep[0] = PATH_SEP[0]; } } diff --git a/src/core/core.c b/src/core/core.c index 0879f9254..c4c3a3ce8 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -259,6 +259,18 @@ bool mCoreAutoloadCheats(struct mCore* core) { return success; } +bool mCoreLoadSaveFile(struct mCore* core, const char* path, bool temporary) { + struct VFile* vf = VFileOpen(path, O_CREAT | O_RDWR); + if (!vf) { + return false; + } + if (temporary) { + return core->loadTemporarySave(core, vf); + } else { + return core->loadSave(core, vf); + } +} + bool mCoreSaveState(struct mCore* core, int slot, int flags) { struct VFile* vf = mCoreGetState(core, slot, true); if (!vf) { @@ -357,6 +369,8 @@ bool mCoreTakeScreenshotVF(struct mCore* core, struct VFile* vf) { PNGWriteClose(png, info); return success; #else + UNUSED(core); + UNUSED(vf); return false; #endif } diff --git a/src/core/directories.c b/src/core/directories.c index e2e2d2c9f..63c66e564 100644 --- a/src/core/directories.c +++ b/src/core/directories.c @@ -10,92 +10,53 @@ #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 void mDirectorySetInit(struct mDirectorySet* dirs) { - dirs->base = 0; - dirs->archive = 0; - dirs->save = 0; - dirs->patch = 0; - dirs->state = 0; - dirs->screenshot = 0; - dirs->cheats = 0; + dirs->base = NULL; + dirs->archive = NULL; + dirs->save = NULL; + dirs->patch = NULL; + dirs->state = NULL; + dirs->screenshot = NULL; + dirs->cheats = NULL; +} + +static void mDirectorySetDetachDir(struct mDirectorySet* dirs, struct VDir* dir) { + if (!dir) { + return; + } + + if (dirs->base == dir) { + dirs->base = NULL; + } + if (dirs->archive == dir) { + dirs->archive = NULL; + } + if (dirs->save == dir) { + dirs->save = NULL; + } + if (dirs->patch == dir) { + dirs->patch = NULL; + } + if (dirs->state == dir) { + dirs->state = NULL; + } + if (dirs->screenshot == dir) { + dirs->screenshot = NULL; + } + if (dirs->cheats == dir) { + dirs->cheats = NULL; + } + + dir->close(dir); } void mDirectorySetDeinit(struct mDirectorySet* dirs) { mDirectorySetDetachBase(dirs); - - if (dirs->archive) { - if (dirs->archive == dirs->save) { - dirs->save = NULL; - } - if (dirs->archive == dirs->patch) { - dirs->patch = NULL; - } - if (dirs->archive == dirs->state) { - dirs->state = NULL; - } - if (dirs->archive == dirs->screenshot) { - dirs->screenshot = NULL; - } - if (dirs->archive == dirs->cheats) { - dirs->cheats = NULL; - } - dirs->archive->close(dirs->archive); - dirs->archive = NULL; - } - - if (dirs->save) { - if (dirs->save == dirs->patch) { - dirs->patch = NULL; - } - if (dirs->save == dirs->state) { - dirs->state = NULL; - } - if (dirs->save == dirs->screenshot) { - dirs->screenshot = NULL; - } - if (dirs->save == dirs->cheats) { - dirs->cheats = NULL; - } - dirs->save->close(dirs->save); - dirs->save = NULL; - } - - if (dirs->patch) { - if (dirs->patch == dirs->state) { - dirs->state = NULL; - } - if (dirs->patch == dirs->screenshot) { - dirs->screenshot = NULL; - } - if (dirs->patch == dirs->cheats) { - dirs->cheats = NULL; - } - dirs->patch->close(dirs->patch); - dirs->patch = NULL; - } - - if (dirs->state) { - if (dirs->state == dirs->screenshot) { - dirs->state = NULL; - } - if (dirs->state == dirs->cheats) { - dirs->cheats = NULL; - } - dirs->state->close(dirs->state); - dirs->state = NULL; - } - - if (dirs->screenshot) { - if (dirs->screenshot == dirs->cheats) { - dirs->cheats = NULL; - } - dirs->screenshot->close(dirs->screenshot); - dirs->screenshot = NULL; - } - - if (dirs->cheats) { - dirs->cheats->close(dirs->cheats); - dirs->cheats = NULL; - } + mDirectorySetDetachDir(dirs, dirs->archive); + mDirectorySetDetachDir(dirs, dirs->save); + mDirectorySetDetachDir(dirs, dirs->patch); + mDirectorySetDetachDir(dirs, dirs->state); + mDirectorySetDetachDir(dirs, dirs->screenshot); + mDirectorySetDetachDir(dirs, dirs->cheats); } void mDirectorySetAttachBase(struct mDirectorySet* dirs, struct VDir* base) { @@ -118,36 +79,20 @@ void mDirectorySetAttachBase(struct mDirectorySet* dirs, struct VDir* base) { } void mDirectorySetDetachBase(struct mDirectorySet* dirs) { - if (dirs->save == dirs->base) { - dirs->save = NULL; - } - if (dirs->patch == dirs->base) { - dirs->patch = NULL; - } - if (dirs->state == dirs->base) { - dirs->state = NULL; - } - if (dirs->screenshot == dirs->base) { - dirs->screenshot = NULL; - } - if (dirs->cheats == dirs->base) { - dirs->cheats = NULL; - } - - if (dirs->base) { - dirs->base->close(dirs->base); - dirs->base = NULL; - } + mDirectorySetDetachDir(dirs, dirs->archive); + mDirectorySetDetachDir(dirs, dirs->base); } struct VFile* mDirectorySetOpenPath(struct mDirectorySet* dirs, const char* path, bool (*filter)(struct VFile*)) { - dirs->archive = VDirOpenArchive(path); + struct VDir* archive = VDirOpenArchive(path); struct VFile* file; - if (dirs->archive) { - file = VDirFindFirst(dirs->archive, filter); + if (archive) { + file = VDirFindFirst(archive, filter); if (!file) { - dirs->archive->close(dirs->archive); - dirs->archive = 0; + archive->close(archive); + } else { + mDirectorySetDetachDir(dirs, dirs->archive); + dirs->archive = archive; } } else { file = VFileOpen(path, O_RDONLY); diff --git a/src/core/interface.c b/src/core/interface.c index 961b83619..d63754895 100644 --- a/src/core/interface.c +++ b/src/core/interface.c @@ -21,6 +21,7 @@ static void _rtcGenericSample(struct mRTCSource* source) { case RTC_NO_OVERRIDE: case RTC_FIXED: case RTC_FAKE_EPOCH: + case RTC_WALLCLOCK_OFFSET: break; } } @@ -39,6 +40,8 @@ static time_t _rtcGenericCallback(struct mRTCSource* source) { return rtc->value / 1000LL; case RTC_FAKE_EPOCH: return (rtc->value + rtc->p->frameCounter(rtc->p) * (rtc->p->frameCycles(rtc->p) * 1000LL) / rtc->p->frequency(rtc->p)) / 1000LL; + case RTC_WALLCLOCK_OFFSET: + return time(0) + rtc->value / 1000LL; } } diff --git a/src/core/library.c b/src/core/library.c index 06bed44ee..44148f7f5 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -243,9 +243,11 @@ void mLibraryLoadDirectory(struct mLibrary* library, const char* base, bool recu struct VFile* vf = dir->openFile(dir, current->filename, O_RDONLY); _mLibraryDeleteEntry(library, current); if (!vf) { + mLibraryEntryFree(current); continue; } _mLibraryAddEntry(library, current->filename, base, vf); + mLibraryEntryFree(current); } mLibraryListingDeinit(&entries); @@ -381,9 +383,12 @@ size_t mLibraryGetEntries(struct mLibrary* library, struct mLibraryListing* out, sqlite3_reset(library->select); _bindConstraints(library->select, constraints); + if (numEntries > SSIZE_MAX) { + numEntries = SSIZE_MAX; + } int countIndex = sqlite3_bind_parameter_index(library->select, ":count"); int offsetIndex = sqlite3_bind_parameter_index(library->select, ":offset"); - sqlite3_bind_int64(library->select, countIndex, numEntries ? numEntries : -1); + sqlite3_bind_int64(library->select, countIndex, numEntries ? (ssize_t) numEntries : -1); sqlite3_bind_int64(library->select, offsetIndex, offset); size_t entryIndex; diff --git a/src/core/map-cache.c b/src/core/map-cache.c index 17df206dd..d51800780 100644 --- a/src/core/map-cache.c +++ b/src/core/map-cache.c @@ -70,11 +70,20 @@ void mMapCacheDeinit(struct mMapCache* cache) { void mMapCacheWriteVRAM(struct mMapCache* cache, uint32_t address) { if (address >= cache->mapStart && address < cache->mapStart + cache->mapSize) { + uint32_t align = 1 << (mMapCacheSystemInfoGetWriteAlign(cache->sysConfig) - mMapCacheSystemInfoGetMapAlign(cache->sysConfig)); address -= cache->mapStart; - struct mMapCacheEntry* status = &cache->status[address >> mMapCacheSystemInfoGetMapAlign(cache->sysConfig)]; - ++status->vramVersion; - status->flags = mMapCacheEntryFlagsClearVramClean(status->flags); - status->tileStatus[mMapCacheEntryFlagsGetPaletteId(status->flags)].vramClean = 0; + address >>= mMapCacheSystemInfoGetMapAlign(cache->sysConfig); + + uint32_t i; + for (i = 0; i < align; ++i) { + if (address + i >= (cache->mapSize >> mMapCacheSystemInfoGetMapAlign(cache->sysConfig))) { + break; + } + struct mMapCacheEntry* status = &cache->status[address + i]; + ++status->vramVersion; + status->flags = mMapCacheEntryFlagsClearVramClean(status->flags); + status->tileStatus[mMapCacheEntryFlagsGetPaletteId(status->flags)].vramClean = 0; + } } } diff --git a/src/core/scripting.c b/src/core/scripting.c index d08761bbf..122888c1c 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -311,7 +311,7 @@ static struct mScriptValue* _mScriptCoreChecksum(const struct mCore* core, int t break; } if (!size) { - return NULL; + return &mScriptValueNull; } void* data = calloc(1, size); core->checksum(core, data, type); @@ -350,7 +350,7 @@ static struct mScriptValue* _mScriptCoreReadRange(struct mCore* core, uint32_t a static struct mScriptValue* _mScriptCoreReadRegister(const struct mCore* core, const char* regName) { int32_t out; if (!core->readRegister(core, regName, &out)) { - return NULL; + return &mScriptValueNull; } struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32); value->value.s32 = out; @@ -365,7 +365,7 @@ static struct mScriptValue* _mScriptCoreSaveState(struct mCore* core, int32_t fl struct VFile* vf = VFileMemChunk(NULL, 0); if (!mCoreSaveStateNamed(core, vf, flags)) { vf->close(vf); - return NULL; + return &mScriptValueNull; } void* buffer = vf->map(vf, vf->size(vf), MAP_READ); struct mScriptValue* value = mScriptStringCreateFromBytes(buffer, vf->size(vf)); @@ -373,6 +373,16 @@ static struct mScriptValue* _mScriptCoreSaveState(struct mCore* core, int32_t fl return value; } +static int _mScriptCoreSaveStateFile(struct mCore* core, const char* path, int flags) { + struct VFile* vf = VFileOpen(path, O_WRONLY | O_TRUNC | O_CREAT); + if (!vf) { + return false; + } + bool ok = mCoreSaveStateNamed(core, vf, flags); + vf->close(vf); + return ok; +} + static int32_t _mScriptCoreLoadState(struct mCore* core, struct mScriptString* buffer, int32_t flags) { struct VFile* vf = VFileFromConstMemory(buffer->buffer, buffer->size); int ret = mCoreLoadStateNamed(core, vf, flags); @@ -380,6 +390,15 @@ static int32_t _mScriptCoreLoadState(struct mCore* core, struct mScriptString* b return ret; } +static int _mScriptCoreLoadStateFile(struct mCore* core, const char* path, int flags) { + struct VFile* vf = VFileOpen(path, O_RDONLY); + if (!vf) { + return false; + } + bool ok = mCoreLoadStateNamed(core, vf, flags); + vf->close(vf); + return ok; +} static void _mScriptCoreTakeScreenshot(struct mCore* core, const char* filename) { if (filename) { struct VFile* vf = VFileOpen(filename, O_WRONLY | O_CREAT | O_TRUNC); @@ -393,6 +412,11 @@ static void _mScriptCoreTakeScreenshot(struct mCore* core, const char* filename) } } +// Loading functions +mSCRIPT_DECLARE_STRUCT_METHOD(mCore, BOOL, loadFile, mCoreLoadFile, 1, CHARP, path); +mSCRIPT_DECLARE_STRUCT_METHOD(mCore, BOOL, autoloadSave, mCoreAutoloadSave, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(mCore, BOOL, loadSaveFile, mCoreLoadSaveFile, 2, CHARP, path, BOOL, temporary); + // Info functions mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, platform, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, U32, frameCounter, 0); @@ -430,10 +454,12 @@ mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WSTR, readRegister, _mScriptCoreReadRegiste mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, writeRegister, _mScriptCoreWriteRegister, 2, CHARP, regName, S32, value); // Savestate functions -mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, saveStateSlot, mCoreSaveState, 2, S32, slot, S32, flags); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, BOOL, saveStateSlot, mCoreSaveState, 2, S32, slot, S32, flags); mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, WSTR, saveStateBuffer, _mScriptCoreSaveState, 1, S32, flags); -mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoadState, 2, S32, slot, S32, flags); -mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateBuffer, _mScriptCoreLoadState, 2, STR, buffer, S32, flags); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, BOOL, saveStateFile, _mScriptCoreSaveStateFile, 2, CHARP, path, S32, flags); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, BOOL, loadStateSlot, mCoreLoadState, 2, S32, slot, S32, flags); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, BOOL, loadStateBuffer, _mScriptCoreLoadState, 2, STR, buffer, S32, flags); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, BOOL, loadStateFile, _mScriptCoreLoadStateFile, 2, CHARP, path, S32, flags); // Miscellaneous functions mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(mCore, screenshot, _mScriptCoreTakeScreenshot, 1, CHARP, filename); @@ -442,6 +468,13 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_CLASS_DOCSTRING( "An instance of an emulator core." ) + mSCRIPT_DEFINE_DOCSTRING("Load a ROM file into the current state of this core") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadFile) + mSCRIPT_DEFINE_DOCSTRING("Load the save data associated with the currently loaded ROM file") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, autoloadSave) + mSCRIPT_DEFINE_DOCSTRING("Load save data from the given path. If the `temporary` flag is set, the given save data will not be written back to disk") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadSaveFile) + mSCRIPT_DEFINE_DOCSTRING("Get which platform is being emulated. See C.PLATFORM for possible values") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, platform) mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame") @@ -504,10 +537,14 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateSlot) mSCRIPT_DEFINE_DOCSTRING("Save state and return as a buffer. See C.SAVESTATE for possible values for `flags`") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateBuffer) + mSCRIPT_DEFINE_DOCSTRING("Save state to the given path. See C.SAVESTATE for possible values for `flags`") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateFile) mSCRIPT_DEFINE_DOCSTRING("Load state from the slot number. See C.SAVESTATE for possible values for `flags`") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateSlot) - mSCRIPT_DEFINE_DOCSTRING("Load state a buffer. See C.SAVESTATE for possible values for `flags`") + mSCRIPT_DEFINE_DOCSTRING("Load state from a buffer. See C.SAVESTATE for possible values for `flags`") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateBuffer) + mSCRIPT_DEFINE_DOCSTRING("Load state from the given path. See C.SAVESTATE for possible values for `flags`") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateFile) mSCRIPT_DEFINE_DOCSTRING("Save a screenshot") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshot) @@ -522,16 +559,26 @@ mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, saveStateSlot) mSCRIPT_S32(SAVESTATE_ALL) mSCRIPT_DEFINE_DEFAULTS_END; +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, saveStateBuffer) + mSCRIPT_S32(SAVESTATE_ALL) +mSCRIPT_DEFINE_DEFAULTS_END; + +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, saveStateFile) + mSCRIPT_NO_DEFAULT, + mSCRIPT_S32(SAVESTATE_ALL) +mSCRIPT_DEFINE_DEFAULTS_END; + mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, loadStateSlot) mSCRIPT_NO_DEFAULT, mSCRIPT_S32(SAVESTATE_ALL & ~SAVESTATE_SAVEDATA) mSCRIPT_DEFINE_DEFAULTS_END; -mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, saveStateBuffer) - mSCRIPT_S32(SAVESTATE_ALL) +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, loadStateBuffer) + mSCRIPT_NO_DEFAULT, + mSCRIPT_S32(SAVESTATE_ALL & ~SAVESTATE_SAVEDATA) mSCRIPT_DEFINE_DEFAULTS_END; -mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, loadStateBuffer) +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, loadStateFile) mSCRIPT_NO_DEFAULT, mSCRIPT_S32(SAVESTATE_ALL & ~SAVESTATE_SAVEDATA) mSCRIPT_DEFINE_DEFAULTS_END; @@ -590,7 +637,7 @@ static struct mScriptValue* _mScriptCoreAdapterGet(struct mScriptCoreAdapter* ad struct mScriptValue val; struct mScriptValue core = mSCRIPT_MAKE(S(mCore), adapter->core); if (!mScriptObjectGet(&core, name, &val)) { - return NULL; + return &mScriptValueNull; } struct mScriptValue* ret = malloc(sizeof(*ret)); @@ -710,14 +757,20 @@ mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptConsole, createBuffer) mSCRIPT_CHARP(NULL) mSCRIPT_DEFINE_DEFAULTS_END; -void mScriptContextAttachLogger(struct mScriptContext* context, struct mLogger* logger) { +static struct mScriptConsole* _ensureConsole(struct mScriptContext* context) { struct mScriptValue* value = mScriptContextEnsureGlobal(context, "console", mSCRIPT_TYPE_MS_S(mScriptConsole)); struct mScriptConsole* console = value->value.opaque; if (!console) { console = calloc(1, sizeof(*console)); value->value.opaque = console; value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + mScriptContextSetDocstring(context, "console", "Singleton instance of struct::mScriptConsole"); } + return console; +} + +void mScriptContextAttachLogger(struct mScriptContext* context, struct mLogger* logger) { + struct mScriptConsole* console = _ensureConsole(context); console->logger = logger; } @@ -771,14 +824,7 @@ mSCRIPT_DEFINE_STRUCT(mScriptTextBuffer) mSCRIPT_DEFINE_END; void mScriptContextSetTextBufferFactory(struct mScriptContext* context, mScriptContextBufferFactory factory, void* cbContext) { - struct mScriptValue* value = mScriptContextEnsureGlobal(context, "console", mSCRIPT_TYPE_MS_S(mScriptConsole)); - struct mScriptConsole* console = value->value.opaque; - if (!console) { - console = calloc(1, sizeof(*console)); - console->logger = mLogGetContext(); - value->value.opaque = console; - value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; - } + struct mScriptConsole* console = _ensureConsole(context); console->textBufferFactory = factory; console->textBufferContext = cbContext; } diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index a89e6db4d..dbc10fe89 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -226,11 +226,9 @@ static bool _parseExpression(struct mDebugger* debugger, struct CLIDebugVector* } if (!mDebuggerEvaluateParseTree(debugger, tree, intValue, segmentValue)) { parseFree(tree); - free(tree); return false; } parseFree(tree); - free(tree); return true; } @@ -599,7 +597,6 @@ static struct ParseTree* _parseTree(const char** string) { if (error) { if (tree) { parseFree(tree); - free(tree); } return NULL; } else { diff --git a/src/debugger/parser.c b/src/debugger/parser.c index 41197b53e..4af61a14e 100644 --- a/src/debugger/parser.c +++ b/src/debugger/parser.c @@ -11,6 +11,8 @@ DEFINE_VECTOR(LexVector, struct Token); +DEFINE_VECTOR(IntList, int32_t); + enum LexState { LEX_ERROR = -1, LEX_ROOT = 0, @@ -493,16 +495,21 @@ static const int _operatorPrecedence[] = { [OP_DEREFERENCE] = 2, }; -static struct ParseTree* _parseTreeCreate() { +static struct ParseTree* _parseTreeCreate(void) { struct ParseTree* tree = malloc(sizeof(struct ParseTree)); tree->token.type = TOKEN_ERROR_TYPE; - tree->rhs = 0; - tree->lhs = 0; + tree->p = NULL; + tree->rhs = NULL; + tree->lhs = NULL; + tree->precedence = INT_MAX; return tree; } -static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, size_t i, int precedence, int* openParens) { - struct ParseTree* newTree = 0; +static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, int* openParens) { + struct ParseTree* newTree = NULL; + bool pop = false; + int precedence = INT_MAX; + size_t i = 0; while (i < LexVectorSize(lv)) { struct Token* token = LexVectorGetPointer(lv, i); int newPrecedence; @@ -517,27 +524,36 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, siz ++i; } else { tree->token.type = TOKEN_ERROR_TYPE; - return i + 1; + ++i; + pop = true; } break; case TOKEN_SEGMENT_TYPE: tree->lhs = _parseTreeCreate(); tree->lhs->token.type = TOKEN_UINT_TYPE; tree->lhs->token.uintValue = token->uintValue; + tree->lhs->p = tree; + tree->lhs->precedence = precedence; tree->rhs = _parseTreeCreate(); + tree->rhs->p = tree; + tree->rhs->precedence = precedence; tree->token.type = TOKEN_SEGMENT_TYPE; - i = _parseExpression(tree->rhs, lv, i + 1, precedence, openParens); + tree = tree->rhs; + ++i; break; case TOKEN_OPEN_PAREN_TYPE: ++*openParens; - i = _parseExpression(tree, lv, i + 1, INT_MAX, openParens); + precedence = INT_MAX; + ++i; break; case TOKEN_CLOSE_PAREN_TYPE: if (*openParens <= 0) { tree->token.type = TOKEN_ERROR_TYPE; } --*openParens; - return i + 1; + ++i; + pop = true; + break; case TOKEN_OPERATOR_TYPE: if (tree->token.type == TOKEN_ERROR_TYPE) { switch (token->operatorValue) { @@ -557,21 +573,44 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, siz newPrecedence = _operatorPrecedence[token->operatorValue]; if (newPrecedence < precedence) { newTree = _parseTreeCreate(); - *newTree = *tree; + memcpy(newTree, tree, sizeof(*tree)); + if (newTree->lhs) { + newTree->lhs->p = newTree; + } + if (newTree->rhs) { + newTree->rhs->p = newTree; + } + newTree->p = tree; tree->lhs = newTree; tree->rhs = _parseTreeCreate(); + tree->rhs->p = tree; + tree->rhs->precedence = newPrecedence; + precedence = newPrecedence; tree->token = *token; - i = _parseExpression(tree->rhs, lv, i + 1, newPrecedence, openParens); - if (tree->token.type == TOKEN_ERROR_TYPE) { - tree->token.type = TOKEN_ERROR_TYPE; - } + tree = tree->rhs; + ++i; } else { - return i; + pop = true; } break; case TOKEN_ERROR_TYPE: tree->token.type = TOKEN_ERROR_TYPE; - return i + 1; + ++i; + pop = true; + break; + } + + if (pop) { + if (tree->token.type == TOKEN_ERROR_TYPE && tree->p) { + tree->p->token.type = TOKEN_ERROR_TYPE; + } + tree = tree->p; + pop = false; + if (!tree) { + break; + } else { + precedence = tree->precedence; + } } } @@ -584,11 +623,13 @@ void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv) { } tree->token.type = TOKEN_ERROR_TYPE; - tree->lhs = 0; - tree->rhs = 0; + tree->lhs = NULL; + tree->rhs = NULL; + tree->p = NULL; + tree->precedence = INT_MAX; int openParens = 0; - _parseExpression(tree, lv, 0, INT_MAX, &openParens); + _parseExpression(tree, lv, &openParens); if (openParens) { if (tree->token.type == TOKEN_IDENTIFIER_TYPE) { free(tree->token.identifierValue); @@ -607,23 +648,40 @@ void lexFree(struct LexVector* lv) { } } -void parseFree(struct ParseTree* tree) { - if (!tree) { - return; - } - - if (tree->lhs) { - parseFree(tree->lhs); - free(tree->lhs); - } - if (tree->rhs) { - parseFree(tree->rhs); - free(tree->rhs); - } - +static void _freeTree(struct ParseTree* tree) { if (tree->token.type == TOKEN_IDENTIFIER_TYPE) { free(tree->token.identifierValue); } + free(tree); +} + +void parseFree(struct ParseTree* tree) { + while (tree) { + if (tree->lhs) { + tree = tree->lhs; + continue; + } + if (tree->rhs) { + tree = tree->rhs; + continue; + } + if (tree->p) { + if (tree->p->lhs == tree) { + tree = tree->p; + _freeTree(tree->lhs); + tree->lhs = NULL; + } else if (tree->p->rhs == tree) { + tree = tree->p; + _freeTree(tree->rhs); + tree->rhs = NULL; + } else { + abort(); + } + } else { + _freeTree(tree); + break; + } + } } static bool _performOperation(struct mDebugger* debugger, enum Operation operation, int32_t current, int32_t next, int32_t* value, int* segment) { @@ -721,56 +779,125 @@ bool mDebuggerEvaluateParseTree(struct mDebugger* debugger, struct ParseTree* tr if (!value) { return false; } - int32_t lhs, rhs; - switch (tree->token.type) { - case TOKEN_UINT_TYPE: - if (segment) { - *segment = -1; - } - *value = tree->token.uintValue; - return true; - case TOKEN_SEGMENT_TYPE: - if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, value, segment)) { - return false; - } - return mDebuggerEvaluateParseTree(debugger, tree->lhs, segment, NULL); - case TOKEN_OPERATOR_TYPE: - switch (tree->token.operatorValue) { - case OP_ASSIGN: - case OP_ADD: - case OP_SUBTRACT: - case OP_MULTIPLY: - case OP_DIVIDE: - case OP_MODULO: - case OP_AND: - case OP_OR: - case OP_XOR: - case OP_LESS: - case OP_GREATER: - case OP_EQUAL: - case OP_NOT_EQUAL: - case OP_LOGICAL_AND: - case OP_LOGICAL_OR: - case OP_LE: - case OP_GE: - case OP_SHIFT_L: - case OP_SHIFT_R: - if (!mDebuggerEvaluateParseTree(debugger, tree->lhs, &lhs, segment)) { - return false; - } - // Fall through - default: - if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, &rhs, segment)) { - return false; + struct IntList stack; + int nextBranch; + bool ok = true; + int32_t tmpVal = 0; + int32_t tmpSegment = -1; + + IntListInit(&stack, 0); + while (ok) { + switch (tree->token.type) { + case TOKEN_UINT_TYPE: + nextBranch = 2; + tmpSegment = -1; + tmpVal = tree->token.uintValue; + break; + case TOKEN_SEGMENT_TYPE: + nextBranch = 0; + break; + case TOKEN_OPERATOR_TYPE: + switch (tree->token.operatorValue) { + case OP_ASSIGN: + case OP_ADD: + case OP_SUBTRACT: + case OP_MULTIPLY: + case OP_DIVIDE: + case OP_MODULO: + case OP_AND: + case OP_OR: + case OP_XOR: + case OP_LESS: + case OP_GREATER: + case OP_EQUAL: + case OP_NOT_EQUAL: + case OP_LOGICAL_AND: + case OP_LOGICAL_OR: + case OP_LE: + case OP_GE: + case OP_SHIFT_L: + case OP_SHIFT_R: + nextBranch = 0; + break; + default: + nextBranch = 1; + break; } break; + case TOKEN_IDENTIFIER_TYPE: + if (!mDebuggerLookupIdentifier(debugger, tree->token.identifierValue, &tmpVal, &tmpSegment)) { + ok = false; + } + nextBranch = 2; + break; + case TOKEN_ERROR_TYPE: + default: + ok = false; + break; + } + if (!ok) { + break; + } + + bool gotTree = false; + while (!gotTree && tree) { + int32_t lhs, rhs; + + switch (nextBranch) { + case 0: + *IntListAppend(&stack) = tmpVal; + *IntListAppend(&stack) = tmpSegment; + *IntListAppend(&stack) = nextBranch; + tree = tree->lhs; + gotTree = true; + break; + case 1: + *IntListAppend(&stack) = tmpVal; + *IntListAppend(&stack) = tmpSegment; + *IntListAppend(&stack) = nextBranch; + tree = tree->rhs; + gotTree = true; + break; + case 2: + if (!IntListSize(&stack)) { + tree = NULL; + break; + } + nextBranch = *IntListGetPointer(&stack, IntListSize(&stack) - 1); + IntListResize(&stack, -1); + tree = tree->p; + if (nextBranch == 0) { + ++nextBranch; + } else if (tree) { + nextBranch = 2; + switch (tree->token.type) { + case TOKEN_OPERATOR_TYPE: + rhs = tmpVal; + lhs = *IntListGetPointer(&stack, IntListSize(&stack) - 2); + tmpSegment = *IntListGetPointer(&stack, IntListSize(&stack) - 1); + ok = _performOperation(debugger, tree->token.operatorValue, lhs, rhs, &tmpVal, &tmpSegment); + break; + case TOKEN_SEGMENT_TYPE: + tmpSegment = *IntListGetPointer(&stack, IntListSize(&stack) - 2); + break; + default: + break; + } + } + IntListResize(&stack, -2); + break; + } + } + if (!tree) { + break; } - return _performOperation(debugger, tree->token.operatorValue, lhs, rhs, value, segment); - case TOKEN_IDENTIFIER_TYPE: - return mDebuggerLookupIdentifier(debugger, tree->token.identifierValue, value, segment); - case TOKEN_ERROR_TYPE: - default: - break; } - return false; + IntListDeinit(&stack); + if (ok) { + *value = tmpVal; + if (segment) { + *segment = tmpSegment; + } + } + return ok; } diff --git a/src/debugger/test/parser.c b/src/debugger/test/parser.c index c1c2840d2..b25d8a68a 100644 --- a/src/debugger/test/parser.c +++ b/src/debugger/test/parser.c @@ -9,7 +9,7 @@ struct LPTest { struct LexVector lv; - struct ParseTree tree; + struct ParseTree* tree; }; #define PARSE(STR) \ @@ -18,7 +18,8 @@ struct LPTest { LexVectorClear(&lp->lv); \ size_t adjusted = lexExpression(&lp->lv, STR, strlen(STR), ""); \ assert_false(adjusted > strlen(STR)); \ - struct ParseTree* tree = &lp->tree; \ + lp->tree = malloc(sizeof(*lp->tree)); \ + struct ParseTree* tree = lp->tree; \ parseLexedExpression(tree, &lp->lv) static int parseSetup(void** state) { @@ -30,7 +31,7 @@ static int parseSetup(void** state) { static int parseTeardown(void** state) { struct LPTest* lp = *state; - parseFree(&lp->tree); + parseFree(lp->tree); lexFree(&lp->lv); LexVectorDeinit(&lp->lv); free(lp); diff --git a/src/feature/updater-main.c b/src/feature/updater-main.c index 4c7277203..4b1f216ff 100644 --- a/src/feature/updater-main.c +++ b/src/feature/updater-main.c @@ -173,25 +173,73 @@ int main(int argc, char* argv[]) { prefix = false; needsUnmount = true; #endif - } else { + } else if (strcmp(extension, "appimage") != 0) { archive = VDirOpenArchive(updateArchive); } - if (!archive) { - puts("Cannot open update archive"); - } else { + if (archive) { puts("Extracting update"); if (extractArchive(archive, root, prefix)) { - puts("Complete"); - const char* command = mUpdateGetCommand(&config); - strlcpy(bin, command, sizeof(bin)); ok = 0; - mUpdateDeregister(&config); } else { puts("An error occurred"); } archive->close(archive); unlink(updateArchive); } +#ifdef __linux__ + else if (strcmp(extension, "appimage") == 0) { + const char* command = mUpdateGetCommand(&config); + strlcpy(bin, command, sizeof(bin)); + if (rename(updateArchive, bin) < 0) { + if (errno == EXDEV) { + // Cross-dev, need to copy manually + int infd = open(updateArchive, O_RDONLY); + int outfd = -1; + if (infd >= 0) { + ok = 2; + } else { + outfd = open(bin, O_CREAT | O_WRONLY | O_TRUNC, 0755); + } + if (outfd < 0) { + ok = 2; + } else { + uint8_t buffer[2048]; + ssize_t size; + while ((size = read(infd, buffer, sizeof(buffer))) > 0) { + if (write(outfd, buffer, size) < size) { + ok = 2; + break; + } + } + if (size < 0) { + ok = 2; + } + close(outfd); + close(infd); + } + if (ok == 2) { + puts("Cannot move update over old file"); + } + } else { + puts("Cannot move update over old file"); + } + } else { + ok = 0; + } + if (ok == 0) { + chmod(bin, 0755); + } + } +#endif + else { + puts("Cannot open update archive"); + } + if (ok == 0) { + puts("Complete"); + const char* command = mUpdateGetCommand(&config); + strlcpy(bin, command, sizeof(bin)); + mUpdateDeregister(&config); + } #ifdef __APPLE__ if (needsUnmount) { char* args[] = {"hdiutil", "detach", devpath, NULL}; diff --git a/src/gb/audio.c b/src/gb/audio.c index 5dc40ac62..94a1c40c4 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -11,6 +11,9 @@ #include #include #include +#ifdef M_CORE_GBA +#include +#endif #ifdef __3DS__ #define blip_add_delta blip_add_delta_fast @@ -35,19 +38,19 @@ static void _updateEnvelopeDead(struct GBAudioEnvelope* envelope); static bool _updateSweep(struct GBAudioSquareChannel* sweep, bool initial); static void _updateSquareSample(struct GBAudioSquareChannel* ch); -static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch); - -static int32_t _cyclesToInvert(struct GBAudioSquareChannel* ch); static int16_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch); static void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate); -static void _updateChannel1(struct mTiming* timing, void* user, uint32_t cyclesLate); -static void _updateChannel2(struct mTiming* timing, void* user, uint32_t cyclesLate); -static void _updateChannel3(struct mTiming* timing, void* user, uint32_t cyclesLate); -static void _fadeChannel3(struct mTiming* timing, void* user, uint32_t cyclesLate); static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate); +static const int _squareChannelDuty[4][8] = { + { 0, 0, 0, 0, 0, 0, 0, 1 }, + { 1, 0, 0, 0, 0, 0, 0, 1 }, + { 1, 0, 0, 0, 0, 1, 1, 1 }, + { 0, 1, 1, 1, 1, 1, 1, 0 }, +}; + void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAudioStyle style) { audio->samples = samples; audio->left = blip_new(BLIP_BUFFER_SIZE); @@ -69,30 +72,9 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu audio->timingFactor = 2; } - audio->frameEvent.context = audio; audio->frameEvent.name = "GB Audio Frame Sequencer"; audio->frameEvent.callback = _updateFrame; audio->frameEvent.priority = 0x10; - audio->ch1Event.context = audio; - audio->ch1Event.name = "GB Audio Channel 1"; - audio->ch1Event.callback = _updateChannel1; - audio->ch1Event.priority = 0x11; - audio->ch2Event.context = audio; - audio->ch2Event.name = "GB Audio Channel 2"; - audio->ch2Event.callback = _updateChannel2; - audio->ch2Event.priority = 0x12; - audio->ch3Event.context = audio; - audio->ch3Event.name = "GB Audio Channel 3"; - audio->ch3Event.callback = _updateChannel3; - audio->ch3Event.priority = 0x13; - audio->ch3Fade.context = audio; - audio->ch3Fade.name = "GB Audio Channel 3 Memory"; - audio->ch3Fade.callback = _fadeChannel3; - audio->ch3Fade.priority = 0x14; - audio->ch4Event.context = audio; - audio->ch4Event.name = "GB Audio Channel 4"; - audio->ch4Event.callback = NULL; // This is pending removal, so calling it will crash - audio->ch4Event.priority = 0x15; audio->sampleEvent.context = audio; audio->sampleEvent.name = "GB Audio Sample"; audio->sampleEvent.callback = _sample; @@ -105,19 +87,10 @@ void GBAudioDeinit(struct GBAudio* audio) { } void GBAudioReset(struct GBAudio* audio) { - mTimingDeschedule(audio->timing, &audio->frameEvent); - mTimingDeschedule(audio->timing, &audio->ch1Event); - mTimingDeschedule(audio->timing, &audio->ch2Event); - mTimingDeschedule(audio->timing, &audio->ch3Event); - mTimingDeschedule(audio->timing, &audio->ch3Fade); - mTimingDeschedule(audio->timing, &audio->ch4Event); mTimingDeschedule(audio->timing, &audio->sampleEvent); if (audio->style != GB_AUDIO_GBA) { mTimingSchedule(audio->timing, &audio->sampleEvent, 0); } - if (audio->style == GB_AUDIO_GBA) { - mTimingSchedule(audio->timing, &audio->frameEvent, 0); - } audio->ch1 = (struct GBAudioSquareChannel) { .sweep = { .time = 8 }, .envelope = { .dead = 2 } }; audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 }; @@ -168,32 +141,35 @@ void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) { } void GBAudioWriteNR10(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x1); if (!_writeSweep(&audio->ch1.sweep, value)) { - mTimingDeschedule(audio->timing, &audio->ch1Event); audio->playingCh1 = false; *audio->nr52 &= ~0x0001; } } void GBAudioWriteNR11(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x1); _writeDuty(&audio->ch1.envelope, value); audio->ch1.control.length = 64 - audio->ch1.envelope.length; } void GBAudioWriteNR12(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x1); if (!_writeEnvelope(&audio->ch1.envelope, value, audio->style)) { - mTimingDeschedule(audio->timing, &audio->ch1Event); audio->playingCh1 = false; *audio->nr52 &= ~0x0001; } } void GBAudioWriteNR13(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x1); audio->ch1.control.frequency &= 0x700; audio->ch1.control.frequency |= GBAudioRegisterControlGetFrequency(value); } void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x1); audio->ch1.control.frequency &= 0xFF; audio->ch1.control.frequency |= GBAudioRegisterControlGetFrequency(value << 8); bool wasStop = audio->ch1.control.stop; @@ -201,12 +177,10 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) { if (!wasStop && audio->ch1.control.stop && audio->ch1.control.length && !(audio->frame & 1)) { --audio->ch1.control.length; if (audio->ch1.control.length == 0) { - mTimingDeschedule(audio->timing, &audio->ch1Event); audio->playingCh1 = false; } } if (GBAudioRegisterControlIsRestart(value << 8)) { - bool wasDead = !audio->playingCh1; audio->playingCh1 = _resetEnvelope(&audio->ch1.envelope); audio->ch1.sweep.realFrequency = audio->ch1.control.frequency; _resetSweep(&audio->ch1.sweep); @@ -220,35 +194,33 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) { } } _updateSquareSample(&audio->ch1); - if (wasDead && audio->playingCh1) { - mTimingSchedule(audio->timing, &audio->ch1Event, _cyclesToInvert(&audio->ch1)); - } else if (!audio->playingCh1) { - mTimingDeschedule(audio->timing, &audio->ch1Event); - } } *audio->nr52 &= ~0x0001; *audio->nr52 |= audio->playingCh1; } void GBAudioWriteNR21(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2); _writeDuty(&audio->ch2.envelope, value); audio->ch2.control.length = 64 - audio->ch2.envelope.length; } void GBAudioWriteNR22(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2); if (!_writeEnvelope(&audio->ch2.envelope, value, audio->style)) { - mTimingDeschedule(audio->timing, &audio->ch2Event); audio->playingCh2 = false; *audio->nr52 &= ~0x0002; } } void GBAudioWriteNR23(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2); audio->ch2.control.frequency &= 0x700; audio->ch2.control.frequency |= GBAudioRegisterControlGetFrequency(value); } void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2); audio->ch2.control.frequency &= 0xFF; audio->ch2.control.frequency |= GBAudioRegisterControlGetFrequency(value << 8); bool wasStop = audio->ch2.control.stop; @@ -256,12 +228,10 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) { if (!wasStop && audio->ch2.control.stop && audio->ch2.control.length && !(audio->frame & 1)) { --audio->ch2.control.length; if (audio->ch2.control.length == 0) { - mTimingDeschedule(audio->timing, &audio->ch2Event); audio->playingCh2 = false; } } if (GBAudioRegisterControlIsRestart(value << 8)) { - bool wasDead = !audio->playingCh2; audio->playingCh2 = _resetEnvelope(&audio->ch2.envelope); if (!audio->ch2.control.length) { @@ -271,39 +241,38 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) { } } _updateSquareSample(&audio->ch2); - if (wasDead && audio->playingCh2) { - mTimingSchedule(audio->timing, &audio->ch2Event, _cyclesToInvert(&audio->ch2)); - } else if (!audio->playingCh2) { - mTimingDeschedule(audio->timing, &audio->ch2Event); - } } *audio->nr52 &= ~0x0002; *audio->nr52 |= audio->playingCh2 << 1; } void GBAudioWriteNR30(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x4); audio->ch3.enable = GBAudioRegisterBankGetEnable(value); if (!audio->ch3.enable) { - mTimingDeschedule(audio->timing, &audio->ch3Event); audio->playingCh3 = false; *audio->nr52 &= ~0x0004; } } void GBAudioWriteNR31(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x4); audio->ch3.length = 256 - value; } void GBAudioWriteNR32(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x4); audio->ch3.volume = GBAudioRegisterBankVolumeGetVolumeGB(value); } void GBAudioWriteNR33(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x4); audio->ch3.rate &= 0x700; audio->ch3.rate |= GBAudioRegisterControlGetRate(value); } void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) { + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x4); audio->ch3.rate &= 0xFF; audio->ch3.rate |= GBAudioRegisterControlGetRate(value << 8); bool wasStop = audio->ch3.stop; @@ -339,25 +308,23 @@ void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) { audio->ch3.sample = 0; } } - mTimingDeschedule(audio->timing, &audio->ch3Fade); - mTimingDeschedule(audio->timing, &audio->ch3Event); if (audio->playingCh3) { audio->ch3.readable = audio->style != GB_AUDIO_DMG; // TODO: Where does this cycle delay come from? - mTimingSchedule(audio->timing, &audio->ch3Event, audio->timingFactor * (4 + 2 * (2048 - audio->ch3.rate))); + audio->ch3.nextUpdate = mTimingCurrentTime(audio->timing) + (6 + 2 * (2048 - audio->ch3.rate)) * audio->timingFactor; } *audio->nr52 &= ~0x0004; *audio->nr52 |= audio->playingCh3 << 2; } void GBAudioWriteNR41(struct GBAudio* audio, uint8_t value) { - GBAudioUpdateChannel4(audio); + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x8); _writeDuty(&audio->ch4.envelope, value); audio->ch4.length = 64 - audio->ch4.envelope.length; } void GBAudioWriteNR42(struct GBAudio* audio, uint8_t value) { - GBAudioUpdateChannel4(audio); + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x8); if (!_writeEnvelope(&audio->ch4.envelope, value, audio->style)) { audio->playingCh4 = false; *audio->nr52 &= ~0x0008; @@ -365,14 +332,14 @@ void GBAudioWriteNR42(struct GBAudio* audio, uint8_t value) { } void GBAudioWriteNR43(struct GBAudio* audio, uint8_t value) { - GBAudioUpdateChannel4(audio); + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x8); audio->ch4.ratio = GBAudioRegisterNoiseFeedbackGetRatio(value); audio->ch4.frequency = GBAudioRegisterNoiseFeedbackGetFrequency(value); audio->ch4.power = GBAudioRegisterNoiseFeedbackGetPower(value); } void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) { - GBAudioUpdateChannel4(audio); + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x8); bool wasStop = audio->ch4.stop; audio->ch4.stop = GBAudioRegisterNoiseControlGetStop(value); if (!wasStop && audio->ch4.stop && audio->ch4.length && !(audio->frame & 1)) { @@ -395,7 +362,7 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) { --audio->ch4.length; } } - if (audio->playingCh4 && audio->ch4.envelope.dead != 2) { + if (audio->playingCh4) { audio->ch4.lastEvent = mTimingCurrentTime(audio->timing); } } @@ -482,17 +449,144 @@ void GBAudioWriteNR52(struct GBAudio* audio, uint8_t value) { audio->skipFrame = false; audio->frame = 7; - if (audio->p && audio->p->timer.internalDiv & 0x400) { + if (audio->p && audio->p->timer.internalDiv & (0x100 << audio->p->doubleSpeed)) { audio->skipFrame = true; } } } void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate) { - struct GBAudio* audio = user; - GBAudioUpdateFrame(audio); - if (audio->style == GB_AUDIO_GBA) { - mTimingSchedule(timing, &audio->frameEvent, audio->timingFactor * FRAME_CYCLES - cyclesLate); +#ifdef M_CORE_GBA + struct GBAAudio* audio = user; + GBAAudioSample(audio, mTimingCurrentTime(timing)); + mTimingSchedule(timing, &audio->psg.frameEvent, audio->psg.timingFactor * FRAME_CYCLES - cyclesLate); + GBAudioUpdateFrame(&audio->psg); +#endif +} + +void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) { + if (!audio->enable) { + return; + } + if (audio->playingCh1 && (channels & 0x1)) { + int period = 4 * (2048 - audio->ch1.control.frequency) * audio->timingFactor; + int32_t diff = timestamp - audio->ch1.lastUpdate; + if (diff >= period) { + diff /= period; + audio->ch1.index = (audio->ch1.index + diff) & 7; + audio->ch1.lastUpdate += diff * period; + _updateSquareSample(&audio->ch1); + } + } + if (audio->playingCh2 && (channels & 0x2)) { + int period = 4 * (2048 - audio->ch2.control.frequency) * audio->timingFactor; + int32_t diff = timestamp - audio->ch2.lastUpdate; + if (diff >= period) { + diff /= period; + audio->ch2.index = (audio->ch2.index + diff) & 7; + audio->ch2.lastUpdate += diff * period; + _updateSquareSample(&audio->ch2); + } + } + if (audio->playingCh3 && (channels & 0x4)) { + int cycles = 2 * (2048 - audio->ch3.rate) * audio->timingFactor; + int32_t diff = timestamp - audio->ch3.nextUpdate; + if (diff >= 0) { + diff = (diff / cycles) + 1; + int volume; + switch (audio->ch3.volume) { + case 0: + volume = 4; + break; + case 1: + volume = 0; + break; + case 2: + volume = 1; + break; + default: + case 3: + volume = 2; + break; + } + int start = 7; + int end = 0; + int mask = 0x1F; + int iter; + switch (audio->style) { + case GB_AUDIO_DMG: + default: + audio->ch3.window += diff; + audio->ch3.window &= 0x1F; + audio->ch3.sample = audio->ch3.wavedata8[audio->ch3.window >> 1]; + if (!(audio->ch3.window & 1)) { + audio->ch3.sample >>= 4; + } + audio->ch3.sample &= 0xF; + break; + case GB_AUDIO_GBA: + if (audio->ch3.size) { + mask = 0x3F; + } else if (audio->ch3.bank) { + end = 4; + } else { + start = 3; + } + for (iter = 0; iter < (diff & mask); ++iter) { + uint32_t bitsCarry = audio->ch3.wavedata32[end] & 0x000000F0; + uint32_t bits; + int i; + for (i = start; i >= end; --i) { + bits = audio->ch3.wavedata32[i] & 0x000000F0; + audio->ch3.wavedata32[i] = ((audio->ch3.wavedata32[i] & 0x0F0F0F0F) << 4) | ((audio->ch3.wavedata32[i] & 0xF0F0F000) >> 12); + audio->ch3.wavedata32[i] |= bitsCarry << 20; + bitsCarry = bits; + } + audio->ch3.sample = bitsCarry >> 4; + } + break; + } + if (audio->ch3.volume > 3) { + audio->ch3.sample += audio->ch3.sample << 1; + } + audio->ch3.sample >>= volume; + audio->ch3.nextUpdate += diff * cycles; + audio->ch3.readable = true; + } + if (audio->style == GB_AUDIO_DMG && audio->ch3.readable) { + diff = timestamp - audio->ch3.nextUpdate + cycles; + if (diff >= 4) { + audio->ch3.readable = false; + } + } + } + if (audio->playingCh4 && (channels & 0x8)) { + int32_t cycles = audio->ch4.ratio ? 2 * audio->ch4.ratio : 1; + cycles <<= audio->ch4.frequency; + cycles *= 8 * audio->timingFactor; + + int32_t diff = timestamp - audio->ch4.lastEvent; + if (diff >= cycles) { + int32_t last; + int samples = 0; + int positiveSamples = 0; + int lsb; + int coeff = 0x60; + if (!audio->ch4.power) { + coeff <<= 8; + } + for (last = 0; last + cycles <= diff; last += cycles) { + lsb = audio->ch4.lfsr & 1; + audio->ch4.lfsr >>= 1; + audio->ch4.lfsr ^= lsb * coeff; + ++samples; + positiveSamples += lsb; + } + audio->ch4.sample = lsb * audio->ch4.envelope.currentVolume; + audio->ch4.nSamples += samples; + audio->ch4.samples += positiveSamples * audio->ch4.envelope.currentVolume; + audio->ch4.lastEvent += last; + } } } @@ -504,6 +598,8 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { audio->skipFrame = false; return; } + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x7); + int frame = (audio->frame + 1) & 7; audio->frame = frame; @@ -516,9 +612,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { audio->playingCh1 = _updateSweep(&audio->ch1, false); *audio->nr52 &= ~0x0001; *audio->nr52 |= audio->playingCh1; - if (!audio->playingCh1) { - mTimingDeschedule(audio->timing, &audio->ch1Event); - } } } // Fall through @@ -527,7 +620,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { if (audio->ch1.control.length && audio->ch1.control.stop) { --audio->ch1.control.length; if (audio->ch1.control.length == 0) { - mTimingDeschedule(audio->timing, &audio->ch1Event); audio->playingCh1 = 0; *audio->nr52 &= ~0x0001; } @@ -536,7 +628,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { if (audio->ch2.control.length && audio->ch2.control.stop) { --audio->ch2.control.length; if (audio->ch2.control.length == 0) { - mTimingDeschedule(audio->timing, &audio->ch2Event); audio->playingCh2 = 0; *audio->nr52 &= ~0x0002; } @@ -545,7 +636,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { if (audio->ch3.length && audio->ch3.stop) { --audio->ch3.length; if (audio->ch3.length == 0) { - mTimingDeschedule(audio->timing, &audio->ch3Event); audio->playingCh3 = 0; *audio->nr52 &= ~0x0004; } @@ -554,7 +644,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { if (audio->ch4.length && audio->ch4.stop) { --audio->ch4.length; if (audio->ch4.length == 0) { - GBAudioUpdateChannel4(audio); audio->playingCh4 = 0; *audio->nr52 &= ~0x0008; } @@ -565,9 +654,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { --audio->ch1.envelope.nextStep; if (audio->ch1.envelope.nextStep == 0) { _updateEnvelope(&audio->ch1.envelope); - if (audio->ch1.envelope.dead == 2) { - mTimingDeschedule(audio->timing, &audio->ch1Event); - } _updateSquareSample(&audio->ch1); } } @@ -576,9 +662,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { --audio->ch2.envelope.nextStep; if (audio->ch2.envelope.nextStep == 0) { _updateEnvelope(&audio->ch2.envelope); - if (audio->ch2.envelope.dead == 2) { - mTimingDeschedule(audio->timing, &audio->ch2Event); - } _updateSquareSample(&audio->ch2); } } @@ -586,7 +669,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { if (audio->playingCh4 && !audio->ch4.envelope.dead) { --audio->ch4.envelope.nextStep; if (audio->ch4.envelope.nextStep == 0) { - GBAudioUpdateChannel4(audio); int8_t sample = audio->ch4.sample; _updateEnvelope(&audio->ch4.envelope); audio->ch4.sample = (sample > 0) * audio->ch4.envelope.currentVolume; @@ -600,31 +682,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { } } -void GBAudioUpdateChannel4(struct GBAudio* audio) { - struct GBAudioNoiseChannel* ch = &audio->ch4; - if (ch->envelope.dead == 2 || !audio->playingCh4) { - return; - } - - int32_t cycles = ch->ratio ? 2 * ch->ratio : 1; - cycles <<= ch->frequency; - cycles *= 8 * audio->timingFactor; - - uint32_t last = 0; - uint32_t now = mTimingCurrentTime(audio->timing) - ch->lastEvent; - - for (; last + cycles <= now; last += cycles) { - int lsb = ch->lfsr & 1; - ch->sample = lsb * ch->envelope.currentVolume; - ++ch->nSamples; - ch->samples += ch->sample; - ch->lfsr >>= 1; - ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8); - } - - ch->lastEvent += last; -} - void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) { int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : -0x8; int sampleLeft = dcOffset; @@ -664,7 +721,6 @@ void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) { sampleRight <<= 3; if (!audio->forceDisableCh[3]) { - GBAudioUpdateChannel4(audio); int16_t sample = audio->style == GB_AUDIO_GBA ? (audio->ch4.sample << 3) : _coalesceNoiseChannel(&audio->ch4); if (audio->ch4Left) { sampleLeft += sample; @@ -683,6 +739,7 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { struct GBAudio* audio = user; int16_t sampleLeft = 0; int16_t sampleRight = 0; + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF); GBAudioSamplePSG(audio, &sampleLeft, &sampleRight); sampleLeft = (sampleLeft * audio->masterVolume * 6) >> 7; sampleRight = (sampleRight * audio->masterVolume * 6) >> 7; @@ -775,30 +832,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudi } static void _updateSquareSample(struct GBAudioSquareChannel* ch) { - ch->sample = ch->control.hi * ch->envelope.currentVolume; -} - -static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) { - ch->control.hi = !ch->control.hi; - _updateSquareSample(ch); - return _cyclesToInvert(ch); -} - -static int32_t _cyclesToInvert(struct GBAudioSquareChannel* ch) { - int period = 4 * (2048 - ch->control.frequency); - switch (ch->envelope.duty) { - case 0: - return ch->control.hi ? period : period * 7; - case 1: - return ch->control.hi ? period * 2 : period * 6; - case 2: - return period * 4; - case 3: - return ch->control.hi ? period * 6 : period * 2; - default: - // This should never be hit - return period * 4; - } + ch->sample = _squareChannelDuty[ch->envelope.duty][ch->index] * ch->envelope.currentVolume; } static int16_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch) { @@ -870,94 +904,6 @@ static bool _updateSweep(struct GBAudioSquareChannel* ch, bool initial) { return true; } -static void _updateChannel1(struct mTiming* timing, void* user, uint32_t cyclesLate) { - struct GBAudio* audio = user; - struct GBAudioSquareChannel* ch = &audio->ch1; - int cycles = _updateSquareChannel(ch); - mTimingSchedule(timing, &audio->ch1Event, audio->timingFactor * cycles - cyclesLate); -} - -static void _updateChannel2(struct mTiming* timing, void* user, uint32_t cyclesLate) { - struct GBAudio* audio = user; - struct GBAudioSquareChannel* ch = &audio->ch2; - int cycles = _updateSquareChannel(ch); - mTimingSchedule(timing, &audio->ch2Event, audio->timingFactor * cycles - cyclesLate); -} - -static void _updateChannel3(struct mTiming* timing, void* user, uint32_t cyclesLate) { - struct GBAudio* audio = user; - struct GBAudioWaveChannel* ch = &audio->ch3; - int i; - int volume; - switch (ch->volume) { - case 0: - volume = 4; - break; - case 1: - volume = 0; - break; - case 2: - volume = 1; - break; - default: - case 3: - volume = 2; - break; - } - int start; - int end; - switch (audio->style) { - case GB_AUDIO_DMG: - default: - ++ch->window; - ch->window &= 0x1F; - ch->sample = ch->wavedata8[ch->window >> 1]; - if (!(ch->window & 1)) { - ch->sample >>= 4; - } - ch->sample &= 0xF; - break; - case GB_AUDIO_GBA: - if (ch->size) { - start = 7; - end = 0; - } else if (ch->bank) { - start = 7; - end = 4; - } else { - start = 3; - end = 0; - } - uint32_t bitsCarry = ch->wavedata32[end] & 0x000000F0; - uint32_t bits; - for (i = start; i >= end; --i) { - bits = ch->wavedata32[i] & 0x000000F0; - ch->wavedata32[i] = ((ch->wavedata32[i] & 0x0F0F0F0F) << 4) | ((ch->wavedata32[i] & 0xF0F0F000) >> 12); - ch->wavedata32[i] |= bitsCarry << 20; - bitsCarry = bits; - } - ch->sample = bitsCarry >> 4; - break; - } - if (ch->volume > 3) { - ch->sample += ch->sample << 1; - } - ch->sample >>= volume; - audio->ch3.readable = true; - if (audio->style == GB_AUDIO_DMG) { - mTimingDeschedule(audio->timing, &audio->ch3Fade); - mTimingSchedule(timing, &audio->ch3Fade, 4 - cyclesLate); - } - int cycles = 2 * (2048 - ch->rate); - mTimingSchedule(timing, &audio->ch3Event, audio->timingFactor * cycles - cyclesLate); -} -static void _fadeChannel3(struct mTiming* timing, void* user, uint32_t cyclesLate) { - UNUSED(timing); - UNUSED(cyclesLate); - struct GBAudio* audio = user; - audio->ch3.readable = false; -} - void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGState* state, uint32_t* flagsOut) { uint32_t flags = 0; uint32_t sweep = 0; @@ -971,30 +917,29 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat flags = GBSerializedAudioFlagsSetCh1Volume(flags, audio->ch1.envelope.currentVolume); flags = GBSerializedAudioFlagsSetCh1Dead(flags, audio->ch1.envelope.dead); - flags = GBSerializedAudioFlagsSetCh1Hi(flags, audio->ch1.control.hi); flags = GBSerializedAudioFlagsSetCh1SweepEnabled(flags, audio->ch1.sweep.enable); flags = GBSerializedAudioFlagsSetCh1SweepOccurred(flags, audio->ch1.sweep.occurred); ch1Flags = GBSerializedAudioEnvelopeSetLength(ch1Flags, audio->ch1.control.length); ch1Flags = GBSerializedAudioEnvelopeSetNextStep(ch1Flags, audio->ch1.envelope.nextStep); ch1Flags = GBSerializedAudioEnvelopeSetFrequency(ch1Flags, audio->ch1.sweep.realFrequency); + ch1Flags = GBSerializedAudioEnvelopeSetDutyIndex(ch1Flags, audio->ch1.index); sweep = GBSerializedAudioSweepSetTime(sweep, audio->ch1.sweep.time & 7); STORE_32LE(ch1Flags, 0, &state->ch1.envelope); STORE_32LE(sweep, 0, &state->ch1.sweep); - STORE_32LE(audio->ch1Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch1.nextEvent); + STORE_32LE(audio->ch1.lastUpdate - mTimingCurrentTime(audio->timing), 0, &state->ch1.lastUpdate); flags = GBSerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume); flags = GBSerializedAudioFlagsSetCh2Dead(flags, audio->ch2.envelope.dead); - flags = GBSerializedAudioFlagsSetCh2Hi(flags, audio->ch2.control.hi); ch2Flags = GBSerializedAudioEnvelopeSetLength(ch2Flags, audio->ch2.control.length); ch2Flags = GBSerializedAudioEnvelopeSetNextStep(ch2Flags, audio->ch2.envelope.nextStep); + ch2Flags = GBSerializedAudioEnvelopeSetDutyIndex(ch2Flags, audio->ch2.index); STORE_32LE(ch2Flags, 0, &state->ch2.envelope); - STORE_32LE(audio->ch2Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch2.nextEvent); + STORE_32LE(audio->ch2.lastUpdate - mTimingCurrentTime(audio->timing), 0, &state->ch2.lastUpdate); flags = GBSerializedAudioFlagsSetCh3Readable(flags, audio->ch3.readable); memcpy(state->ch3.wavebanks, audio->ch3.wavedata32, sizeof(state->ch3.wavebanks)); STORE_16LE(audio->ch3.length, 0, &state->ch3.length); - STORE_32LE(audio->ch3Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch3.nextEvent); - STORE_32LE(audio->ch3Fade.when - mTimingCurrentTime(audio->timing), 0, &state->ch1.nextCh3Fade); + STORE_32LE(audio->ch3.nextUpdate - mTimingCurrentTime(audio->timing), 0, &state->ch3.nextEvent); flags = GBSerializedAudioFlagsSetCh4Volume(flags, audio->ch4.envelope.currentVolume); flags = GBSerializedAudioFlagsSetCh4Dead(flags, audio->ch4.envelope.dead); @@ -1039,7 +984,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt LOAD_32LE(sweep, 0, &state->ch1.sweep); audio->ch1.envelope.currentVolume = GBSerializedAudioFlagsGetCh1Volume(flags); audio->ch1.envelope.dead = GBSerializedAudioFlagsGetCh1Dead(flags); - audio->ch1.control.hi = GBSerializedAudioFlagsGetCh1Hi(flags); audio->ch1.sweep.enable = GBSerializedAudioFlagsGetCh1SweepEnabled(flags); audio->ch1.sweep.occurred = GBSerializedAudioFlagsGetCh1SweepOccurred(flags); audio->ch1.sweep.time = GBSerializedAudioSweepGetTime(sweep); @@ -1049,34 +993,25 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt audio->ch1.control.length = GBSerializedAudioEnvelopeGetLength(ch1Flags); audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags); audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(ch1Flags); - LOAD_32LE(when, 0, &state->ch1.nextEvent); - if (audio->ch1.envelope.dead < 2 && audio->playingCh1) { - mTimingSchedule(audio->timing, &audio->ch1Event, when); - } + audio->ch1.index = GBSerializedAudioEnvelopeGetDutyIndex(ch1Flags); + LOAD_32LE(audio->ch1.lastUpdate, 0, &state->ch1.lastUpdate); + audio->ch1.lastUpdate += mTimingCurrentTime(audio->timing); LOAD_32LE(ch2Flags, 0, &state->ch2.envelope); audio->ch2.envelope.currentVolume = GBSerializedAudioFlagsGetCh2Volume(flags); audio->ch2.envelope.dead = GBSerializedAudioFlagsGetCh2Dead(flags); - audio->ch2.control.hi = GBSerializedAudioFlagsGetCh2Hi(flags); audio->ch2.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags); audio->ch2.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch2Flags); - LOAD_32LE(when, 0, &state->ch2.nextEvent); - if (audio->ch2.envelope.dead < 2 && audio->playingCh2) { - mTimingSchedule(audio->timing, &audio->ch2Event, when); - } + audio->ch2.index = GBSerializedAudioEnvelopeGetDutyIndex(ch2Flags); + LOAD_32LE(audio->ch2.lastUpdate, 0, &state->ch2.lastUpdate); + audio->ch2.lastUpdate += mTimingCurrentTime(audio->timing); audio->ch3.readable = GBSerializedAudioFlagsGetCh3Readable(flags); // TODO: Big endian? memcpy(audio->ch3.wavedata32, state->ch3.wavebanks, sizeof(audio->ch3.wavedata32)); LOAD_16LE(audio->ch3.length, 0, &state->ch3.length); - LOAD_32LE(when, 0, &state->ch3.nextEvent); - if (audio->playingCh3) { - mTimingSchedule(audio->timing, &audio->ch3Event, when); - } - LOAD_32LE(when, 0, &state->ch1.nextCh3Fade); - if (audio->ch3.readable && audio->style == GB_AUDIO_DMG) { - mTimingSchedule(audio->timing, &audio->ch3Fade, when); - } + LOAD_32LE(audio->ch3.nextUpdate, 0, &state->ch3.nextEvent); + audio->ch3.nextUpdate += mTimingCurrentTime(audio->timing); LOAD_32LE(ch4Flags, 0, &state->ch4.envelope); audio->ch4.envelope.currentVolume = GBSerializedAudioFlagsGetCh4Volume(flags); diff --git a/src/gb/gb.c b/src/gb/gb.c index edcd548d1..81d059b00 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -189,6 +189,9 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf) { if (gb->cpu) { struct SM83Core* cpu = gb->cpu; + if (!gb->memory.romBase) { + GBMBCSwitchBank0(gb, 0); + } cpu->memory.setActiveRegion(cpu, cpu->pc); } diff --git a/src/gb/io.c b/src/gb/io.c index deed1153f..c6ad66c36 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -403,9 +403,10 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { case GB_REG_WAVE_D: case GB_REG_WAVE_E: case GB_REG_WAVE_F: - if (!gb->audio.playingCh3 || gb->audio.style != GB_AUDIO_DMG) { + GBAudioRun(&gb->audio, mTimingCurrentTime(gb->audio.timing), 0x4); + if (!gb->audio.playingCh3) { gb->audio.ch3.wavedata8[address - GB_REG_WAVE_0] = value; - } else if(gb->audio.ch3.readable) { + } else if (gb->audio.ch3.readable || gb->audio.style == GB_AUDIO_CGB) { gb->audio.ch3.wavedata8[gb->audio.ch3.window >> 1] = value; } break; @@ -607,7 +608,8 @@ uint8_t GBIORead(struct GB* gb, unsigned address) { case GB_REG_WAVE_E: case GB_REG_WAVE_F: if (gb->audio.playingCh3) { - if (gb->audio.ch3.readable || gb->audio.style != GB_AUDIO_DMG) { + GBAudioRun(&gb->audio, mTimingCurrentTime(gb->audio.timing), 0x4); + if (gb->audio.ch3.readable || gb->audio.style == GB_AUDIO_CGB) { return gb->audio.ch3.wavedata8[gb->audio.ch3.window >> 1]; } else { return 0xFF; @@ -620,6 +622,7 @@ uint8_t GBIORead(struct GB* gb, unsigned address) { if (gb->model < GB_MODEL_CGB) { mLOG(GB_IO, GAME_ERROR, "Reading from CGB register FF%02X in DMG mode", address); } else if (gb->audio.enable) { + GBAudioRun(&gb->audio, mTimingCurrentTime(gb->audio.timing), 0x3); return (gb->audio.ch1.sample) | (gb->audio.ch2.sample << 4); } break; @@ -627,7 +630,7 @@ uint8_t GBIORead(struct GB* gb, unsigned address) { if (gb->model < GB_MODEL_CGB) { mLOG(GB_IO, GAME_ERROR, "Reading from CGB register FF%02X in DMG mode", address); } else if (gb->audio.enable) { - GBAudioUpdateChannel4(&gb->audio); + GBAudioRun(&gb->audio, mTimingCurrentTime(gb->audio.timing), 0xC); return (gb->audio.ch3.sample) | (gb->audio.ch4.sample << 4); } break; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 9e8724964..282891cfe 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -1402,11 +1402,16 @@ void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) { if (value < 0x10) { GBMBCSwitchSramBank(gb, value); memory->mbcState.pocketCam.registersActive = false; + memory->directSramAccess = true; } else { memory->mbcState.pocketCam.registersActive = true; + memory->directSramAccess = false; } break; case 0x5: + if (!memory->mbcState.pocketCam.registersActive) { + break; + } address &= 0x7F; if (address == 0 && value & 1) { value &= 6; // TODO: Timing diff --git a/src/gb/renderers/cache-set.c b/src/gb/renderers/cache-set.c index ebd0d0899..bfc56c2a3 100644 --- a/src/gb/renderers/cache-set.c +++ b/src/gb/renderers/cache-set.c @@ -119,6 +119,7 @@ void GBVideoCacheWriteVideoRegister(struct mCacheSet* cache, uint16_t address, u sysconfig = mMapCacheSystemInfoSetMacroTileSize(sysconfig, 5); sysconfig = mMapCacheSystemInfoSetPaletteBPP(sysconfig, 1); sysconfig = mMapCacheSystemInfoSetMapAlign(sysconfig, 0); + sysconfig = mMapCacheSystemInfoSetWriteAlign(sysconfig, 0); sysconfig = mMapCacheSystemInfoSetTilesHigh(sysconfig, 5); sysconfig = mMapCacheSystemInfoSetTilesWide(sysconfig, 5); mMapCacheConfigureSystem(map, sysconfig); diff --git a/src/gb/serialize.c b/src/gb/serialize.c index d3fd8f12a..ad7ebe756 100644 --- a/src/gb/serialize.c +++ b/src/gb/serialize.c @@ -14,7 +14,7 @@ mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate", "gb.serialize"); MGBA_EXPORT const uint32_t GBSavestateMagic = 0x00400000; -MGBA_EXPORT const uint32_t GBSavestateVersion = 0x00000002; +MGBA_EXPORT const uint32_t GBSavestateVersion = 0x00000003; void GBSerialize(struct GB* gb, struct GBSerializedState* state) { STORE_32LE(GBSavestateMagic + GBSavestateVersion, 0, &state->versionMagic); diff --git a/src/gb/sio/lockstep.c b/src/gb/sio/lockstep.c index d739140e5..4d1f1f549 100644 --- a/src/gb/sio/lockstep.c +++ b/src/gb/sio/lockstep.c @@ -157,7 +157,7 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) { } } // Tell the other GBs they can continue up to where we were - node->p->d.addCycles(&node->p->d, 0, node->eventDiff); + node->p->d.addCycles(&node->p->d, node->id, node->eventDiff); #ifndef NDEBUG node->phase = node->p->d.transferActive; #endif @@ -252,6 +252,12 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu mLockstepLock(&node->p->d); bool claimed = false; if (ATOMIC_CMPXCHG(node->p->masterClaimed, claimed, true)) { + if (node->id != 0) { + node->p->players[0]->id = 1; + node->p->players[1] = node->p->players[0]; + node->p->players[0] = node->p->players[1]; + node->id = 0; + } ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); ATOMIC_STORE(node->p->d.transferCycles, GBSIOCyclesPerTransfer[(value >> 1) & 1]); mTimingDeschedule(&driver->p->p->timing, &driver->p->event); diff --git a/src/gba/audio.c b/src/gba/audio.c index fc264349f..c4f989f63 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -25,6 +25,7 @@ mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio", "gba.audio"); const unsigned GBA_AUDIO_SAMPLES = 2048; const int GBA_AUDIO_VOLUME_MAX = 0x100; +static const int SAMPLE_INTERVAL = GBA_ARM7TDMI_FREQUENCY / 0x4000; static const int CLOCKS_PER_FRAME = 0x800; static int _applyBias(struct GBAAudio* audio, int sample); @@ -43,6 +44,7 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) { GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA); audio->psg.timing = &audio->p->timing; audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY; + audio->psg.frameEvent.context = audio; audio->samples = samples; // Guess too large; we hang producing extra samples if we guess too low blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000); @@ -57,6 +59,8 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) { void GBAAudioReset(struct GBAAudio* audio) { GBAudioReset(&audio->psg); + mTimingDeschedule(&audio->p->timing, &audio->psg.frameEvent); + mTimingSchedule(&audio->p->timing, &audio->psg.frameEvent, 0); mTimingDeschedule(&audio->p->timing, &audio->sampleEvent); mTimingSchedule(&audio->p->timing, &audio->sampleEvent, 0); audio->chA.dmaSource = 1; @@ -66,18 +70,22 @@ void GBAAudioReset(struct GBAAudio* audio) { audio->chA.internalSample = 0; audio->chA.internalRemaining = 0; memset(audio->chA.fifo, 0, sizeof(audio->chA.fifo)); - audio->chA.sample = 0; audio->chB.fifoWrite = 0; audio->chB.fifoRead = 0; audio->chB.internalSample = 0; audio->chB.internalRemaining = 0; memset(audio->chB.fifo, 0, sizeof(audio->chB.fifo)); - audio->chB.sample = 0; - audio->sampleRate = 0x8000; + int i; + for (i = 0; i < 8; ++i) { + audio->chA.samples[i] = 0; + audio->chB.samples[i] = 0; + } audio->soundbias = 0x200; audio->volume = 0; audio->volumeChA = false; audio->volumeChB = false; + audio->lastSample = 0; + audio->sampleIndex = 0; audio->chARight = false; audio->chALeft = false; audio->chATimer = false; @@ -85,7 +93,7 @@ void GBAAudioReset(struct GBAAudio* audio) { audio->chBLeft = false; audio->chBTimer = false; audio->enable = false; - audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate; + audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / 0x8000; audio->psg.sampleInterval = audio->sampleInterval; blip_clear(audio->psg.left); @@ -137,56 +145,67 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* } void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR10(&audio->psg, value); } void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR11(&audio->psg, value); GBAudioWriteNR12(&audio->psg, value >> 8); } void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR13(&audio->psg, value); GBAudioWriteNR14(&audio->psg, value >> 8); } void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR21(&audio->psg, value); GBAudioWriteNR22(&audio->psg, value >> 8); } void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR23(&audio->psg, value); GBAudioWriteNR24(&audio->psg, value >> 8); } void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); audio->psg.ch3.size = GBAudioRegisterBankGetSize(value); audio->psg.ch3.bank = GBAudioRegisterBankGetBank(value); GBAudioWriteNR30(&audio->psg, value); } void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR31(&audio->psg, value); audio->psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value >> 8); } void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR33(&audio->psg, value); GBAudioWriteNR34(&audio->psg, value >> 8); } void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR41(&audio->psg, value); GBAudioWriteNR42(&audio->psg, value >> 8); } void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR43(&audio->psg, value); GBAudioWriteNR44(&audio->psg, value >> 8); } void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR50(&audio->psg, value); GBAudioWriteNR51(&audio->psg, value >> 8); } @@ -218,6 +237,7 @@ void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) { audio->soundbias = value; + audio->sampleInterval = 0x200 >> GBARegisterSOUNDBIASGetResolution(value); } void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) { @@ -229,6 +249,7 @@ void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) { bank = 1; } + GBAudioRun(&audio->psg, mTimingCurrentTime(audio->psg.timing), 0x4); audio->psg.ch3.wavedata32[address | (bank * 4)] = value; } @@ -241,6 +262,7 @@ uint32_t GBAAudioReadWaveRAM(struct GBAAudio* audio, int address) { bank = 1; } + GBAudioRun(&audio->psg, mTimingCurrentTime(audio->psg.timing), 0x4); return audio->psg.ch3.wavedata32[address | (bank * 4)]; } @@ -297,7 +319,14 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) { channel->fifoRead = 0; } } - channel->sample = channel->internalSample; + int32_t until = mTimingUntil(&audio->p->timing, &audio->sampleEvent) - 1; + int bits = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); + until += 1 << (9 - GBARegisterSOUNDBIASGetResolution(audio->soundbias)); + until >>= 9 - GBARegisterSOUNDBIASGetResolution(audio->soundbias); + int i; + for (i = bits - until; i < bits; ++i) { + channel->samples[i] = channel->internalSample; + } if (channel->internalRemaining) { channel->internalSample >>= 8; --channel->internalRemaining; @@ -314,61 +343,100 @@ static int _applyBias(struct GBAAudio* audio, int sample) { return ((sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) * audio->masterVolume * 3) >> 4; } +void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp) { + timestamp -= audio->lastSample; + timestamp -= audio->sampleIndex * audio->sampleInterval; // TODO: This can break if the interval changes between samples + + int maxSample = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); + int sample; + for (sample = audio->sampleIndex; timestamp >= audio->sampleInterval && sample < maxSample; ++sample, timestamp -= audio->sampleInterval) { + int16_t sampleLeft = 0; + int16_t sampleRight = 0; + int psgShift = 4 - audio->volume; + GBAudioRun(&audio->psg, sample * audio->sampleInterval + audio->lastSample, 0xF); + GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight); + sampleLeft >>= psgShift; + sampleRight >>= psgShift; + + if (audio->mixer) { + audio->mixer->step(audio->mixer); + } + if (!audio->externalMixing) { + if (!audio->forceDisableChA) { + if (audio->chALeft) { + sampleLeft += (audio->chA.samples[sample] << 2) >> !audio->volumeChA; + } + + if (audio->chARight) { + sampleRight += (audio->chA.samples[sample] << 2) >> !audio->volumeChA; + } + } + + if (!audio->forceDisableChB) { + if (audio->chBLeft) { + sampleLeft += (audio->chB.samples[sample] << 2) >> !audio->volumeChB; + } + + if (audio->chBRight) { + sampleRight += (audio->chB.samples[sample] << 2) >> !audio->volumeChB; + } + } + } + + sampleLeft = _applyBias(audio, sampleLeft); + sampleRight = _applyBias(audio, sampleRight); + audio->currentSamples[sample].left = sampleLeft; + audio->currentSamples[sample].right = sampleRight; + } + + audio->sampleIndex = sample; + if (sample == maxSample) { + audio->lastSample += SAMPLE_INTERVAL; + audio->sampleIndex = 0; + } +} + static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { struct GBAAudio* audio = user; - int16_t sampleLeft = 0; - int16_t sampleRight = 0; - int psgShift = 4 - audio->volume; - GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight); - sampleLeft >>= psgShift; - sampleRight >>= psgShift; + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing) - cyclesLate); - if (audio->mixer) { - audio->mixer->step(audio->mixer); - } - if (!audio->externalMixing) { - if (!audio->forceDisableChA) { - if (audio->chALeft) { - sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA; - } - - if (audio->chARight) { - sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA; - } - } - - if (!audio->forceDisableChB) { - if (audio->chBLeft) { - sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB; - } - - if (audio->chBRight) { - sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB; - } - } - } - - sampleLeft = _applyBias(audio, sampleLeft); - sampleRight = _applyBias(audio, sampleRight); + int samples = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); + int sampleMask = 1 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); + memset(audio->chA.samples, audio->chA.samples[samples - 1], sizeof(audio->chA.samples)); + memset(audio->chB.samples, audio->chB.samples[samples - 1], sizeof(audio->chB.samples)); mCoreSyncLockAudio(audio->p->sync); unsigned produced; - if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) { - blip_add_delta(audio->psg.left, audio->clock, sampleLeft - audio->lastLeft); - blip_add_delta(audio->psg.right, audio->clock, sampleRight - audio->lastRight); - audio->lastLeft = sampleLeft; - audio->lastRight = sampleRight; - audio->clock += audio->sampleInterval; - if (audio->clock >= CLOCKS_PER_FRAME) { - blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME); - blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME); - audio->clock -= CLOCKS_PER_FRAME; + int32_t sampleSumLeft = 0; + int32_t sampleSumRight = 0; + int i; + for (i = 0; i < samples; ++i) { + int16_t sampleLeft = audio->currentSamples[i].left; + int16_t sampleRight = audio->currentSamples[i].right; + sampleSumLeft += sampleLeft; + sampleSumRight += sampleRight; + if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) { + blip_add_delta(audio->psg.left, audio->clock, sampleLeft - audio->lastLeft); + blip_add_delta(audio->psg.right, audio->clock, sampleRight - audio->lastRight); + audio->lastLeft = sampleLeft; + audio->lastRight = sampleRight; + audio->clock += audio->sampleInterval; + if (audio->clock >= CLOCKS_PER_FRAME) { + blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME); + blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME); + audio->clock -= CLOCKS_PER_FRAME; + } + } + // TODO: Post all frames + if (audio->p->stream && audio->p->stream->postAudioFrame && (i & (sampleMask - 1)) == sampleMask - 1) { + sampleSumLeft /= sampleMask; + sampleSumRight /= sampleMask; + audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight); + sampleSumLeft = 0; + sampleSumRight = 0; } } produced = blip_samples_avail(audio->psg.left); - if (audio->p->stream && audio->p->stream->postAudioFrame) { - audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight); - } bool wait = produced >= audio->samples; if (!mCoreSyncProduceAudio(audio->p->sync, audio->psg.left, audio->samples)) { // Interrupted @@ -379,7 +447,7 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { audio->p->stream->postAudioBuffer(audio->p->stream, audio->psg.left, audio->psg.right); } - mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval - cyclesLate); + mTimingSchedule(timing, &audio->sampleEvent, SAMPLE_INTERVAL - cyclesLate); } void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) { @@ -387,12 +455,18 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* STORE_32(audio->chA.internalSample, 0, &state->audio.internalA); STORE_32(audio->chB.internalSample, 0, &state->audio.internalB); - state->audio.sampleA = audio->chA.sample; - state->audio.sampleB = audio->chB.sample; + memcpy(state->samples.chA, audio->chA.samples, sizeof(audio->chA.samples)); + memcpy(state->samples.chB, audio->chB.samples, sizeof(audio->chB.samples)); + + size_t i; + for (i = 0; i < GBA_MAX_SAMPLES; ++i) { + STORE_16(audio->currentSamples[i].left, 0, &state->currentSamples[i].left); + STORE_16(audio->currentSamples[i].right, 0, &state->currentSamples[i].right); + } + STORE_32(audio->lastSample, 0, &state->audio.lastSample); int readA = audio->chA.fifoRead; int readB = audio->chB.fifoRead; - size_t i; for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) { STORE_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA); STORE_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB); @@ -426,6 +500,11 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* flags = GBASerializedAudioFlagsSetFIFOInternalSamplesA(flags, audio->chA.internalRemaining); flags = GBASerializedAudioFlagsSetFIFOInternalSamplesB(flags, audio->chB.internalRemaining); STORE_16(flags, 0, &state->audio.gbaFlags); + + GBASerializedAudioFlags2 flags2 = 0; + flags2 = GBASerializedAudioFlags2SetSampleIndex(flags2, audio->sampleIndex); + STORE_32(flags2, 0, &state->audio.gbaFlags2); + STORE_32(audio->sampleEvent.when - mTimingCurrentTime(&audio->p->timing), 0, &state->audio.nextSample); } @@ -434,12 +513,18 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState LOAD_32(audio->chA.internalSample, 0, &state->audio.internalA); LOAD_32(audio->chB.internalSample, 0, &state->audio.internalB); - audio->chA.sample = state->audio.sampleA; - audio->chB.sample = state->audio.sampleB; + memcpy(audio->chA.samples, state->samples.chA, sizeof(audio->chA.samples)); + memcpy(audio->chB.samples, state->samples.chB, sizeof(audio->chB.samples)); + + size_t i; + for (i = 0; i < GBA_MAX_SAMPLES; ++i) { + LOAD_16(audio->currentSamples[i].left, 0, &state->currentSamples[i].left); + LOAD_16(audio->currentSamples[i].right, 0, &state->currentSamples[i].right); + } + LOAD_32(audio->lastSample, 0, &state->audio.lastSample); int readA = 0; int readB = 0; - size_t i; for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) { LOAD_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA); LOAD_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB); @@ -456,8 +541,15 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState audio->chA.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesA(flags); audio->chB.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesB(flags); + GBASerializedAudioFlags2 flags2; + LOAD_32(flags2, 0, &state->audio.gbaFlags2); + audio->sampleIndex = GBASerializedAudioFlags2GetSampleIndex(flags2); + uint32_t when; LOAD_32(when, 0, &state->audio.nextSample); + if (state->versionMagic < 0x01000007) { + audio->lastSample = when - SAMPLE_INTERVAL; + } mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when); } diff --git a/src/gba/extra/audio-mixer.c b/src/gba/extra/audio-mixer.c index fe9ed6901..503fdc8ad 100644 --- a/src/gba/extra/audio-mixer.c +++ b/src/gba/extra/audio-mixer.c @@ -125,7 +125,7 @@ static void _stepSample(struct GBAAudioMixer* mixer, struct GBAMP2kTrack* track) for (nSample = 0; nSample < updates; ++nSample) { int8_t sample = memory->load8(cpu, sampleBase + sampleI, 0); - struct GBAStereoSample stereo = { + struct mStereoSample stereo = { (sample * track->channel->leftVolume * track->channel->envelopeV) >> 9, (sample * track->channel->rightVolume * track->channel->envelopeV) >> 9 }; @@ -277,7 +277,7 @@ void _mp2kStep(struct GBAAudioMixer* mixer) { uint32_t interval = mixer->p->sampleInterval / OVERSAMPLE; int i; for (i = 0; i < OVERSAMPLE; ++i) { - struct GBAStereoSample sample = {0}; + struct mStereoSample sample = {0}; size_t track; for (track = 0; track < MP2K_MAX_SOUND_CHANNELS; ++track) { if (!mixer->activeTracks[track].channel->status) { diff --git a/src/gba/gba.c b/src/gba/gba.c index de79671c5..1c0de38e4 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -399,14 +399,15 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { } GBAUnloadROM(gba); gba->romVf = vf; + gba->isPristine = true; gba->pristineRomSize = vf->size(vf); vf->seek(vf, 0, SEEK_SET); if (gba->pristineRomSize > SIZE_CART0) { - gba->isPristine = false; char ident; vf->seek(vf, 0xAC, SEEK_SET); vf->read(vf, &ident, 1); if (ident == 'M') { + gba->isPristine = false; gba->memory.romSize = 0x01000000; #ifdef FIXED_ROM_BUFFER gba->memory.rom = romBuffer; @@ -417,8 +418,8 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { gba->memory.rom = vf->map(vf, SIZE_CART0, MAP_READ); gba->memory.romSize = SIZE_CART0; } + gba->pristineRomSize = SIZE_CART0; } else { - gba->isPristine = true; gba->memory.rom = vf->map(vf, gba->pristineRomSize, MAP_READ); gba->memory.romSize = gba->pristineRomSize; } @@ -583,6 +584,10 @@ void GBADebug(struct GBA* gba, uint16_t flags) { } bool GBAIsROM(struct VFile* vf) { + if (!vf) { + return false; + } + #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { @@ -594,9 +599,6 @@ bool GBAIsROM(struct VFile* vf) { return isGBA; } #endif - if (!vf) { - return false; - } uint8_t signature[sizeof(GBA_ROM_MAGIC) + sizeof(GBA_ROM_MAGIC2)]; if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) { diff --git a/src/gba/io.c b/src/gba/io.c index 0cd3c4e25..ca0cbe5b5 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -199,7 +199,7 @@ const char* const GBAIORegisterNames[] = { "IME" }; -static const int _isValidRegister[REG_MAX >> 1] = { +static const int _isValidRegister[REG_INTERNAL_MAX >> 1] = { // Video 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -238,10 +238,12 @@ static const int _isValidRegister[REG_MAX >> 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Interrupts - 1, 1, 1, 0, 1 + 1, 1, 1, 0, 1, 0, 0, 0, + // Internal registers + 1, 1 }; -static const int _isRSpecialRegister[REG_MAX >> 1] = { +static const int _isRSpecialRegister[REG_INTERNAL_MAX >> 1] = { // Video 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, @@ -280,9 +282,12 @@ static const int _isRSpecialRegister[REG_MAX >> 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Interrupts + 0, 0, 0, 0, 0, 0, 0, 0, + // Internal registers + 1, 1 }; -static const int _isWSpecialRegister[REG_MAX >> 1] = { +static const int _isWSpecialRegister[REG_INTERNAL_MAX >> 1] = { // Video 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -321,7 +326,9 @@ static const int _isWSpecialRegister[REG_MAX >> 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Interrupts - 1, 1, 0, 0, 1 + 1, 1, 0, 0, 1, 0, 0, 0, + // Internal registers + 1, 1 }; void GBAIOInit(struct GBA* gba) { @@ -333,6 +340,8 @@ void GBAIOInit(struct GBA* gba) { gba->memory.io[REG_BG2PD >> 1] = 0x100; gba->memory.io[REG_BG3PA >> 1] = 0x100; gba->memory.io[REG_BG3PD >> 1] = 0x100; + gba->memory.io[REG_INTERNAL_EXWAITCNT_LO >> 1] = 0x20; + gba->memory.io[REG_INTERNAL_EXWAITCNT_HI >> 1] = 0xD00; if (!gba->biosVf) { gba->memory.io[REG_VCOUNT >> 1] = 0x7E; @@ -416,6 +425,7 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { value |= gba->memory.io[REG_SOUNDCNT_X >> 1] & 0xF; break; case REG_SOUNDBIAS: + value &= 0xC3FE; GBAAudioWriteSOUNDBIAS(&gba->audio, value); break; @@ -573,6 +583,12 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { case REG_MAX: // Some bad interrupt libraries will write to this break; + case REG_EXWAITCNT_HI: + // This register sits outside of the normal I/O block, so we need to stash it somewhere unused + address = REG_INTERNAL_EXWAITCNT_HI; + value &= 0xFF00; + GBAAdjustEWRAMWaitstates(gba, value); + break; case REG_DEBUG_ENABLE: gba->debug = value == 0xC0DE; return; @@ -842,7 +858,6 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { gba->memory.io[REG_JOYSTAT >> 1] &= ~JOYSTAT_RECV; break; - case REG_SOUNDBIAS: case REG_POSTFLG: mLOG(GBA_IO, STUB, "Stub I/O register read: %03x", address); break; @@ -897,6 +912,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { case REG_BLDALPHA: case REG_SOUNDCNT_HI: case REG_SOUNDCNT_X: + case REG_SOUNDBIAS: case REG_DMA0CNT_HI: case REG_DMA1CNT_HI: case REG_DMA2CNT_HI: @@ -935,6 +951,11 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { case 0x206: mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address); return 0; + // These registers sit outside of the normal I/O block, so we need to stash them somewhere unused + case REG_EXWAITCNT_LO: + case REG_EXWAITCNT_HI: + address += REG_INTERNAL_EXWAITCNT_LO - REG_EXWAITCNT_LO; + break; case REG_DEBUG_ENABLE: if (gba->debug) { return 0x1DEA; @@ -949,7 +970,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) { int i; - for (i = 0; i < REG_MAX; i += 2) { + for (i = 0; i < REG_INTERNAL_MAX; i += 2) { if (_isRSpecialRegister[i >> 1]) { STORE_16(gba->memory.io[i >> 1], i, state->io); } else if (_isValidRegister[i >> 1]) { @@ -990,6 +1011,9 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { GBAIOWrite(gba, i, reg); } } + if (state->versionMagic >= 0x01000006) { + GBAIOWrite(gba, REG_EXWAITCNT_HI, gba->memory.io[REG_INTERNAL_EXWAITCNT_HI >> 1]); + } uint32_t when; for (i = 0; i < 4; ++i) { diff --git a/src/gba/memory.c b/src/gba/memory.c index fddef9223..1878e7bb2 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -127,6 +127,7 @@ void GBAMemoryReset(struct GBA* gba) { memset(gba->memory.io, 0, sizeof(gba->memory.io)); GBAAdjustWaitstates(gba, 0); + GBAAdjustEWRAMWaitstates(gba, 0x0D00); gba->memory.activeRegion = -1; gba->memory.agbPrintProtect = 0; @@ -1239,9 +1240,13 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o if ((address & 0x0001FFFF) < SIZE_VRAM) { LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); STORE_32(value, address & 0x0001FFFC, gba->video.vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFC); + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) | 2); } else { LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); STORE_32(value, address & 0x00017FFC, gba->video.vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFC); + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) | 2); } break; case REGION_OAM: @@ -1308,9 +1313,11 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o if ((address & 0x0001FFFF) < SIZE_VRAM) { LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram); STORE_16(value, address & 0x0001FFFE, gba->video.vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); } else { LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram); STORE_16(value, address & 0x00017FFE, gba->video.vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); } break; case REGION_OAM: @@ -1708,6 +1715,31 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { } } +void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters) { + struct GBAMemory* memory = &gba->memory; + struct ARMCore* cpu = gba->cpu; + + int wait = 15 - ((parameters >> 8) & 0xF); + if (wait) { + memory->waitstatesNonseq16[REGION_WORKING_RAM] = wait; + memory->waitstatesSeq16[REGION_WORKING_RAM] = wait; + memory->waitstatesNonseq32[REGION_WORKING_RAM] = 2 * wait + 1; + memory->waitstatesSeq32[REGION_WORKING_RAM] = 2 * wait + 1; + + cpu->memory.activeSeqCycles32 = memory->waitstatesSeq32[memory->activeRegion]; + cpu->memory.activeSeqCycles16 = memory->waitstatesSeq16[memory->activeRegion]; + + cpu->memory.activeNonseqCycles32 = memory->waitstatesNonseq32[memory->activeRegion]; + cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[memory->activeRegion]; + } else { + if (!gba->hardCrash) { + mLOG(GBA_MEM, GAME_ERROR, "Cannot set EWRAM to 0 waitstates"); + } else { + mLOG(GBA_MEM, FATAL, "Cannot set EWRAM to 0 waitstates"); + } + } +} + int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; diff --git a/src/gba/overrides.c b/src/gba/overrides.c index 2e0ce10de..823957d5c 100644 --- a/src/gba/overrides.c +++ b/src/gba/overrides.c @@ -70,6 +70,9 @@ static const struct GBACartridgeOverride _overrides[] = { { "AI2E", SAVEDATA_FORCE_NONE, HW_NONE, IDLE_LOOP_NONE, false }, { "AI2P", SAVEDATA_FORCE_NONE, HW_NONE, IDLE_LOOP_NONE, false }, + // Game Boy Wars Advance 1+2 + { "BGWJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false }, + // Golden Sun: The Lost Age { "AGFE", SAVEDATA_FLASH512, HW_NONE, 0x801353A, false }, diff --git a/src/gba/renderers/cache-set.c b/src/gba/renderers/cache-set.c index 95093ae9a..51512721d 100644 --- a/src/gba/renderers/cache-set.c +++ b/src/gba/renderers/cache-set.c @@ -160,7 +160,7 @@ static void GBAVideoCacheWriteBGCNT(struct mCacheSet* cache, size_t bg, uint16_t int size = GBARegisterBGCNTGetSize(value); int tilesWide = 0; int tilesHigh = 0; - mMapCacheSystemInfo sysconfig = 0; + mMapCacheSystemInfo sysconfig = mMapCacheSystemInfoSetWriteAlign(0, 1); if (map->mapParser == mapParser0) { map->tileCache = mTileCacheSetGetPointer(&cache->tiles, p); sysconfig = mMapCacheSystemInfoSetPaletteBPP(sysconfig, 2 + p); diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index 02374d8e4..75b7b7ced 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -85,25 +85,37 @@ static const char* const _vertexShader = "}"; static const char* const _renderTile16 = + "#ifndef VRAM_MASK\n" + "#define VRAM_MASK\n" + "#endif\n" "int renderTile(int tile, int paletteId, ivec2 localCoord) {\n" " int address = charBase + tile * 16 + (localCoord.x >> 2) + (localCoord.y << 1);\n" - " int halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0).r;\n" + " int halfrow = texelFetch(vram, ivec2(address & 255, (address >> 8) VRAM_MASK), 0).r;\n" " int entry = (halfrow >> (4 * (localCoord.x & 3))) & 15;\n" " if (entry == 0) {\n" " discard;\n" " }\n" " return paletteId * 16 + entry;\n" + "}\n" + "int mask(int tile) {\n" + " return tile & 31;\n" "}"; static const char* const _renderTile256 = + "#ifndef VRAM_MASK\n" + "#define VRAM_MASK\n" + "#endif\n" "int renderTile(int tile, int paletteId, ivec2 localCoord) {\n" " int address = charBase + tile * 32 + (localCoord.x >> 1) + (localCoord.y << 2);\n" - " int halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0).r;\n" + " int halfrow = texelFetch(vram, ivec2(address & 255, (address >> 8) VRAM_MASK), 0).r;\n" " int entry = (halfrow >> (8 * (localCoord.x & 1))) & 255;\n" " if (entry == 0) {\n" " discard;\n" " }\n" " return entry;\n" + "}" + "int mask(int tile) {\n" + " return tile & 15;\n" "}"; static const struct GBAVideoGLUniform _uniformsMode0[] = { @@ -165,9 +177,9 @@ static const char* const _renderMode0 = " coord.y ^= 7;\n" " }\n" " int tile = map & 1023;\n" - " int paletteEntry = renderTile(tile, map >> 12, coord & 7);\n" + " int paletteEntry = renderTile(tile, (map >> 12) & 15, coord & 7);\n" " color = texelFetch(palette, ivec2(paletteEntry, int(texCoord.y)), 0);\n" - "}"; + "}\n"; static const char* const _fetchTileOverflow = "int fetchTile(ivec2 coord) {\n" @@ -192,6 +204,7 @@ static const struct GBAVideoGLUniform _uniformsMode2[] = { { "vram", GBA_GL_BG_VRAM, }, { "palette", GBA_GL_BG_PALETTE, }, { "screenBase", GBA_GL_BG_SCREENBASE, }, + { "oldCharBase", GBA_GL_BG_OLDCHARBASE, }, { "charBase", GBA_GL_BG_CHARBASE, }, { "size", GBA_GL_BG_SIZE, }, { "offset", GBA_GL_BG_OFFSET, }, @@ -228,6 +241,7 @@ static const char* const _renderMode2 = "uniform isampler2D vram;\n" "uniform sampler2D palette;\n" "uniform int screenBase;\n" + "uniform ivec2 oldCharBase;\n" "uniform int charBase;\n" "uniform int size;\n" "uniform ivec4 transform[160];\n" @@ -244,7 +258,17 @@ static const char* const _renderMode2 = " int mapAddress = screenBase + (map >> 1);\n" " int twomaps = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0).r;\n" " int tile = (twomaps >> (8 * (map & 1))) & 255;\n" - " int address = charBase + tile * 32 + ((coord.x >> 9) & 3) + ((coord.y >> 6) & 0x1C);\n" + " int newCharBase = charBase;\n" + " if (newCharBase != oldCharBase.x) {\n" + " int y = int(texCoord.y);\n" + // If the charbase has changed (and the scale is greater than 1), we might still be drawing + // the tile associated with the pixel above us. If we're still on that tile, we want to use + // the charbase associated with it instead of the new one. Cf. https://mgba.io/i/1631 + " if (y == oldCharBase.y && transform[y - 1].w >> 11 == coord.y >> 11) {\n" + " newCharBase = oldCharBase.x;\n" + " }\n" + " }\n" + " int address = newCharBase + tile * 32 + ((coord.x >> 9) & 3) + ((coord.y >> 6) & 0x1C);\n" " int halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0).r;\n" " int entry = (halfrow >> (8 * ((coord.x >> 8) & 1))) & 255;\n" " if (entry == 0) {\n" @@ -415,6 +439,7 @@ static const struct GBAVideoGLUniform _uniformsObj[] = { { "objwin", GBA_GL_OBJ_OBJWIN, }, { "mosaic", GBA_GL_OBJ_MOSAIC, }, { "cyclesRemaining", GBA_GL_OBJ_CYCLES, }, + { "tile", GBA_GL_OBJ_TILE, }, { 0 } }; @@ -424,6 +449,7 @@ static const char* const _renderObj = "uniform isampler2D vram;\n" "uniform sampler2D palette;\n" "uniform int charBase;\n" + "uniform int tile;\n" "uniform int stride;\n" "uniform int localPalette;\n" "uniform ivec4 inflags;\n" @@ -437,6 +463,8 @@ static const char* const _renderObj = "OUT(2) out ivec4 window;\n" "int renderTile(int tile, int paletteId, ivec2 localCoord);\n" + "int mask(int);\n" + "#define VRAM_MASK & 191\n" "void main() {\n" " vec2 incoord = texCoord;\n" @@ -461,12 +489,12 @@ static const char* const _renderObj = " if ((coord & ~(dims.xy - 1)) != ivec2(0, 0)) {\n" " discard;\n" " }\n" - " int paletteEntry = renderTile((coord.x >> 3) + (coord.y >> 3) * stride, localPalette, coord & 7);\n" + " int paletteEntry = renderTile(mask((coord.x >> 3) + tile) + (coord.y >> 3) * stride, localPalette, coord & 7);\n" " color = texelFetch(palette, ivec2(paletteEntry + 256, int(texCoord.y) + mosaic.w), 0);\n" " flags = inflags;\n" " gl_FragDepth = float(flags.x) / 16.;\n" " window = ivec4(objwin, 0);\n" - "}"; + "}\n"; static const struct GBAVideoGLUniform _uniformsObjPriority[] = { { "loc", GBA_GL_VS_LOC, }, @@ -996,42 +1024,34 @@ uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, case REG_BG0HOFS: value &= 0x01FF; glRenderer->bg[0].x = value; - dirty = false; break; case REG_BG0VOFS: value &= 0x01FF; glRenderer->bg[0].y = value; - dirty = false; break; case REG_BG1HOFS: value &= 0x01FF; glRenderer->bg[1].x = value; - dirty = false; break; case REG_BG1VOFS: value &= 0x01FF; glRenderer->bg[1].y = value; - dirty = false; break; case REG_BG2HOFS: value &= 0x01FF; glRenderer->bg[2].x = value; - dirty = false; break; case REG_BG2VOFS: value &= 0x01FF; glRenderer->bg[2].y = value; - dirty = false; break; case REG_BG3HOFS: value &= 0x01FF; glRenderer->bg[3].x = value; - dirty = false; break; case REG_BG3VOFS: value &= 0x01FF; glRenderer->bg[3].y = value; - dirty = false; break; case REG_BG2PA: glRenderer->bg[2].affine.dx = value; @@ -1330,6 +1350,7 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { if (_needsVramUpload(glRenderer, y) || glRenderer->oamDirty || glRenderer->regsDirty) { if (glRenderer->firstY >= 0) { _drawScanlines(glRenderer, y - 1); + glRenderer->firstY = y; glBindVertexArray(0); } } @@ -1604,6 +1625,7 @@ static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer) static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value) { bg->priority = GBARegisterBGCNTGetPriority(value); + bg->oldCharBase = bg->charBase; bg->charBase = GBARegisterBGCNTGetCharBase(value) << 13; bg->mosaic = GBARegisterBGCNTGetMosaic(value); bg->multipalette = GBARegisterBGCNTGet256Color(value); @@ -1709,6 +1731,15 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB int align = GBAObjAttributesAIs256Color(sprite->a) && !GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt); unsigned charBase = (BASE_TILE >> 1) + (GBAObjAttributesCGetTile(sprite->c) & ~align) * 0x10; + unsigned tile = 0; + if (!GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt)) { + if (GBAObjAttributesAIs256Color(sprite->a)) { + tile = (charBase >> 5) & 0xF; + } else { + tile = (charBase >> 4) & 0x1F; + } + charBase &= ~0x1FF; + } int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> 3) : (0x20 >> GBAObjAttributesAGet256Color(sprite->a)); int totalWidth = width; @@ -1744,6 +1775,7 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB glUniform1i(uniforms[GBA_GL_OBJ_VRAM], 0); glUniform1i(uniforms[GBA_GL_OBJ_PALETTE], 1); glUniform1i(uniforms[GBA_GL_OBJ_CHARBASE], charBase); + glUniform1i(uniforms[GBA_GL_OBJ_TILE], tile); glUniform1i(uniforms[GBA_GL_OBJ_STRIDE], stride); glUniform1i(uniforms[GBA_GL_OBJ_LOCALPALETTE], GBAObjAttributesCGetPalette(sprite->c)); glUniform4i(uniforms[GBA_GL_OBJ_INFLAGS], GBAObjAttributesCGetPriority(sprite->c), @@ -1770,9 +1802,9 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB glUniformMatrix2fv(uniforms[GBA_GL_OBJ_TRANSFORM], 1, GL_FALSE, (GLfloat[]) { flipX, 0, 0, flipY }); } glUniform4i(uniforms[GBA_GL_OBJ_DIMS], width, height, totalWidth, totalHeight); - glDisable(GL_STENCIL_TEST); if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN) { // OBJWIN writes do not affect pixel priority + glDisable(GL_STENCIL_TEST); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glStencilMask(0); @@ -1780,6 +1812,7 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB glUniform3i(uniforms[GBA_GL_OBJ_OBJWIN], window, renderer->bldb, renderer->bldy); glDrawBuffers(3, (GLenum[]) { GL_NONE, GL_NONE, GL_COLOR_ATTACHMENT2 }); } else { + glEnable(GL_STENCIL_TEST); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glStencilMask(1); @@ -1804,7 +1837,6 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB // Update the pixel priority for already-written pixels shader = &renderer->objShader[2]; uniforms = shader->uniforms; - glEnable(GL_STENCIL_TEST); glStencilFunc(GL_EQUAL, 1, 1); glUseProgram(shader->program); glDrawBuffers(2, (GLenum[]) { GL_NONE, GL_COLOR_ATTACHMENT1 }); @@ -1872,10 +1904,12 @@ void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, glBindVertexArray(shader->vao); _prepareTransform(renderer, background, uniforms, y); glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase); + glUniform2i(uniforms[GBA_GL_BG_OLDCHARBASE], background->oldCharBase, renderer->firstY); glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase); glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); + background->oldCharBase = background->charBase; } void GBAVideoGLRendererDrawBackgroundMode3(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { diff --git a/src/gba/renderers/software-obj.c b/src/gba/renderers/software-obj.c index 3fb364f56..8d67199ec 100644 --- a/src/gba/renderers/software-obj.c +++ b/src/gba/renderers/software-obj.c @@ -71,10 +71,10 @@ } #define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2); -#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * stride + (localY & 0x7) * 4; +#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * stride + (localY & 0x7) * 4 + maskHi; #define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \ - LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFE), vramBase); \ + LOAD_16(tileData, (yBase + ((xBase + charBase) & maskLo)) & 0x7FFE, vramBase); \ tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \ current = renderer->spriteLayer[outX]; \ if ((current & FLAG_ORDER_MASK) > flags) { \ @@ -86,7 +86,7 @@ } #define SPRITE_DRAW_PIXEL_16_NORMAL_OBJWIN(localX) \ - LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFE), vramBase); \ + LOAD_16(tileData, (yBase + ((xBase + charBase) & maskLo)) & 0x7FFE, vramBase); \ tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \ current = renderer->spriteLayer[outX]; \ if ((current & FLAG_ORDER_MASK) > flags) { \ @@ -99,17 +99,17 @@ } #define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \ - LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFE), vramBase); \ + LOAD_16(tileData, (yBase + ((xBase + charBase) & maskLo)) & 0x7FFE, vramBase); \ tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \ if (tileData) { \ renderer->row[outX] |= FLAG_OBJWIN; \ } #define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6); -#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * stride + (localY & 0x7) * 8; +#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * stride + (localY & 0x7) * 8 + maskHi; #define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \ - LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFE), vramBase); \ + LOAD_16(tileData, (yBase + ((xBase + charBase) & maskLo)) & 0x7FFE, vramBase); \ tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \ current = renderer->spriteLayer[outX]; \ if ((current & FLAG_ORDER_MASK) > flags) { \ @@ -121,7 +121,7 @@ } #define SPRITE_DRAW_PIXEL_256_NORMAL_OBJWIN(localX) \ - LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFE), vramBase); \ + LOAD_16(tileData, (yBase + ((xBase + charBase) & maskLo)) & 0x7FFE, vramBase); \ tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \ current = renderer->spriteLayer[outX]; \ if ((current & FLAG_ORDER_MASK) > flags) { \ @@ -134,7 +134,7 @@ } #define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \ - LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFE), vramBase); \ + LOAD_16(tileData, (yBase + ((xBase + charBase) & maskLo)) & 0x7FFE, vramBase); \ tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \ if (tileData) { \ renderer->row[outX] |= FLAG_OBJWIN; \ @@ -157,6 +157,8 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1]; unsigned align = GBAObjAttributesAIs256Color(sprite->a) && !GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt); unsigned charBase = (GBAObjAttributesCGetTile(sprite->c) & ~align) * 0x20; + unsigned maskLo = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? 0x7FFE : 0x3FE; + unsigned maskHi = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? 0 : charBase & 0x7C00; if (GBARegisterDISPCNTGetMode(renderer->dispcnt) >= 3 && GBAObjAttributesCGetTile(sprite->c) < 512) { return 0; } diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 35737a9e3..ef82fec93 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -141,30 +141,11 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) { for (i = 0; i < 4; ++i) { struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i]; + memset(bg, 0, sizeof(*bg)); bg->index = i; - bg->enabled = 0; - bg->priority = 0; - bg->charBase = 0; - bg->mosaic = 0; - bg->multipalette = 0; - bg->screenBase = 0; - bg->overflow = 0; - bg->size = 0; - bg->target1 = 0; - bg->target2 = 0; - bg->x = 0; - bg->y = 0; - bg->refx = 0; - bg->refy = 0; bg->dx = 256; - bg->dmx = 0; - bg->dy = 0; bg->dmy = 256; - bg->sx = 0; - bg->sy = 0; bg->yCache = -1; - bg->offsetX = 0; - bg->offsetY = 0; } } diff --git a/src/gba/serialize.c b/src/gba/serialize.c index a7294b6b5..c64d7c22b 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -15,7 +15,7 @@ #include MGBA_EXPORT const uint32_t GBASavestateMagic = 0x01000000; -MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000004; +MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000007; mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize"); diff --git a/src/gba/video.c b/src/gba/video.c index 659313575..7268f19de 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -377,7 +377,12 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState break; } uint32_t when; - LOAD_32(when, 0, &state->video.nextEvent); + if (state->versionMagic < 0x01000007) { + // This field was moved in v7 + LOAD_32(when, 0, &state->audio.lastSample); + } else { + LOAD_32(when, 0, &state->video.nextEvent); + } mTimingSchedule(&video->p->timing, &video->event, when); LOAD_16(video->vcount, REG_VCOUNT, state->io); diff --git a/src/platform/cmake/FindFeature.cmake b/src/platform/cmake/FindFeature.cmake index afb2393dd..93aefa2fe 100644 --- a/src/platform/cmake/FindFeature.cmake +++ b/src/platform/cmake/FindFeature.cmake @@ -1,4 +1,34 @@ include(FindPkgConfig) +macro(_export SUFFIX) + if(DEFINED ${REQUIRE}_${SUFFIX}) + set(${UREQUIRE}_${SUFFIX} ${${REQUIRE}_${SUFFIX}} PARENT_SCOPE) + elseif(DEFINED ${UREQUIRE}_${SUFFIX}) + set(${UREQUIRE}_${SUFFIX} ${${UREQUIRE}_${SUFFIX}} PARENT_SCOPE) + endif() +endmacro() + +macro(_exportLibraries SUFFIX) + set(IS_FRAMEWORK OFF) + set(LIBS) + if(DEFINED ${REQUIRE}_${SUFFIX}) + set(PREFIX ${REQUIRE}) + elseif(DEFINED ${UREQUIRE}_${SUFFIX}) + set(PREFIX ${UREQUIRE}) + endif() + foreach(LIB IN LISTS ${PREFIX}_${SUFFIX}) + if(LIB STREQUAL "-framework") + set(IS_FRAMEWORK ON) + elseif(IS_FRAMEWORK) + list(APPEND LIBS "-framework ${LIB}") + set(IS_FRAMEWORK OFF) + else() + list(APPEND LIBS ${LIB}) + endif() + endforeach() + unset(PREFIX) + set(${UREQUIRE}_${SUFFIX} ${LIBS} PARENT_SCOPE) +endmacro() + function(find_feature FEATURE_NAME FEATURE_REQUIRES) if (NOT ${FEATURE_NAME}) return() @@ -28,60 +58,22 @@ function(find_feature FEATURE_NAME FEATURE_REQUIRES) endif() if(${REQUIRE}_FOUND) string(TOUPPER ${REQUIRE} UREQUIRE) - if(DEFINED ${REQUIRE}_CFLAGS_OTHER) - set(${UREQUIRE}_CFLAGS_OTHER ${${REQUIRE}_CFLAGS_OTHER} PARENT_SCOPE) - elseif(DEFINED ${UREQUIRE}_CFLAGS_OTHER) - set(${UREQUIRE}_CFLAGS_OTHER ${${UREQUIRE}_CFLAGS_OTHER} PARENT_SCOPE) - endif() - if(DEFINED ${REQUIRE}_FOUND) - set(${UREQUIRE}_FOUND ${${REQUIRE}_FOUND} PARENT_SCOPE) - elseif(DEFINED ${UREQUIRE}_FOUND) - set(${UREQUIRE}_FOUND ${${UREQUIRE}_FOUND} PARENT_SCOPE) - endif() - if(DEFINED ${REQUIRE}_INCLUDE_DIRS) - set(${UREQUIRE}_INCLUDE_DIRS ${${REQUIRE}_INCLUDE_DIRS} PARENT_SCOPE) - elseif(DEFINED ${UREQUIRE}_INCLUDE_DIRS) - set(${UREQUIRE}_INCLUDE_DIRS ${${UREQUIRE}_INCLUDE_DIRS} PARENT_SCOPE) - endif() - if(DEFINED ${REQUIRE}_INCLUDE_DIR) - set(${UREQUIRE}_INCLUDE_DIR ${${REQUIRE}_INCLUDE_DIR} PARENT_SCOPE) - elseif(DEFINED ${UREQUIRE}_INCLUDE_DIR) - set(${UREQUIRE}_INCLUDE_DIR ${${UREQUIRE}_INCLUDE_DIR} PARENT_SCOPE) - endif() - if(DEFINED ${REQUIRE}_VERSION_STRING) - set(${UREQUIRE}_VERSION_STRING ${${REQUIRE}_VERSION_STRING} PARENT_SCOPE) - elseif(DEFINED ${UREQUIRE}_VERSION_STRING) - set(${UREQUIRE}_VERSION_STRING ${${UREQUIRE}_VERSION_STRING} PARENT_SCOPE) - endif() - if(DEFINED ${REQUIRE}_VERSION_MAJOR) - set(${UREQUIRE}_VERSION_MAJOR ${${REQUIRE}_VERSION_MAJOR} PARENT_SCOPE) - elseif(DEFINED ${UREQUIRE}_VERSION_MAJOR) - set(${UREQUIRE}_VERSION_MAJOR ${${UREQUIRE}_VERSION_MAJOR} PARENT_SCOPE) - endif() - if(DEFINED ${REQUIRE}_VERSION_MINOR) - set(${UREQUIRE}_VERSION_MINOR ${${REQUIRE}_VERSION_MINOR} PARENT_SCOPE) - elseif(DEFINED ${UREQUIRE}_VERSION_MINOR) - set(${UREQUIRE}_VERSION_MINOR ${${UREQUIRE}_VERSION_MINOR} PARENT_SCOPE) - endif() + _export(CFLAGS_OTHER) + _export(FOUND) + _export(INCLUDE_DIRS) + _export(INCLUDE_DIR) + _export(VERSION_STRING) + _export(VERSION_MAJOR) + _export(VERSION_MINOR) if (APPLE) - set(IS_FRAMEWORK OFF) - set(LIBS) - foreach(LIB IN LISTS ${REQUIRE}_LIBRARIES) - if(LIB STREQUAL "-framework") - set(IS_FRAMEWORK ON) - elseif(IS_FRAMEWORK) - list(APPEND LIBS "-framework ${LIB}") - set(IS_FRAMEWORK OFF) - else() - list(APPEND LIBS ${LIB}) - endif() - endforeach() - set(${UREQUIRE}_LIBRARIES ${LIBS} PARENT_SCOPE) + _exportLibraries(LIBRARIES) + _exportLibraries(STATIC_LIBRARIES) else() - set(${UREQUIRE}_LIBRARIES ${${REQUIRE}_LIBRARIES} PARENT_SCOPE) + _export(LIBRARIES) + _export(STATIC_LIBRARIES) endif() - set(${UREQUIRE}_LIBRARY_DIRS ${${REQUIRE}_LIBRARY_DIRS} PARENT_SCOPE) - set(${UREQUIRE}_LDFLAGS_OTHER ${${REQUIRE}_LDFLAGS_OTHER} PARENT_SCOPE) + _export(LIBRARY_DIRS) + _export(LDFLAGS_OTHER) set(FOUND ON) break() endif() diff --git a/src/platform/psp2/psp2-context.c b/src/platform/psp2/psp2-context.c index 82f0c6029..f860bc4a1 100644 --- a/src/platform/psp2/psp2-context.c +++ b/src/platform/psp2/psp2-context.c @@ -88,7 +88,7 @@ static vita2d_texture* backdrop = 0; #define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 16) static struct mPSP2AudioContext { - struct GBAStereoSample buffer[PSP2_AUDIO_BUFFER_SIZE]; + struct mStereoSample buffer[PSP2_AUDIO_BUFFER_SIZE]; size_t writeOffset; size_t readOffset; size_t samples; @@ -255,7 +255,7 @@ static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* rig } ConditionWait(&audioContext.cond, &audioContext.mutex); } - struct GBAStereoSample* samples = &audioContext.buffer[audioContext.writeOffset]; + struct mStereoSample* samples = &audioContext.buffer[audioContext.writeOffset]; blip_read_samples(left, &samples[0].left, PSP2_SAMPLES, true); blip_read_samples(right, &samples[0].right, PSP2_SAMPLES, true); audioContext.samples += PSP2_SAMPLES; diff --git a/src/platform/qt/ApplicationUpdatePrompt.cpp b/src/platform/qt/ApplicationUpdatePrompt.cpp index 7f7b375c7..adadbee29 100644 --- a/src/platform/qt/ApplicationUpdatePrompt.cpp +++ b/src/platform/qt/ApplicationUpdatePrompt.cpp @@ -24,13 +24,23 @@ ApplicationUpdatePrompt::ApplicationUpdatePrompt(QWidget* parent) ApplicationUpdater* updater = GBAApp::app()->updater(); ApplicationUpdater::UpdateInfo info = updater->updateInfo(); QString updateText(tr("An update to %1 is available.\n").arg(QLatin1String(projectName))); + bool available; #if defined(Q_OS_WIN) || defined(Q_OS_MAC) - updateText += tr("\nDo you want to download and install it now? You will need to restart the emulator when the download is complete."); - m_okDownload = connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &ApplicationUpdatePrompt::startUpdate); + available = true; +#elif defined(Q_OS_LINUX) + QString path = QCoreApplication::applicationDirPath(); + QFileInfo finfo(path + "/../../AppRun"); + available = finfo.exists() && finfo.isExecutable(); #else - updateText += tr("\nAuto-update is not available on this platform. If you wish to update you will need to do it manually."); - connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &QWidget::close); + available = false; #endif + if (available) { + updateText += tr("\nDo you want to download and install it now? You will need to restart the emulator when the download is complete."); + m_okDownload = connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &ApplicationUpdatePrompt::startUpdate); + } else { + updateText += tr("\nAuto-update is not available on this platform. If you wish to update you will need to do it manually."); + connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &QWidget::close); + } m_ui.text->setText(updateText); m_ui.details->setText(tr("Current version: %1\nNew version: %2\nDownload size: %3") .arg(QLatin1String(projectVersion)) diff --git a/src/platform/qt/ApplicationUpdater.cpp b/src/platform/qt/ApplicationUpdater.cpp index 15334361e..ce752ecce 100644 --- a/src/platform/qt/ApplicationUpdater.cpp +++ b/src/platform/qt/ApplicationUpdater.cpp @@ -150,7 +150,15 @@ const char* ApplicationUpdater::platform() { return uninstallInfo.exists() ? "win32-installer" : "win32"; #endif #elif defined(Q_OS_MACOS) +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + // Modern macOS build + return "macos"; +#else + // Legacy "OS X" build return "osx"; +#endif +#elif defined(Q_OS_LINUX) && defined(__x86_64__) + return "appimage-x64"; #else // Return one that will be up to date, but we can't download return "win64"; diff --git a/src/platform/qt/AudioDevice.cpp b/src/platform/qt/AudioDevice.cpp index 9baf12712..74f863444 100644 --- a/src/platform/qt/AudioDevice.cpp +++ b/src/platform/qt/AudioDevice.cpp @@ -45,17 +45,17 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) { return 0; } - maxSize /= sizeof(GBAStereoSample); + maxSize /= sizeof(mStereoSample); mCoreSyncLockAudio(&m_context->impl->sync); int available = std::min({ blip_samples_avail(m_context->core->getAudioChannel(m_context->core, 0)), maxSize, std::numeric_limits::max() }); - blip_read_samples(m_context->core->getAudioChannel(m_context->core, 0), &reinterpret_cast(data)->left, available, true); - blip_read_samples(m_context->core->getAudioChannel(m_context->core, 1), &reinterpret_cast(data)->right, available, true); + blip_read_samples(m_context->core->getAudioChannel(m_context->core, 0), &reinterpret_cast(data)->left, available, true); + blip_read_samples(m_context->core->getAudioChannel(m_context->core, 1), &reinterpret_cast(data)->right, available, true); mCoreSyncConsumeAudio(&m_context->impl->sync); - return available * sizeof(GBAStereoSample); + return available * sizeof(mStereoSample); } qint64 AudioDevice::writeData(const char*, qint64) { diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index bf8948573..6f26c9c5a 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -250,12 +250,13 @@ endif() if(ENABLE_SCRIPTING) list(APPEND SOURCE_FILES - ScriptingController.cpp - ScriptingTextBuffer.cpp - ScriptingView.cpp) + scripting/ScriptingController.cpp + scripting/ScriptingTextBuffer.cpp + scripting/ScriptingTextBufferModel.cpp + scripting/ScriptingView.cpp) list(APPEND UI_FILES - ScriptingView.ui) + scripting/ScriptingView.ui) endif() if(TARGET Qt6::Core) @@ -265,7 +266,14 @@ else() endif() if(BUILD_UPDATER) - file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/updater.qrc INPUT ${CMAKE_CURRENT_SOURCE_DIR}/updater.qrc.in) + if(DEFINED CMAKE_CONFIGURATION_TYPES) + # Required for e.g. MSVC + file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/updater.qrc INPUT ${CMAKE_CURRENT_SOURCE_DIR}/updater.qrc.in) + else() + # Required for qt_add_resources to manage dependencies properly + # TODO: Figure out how to do this with MSVC too + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/updater-config.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/updater.qrc) + endif() if(TARGET Qt6::Core) qt_add_resources(UPDATER_RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/updater.qrc) else() @@ -304,7 +312,6 @@ if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY) install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/shaders DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) endif() if(ENABLE_SCRIPTING AND USE_LUA) - message(STATUS ${CMAKE_SOURCE_DIR}/res/scripts) install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/scripts DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) endif() install(FILES ${CMAKE_SOURCE_DIR}/res/nointro.dat DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) @@ -395,7 +402,10 @@ if(QT_STATIC) find_package(Cups) find_package(${QT}PrintSupport) list(APPEND QT_LIBRARIES Cups ${QT}::PrintSupport ${QT}::QCocoaIntegrationPlugin ${QT}::CoreAudioPlugin ${QT}::AVFServicePlugin ${QT}::QCocoaPrinterSupportPlugin) - list(APPEND QT_LIBRARIES ${QT}AccessibilitySupport ${QT}CglSupport ${QT}ClipboardSupport ${QT}FontDatabaseSupport ${QT}GraphicsSupport ${QT}ThemeSupport) + list(APPEND QT_LIBRARIES ${QT}AccessibilitySupport ${QT}ClipboardSupport ${QT}FontDatabaseSupport ${QT}GraphicsSupport ${QT}ThemeSupport) + if(CMAKE_SYSTEM_VERSION VERSION_LESS "19.0") + list(APPEND QT_LIBRARIES ${QT}CglSupport) + endif() list(APPEND QT_LIBRARIES "-framework AVFoundation" "-framework CoreMedia" "-framework SystemConfiguration" "-framework Security") set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE}") elseif(UNIX) diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 4fc4037e0..767cd277f 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -136,7 +136,7 @@ ConfigController::ConfigController(QObject* parent) m_subparsers[1].usage = "Frontend options:\n" " --ecard FILE Scan an e-Reader card in the first loaded game\n" - " Can be paassed multiple times for multiple cards\n" + " Can be passed multiple times for multiple cards\n" " --mb FILE Boot a multiboot image with FILE inserted into the ROM slot"; m_subparsers[1].parse = nullptr; m_subparsers[1].parseLong = [](struct mSubParser* parser, const char* option, const char* arg) { diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 4491a0f33..8814f4443 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -287,6 +287,7 @@ void CoreController::loadConfig(ConfigController* config) { m_fastForwardMute = config->getOption("fastForwardMute", -1).toInt(); mCoreConfigCopyValue(&m_threadContext.core->config, config->config(), "volume"); mCoreConfigCopyValue(&m_threadContext.core->config, config->config(), "mute"); + m_preload = config->getOption("preload").toInt(); int playerId = m_multiplayer->playerId(this) + 1; QVariant savePlayerId = config->getOption("savePlayerId"); @@ -829,7 +830,11 @@ void CoreController::replaceGame(const QString& path) { QString fname = info.canonicalFilePath(); Interrupter interrupter(this); mDirectorySetDetachBase(&m_threadContext.core->dirs); - mCoreLoadFile(m_threadContext.core, fname.toUtf8().constData()); + if (m_preload) { + mCorePreloadFile(m_threadContext.core, fname.toUtf8().constData()); + } else { + mCoreLoadFile(m_threadContext.core, fname.toUtf8().constData()); + } updateROMInfo(); } @@ -893,6 +898,11 @@ void CoreController::setFakeEpoch(const QDateTime& time) { m_threadContext.core->rtc.value = time.toMSecsSinceEpoch(); } +void CoreController::setTimeOffset(qint64 offset) { + m_threadContext.core->rtc.override = RTC_WALLCLOCK_OFFSET; + m_threadContext.core->rtc.value = offset * 1000LL; +} + void CoreController::scanCard(const QString& path) { #ifdef M_CORE_GBA QImage image(path); diff --git a/src/platform/qt/CoreController.h b/src/platform/qt/CoreController.h index 03c929c48..3e5ca6b4d 100644 --- a/src/platform/qt/CoreController.h +++ b/src/platform/qt/CoreController.h @@ -174,6 +174,7 @@ public slots: void setRealTime(); void setFixedTime(const QDateTime& time); void setFakeEpoch(const QDateTime& time); + void setTimeOffset(qint64 offset); void importSharkport(const QString& path); void exportSharkport(const QString& path); @@ -240,6 +241,7 @@ private: mCoreThread m_threadContext{}; bool m_patched = false; + bool m_preload = false; uint32_t m_crc32; QString m_internalTitle; diff --git a/src/platform/qt/CoreManager.cpp b/src/platform/qt/CoreManager.cpp index 05fccf1c2..b5fc3e413 100644 --- a/src/platform/qt/CoreManager.cpp +++ b/src/platform/qt/CoreManager.cpp @@ -62,13 +62,16 @@ CoreController* CoreManager::loadGame(const QString& path) { VFile* vfOriginal = VDirFindFirst(archive, [](VFile* vf) { return mCoreIsCompatible(vf) != mPLATFORM_NONE; }); - ssize_t size; - if (vfOriginal && (size = vfOriginal->size(vfOriginal)) > 0) { - void* mem = vfOriginal->map(vfOriginal, size, MAP_READ); - vf = VFileMemChunk(mem, size); - vfOriginal->unmap(vfOriginal, mem, size); + if (vfOriginal) { + ssize_t size = vfOriginal->size(vfOriginal); + if (size > 0) { + void* mem = vfOriginal->map(vfOriginal, size, MAP_READ); + vf = VFileMemChunk(mem, size); + vfOriginal->unmap(vfOriginal, mem, size); + } vfOriginal->close(vfOriginal); } + archive->close(archive); } QDir dir(info.dir()); if (!vf) { diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 678f9d612..21703dd5f 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -56,10 +56,13 @@ uint qHash(const QSurfaceFormat& format, uint seed) { } void mGLWidget::initializeGL() { - m_vao.create(); - m_program.create(); + m_vao = std::make_unique(); + m_vao->create(); - m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, R"(#version 150 core + m_program = std::make_unique(); + m_program->create(); + + m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, R"(#version 150 core in vec4 position; out vec2 texCoord; void main() { @@ -67,7 +70,7 @@ void mGLWidget::initializeGL() { texCoord = (position.st + 1.0) * 0.5; })"); - m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, R"(#version 150 core + m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, R"(#version 150 core in vec2 texCoord; out vec4 color; uniform sampler2D tex; @@ -75,38 +78,51 @@ void mGLWidget::initializeGL() { color = vec4(texture(tex, texCoord).rgb, 1.0); })"); - m_program.link(); - m_program.setUniformValue("tex", 0); - m_positionLocation = m_program.attributeLocation("position"); + m_program->link(); + m_program->setUniformValue("tex", 0); + m_positionLocation = m_program->attributeLocation("position"); + + m_vaoDone = false; connect(&m_refresh, &QTimer::timeout, this, static_cast(&QWidget::update)); } -void mGLWidget::finalizeVAO() { +bool mGLWidget::finalizeVAO() { + if (!context() || !m_vao) { + return false; + } QOpenGLFunctions_Baseline* fn = context()->versionFunctions(); + if (!fn) { + return false; + } fn->glGetError(); // Clear the error - m_vao.bind(); + m_vao->bind(); fn->glBindBuffer(GL_ARRAY_BUFFER, m_vbo); fn->glEnableVertexAttribArray(m_positionLocation); fn->glVertexAttribPointer(m_positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); - m_vao.release(); + m_vao->release(); if (fn->glGetError() == GL_NO_ERROR) { m_vaoDone = true; } + return m_vaoDone; +} + +void mGLWidget::reset() { + m_vaoDone = false; } void mGLWidget::paintGL() { - if (!m_vaoDone) { - finalizeVAO(); + if (!m_vaoDone && !finalizeVAO()) { + return; } QOpenGLFunctions_Baseline* fn = context()->versionFunctions(); - m_program.bind(); - m_vao.bind(); + m_program->bind(); + m_vao->bind(); fn->glBindTexture(GL_TEXTURE_2D, m_tex); fn->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); fn->glBindTexture(GL_TEXTURE_2D, 0); - m_vao.release(); - m_program.release(); + m_vao->release(); + m_program->release(); // TODO: Better timing ++m_refreshResidue; @@ -202,7 +218,12 @@ void DisplayGL::startDrawing(std::shared_ptr controller) { CoreController::Interrupter interrupter(controller); QMetaObject::invokeMethod(m_painter.get(), "start"); if (!m_gl) { - setUpdatesEnabled(false); + if (QGuiApplication::platformName() == "windows") { + setUpdatesEnabled(false); + } + } else { + show(); + m_gl->reset(); } } @@ -265,6 +286,9 @@ void DisplayGL::stopDrawing() { m_hasStarted = false; CoreController::Interrupter interrupter(m_context); QMetaObject::invokeMethod(m_painter.get(), "stop", Qt::BlockingQueuedConnection); + if (m_gl) { + hide(); + } setUpdatesEnabled(true); } m_context.reset(); @@ -274,9 +298,7 @@ void DisplayGL::pauseDrawing() { if (m_hasStarted) { m_isDrawing = false; QMetaObject::invokeMethod(m_painter.get(), "pause", Qt::BlockingQueuedConnection); -#ifndef Q_OS_MAC setUpdatesEnabled(true); -#endif } } @@ -284,11 +306,9 @@ void DisplayGL::unpauseDrawing() { if (m_hasStarted) { m_isDrawing = true; QMetaObject::invokeMethod(m_painter.get(), "unpause", Qt::BlockingQueuedConnection); -#ifndef Q_OS_MAC - if (!m_gl) { + if (!m_gl && QGuiApplication::platformName() == "windows") { setUpdatesEnabled(false); } -#endif } } diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 54256ed30..dfaee22f5 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -32,6 +32,7 @@ #include #include +#include #include "CoreController.h" #include "VideoProxy.h" @@ -51,7 +52,8 @@ Q_OBJECT public: void setTex(GLuint tex) { m_tex = tex; } void setVBO(GLuint vbo) { m_vbo = vbo; } - void finalizeVAO(); + bool finalizeVAO(); + void reset(); protected: void initializeGL() override; @@ -62,8 +64,8 @@ private: GLuint m_vbo; bool m_vaoDone = false; - QOpenGLVertexArrayObject m_vao; - QOpenGLShaderProgram m_program; + std::unique_ptr m_vao; + std::unique_ptr m_program; GLuint m_positionLocation; QTimer m_refresh; diff --git a/src/platform/qt/IOViewer.cpp b/src/platform/qt/IOViewer.cpp index 0d5d8afa3..46c0d8dbb 100644 --- a/src/platform/qt/IOViewer.cpp +++ b/src/platform/qt/IOViewer.cpp @@ -1584,6 +1584,9 @@ IOViewer::IOViewer(std::shared_ptr controller, QWidget* parent) m_width = 1; break; #endif + case mPLATFORM_NONE: + maxRegs = 0; + break; } for (unsigned i = 0; i < maxRegs; ++i) { diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 9a8458e1d..a0fb91397 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -341,6 +341,11 @@ void InputController::registerGyroAxisX(int axis) { #ifdef BUILD_SDL if (m_playerAttached) { m_sdlPlayer.rotation.gyroX = axis; + if (m_sdlPlayer.rotation.gyroY == axis) { + m_sdlPlayer.rotation.gyroZ = axis; + } else { + m_sdlPlayer.rotation.gyroZ = -1; + } } #endif } @@ -349,6 +354,11 @@ void InputController::registerGyroAxisY(int axis) { #ifdef BUILD_SDL if (m_playerAttached) { m_sdlPlayer.rotation.gyroY = axis; + if (m_sdlPlayer.rotation.gyroX == axis) { + m_sdlPlayer.rotation.gyroZ = axis; + } else { + m_sdlPlayer.rotation.gyroZ = -1; + } } #endif } diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index aa76ed523..3198a4c90 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -9,6 +9,7 @@ #include "GamepadAxisEvent.h" #include "GamepadButtonEvent.h" #include "VFileDevice.h" +#include "utils.h" #include #include @@ -251,6 +252,10 @@ void LoadSaveState::focusInEvent(QFocusEvent*) { void LoadSaveState::paintEvent(QPaintEvent*) { QPainter painter(this); + + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); QRect full(QPoint(), size()); + painter.fillRect(full, Qt::black); + painter.drawPixmap(clampSize(m_dims, size(), m_lockAspectRatio, m_lockIntegerScaling), m_background); painter.fillRect(full, QColor(0, 0, 0, 128)); } diff --git a/src/platform/qt/LoadSaveState.h b/src/platform/qt/LoadSaveState.h index 5596c1b10..f74e5b24b 100644 --- a/src/platform/qt/LoadSaveState.h +++ b/src/platform/qt/LoadSaveState.h @@ -32,6 +32,10 @@ public: void setInputController(InputController* controller); void setMode(LoadSave mode); + void setBackground(const QPixmap& pixmap) { m_background = pixmap; } + void setDimensions(const QSize& dims) { m_dims = dims; } + void setLockIntegerScaling(bool lockIntegerScaling) { m_lockIntegerScaling = lockIntegerScaling; } + void setLockAspectRatio(bool lockApsectRatio) { m_lockAspectRatio = lockApsectRatio; } signals: void closed(); @@ -54,6 +58,11 @@ private: int m_currentFocus; QPixmap m_currentImage; + QPixmap m_background; + + QSize m_dims; + bool m_lockAspectRatio; + bool m_lockIntegerScaling; }; } diff --git a/src/platform/qt/ReportView.cpp b/src/platform/qt/ReportView.cpp index edfddd21c..b38483e7f 100644 --- a/src/platform/qt/ReportView.cpp +++ b/src/platform/qt/ReportView.cpp @@ -61,6 +61,10 @@ #include #endif +#ifdef USE_LUA +#include +#endif + #ifdef USE_LZMA #include <7zVersion.h> #endif @@ -168,6 +172,11 @@ void ReportView::generateReport() { #else swReport << QString("libLZMA not linked"); #endif +#ifdef USE_LUA + swReport << QString("Lua version: %1").arg(QLatin1String(LUA_RELEASE)); +#else + swReport << QString("Lua not linked"); +#endif #ifdef USE_MINIZIP swReport << QString("minizip linked"); #else diff --git a/src/platform/qt/SensorView.cpp b/src/platform/qt/SensorView.cpp index 1742a26ee..22c2f5f3f 100644 --- a/src/platform/qt/SensorView.cpp +++ b/src/platform/qt/SensorView.cpp @@ -69,6 +69,14 @@ void SensorView::setController(std::shared_ptr controller) { connect(m_ui.timeFakeEpoch, &QRadioButton::clicked, [controller, this] () { controller->setFakeEpoch(m_ui.time->dateTime().toUTC()); }); + connect(m_ui.timeOffset, &QRadioButton::clicked, [controller, this] () { + controller->setTimeOffset(m_ui.offsetSeconds->value()); + }); + connect(m_ui.offsetSeconds, qOverload(&QSpinBox::valueChanged), [controller, this] (int value) { + if (m_ui.timeOffset->isChecked()) { + controller->setTimeOffset(value); + } + }); m_ui.timeButtons->checkedButton()->clicked(); connect(controller.get(), &CoreController::stopping, [this]() { diff --git a/src/platform/qt/SensorView.ui b/src/platform/qt/SensorView.ui index d75b5329d..e490fd03c 100644 --- a/src/platform/qt/SensorView.ui +++ b/src/platform/qt/SensorView.ui @@ -6,8 +6,8 @@ 0 0 - 434 - 319 + 448 + 332 @@ -19,28 +19,18 @@ Sensors - + QLayout::SetFixedSize - + Realtime clock - - - - Fixed time - - - timeButtons - - - @@ -54,7 +44,53 @@ + + + + Fixed time + + + timeButtons + + + + + + + Now + + + + + + Offset time + + + timeButtons + + + + + + + true + + + sec + + + -99999999 + + + 99999999 + + + 1 + + + + Start time at @@ -64,14 +100,7 @@ - - - - Now - - - - + true @@ -143,7 +172,7 @@ - + @@ -284,10 +313,10 @@ false - -2147483647 + -1073741823 - 2147483647 + 1073741823 0 diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index c32662fb8..b01b97a5d 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #ifdef USE_SQLITE3 @@ -51,14 +50,13 @@ #include "ReportView.h" #include "ROMInfo.h" #include "SaveConverter.h" -#include "ScriptingView.h" +#include "scripting/ScriptingView.h" #include "SensorView.h" #include "ShaderSelector.h" #include "ShortcutController.h" #include "TileView.h" #include "VideoProxy.h" #include "VideoView.h" -#include "utils.h" #ifdef USE_DISCORD_RPC #include "DiscordCoordinator.h" @@ -113,14 +111,12 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi m_libraryView = new LibraryController(nullptr, ConfigController::configDir() + "/library.sqlite3", m_config); ConfigOption* showLibrary = m_config->addOption("showLibrary"); showLibrary->connect([this](const QVariant& value) { - if (value.toBool()) { - if (m_controller) { - m_screenWidget->layout()->addWidget(m_libraryView); - } else { + if (!m_controller) { + if (value.toBool()) { attachWidget(m_libraryView); + } else { + attachWidget(m_screenWidget); } - } else { - detachWidget(m_libraryView); } }, this); m_config->updateOption("showLibrary"); @@ -150,7 +146,6 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi resizeFrame(QSize(GB_VIDEO_HORIZONTAL_PIXELS * i, GB_VIDEO_VERTICAL_PIXELS * i)); #endif setLogo(); - setCentralWidget(m_screenWidget); connect(this, &Window::shutdown, m_logView, &QWidget::hide); connect(&m_fpsTimer, &QTimer::timeout, this, &Window::showFPS); @@ -478,7 +473,7 @@ void Window::parseCard() { QString("oh"), QMessageBox::Ok); dialog->setAttribute(Qt::WA_DeleteOnClose); auto status = std::make_shared>(0, filenames.size()); - GBAApp::app()->submitWorkerJob([filenames, dialog, status]() { + GBAApp::app()->submitWorkerJob([filenames, status]() { int success = 0; for (QString filename : filenames) { if (filename.isEmpty()) { @@ -512,6 +507,9 @@ void Window::parseCard() { } status->first = success; }, [dialog, status]() { + if (status->second == 0) { + return; + } dialog->setText(tr("%1 of %2 e-Reader cards converted successfully.").arg(status->first).arg(status->second)); dialog->show(); }); @@ -883,7 +881,6 @@ void Window::gameStarted() { action.value()->setEnabled(m_controller->platform() == action.key()); } QSize size = m_controller->screenDimensions(); - m_screenWidget->setDimensions(size.width(), size.height()); m_config->updateOption("lockIntegerScaling"); m_config->updateOption("lockAspectRatio"); m_config->updateOption("interframeBlending"); @@ -977,7 +974,6 @@ void Window::gameStopped() { m_audioProcessor.reset(); } m_display->stopDrawing(); - detachWidget(m_display.get()); setLogo(); if (m_display) { #ifdef M_CORE_GB @@ -988,6 +984,7 @@ void Window::gameStopped() { } m_controller.reset(); + detachWidget(); updateTitle(); if (m_pendingClose) { @@ -1039,7 +1036,7 @@ void Window::unimplementedBiosCall(int) { void Window::reloadDisplayDriver() { if (m_controller) { m_display->stopDrawing(); - detachWidget(m_display.get()); + detachWidget(); } m_display = std::unique_ptr(Display::create(this)); if (!m_display) { @@ -1054,7 +1051,7 @@ void Window::reloadDisplayDriver() { #endif connect(m_display.get(), &QGBA::Display::hideCursor, [this]() { - if (static_cast(m_screenWidget->layout())->currentWidget() == m_display.get()) { + if (centralWidget() == m_display.get()) { m_screenWidget->setCursor(Qt::BlankCursor); } }); @@ -1215,15 +1212,13 @@ void Window::openStateWindow(LoadSave ls) { m_stateWindow = new LoadSaveState(m_controller); connect(this, &Window::shutdown, m_stateWindow, &QWidget::close); connect(m_stateWindow, &LoadSaveState::closed, [this]() { - detachWidget(m_stateWindow); - static_cast(m_screenWidget->layout())->setCurrentWidget(m_display.get()); + attachWidget(m_display.get()); m_stateWindow = nullptr; QMetaObject::invokeMethod(this, "setFocus", Qt::QueuedConnection); }); if (!wasPaused) { m_controller->setPaused(true); connect(m_stateWindow, &LoadSaveState::closed, [this]() { - m_screenWidget->filter(m_config->getOption("resampleVideo").toInt()); if (m_controller) { m_controller->setPaused(false); } @@ -1232,6 +1227,10 @@ void Window::openStateWindow(LoadSave ls) { m_stateWindow->setAttribute(Qt::WA_DeleteOnClose); m_stateWindow->setMode(ls); + m_stateWindow->setDimensions(m_controller->screenDimensions()); + m_config->updateOption("lockAspectRatio"); + m_config->updateOption("lockIntegerScaling"); + QImage still(m_controller->getPixels()); if (still.format() != QImage::Format_RGB888) { still = still.convertToFormat(QImage::Format_RGB888); @@ -1249,8 +1248,7 @@ void Window::openStateWindow(LoadSave ls) { QPixmap pixmap; pixmap.convertFromImage(output); - m_screenWidget->setPixmap(pixmap); - m_screenWidget->filter(true); + m_stateWindow->setBackground(pixmap); #ifndef Q_OS_MAC menuBar()->show(); @@ -1318,10 +1316,6 @@ void Window::setupMenu(QMenuBar* menubar) { #ifdef M_CORE_GBA Action* scanCard = addGameAction(tr("Scan e-Reader dotcodes..."), "scanCard", this, &Window::scanCard, "file"); m_platformActions.insert(mPLATFORM_GBA, scanCard); - -#ifdef USE_FFMPEG - m_actions.addAction(tr("Convert e-Reader card image to raw..."), "parseCard", this, &Window::parseCard, "file"); -#endif #endif addGameAction(tr("ROM &info..."), "romInfo", openControllerTView(), "file"); @@ -1403,7 +1397,7 @@ void Window::setupMenu(QMenuBar* menubar) { #endif m_actions.addAction(tr("About..."), "about", openTView(), "file")->setRole(Action::Role::ABOUT); - m_actions.addAction(tr("E&xit"), "quit", static_cast(this), &QWidget::close, "file", QKeySequence::Quit)->setRole(Action::Role::SETTINGS); + m_actions.addAction(tr("E&xit"), "quit", static_cast(this), &QWidget::close, "file", QKeySequence::Quit)->setRole(Action::Role::QUIT); m_actions.addMenu(tr("&Emulation"), "emu"); addGameAction(tr("&Reset"), "reset", &CoreController::reset, "emu", QKeySequence("Ctrl+R")); @@ -1536,8 +1530,8 @@ void Window::setupMenu(QMenuBar* menubar) { if (m_display) { m_display->lockAspectRatio(value.toBool()); } - if (m_controller) { - m_screenWidget->setLockAspectRatio(value.toBool()); + if (m_stateWindow) { + m_stateWindow->setLockAspectRatio(value.toBool()); } }, this); m_config->updateOption("lockAspectRatio"); @@ -1548,8 +1542,8 @@ void Window::setupMenu(QMenuBar* menubar) { if (m_display) { m_display->lockIntegerScaling(value.toBool()); } - if (m_controller) { - m_screenWidget->setLockIntegerScaling(value.toBool()); + if (m_stateWindow) { + m_stateWindow->setLockIntegerScaling(value.toBool()); } }, this); m_config->updateOption("lockIntegerScaling"); @@ -1569,9 +1563,6 @@ void Window::setupMenu(QMenuBar* menubar) { if (m_display) { m_display->filter(value.toBool()); } - if (m_controller) { - m_screenWidget->filter(value.toBool()); - } }, this); m_config->updateOption("resampleVideo"); @@ -1651,6 +1642,9 @@ void Window::setupMenu(QMenuBar* menubar) { m_actions.addAction(tr("Game Pak sensors..."), "sensorWindow", openNamedControllerTView(&m_sensorView, &m_inputController), "tools"); addGameAction(tr("&Cheats..."), "cheatsWindow", openControllerTView(), "tools"); +#ifdef ENABLE_SCRIPTING + m_actions.addAction(tr("Scripting..."), "scripting", this, &Window::scriptingOpen, "tools"); +#endif m_actions.addSeparator("tools"); m_actions.addAction(tr("Settings..."), "settings", this, &Window::openSettingsWindow, "tools")->setRole(Action::Role::SETTINGS); @@ -1664,17 +1658,15 @@ void Window::setupMenu(QMenuBar* menubar) { m_platformActions.insert(mPLATFORM_GBA, gdbWindow); #endif #endif -#ifdef ENABLE_SCRIPTING - m_actions.addAction(tr("Scripting..."), "scripting", this, &Window::scriptingOpen, "tools"); -#endif #if defined(USE_DEBUGGERS) || defined(ENABLE_SCRIPTING) m_actions.addSeparator("tools"); #endif - addGameAction(tr("View &palette..."), "paletteWindow", openControllerTView(), "tools"); - addGameAction(tr("View &sprites..."), "spriteWindow", openControllerTView(), "tools"); - addGameAction(tr("View &tiles..."), "tileWindow", openControllerTView(), "tools"); - addGameAction(tr("View &map..."), "mapWindow", openControllerTView(), "tools"); + m_actions.addMenu(tr("Game state views"), "stateViews", "tools"); + addGameAction(tr("View &palette..."), "paletteWindow", openControllerTView(), "stateViews"); + addGameAction(tr("View &sprites..."), "spriteWindow", openControllerTView(), "stateViews"); + addGameAction(tr("View &tiles..."), "tileWindow", openControllerTView(), "stateViews"); + addGameAction(tr("View &map..."), "mapWindow", openControllerTView(), "stateViews"); addGameAction(tr("&Frame inspector..."), "frameWindow", [this]() { if (!m_frameView) { @@ -1690,11 +1682,16 @@ void Window::setupMenu(QMenuBar* menubar) { m_frameView->setAttribute(Qt::WA_DeleteOnClose); } m_frameView->show(); - }, "tools"); + }, "stateViews"); - addGameAction(tr("View memory..."), "memoryView", openControllerTView(), "tools"); - addGameAction(tr("Search memory..."), "memorySearch", openControllerTView(), "tools"); - addGameAction(tr("View &I/O registers..."), "ioViewer", openControllerTView(), "tools"); + addGameAction(tr("View memory..."), "memoryView", openControllerTView(), "stateViews"); + addGameAction(tr("Search memory..."), "memorySearch", openControllerTView(), "stateViews"); + addGameAction(tr("View &I/O registers..."), "ioViewer", openControllerTView(), "stateViews"); + +#if defined(USE_FFMPEG) && defined(M_CORE_GBA) + m_actions.addSeparator("tools"); + m_actions.addAction(tr("Convert e-Reader card image to raw..."), "parseCard", this, &Window::parseCard, "tools"); +#endif m_actions.addSeparator("tools"); addGameAction(tr("Record debug video log..."), "recordVL", this, &Window::startVideoLog, "tools"); @@ -1894,13 +1891,12 @@ void Window::setupOptions() { } void Window::attachWidget(QWidget* widget) { - m_screenWidget->layout()->addWidget(widget); - m_screenWidget->unsetCursor(); - static_cast(m_screenWidget->layout())->setCurrentWidget(widget); + takeCentralWidget(); + setCentralWidget(widget); } -void Window::detachWidget(QWidget* widget) { - m_screenWidget->layout()->removeWidget(widget); +void Window::detachWidget() { + m_config->updateOption("showLibrary"); } void Window::appendMRU(const QString& fname) { @@ -1996,7 +1992,7 @@ void Window::focusCheck() { } void Window::updateFrame() { - if (static_cast(m_screenWidget->layout())->currentWidget() != m_display.get()) { + if (!m_controller) { return; } QPixmap pixmap; @@ -2171,17 +2167,12 @@ void Window::updateMute() { void Window::setLogo() { m_screenWidget->setPixmap(m_logo); m_screenWidget->setDimensions(m_logo.width(), m_logo.height()); - m_screenWidget->setLockIntegerScaling(false); - m_screenWidget->setLockAspectRatio(true); - m_screenWidget->filter(true); m_screenWidget->unsetCursor(); } WindowBackground::WindowBackground(QWidget* parent) : QWidget(parent) { - setLayout(new QStackedLayout()); - layout()->setContentsMargins(0, 0, 0, 0); } void WindowBackground::setPixmap(const QPixmap& pmap) { @@ -2202,24 +2193,12 @@ void WindowBackground::setDimensions(int width, int height) { m_aspectHeight = height; } -void WindowBackground::setLockIntegerScaling(bool lock) { - m_lockIntegerScaling = lock; -} - -void WindowBackground::setLockAspectRatio(bool lock) { - m_lockAspectRatio = lock; -} - -void WindowBackground::filter(bool filter) { - m_filter = filter; -} - void WindowBackground::paintEvent(QPaintEvent* event) { QWidget::paintEvent(event); const QPixmap& logo = pixmap(); QPainter painter(this); - painter.setRenderHint(QPainter::SmoothPixmapTransform, m_filter); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); painter.fillRect(QRect(QPoint(), size()), Qt::black); - QRect full(clampSize(QSize(m_aspectWidth, m_aspectHeight), size(), m_lockAspectRatio, m_lockIntegerScaling)); + QRect full(clampSize(QSize(m_aspectWidth, m_aspectHeight), size(), true, false)); painter.drawPixmap(full, logo); } diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index f129c44e5..f30dac4c5 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -24,7 +24,7 @@ #include "LogController.h" #include "SettingsView.h" #ifdef ENABLE_SCRIPTING -#include "ScriptingController.h" +#include "scripting/ScriptingController.h" #endif namespace QGBA { @@ -167,7 +167,7 @@ private: void openStateWindow(LoadSave); void attachWidget(QWidget* widget); - void detachWidget(QWidget* widget); + void detachWidget(); void appendMRU(const QString& fname); void clearMRU(); @@ -277,7 +277,6 @@ public: void setDimensions(int width, int height); void setLockIntegerScaling(bool lock); void setLockAspectRatio(bool lock); - void filter(bool filter); const QPixmap& pixmap() const { return m_pixmap; } @@ -289,9 +288,6 @@ private: QSize m_sizeHint; int m_aspectWidth; int m_aspectHeight; - bool m_lockAspectRatio; - bool m_lockIntegerScaling; - bool m_filter; }; } diff --git a/src/platform/qt/ScriptingController.cpp b/src/platform/qt/scripting/ScriptingController.cpp similarity index 85% rename from src/platform/qt/ScriptingController.cpp rename to src/platform/qt/scripting/ScriptingController.cpp index 033df225f..c441592b9 100644 --- a/src/platform/qt/ScriptingController.cpp +++ b/src/platform/qt/scripting/ScriptingController.cpp @@ -3,10 +3,11 @@ * 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 "ScriptingController.h" +#include "scripting/ScriptingController.h" #include "CoreController.h" -#include "ScriptingTextBuffer.h" +#include "scripting/ScriptingTextBuffer.h" +#include "scripting/ScriptingTextBufferModel.h" using namespace QGBA; @@ -33,6 +34,9 @@ ScriptingController::ScriptingController(QObject* parent) } }; + m_bufferModel = new ScriptingTextBufferModel(this); + QObject::connect(m_bufferModel, &ScriptingTextBufferModel::textBufferCreated, this, &ScriptingController::textBufferCreated); + init(); } @@ -87,10 +91,7 @@ void ScriptingController::clearController() { void ScriptingController::reset() { CoreController::Interrupter interrupter(m_controller); - for (ScriptingTextBuffer* buffer : m_buffers) { - delete buffer; - } - m_buffers.clear(); + m_bufferModel->reset(); mScriptContextDetachCore(&m_scriptContext); mScriptContextDeinit(&m_scriptContext); m_engines.clear(); @@ -106,21 +107,13 @@ void ScriptingController::runCode(const QString& code) { load(vf, "*prompt"); } -mScriptTextBuffer* ScriptingController::createTextBuffer(void* context) { - ScriptingController* self = static_cast(context); - ScriptingTextBuffer* buffer = new ScriptingTextBuffer(self); - self->m_buffers.append(buffer); - emit self->textBufferCreated(buffer); - return buffer->textBuffer(); -} - void ScriptingController::init() { mScriptContextInit(&m_scriptContext); mScriptContextAttachStdlib(&m_scriptContext); mScriptContextRegisterEngines(&m_scriptContext); mScriptContextAttachLogger(&m_scriptContext, &m_logger); - mScriptContextSetTextBufferFactory(&m_scriptContext, &ScriptingController::createTextBuffer, this); + m_bufferModel->attachToContext(&m_scriptContext); HashTableEnumerate(&m_scriptContext.engines, [](const char* key, void* engine, void* context) { ScriptingController* self = static_cast(context); diff --git a/src/platform/qt/ScriptingController.h b/src/platform/qt/scripting/ScriptingController.h similarity index 89% rename from src/platform/qt/ScriptingController.h rename to src/platform/qt/scripting/ScriptingController.h index 0981fcab2..03e40f7cf 100644 --- a/src/platform/qt/ScriptingController.h +++ b/src/platform/qt/scripting/ScriptingController.h @@ -15,10 +15,13 @@ #include +class QTextDocument; + namespace QGBA { class CoreController; class ScriptingTextBuffer; +class ScriptingTextBufferModel; class ScriptingController : public QObject { Q_OBJECT @@ -33,7 +36,7 @@ public: bool load(VFileDevice& vf, const QString& name); mScriptContext* context() { return &m_scriptContext; } - QList textBuffers() { return m_buffers; } + ScriptingTextBufferModel* textBufferModel() const { return m_bufferModel; } signals: void log(const QString&); @@ -59,7 +62,7 @@ private: mScriptEngineContext* m_activeEngine = nullptr; QHash m_engines; - QList m_buffers; + ScriptingTextBufferModel* m_bufferModel; std::shared_ptr m_controller; }; diff --git a/src/platform/qt/ScriptingTextBuffer.cpp b/src/platform/qt/scripting/ScriptingTextBuffer.cpp similarity index 100% rename from src/platform/qt/ScriptingTextBuffer.cpp rename to src/platform/qt/scripting/ScriptingTextBuffer.cpp diff --git a/src/platform/qt/ScriptingTextBuffer.h b/src/platform/qt/scripting/ScriptingTextBuffer.h similarity index 97% rename from src/platform/qt/ScriptingTextBuffer.h rename to src/platform/qt/scripting/ScriptingTextBuffer.h index c771003f2..b70e9f82a 100644 --- a/src/platform/qt/ScriptingTextBuffer.h +++ b/src/platform/qt/scripting/ScriptingTextBuffer.h @@ -18,7 +18,7 @@ class ScriptingTextBuffer : public QObject { Q_OBJECT public: - ScriptingTextBuffer(QObject* parent); + ScriptingTextBuffer(QObject* parent = nullptr); QTextDocument* document() { return &m_document; }; mScriptTextBuffer* textBuffer() { return &m_shim; } @@ -47,12 +47,12 @@ private: static void deinit(struct mScriptTextBuffer*); static void setName(struct mScriptTextBuffer*, const char* name); - + static uint32_t getX(const struct mScriptTextBuffer*); static uint32_t getY(const struct mScriptTextBuffer*); static uint32_t cols(const struct mScriptTextBuffer*); static uint32_t rows(const struct mScriptTextBuffer*); - + static void print(struct mScriptTextBuffer*, const char* text); static void clear(struct mScriptTextBuffer*); static void setSize(struct mScriptTextBuffer*, uint32_t cols, uint32_t rows); diff --git a/src/platform/qt/scripting/ScriptingTextBufferModel.cpp b/src/platform/qt/scripting/ScriptingTextBufferModel.cpp new file mode 100644 index 000000000..d62bb62df --- /dev/null +++ b/src/platform/qt/scripting/ScriptingTextBufferModel.cpp @@ -0,0 +1,77 @@ +/* Copyright (c) 2013-2022 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "ScriptingTextBufferModel.h" + +#include "ScriptingTextBuffer.h" + +#include + +using namespace QGBA; + +ScriptingTextBufferModel::ScriptingTextBufferModel(QObject* parent) + : QAbstractListModel(parent) +{ + // initializers only +} + +void ScriptingTextBufferModel::attachToContext(mScriptContext* context) +{ + mScriptContextSetTextBufferFactory(context, &ScriptingTextBufferModel::createTextBuffer, this); +} + +void ScriptingTextBufferModel::reset() { + beginResetModel(); + QList toDelete = m_buffers; + m_buffers.clear(); + endResetModel(); + for (ScriptingTextBuffer* buffer : toDelete) { + delete buffer; + } +} + +mScriptTextBuffer* ScriptingTextBufferModel::createTextBuffer(void* context) { + ScriptingTextBufferModel* self = static_cast(context); + self->beginInsertRows(QModelIndex(), self->m_buffers.size(), self->m_buffers.size() + 1); + ScriptingTextBuffer* buffer = new ScriptingTextBuffer; + if (buffer->thread() != self->thread()) { + buffer->moveToThread(self->thread()); + } + buffer->setParent(self); + QObject::connect(buffer, &ScriptingTextBuffer::bufferNameChanged, self, &ScriptingTextBufferModel::bufferNameChanged); + self->m_buffers.append(buffer); + emit self->textBufferCreated(buffer); + self->endInsertRows(); + return buffer->textBuffer(); +} + +void ScriptingTextBufferModel::bufferNameChanged(const QString&) { + ScriptingTextBuffer* buffer = qobject_cast(sender()); + int row = m_buffers.indexOf(buffer); + if (row < 0) { + return; + } + QModelIndex idx = index(row, 0); + emit dataChanged(idx, idx, { Qt::DisplayRole }); +} + +int ScriptingTextBufferModel::rowCount(const QModelIndex& parent) const { + if (parent.isValid()) { + return 0; + } + return m_buffers.size(); +} + +QVariant ScriptingTextBufferModel::data(const QModelIndex& index, int role) const { + if (index.parent().isValid() || index.row() < 0 || index.row() >= m_buffers.size() || index.column() != 0) { + return QVariant(); + } + if (role == Qt::DisplayRole) { + return m_buffers[index.row()]->document()->metaInformation(QTextDocument::DocumentTitle); + } else if (role == ScriptingTextBufferModel::DocumentRole) { + return QVariant::fromValue(m_buffers[index.row()]->document()); + } + return QVariant(); +} diff --git a/src/platform/qt/scripting/ScriptingTextBufferModel.h b/src/platform/qt/scripting/ScriptingTextBufferModel.h new file mode 100644 index 000000000..52b552049 --- /dev/null +++ b/src/platform/qt/scripting/ScriptingTextBufferModel.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2013-2022 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + +#include + +#include + +struct mScriptTextBuffer; + +namespace QGBA { + +class ScriptingTextBuffer; + +class ScriptingTextBufferModel : public QAbstractListModel { +Q_OBJECT + +public: + enum ItemDataRole { + DocumentRole = Qt::UserRole + 1, + }; + + ScriptingTextBufferModel(QObject* parent = nullptr); + + void attachToContext(mScriptContext* context); + + int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + +signals: + void textBufferCreated(ScriptingTextBuffer*); + +public slots: + void reset(); + +private slots: + void bufferNameChanged(const QString&); + +private: + static mScriptTextBuffer* createTextBuffer(void* context); + + QList m_buffers; +}; + +} diff --git a/src/platform/qt/ScriptingView.cpp b/src/platform/qt/scripting/ScriptingView.cpp similarity index 65% rename from src/platform/qt/ScriptingView.cpp rename to src/platform/qt/scripting/ScriptingView.cpp index 1252f285c..3878b668a 100644 --- a/src/platform/qt/ScriptingView.cpp +++ b/src/platform/qt/scripting/ScriptingView.cpp @@ -3,12 +3,13 @@ * 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 "ScriptingView.h" +#include "scripting/ScriptingView.h" #include "GBAApp.h" #include "ConfigController.h" -#include "ScriptingController.h" -#include "ScriptingTextBuffer.h" +#include "scripting/ScriptingController.h" +#include "scripting/ScriptingTextBuffer.h" +#include "scripting/ScriptingTextBufferModel.h" using namespace QGBA; @@ -19,26 +20,34 @@ ScriptingView::ScriptingView(ScriptingController* controller, ConfigController* { m_ui.setupUi(this); + ScriptingTextBufferModel* bufferModel = controller->textBufferModel(); m_ui.prompt->setFont(GBAApp::app()->monospaceFont()); m_ui.log->setNewlineTerminated(true); + m_ui.buffers->setModel(bufferModel); connect(m_ui.prompt, &QLineEdit::returnPressed, this, &ScriptingView::submitRepl); connect(m_ui.runButton, &QAbstractButton::clicked, this, &ScriptingView::submitRepl); + + connect(bufferModel, &QAbstractItemModel::modelAboutToBeReset, this, &ScriptingView::controllerReset); + connect(bufferModel, &QAbstractItemModel::rowsInserted, this, [this, bufferModel](const QModelIndex&, int row, int) { + m_ui.buffers->setCurrentIndex(bufferModel->index(row, 0)); + }); + connect(m_controller, &ScriptingController::log, m_ui.log, &LogWidget::log); connect(m_controller, &ScriptingController::warn, m_ui.log, &LogWidget::warn); connect(m_controller, &ScriptingController::error, m_ui.log, &LogWidget::error); - connect(m_controller, &ScriptingController::textBufferCreated, this, &ScriptingView::addTextBuffer); - connect(m_ui.buffers, &QListWidget::currentRowChanged, this, &ScriptingView::selectBuffer); + connect(m_ui.buffers->selectionModel(), &QItemSelectionModel::currentChanged, this, &ScriptingView::selectBuffer); connect(m_ui.load, &QAction::triggered, this, &ScriptingView::load); connect(m_ui.reset, &QAction::triggered, controller, &ScriptingController::reset); m_mruFiles = m_config->getMRU(ConfigController::MRU::Script); updateMRU(); - for (ScriptingTextBuffer* buffer : controller->textBuffers()) { - addTextBuffer(buffer); - } + m_blankDocument = new QTextDocument(this); + m_blankDocument->setDocumentLayout(new QPlainTextDocumentLayout(m_blankDocument)); + + m_ui.buffers->setCurrentIndex(bufferModel->index(0, 0)); } void ScriptingView::submitRepl() { @@ -57,23 +66,17 @@ void ScriptingView::load() { } } -void ScriptingView::addTextBuffer(ScriptingTextBuffer* buffer) { - QTextDocument* document = buffer->document(); - m_textBuffers.append(buffer); - QListWidgetItem* item = new QListWidgetItem(document->metaInformation(QTextDocument::DocumentTitle)); - connect(buffer, &ScriptingTextBuffer::bufferNameChanged, this, [item](const QString& name) { - item->setText(name); - }); - connect(buffer, &QObject::destroyed, this, [this, buffer, item]() { - m_textBuffers.removeAll(buffer); - m_ui.buffers->removeItemWidget(item); - }); - m_ui.buffers->addItem(item); - m_ui.buffers->setCurrentItem(item); +void ScriptingView::controllerReset() { + selectBuffer(QModelIndex()); } -void ScriptingView::selectBuffer(int index) { - m_ui.buffer->setDocument(m_textBuffers[index]->document()); +void ScriptingView::selectBuffer(const QModelIndex& current, const QModelIndex&) { + if (current.isValid()) { + m_ui.buffer->setDocument(current.data(ScriptingTextBufferModel::DocumentRole).value()); + } else { + // If there is no selected buffer, use the blank document. + m_ui.buffer->setDocument(m_blankDocument); + } } QString ScriptingView::getFilters() const { diff --git a/src/platform/qt/ScriptingView.h b/src/platform/qt/scripting/ScriptingView.h similarity index 85% rename from src/platform/qt/ScriptingView.h rename to src/platform/qt/scripting/ScriptingView.h index d6fec7847..1f90a3f19 100644 --- a/src/platform/qt/ScriptingView.h +++ b/src/platform/qt/scripting/ScriptingView.h @@ -23,8 +23,8 @@ private slots: void submitRepl(); void load(); - void addTextBuffer(ScriptingTextBuffer*); - void selectBuffer(int); + void controllerReset(); + void selectBuffer(const QModelIndex& current, const QModelIndex& = QModelIndex()); private: QString getFilters() const; @@ -36,8 +36,8 @@ private: ConfigController* m_config; ScriptingController* m_controller; - QList m_textBuffers; QStringList m_mruFiles; + QTextDocument* m_blankDocument; }; } diff --git a/src/platform/qt/ScriptingView.ui b/src/platform/qt/scripting/ScriptingView.ui similarity index 98% rename from src/platform/qt/ScriptingView.ui rename to src/platform/qt/scripting/ScriptingView.ui index 61afa257f..4e231f2fb 100644 --- a/src/platform/qt/ScriptingView.ui +++ b/src/platform/qt/scripting/ScriptingView.ui @@ -16,7 +16,7 @@ - + 0 diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts index 60e4af135..67d1c294d 100644 --- a/src/platform/qt/ts/mgba-de.ts +++ b/src/platform/qt/ts/mgba-de.ts @@ -1185,21 +1185,21 @@ Game Boy Advance ist eine eingetragene Marke von Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. Möchtest Du es jetzt herunterladen und installieren? Du wirst den Emulator nach Abschluss des Downloads neu starten müssen. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. Automatische Updates sind für diese Plattform nicht verfügbar. Wenn Du updaten möchtest, musst Du dies manuell tun. - + Current version: %1 New version: %2 Download size: %3 @@ -1208,17 +1208,17 @@ Neue Version: %2 Download-Größe: %3 - + Downloading update... Update wird heruntergeladen... - + Downloading failed. Please update manually. Download fehlgeschlagen. Bitte führe das Update manuell durch. - + Downloading done. Press OK to restart %1 and install the update. Download abgeschlossen. Klicke auf OK, um %1 neuzustarten und das Update zu installieren. @@ -1241,7 +1241,7 @@ Download-Größe: %3 Unbekannt - + (None) (keiner) @@ -1272,17 +1272,22 @@ Download-Größe: %3 QGBA::CheatsView - - + + Autodetect (recommended) Automatisch erkennen (empfohlen) - - + + Select cheats file Cheat-Datei auswählen + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1292,43 +1297,43 @@ Download-Größe: %3 r%1-%2 %3 zurücksetzen - - + + Rewinding not currently enabled Rücklauf ist derzeit nicht aktiviert - + Reset the game? Spiel zurücksetzen? - + Most games will require a reset to load the new save. Do you want to reset now? Die meisten Spiele müssen zurückgesetzt werden, um einen neuen Spielstand zu laden. Möchtest Du das Spiel jetzt zurücksetzen? - + Failed to open save file: %1 Fehler beim Öffnen der Speicherdatei: %1 - + Failed to open game file: %1 Fehler beim Öffnen der Spieldatei: %1 - + Can't yank pack in unexpected platform! Das GamePak kann nur auf unterstützten Plattformen herausgezogen werden! - + Failed to open snapshot file for reading: %1 Konnte Snapshot-Datei %1 nicht zum Lesen öffnen - + Failed to open snapshot file for writing: %1 Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen @@ -1341,12 +1346,12 @@ Download-Größe: %3 Fehler beim Öffnen der Spieldatei: %1 - + Could not load game. Are you sure it's in the correct format? Konnte das Spiel nicht laden. Bist Du sicher, dass es im korrekten Format vorliegt? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Fehler beim Laden der Spielstand-Datei; Speicherdaten können nicht aktualisiert werden. Bitte stelle sicher, dass das Speicherdaten-Verzeichnis ohne zusätzliche Berechtigungen (z.B. UAC in Windows) beschreibbar ist. @@ -3410,27 +3415,27 @@ Download-Größe: %3 QGBA::LoadSaveState - + Load State Savestate laden - + Save State Savestate speichern - + Empty Leer - + Corrupted Defekt - + Slot %1 Speicherplatz %1 @@ -3482,47 +3487,47 @@ Download-Größe: %3 QGBA::LogController - + [%1] %2: %3 [%1] %2: %3 - + An error occurred Ein Fehler ist aufgetreten - + DEBUG DEBUG - + STUB STUB - + INFO INFO - + WARN WARN - + ERROR ERROR - + FATAL FATAL - + GAME ERROR GAME ERROR @@ -3646,42 +3651,42 @@ Download-Größe: %3 Laden - + All Alle - + Load TBL TBL laden - + Save selected memory Ausgewählten Speicher abspeichern - + Failed to open output file: %1 Fehler beim Öffnen der Ausgabedatei: %1 - + Load memory Lade Speicher - + Failed to open input file: %1 Fehler beim Laden der Eingabedatei: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3859,12 +3864,12 @@ Download-Größe: %3 QGBA::ReportView - + Bug report archive Fehlerbericht speichern - + ZIP archive (*.zip) ZIP-Archiv (*.zip) @@ -3932,97 +3937,105 @@ Download-Größe: %3 Der Spielstand konnte nicht zwischen verschiedenen Plattformen konvertiert werden + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (erzwinge Version 1.x) - + None Keiner - + None (Still Image) Keiner (Standbild) - + Keyboard Tastatur - + Controllers Gamepads - + Shortcuts Tastenkürzel - - + + Shaders Shader - + Select BIOS BIOS auswählen - + Select directory Verzeichnis auswählen - + (%1×%2) (%1×%2) - + Never Nie - + Just now Gerade eben - + Less than an hour ago Vor weniger als einer Stunde - + %n hour(s) ago Vor %n Stunde @@ -4030,7 +4043,7 @@ Download-Größe: %3 - + %n day(s) ago Vor %n Tag @@ -4129,105 +4142,105 @@ Download-Größe: %3 QGBA::Window - + Game Boy Advance ROMs (%1) Game Boy Advance-ROMs (%1) - + Game Boy ROMs (%1) Game Boy-ROMs (%1) - + All ROMs (%1) Alle ROMs (%1) - + %1 Video Logs (*.mvl) %1 Video-Logs (*.mvl) - + Archives (%1) Archive (%1) - - - + + + Select ROM ROM auswählen - - + + Select save Speicherdatei wählen - + Select patch Patch wählen - + Patches (*.ips *.ups *.bps) Korrekturen (*.ips *.ups *.bps) - + Select e-Reader card images Bilder der Lesegerät-Karte auswählen - + Image file (*.png *.jpg *.jpeg) Bilddatei (*.png *.jpg *.jpeg) - + Conversion finished Konvertierung abgeschlossen - + %1 of %2 e-Reader cards converted successfully. %1 von %2 Lesegerät-Karten erfolgreich konvertiert. - + Select image Bild auswählen - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Bild-Datei (*.png *.gif *.jpg *.jpeg);;Alle Dateien (*) - + GameShark saves (*.sps *.xps) GameShark-Speicherdaten (*.sps *.xps) - + Select video log Video-Log auswählen - + Video logs (*.mvl) Video-Logs (*.mvl) - + Crash Absturz - + The game has crashed with the following error: %1 @@ -4236,669 +4249,679 @@ Download-Größe: %3 %1 - + Unimplemented BIOS call Nicht implementierter BIOS-Aufruf - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Es konnte kein geeignetes Ausgabegerät erstellt werden, stattdessen wird Software-Rendering als Rückfalloption genutzt. Spiele laufen möglicherweise langsamer, besonders innerhalb großer Fenster. - + Really make portable? Portablen Modus wirklich aktivieren? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren? - + Restart needed Neustart benötigt - + Some changes will not take effect until the emulator is restarted. Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde. - + - Player %1 of %2 - Spieler %1 von %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 Bilder/Sekunde) - %4 - + &File &Datei - + Load &ROM... &ROM laden... - + Load ROM in archive... ROM aus Archiv laden... - + Save games Spielstände - + Automatically determine Automatisch erkennen - + Use player %0 save game Verwende Spielstand von Spieler %0 - + Load &patch... &Patch laden... - + Boot BIOS BIOS booten - + Replace ROM... ROM ersetzen... - + Convert e-Reader card image to raw... Lesegerät-Kartenbild in Rohdaten umwandeln … - + ROM &info... ROM-&Informationen... - + Recent Zuletzt verwendet - + Make portable Portablen Modus aktivieren - + &Load state Savestate (aktueller Zustand) &laden - + Load state file... Savestate-Datei laden... - + &Save state Savestate (aktueller Zustand) &speichern - + Save state file... Savestate-Datei speichern... - + Quick load Schnell laden - + Quick save Schnell speichern - + Load recent Lade zuletzt gespeicherten Savestate - + Save recent Speichere aktuellen Zustand - + Undo load state Laden des Savestate rückgängig machen - + Undo save state Speichern des Savestate rückgängig machen - - + + State &%1 Savestate &%1 - + Load camera image... Lade Kamerabild... - + Convert save game... Spielstand konvertieren... - + Reset needed Zurücksetzen erforderlich - + Some changes will not take effect until the game is reset. Einige Änderungen werden erst dann wirksam, wenn das Spiel zurückgesetzt wird. - + New multiplayer window Neues Multiplayer-Fenster - + Connect to Dolphin... Mit Dolphin verbinden... - + Report bug... Fehler melden... - + E&xit &Beenden - + &Emulation &Emulation - + &Reset Zu&rücksetzen - + Sh&utdown Schli&eßen - + Yank game pak Spielmodul herausziehen - + &Pause &Pause - + &Next frame &Nächstes Bild - + Fast forward (held) Schneller Vorlauf (gehalten) - + &Fast forward Schneller &Vorlauf - + Fast forward speed Vorlauf-Geschwindigkeit - + Unbounded Unbegrenzt - + %0x %0x - + Rewind (held) Zurückspulen (gehalten) - + Re&wind Zur&ückspulen - + Step backwards Schrittweiser Rücklauf - + Solar sensor Sonnen-Sensor - + Increase solar level Sonnen-Level erhöhen - + Decrease solar level Sonnen-Level verringern - + Brightest solar level Hellster Sonnen-Level - + Darkest solar level Dunkelster Sonnen-Level - + Brightness %1 Helligkeit %1 - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Audio/&Video - + Frame size Bildgröße - + Toggle fullscreen Vollbildmodus umschalten - + Lock aspect ratio Seitenverhältnis korrigieren - + Force integer scaling Pixelgenaue Skalierung (Integer scaling) - + Interframe blending Interframe-Überblendung - + Frame&skip Frame&skip - + Mute Stummschalten - + FPS target Bildwiederholrate - + Take &screenshot &Screenshot erstellen - + F12 F12 - + + Scripting... + + + + + Game state views + + + + Clear Leeren - + Game Boy Printer... Game Boy Printer... - + Video layers Video-Ebenen - + Audio channels Audio-Kanäle - + Adjust layer placement... Lage der Bildebenen anpassen... - + &Tools &Werkzeuge - + View &logs... &Logs ansehen... - + Game &overrides... Spiel-&Überschreibungen... - + &Cheats... &Cheats... - + Open debugger console... Debugger-Konsole öffnen... - + Start &GDB server... &GDB-Server starten... - + Settings... Einstellungen... - + Select folder Ordner auswählen - + Save games (%1) Spielstände (%1) - + Select save game Spielstand auswählen - + mGBA save state files (%1) mGBA-Savestates (%1) - - + + Select save state Savestate auswählen - + Select e-Reader dotcode e-Reader-Code auswählen - + e-Reader card (*.raw *.bin *.bmp) e-Reader-Karte (*.raw *.bin *.bmp) - + GameShark saves (*.gsv *.sps *.xps) GameShark-Spielstände (*.gsv *.sps *.xps) - + Couldn't Start Konnte nicht gestartet werden - + Could not start game. Spiel konnte nicht gestartet werden. - + Add folder to library... Ordner zur Bibliothek hinzufügen... - + Load alternate save game... Alternativen Spielstand laden... - + Load temporary save game... Temporären Spielstand laden... - + Scan e-Reader dotcodes... e-Reader-Code einlesen... - + Import GameShark Save... GameShare-Speicherstand importieren... - + Export GameShark Save... GameShark-Speicherstand exportieren... - + About... Über... - + %1× %1x - + Bilinear filtering Bilineare Filterung - + Native (59.7275) Nativ (59.7275) - + Record A/V... Audio/Video aufzeichnen... - + Record GIF/WebP/APNG... GIF/WebP/APNG aufzeichnen... - + Game Pak sensors... Spielmodul-Sensoren... - + View &palette... &Palette betrachten... - + View &sprites... &Sprites betrachten... - + View &tiles... &Tiles betrachten... - + View &map... &Map betrachten... - + &Frame inspector... &Bildbetrachter... - + View memory... Speicher betrachten... - + Search memory... Speicher durchsuchen... - + View &I/O registers... &I/O-Register betrachten... - + Record debug video log... Video-Protokoll aufzeichnen... - + Stop debug video log Aufzeichnen des Video-Protokolls beenden - + Exit fullscreen Vollbildmodus beenden - + GameShark Button (held) GameShark-Taste (gehalten) - + Autofire Autofeuer - + Autofire A Autofeuer A - + Autofire B Autofeuer B - + Autofire L Autofeuer L - + Autofire R Autofeuer R - + Autofire Start Autofeuer Start - + Autofire Select Autofeuer Select - + Autofire Up Autofeuer nach oben - + Autofire Right Autofeuer rechts - + Autofire Down Autofeuer nach unten - + Autofire Left Autofeuer links @@ -5144,6 +5167,59 @@ Download-Größe: %3 %1 GameShark Advance SP %2 Spielstand + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + Zu&rücksetzen + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5157,64 +5233,74 @@ Download-Größe: %3 Echtzeituhr - + Fixed time Feste Zeit - + System time Systemzeit - + Start time at Starte Zeit ab - + Now Jetzt - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP dd.MM.yy HH:mm:ss - + Light sensor Lichtsensor - + Brightness Helligkeit - + Tilt sensor Neigungssensor - - + + Set Y Setze Y - - + + Set X Setze X - + Gyroscope Gyroskop - + Sensitivity Empfindlichkeit diff --git a/src/platform/qt/ts/mgba-en.ts b/src/platform/qt/ts/mgba-en.ts index 4398dde76..feae91b5a 100644 --- a/src/platform/qt/ts/mgba-en.ts +++ b/src/platform/qt/ts/mgba-en.ts @@ -1183,36 +1183,36 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + Current version: %1 New version: %2 Download size: %3 - + Downloading update... - + Downloading failed. Please update manually. - + Downloading done. Press OK to restart %1 and install the update. @@ -1235,7 +1235,7 @@ Download size: %3 - + (None) @@ -1266,17 +1266,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) - - + + Select cheats file + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1286,43 +1291,43 @@ Download size: %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 - + Failed to open game file: %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -1335,12 +1340,12 @@ Download size: %3 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3404,27 +3409,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State - + Save State - + Empty - + Corrupted - + Slot %1 @@ -3476,47 +3481,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 - + An error occurred - + DEBUG - + STUB - + INFO - + WARN - + ERROR - + FATAL - + GAME ERROR @@ -3640,42 +3645,42 @@ Download size: %3 - + All - + Load TBL - + Save selected memory - + Failed to open output file: %1 - + Load memory - + Failed to open input file: %1 - + TBL - + ISO-8859-1 @@ -3853,12 +3858,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -3926,97 +3931,105 @@ Download size: %3 + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4024,7 +4037,7 @@ Download size: %3 - + %n day(s) ago @@ -4123,774 +4136,784 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) - + Game Boy ROMs (%1) - + All ROMs (%1) - + %1 Video Logs (*.mvl) - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + + Scripting... + + + + + Game state views + + + + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear @@ -5136,6 +5159,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + + + + + 0 + + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5149,64 +5225,74 @@ Download size: %3 - + Fixed time - + System time - + Start time at - + Now - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP - + Light sensor - + Brightness - + Tilt sensor - - + + Set Y - - + + Set X - + Gyroscope - + Sensitivity diff --git a/src/platform/qt/ts/mgba-es.ts b/src/platform/qt/ts/mgba-es.ts index ecbb3000c..daed1612b 100644 --- a/src/platform/qt/ts/mgba-es.ts +++ b/src/platform/qt/ts/mgba-es.ts @@ -1185,21 +1185,21 @@ Game Boy Advance es una marca registrada de Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. ¿Quieres descargarlo e instalarlo ahora? Deberá reiniciar el emulador cuando se complete la descarga. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. La actualización automática no está disponible en esta plataforma. Si desea actualizar, deberá hacerlo manualmente. - + Current version: %1 New version: %2 Download size: %3 @@ -1208,17 +1208,17 @@ Nueva versión: %2 Tamaño de la descarga: %3 - + Downloading update... Descargando actualización... - + Downloading failed. Please update manually. La descarga ha fallado. Por favor, actualiza manualmente. - + Downloading done. Press OK to restart %1 and install the update. Descarga finalizada. Presiona OK para reiniciar %1 e instalar la actualización. @@ -1241,7 +1241,7 @@ Tamaño de la descarga: %3 Desconocido - + (None) (Ninguno) @@ -1272,17 +1272,22 @@ Tamaño de la descarga: %3 QGBA::CheatsView - - + + Autodetect (recommended) Autodetectar (recomendado) - - + + Select cheats file Seleccionar archivo de trucos + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1292,43 +1297,43 @@ Tamaño de la descarga: %3 Reiniciar r%1-%2 %3 - - + + Rewinding not currently enabled Rebobinado desactivado actualmente - + Reset the game? ¿Reiniciar el juego? - + Most games will require a reset to load the new save. Do you want to reset now? La mayoría de juegos requieren reiniciar para cargar la nueva partida guardada. ¿Quieres reiniciar ahora? - + Failed to open save file: %1 Error al abrir el archivo de guardado: %1 - + Failed to open game file: %1 Error al abrir el archivo del juego: %1 - + Can't yank pack in unexpected platform! ¡No se puede remover el cartucho en esta plataforma! - + Failed to open snapshot file for reading: %1 Error al leer del archivo de captura: %1 - + Failed to open snapshot file for writing: %1 Error al escribir al archivo de captura: %1 @@ -1341,12 +1346,12 @@ Tamaño de la descarga: %3 Error al abrir el archivo del juego: %1 - + Could not load game. Are you sure it's in the correct format? No se pudo cargar el juego. ¿Estás seguro de que está en el formato correcto? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Error al abrir el archivo de guardado; las partidas guardadas no se pueden actualizar. Por favor, asegúrese de que es posible escribir en el directorio de partidas guardadas sin necesidad de privilegios adicionales (Por ejemplo, UAC en Windows). @@ -3410,27 +3415,27 @@ Tamaño de la descarga: %3 QGBA::LoadSaveState - + Load State Cargar estado - + Save State Guardar estado - + Empty Vacío - + Corrupted Dañado - + Slot %1 Espacio %1 @@ -3482,47 +3487,47 @@ Tamaño de la descarga: %3 QGBA::LogController - + [%1] %2: %3 [%1] %2: %3 - + An error occurred Ocurrió un error - + DEBUG DEPURACIÓN - + STUB STUB - + INFO INFORMACIÓN - + WARN ADVERTENCIA - + ERROR ERROR - + FATAL FATAL - + GAME ERROR ERROR DE JUEGO @@ -3646,42 +3651,42 @@ Tamaño de la descarga: %3 Cargar - + All Todo - + Load TBL Cargar TBL - + Save selected memory Guardar memoria seleccionada - + Failed to open output file: %1 Error al abrir el archivo de salida: %1 - + Load memory Cargar memoria - + Failed to open input file: %1 Error al abrir el archivo de entrada: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3859,12 +3864,12 @@ Tamaño de la descarga: %3 QGBA::ReportView - + Bug report archive Archivo del reporte de bugs - + ZIP archive (*.zip) Archivo ZIP (*.zip) @@ -3932,97 +3937,105 @@ Tamaño de la descarga: %3 No se pueden convertir los estados guardados entre plataformas distintas + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (forzar versión 1.x) - + None Ninguno - + None (Still Image) Nada (imagen estática) - + Keyboard Teclado - + Controllers Controladores - + Shortcuts Atajos de teclado - - + + Shaders Shaders - + Select BIOS Seleccionar BIOS - + Select directory Elegir carpeta - + (%1×%2) - + Never Nunca - + Just now Ahora mismo - + Less than an hour ago Hace menos de una hora - + %n hour(s) ago Hace %n hora @@ -4030,7 +4043,7 @@ Tamaño de la descarga: %3 - + %n day(s) ago Hace %n dia @@ -4129,100 +4142,100 @@ Tamaño de la descarga: %3 QGBA::Window - + Game Boy Advance ROMs (%1) ROMs de Game Boy Advance (%1) - + Game Boy ROMs (%1) ROMs de Game Boy (%1) - + All ROMs (%1) Todas las ROMs (%1) - + %1 Video Logs (*.mvl) Video-registros de %1 (*.mvl) - + Archives (%1) Contenedores (%1) - - - + + + Select ROM Seleccionar ROM - + Select folder Seleccionar carpeta - - + + Select save Seleccionar guardado - + Select patch Seleccionar parche - + Patches (*.ips *.ups *.bps) Parches (*.ips *.ups *.bps) - + Select e-Reader dotcode Seleccionar dotcode del e-Reader - + e-Reader card (*.raw *.bin *.bmp) Tarjeta e-Reader (*.raw *.bin *.bmp) - + Select image Seleccionar imagen - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Archivo de imagen (*.png *.gif *.jpg *.jpeg);;Todos los archivos (*) - + GameShark saves (*.sps *.xps) Guardados de GameShark (*.sps *.xps) - + Select video log Seleccionar video-registro - + Video logs (*.mvl) Video-registros (*.mvl) - + Crash Cierre inesperado - + The game has crashed with the following error: %1 @@ -4231,674 +4244,684 @@ Tamaño de la descarga: %3 %1 - + Unimplemented BIOS call Llamada a BIOS no implementada - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Este juego utiliza una llamada al BIOS que no se ha implementado. Utiliza el BIOS oficial para obtener la mejor experiencia. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. No se pudo crear un dispositivo de pantalla apropiado, recurriendo a software. Los juegos pueden funcionar lentamente, especialmente con ventanas grandes. - + Really make portable? ¿Hacer "portable"? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Esto hará que el emulador cargue su configuración desde el mismo directorio que el ejecutable. ¿Quieres continuar? - + Restart needed Reinicio necesario - + Some changes will not take effect until the emulator is restarted. Algunos cambios no surtirán efecto hasta que se reinicie el emulador. - + - Player %1 of %2 - Jugador %1 de %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Archivo - + Load &ROM... Cargar &ROM... - + Load ROM in archive... Cargar ROM desde contenedor... - + Add folder to library... Agregar carpeta a la biblioteca... - + Save games Datos de guardado - + Automatically determine Determinar automáticamente - + Use player %0 save game Usar la partida guardada del jugador %0 - + Load &patch... Cargar &parche... - + Boot BIOS Arrancar BIOS - + Replace ROM... Reemplazar ROM... - + ROM &info... &Información de la ROM... - + Recent Recientes - + Make portable Hacer "portable" - + &Load state Ca&rgar estado - + Report bug... Reportar bug... - + About... Acerca de... - + Game Pak sensors... Sensores del cartucho... - + Clear Limpiar - + Load state file... Cargar archivo de estado... - + Save games (%1) Juegos guardados (%1) - + Select save game Elegir juego guardado - + mGBA save state files (%1) Archivos estados guardados mGBA (%1) - - + + Select save state Elegir estado guardado - + Select e-Reader card images Elegir imágenes de tarjeta e-Reader - + Image file (*.png *.jpg *.jpeg) Archivo de imagen (*.png *.jpg *.jpeg) - + Conversion finished Conversión terminada - + %1 of %2 e-Reader cards converted successfully. %1 de %2 tarjetas e-Reader convertidas con éxito. - + Load alternate save game... Elegir juego guardado alterno... - + Load temporary save game... Elegir juego guardado temporal... - + Convert e-Reader card image to raw... Convertir imagen de tarjeta e-Reader a raw... - + &Save state Guardar e&stado - + Save state file... Guardar archivo de estado... - + Quick load Cargado rápido - + Quick save Guardado rápido - + Load recent Cargar reciente - + Save recent Guardar reciente - + Undo load state Deshacer cargar estado - + Undo save state Deshacer guardar estado - - + + State &%1 Estado &%1 - + Load camera image... Cargar imagen para la cámara... - + Convert save game... Convertir juego guardado... - + GameShark saves (*.gsv *.sps *.xps) Partidas guardadas de GameShark (*.gsv *.sps *.xps) - + Reset needed Reinicio necesario - + Some changes will not take effect until the game is reset. Algunos cambios no tendrán efecto hasta que se reinicie el juego. - + New multiplayer window Nueva ventana multijugador - + Connect to Dolphin... Conectar a Dolphin... - + E&xit Salir (&X) - + &Emulation &Emulación - + &Reset &Reinicializar - + Sh&utdown Apagar (&U) - + Yank game pak Tirar del cartucho - + &Pause &Pausar - + &Next frame Cuadro siguie&nte - + Fast forward (held) Avance rápido (mantener) - + &Fast forward &Avance rápido - + Fast forward speed Velocidad de avance rápido - + Unbounded Sin límite - + %0x %0x - + Rewind (held) Rebobinar (mantener) - + Re&wind Re&bobinar - + Step backwards Paso hacia atrás - + Solar sensor Sensor solar - + Increase solar level Subir nivel - + Decrease solar level Bajar nivel - + Brightest solar level Más claro - + Darkest solar level Más oscuro - + Brightness %1 Brillo %1 - + Audio/&Video Audio/&video - + Frame size Tamaño del cuadro - + Toggle fullscreen Pantalla completa - + Lock aspect ratio Bloquear proporción de aspecto - + Force integer scaling Forzar escala a enteros - + Bilinear filtering Filtro bilineal - + Frame&skip &Salto de cuadros - + Mute Silenciar - + FPS target Objetivo de FPS - + Native (59.7275) Nativo (59,7275) - + Take &screenshot Tomar pan&tallazo - + F12 F12 - + Game Boy Printer... Game Boy Printer... - + BattleChip Gate... BattleChip Gate... - + %1× %1× - + Interframe blending Mezcla entre cuadros - + Record A/V... Grabar A/V... - + Video layers Capas de video - + Audio channels Canales de audio - + Adjust layer placement... Ajustar ubicación de capas... - + &Tools Herramien&tas - + View &logs... Ver re&gistros... - + Game &overrides... Ajustes específic&os por juego... - + Couldn't Start No se pudo iniciar - + Could not start game. No se pudo iniciar el juego. - + Scan e-Reader dotcodes... Escanear dotcodes del e-Reader... - + Import GameShark Save... Importar desde GameShark... - + Export GameShark Save... Exportar a GameShark... - + Record GIF/WebP/APNG... Grabar GIF/WebP/APNG... - + &Cheats... Tru&cos... - + Settings... Ajustes... - + Open debugger console... Abrir consola de depuración... - + Start &GDB server... Iniciar servidor &GDB... - + + Scripting... + + + + + Game state views + + + + View &palette... Ver &paleta... - + View &sprites... Ver &sprites... - + View &tiles... Ver m&osaicos... - + View &map... Ver &mapa... - + &Frame inspector... Inspec&tor de cuadros... - + View memory... Ver memoria... - + Search memory... Buscar memoria... - + View &I/O registers... Ver registros &I/O... - + Record debug video log... Grabar registro de depuración de video... - + Stop debug video log Detener registro de depuración de video - + Exit fullscreen Salir de pantalla completa - + GameShark Button (held) Botón GameShark (mantener) - + Autofire Disparo automático - + Autofire A Disparo automático A - + Autofire B Disparo automático B - + Autofire L Disparo automático L - + Autofire R Disparo automático R - + Autofire Start Disparo automático Start - + Autofire Select Disparo automático Select - + Autofire Up Disparo automático Arriba - + Autofire Right Disparo automático Derecha - + Autofire Down Disparo automático Abajo - + Autofire Left Disparo automático Izquierda @@ -5144,6 +5167,59 @@ Tamaño de la descarga: %3 %1 GameShark Advance SP %2 partida guardada + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + &Reinicializar + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5157,64 +5233,74 @@ Tamaño de la descarga: %3 Reloj en tiempo real - + Fixed time Hora fija - + System time Hora del sistema - + Start time at Empezar desde esta hora - + Now Ahora - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP dd/MM/yy HH:mm:ss - + Light sensor Sensor de luz - + Brightness Brillo - + Tilt sensor Sensor de inclinación - - + + Set Y Ajustar Y - - + + Set X Ajustar X - + Gyroscope Giroscopio - + Sensitivity Sensibilidad diff --git a/src/platform/qt/ts/mgba-fi.ts b/src/platform/qt/ts/mgba-fi.ts index a616a917f..4f4f313f1 100644 --- a/src/platform/qt/ts/mgba-fi.ts +++ b/src/platform/qt/ts/mgba-fi.ts @@ -1184,36 +1184,36 @@ Game Boy Advance on Nintendo Co., Ltd rekisteröimä tuotemerkki. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + Current version: %1 New version: %2 Download size: %3 - + Downloading update... - + Downloading failed. Please update manually. - + Downloading done. Press OK to restart %1 and install the update. @@ -1236,7 +1236,7 @@ Download size: %3 - + (None) @@ -1267,17 +1267,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) - - + + Select cheats file + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1287,43 +1292,43 @@ Download size: %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 - + Failed to open game file: %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -1336,12 +1341,12 @@ Download size: %3 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3405,27 +3410,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State - + Save State - + Empty - + Corrupted - + Slot %1 @@ -3477,47 +3482,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 - + An error occurred - + DEBUG - + STUB - + INFO - + WARN - + ERROR - + FATAL - + GAME ERROR @@ -3641,42 +3646,42 @@ Download size: %3 Ladata - + All - + Load TBL - + Save selected memory - + Failed to open output file: %1 - + Load memory - + Failed to open input file: %1 - + TBL - + ISO-8859-1 @@ -3854,12 +3859,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -3927,97 +3932,105 @@ Download size: %3 + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4025,7 +4038,7 @@ Download size: %3 - + %n day(s) ago @@ -4124,774 +4137,784 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) - + Game Boy ROMs (%1) - + All ROMs (%1) - + %1 Video Logs (*.mvl) - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + GameShark saves (*.gsv *.sps *.xps) - + Convert e-Reader card image to raw... - + Import GameShark Save... - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + + Scripting... + + + + + Game state views + + + + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear @@ -5137,6 +5160,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + + + + + 0 + + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5150,64 +5226,74 @@ Download size: %3 - + Fixed time - + System time - + Start time at - + Now - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP - + Light sensor - + Brightness - + Tilt sensor - - + + Set Y - - + + Set X - + Gyroscope - + Sensitivity diff --git a/src/platform/qt/ts/mgba-fr.ts b/src/platform/qt/ts/mgba-fr.ts index d6edc2344..7be421752 100644 --- a/src/platform/qt/ts/mgba-fr.ts +++ b/src/platform/qt/ts/mgba-fr.ts @@ -1186,21 +1186,21 @@ Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. Voulez-vous la télécharger et l'installer maintenant ? Vous devrez redémarrer l'émulateur lorsque le téléchargement sera terminé. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. La mise à jour automatique n'est pas disponible sur cette plateforme. Si vous souhaitez effectuer une mise à jour, vous devrez le faire manuellement. - + Current version: %1 New version: %2 Download size: %3 @@ -1209,17 +1209,17 @@ Nouvelle version : %2 Taille du téléchargement : %3 - + Downloading update... Téléchargement de la mise à jour... - + Downloading failed. Please update manually. Le téléchargement a échoué. Veuillez mettre à jour manuellement. - + Downloading done. Press OK to restart %1 and install the update. Téléchargement terminé. Appuyez sur OK pour redémarrer %1 et installer la mise à jour. @@ -1242,7 +1242,7 @@ Taille du téléchargement : %3 Inconnue - + (None) (Aucune) @@ -1273,17 +1273,22 @@ Taille du téléchargement : %3 QGBA::CheatsView - - + + Autodetect (recommended) Détecter automatiquement (recommandé) - - + + Select cheats file Choisir un fichier de cheats + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1293,43 +1298,43 @@ Taille du téléchargement : %3 Réinitialiser r%1-%2 %3 - - + + Rewinding not currently enabled Le rembobinage n'est pas actuellement activé - + Reset the game? Réinitialiser le jeu ? - + Most games will require a reset to load the new save. Do you want to reset now? La plupart des jeux nécessitent une réinitialisation pour charger la nouvelle sauvegarde. Voulez-vous réinitialiser maintenant ? - + Failed to open save file: %1 Échec de l'ouverture du fichier de sauvegarde : %1 - + Failed to open game file: %1 Échec de l'ouverture du fichier de jeu : %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 Échec de l'ouverture de l'instantané pour lire : %1 - + Failed to open snapshot file for writing: %1 Échec de l'ouverture de l'instantané pour écrire : %1 @@ -1342,12 +1347,12 @@ Taille du téléchargement : %3 Échec de l'ouverture du fichier de jeu : %1 - + Could not load game. Are you sure it's in the correct format? Impossible de charger le jeu. Êtes-vous sûr qu'il est dans le bon format ? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Impossible d'ouvrir le fichier de sauvegarde ; les sauvegardes en jeu ne peuvent pas être mises à jour. Veuillez vous assurer que le répertoire de sauvegarde est accessible en écriture sans privilèges supplémentaires (par exemple, UAC sous Windows). @@ -3422,27 +3427,27 @@ Taille du téléchargement : %3 QGBA::LoadSaveState - + Load State Charger un état - + Save State Sauvegarder un état - + Empty Vide - + Corrupted Corrompue - + Slot %1 Emplacement %1 @@ -3494,53 +3499,53 @@ Taille du téléchargement : %3 QGBA::LogController - + [%1] %2: %3 [%1] %2 : %3 - + An error occurred Une erreur est survenue - + DEBUG There is no need to translate this. DEBUG - + STUB There is no need to translate this. STUB - + INFO There is no need to translate this. INFO - + WARN There is no need to translate this. WARN - + ERROR There is no need to translate this. ERROR - + FATAL There is no need to translate this. FATAL - + GAME ERROR There is no need to translate this. GAME ERROR @@ -3665,42 +3670,42 @@ Taille du téléchargement : %3 Charger - + All Tout - + Load TBL Charger le TBL - + Save selected memory Sauvegarder la mémoire sélectionné - + Failed to open output file: %1 Impossible d'ouvrir le fichier de sortie : %1 - + Load memory Charger la mémoire - + Failed to open input file: %1 Impossible d'ouvrir le fichier d'entrée : %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3878,12 +3883,12 @@ Taille du téléchargement : %3 QGBA::ReportView - + Bug report archive Archive de signalement d'erreur - + ZIP archive (*.zip) Archive ZIP (*.zip) @@ -3951,97 +3956,105 @@ Taille du téléchargement : %3 + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia Qt Multimédia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (version forcée 1.x) - + None Aucun - + None (Still Image) Aucun (Image fixe) - + Keyboard Clavier - + Controllers Contrôleurs - + Shortcuts Raccourcis - - + + Shaders Shaders - + Select BIOS Choisir le BIOS - + Select directory - + (%1×%2) (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4049,7 +4062,7 @@ Taille du téléchargement : %3 - + %n day(s) ago @@ -4148,120 +4161,120 @@ Taille du téléchargement : %3 QGBA::Window - + Game Boy Advance ROMs (%1) ROMs de Game Boy Advance (%1) - + Game Boy ROMs (%1) ROMs de Game Boy (%1) - + All ROMs (%1) Toutes les ROM (%1) - + %1 Video Logs (*.mvl) %1 Journaux vidéo (*.mvl) - + Archives (%1) Archives (%1) - - - + + + Select ROM Choisir une ROM - + Select folder Choisir un dossier - - + + Select save Choisir une sauvegarde - + Select patch Sélectionner un correctif - + Patches (*.ips *.ups *.bps) Correctifs/Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode Sélectionnez le numéro de point du e-Reader - + e-Reader card (*.raw *.bin *.bmp) e-Reader carte (*.raw *.bin *.bmp) - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Select image Choisir une image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Image (*.png *.gif *.jpg *.jpeg);;Tous les fichiers (*) - + GameShark saves (*.sps *.xps) Sauvegardes GameShark (*.sps *.xps) - + Select video log Sélectionner un journal vidéo - + Video logs (*.mvl) Journaux vidéo (*.mvl) - + Crash Plantage - + The game has crashed with the following error: %1 @@ -4270,654 +4283,664 @@ Taille du téléchargement : %3 %1 - + Unimplemented BIOS call Requête au BIOS non supporté - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Ce jeu utilise un appel BIOS qui n'est pas implémenté. Veuillez utiliser le BIOS officiel pour une meilleure expérience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Échec de la création d'un périphérique d'affichage approprié, retour à l'affichage du logiciel. Les jeux peuvent fonctionner lentement, en particulier avec des fenêtres plus grandes. - + Really make portable? Vraiment rendre portable ? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Cela amènera l'émulateur à charger sa configuration depuis le même répertoire que l'exécutable. Souhaitez vous continuer ? - + Restart needed Un redémarrage est nécessaire - + Some changes will not take effect until the emulator is restarted. Certains changements ne prendront effet qu'après le redémarrage de l'émulateur. - + - Player %1 of %2 - Joueur %1 of %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Fichier - + Load &ROM... Charger une &ROM… - + Load ROM in archive... Charger la ROM d'une archive… - + Add folder to library... Ajouter un dossier à la bibliothèque… - + Load &patch... Charger un c&orrectif… - + Boot BIOS Démarrer le BIOS - + Replace ROM... Remplacer la ROM… - + + Game state views + + + + Convert e-Reader card image to raw... - + ROM &info... &Infos sur la ROM… - + Recent Récent - + Make portable Rendre portable - + &Load state &Charger un état - + &Save state &Sauvegarder un état - + Quick load Chargement rapide - + Quick save Sauvegarde rapide - + Load recent Charger un fichier récent - + Save recent Sauvegarder un fichier récent - + Undo load state Annuler le chargement de l'état - + Undo save state Annuler la sauvegarde de l'état - - + + State &%1 État &%1 - + Load camera image... Charger une image de la caméra… - + Convert save game... - + New multiplayer window Nouvelle fenêtre multijoueur - + Connect to Dolphin... - + Report bug... Signalement de l'erreur… - + E&xit &Quitter - + &Emulation &Émulation - + &Reset &Réinitialiser - + Sh&utdown Extin&ction - + Yank game pak Yank game pak - + &Pause &Pause - + &Next frame &Image suivante - + Fast forward (held) Avance rapide (maintenir) - + &Fast forward A&vance rapide - + Fast forward speed Vitesse de l'avance rapide - + Unbounded Sans limites - + %0x %0x - + Rewind (held) Rembobiner (maintenir) - + Re&wind Rem&bobiner - + Step backwards Retour en arrière - + Solar sensor Capteur solaire - + Increase solar level Augmenter le niveau solaire - + Decrease solar level Diminuer le niveau solaire - + Brightest solar level Tester le niveau solaire - + Darkest solar level Assombrir le niveau solaire - + Brightness %1 Luminosité %1 - + Audio/&Video Audio/&Vidéo - + Frame size Taille de l'image - + Toggle fullscreen Basculer en plein écran - + Lock aspect ratio Bloquer les proportions - + Force integer scaling Forcer la mise à l'échelle par des nombres entiers - + Bilinear filtering Filtrage bilinèaire - + Frame&skip &Saut d'image - + Mute Muet - + FPS target FPS ciblé - + Take &screenshot Prendre une ca&pture d'écran - + F12 F12 - + Game Boy Printer... Imprimante GameBoy… - + Video layers Couches vidéo - + Audio channels Canaux audio - + Adjust layer placement... Ajuster la disposition… - + &Tools Ou&tils - + View &logs... Voir les &journaux… - + Game &overrides... - + Couldn't Start N'a pas pu démarrer - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + GameShark saves (*.gsv *.sps *.xps) - + Could not start game. Impossible de démarrer le jeu. - + Load alternate save game... - + Load temporary save game... - + Scan e-Reader dotcodes... Scanner les dotcodes e-Reader... - + Load state file... Charger le fichier d'état... - + Save state file... Enregistrer le fichier d'état... - + Import GameShark Save... Importer la sauvegarde de GameShark... - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games Sauvegarder les jeux - + Export GameShark Save... Exporter la sauvegarde de GameShark... - + Automatically determine - + Use player %0 save game - + About... À propos de… - + BattleChip Gate... - + %1× %1× - + Interframe blending Mélange d'images - + Native (59.7275) Natif (59.7275) - + Record A/V... Enregistrer A/V... - + Record GIF/WebP/APNG... Enregistrer GIF/WebP/APNG... - + Game Pak sensors... Capteurs de la Game Pak... - + &Cheats... &Cheats… - + Settings... Paramètres… - + Open debugger console... Ouvrir la console de débug… - + Start &GDB server... Démarrer le serveur &GDB… - + + Scripting... + + + + View &palette... Voir la &palette… - + View &sprites... Voir les &sprites… - + View &tiles... Voir les &tiles… - + View &map... Voir la &map… - + &Frame inspector... Inspecteur de &frame... - + View memory... Voir la mémoire… - + Search memory... Recherche dans la mémoire… - + View &I/O registers... Voir les registres d'&E/S... - + Record debug video log... Enregistrer le journal vidéo de débogage... - + Stop debug video log Arrêter le journal vidéo de débogage - + Exit fullscreen Quitter le plein écran - + GameShark Button (held) Bouton GameShark (maintenir) - + Autofire Tir automatique - + Autofire A Tir automatique A - + Autofire B Tir automatique B - + Autofire L Tir automatique L - + Autofire R Tir automatique R - + Autofire Start Tir automatique Start - + Autofire Select Tir automatique Select - + Autofire Up Tir automatique Up - + Autofire Right Tir automatique Right - + Autofire Down Tir automatique Down - + Autofire Left Tir automatique Gauche - + Clear Vider @@ -5163,6 +5186,59 @@ Taille du téléchargement : %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + &Réinitialiser + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5176,65 +5252,75 @@ Taille du téléchargement : %3 Horloge en temps réel - + Fixed time Heure fixe - + System time Heure du système - + Start time at Heure de début à - + Now Maintenant - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP This is the most common format used in the francophile world dd/MM/yy HH:mm:ss - + Light sensor Capteur de lumière - + Brightness Luminosité - + Tilt sensor Capteur d'inclinaison - - + + Set Y Ensemble Y - - + + Set X Ensemble X - + Gyroscope Gyroscope - + Sensitivity Sensibilité diff --git a/src/platform/qt/ts/mgba-hu.ts b/src/platform/qt/ts/mgba-hu.ts index af928439c..6f1ab8711 100644 --- a/src/platform/qt/ts/mgba-hu.ts +++ b/src/platform/qt/ts/mgba-hu.ts @@ -1184,36 +1184,36 @@ A Game Boy Advance a Nintendo Co., Ltd. bejegyzett védjegye - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + Current version: %1 New version: %2 Download size: %3 - + Downloading update... - + Downloading failed. Please update manually. - + Downloading done. Press OK to restart %1 and install the update. @@ -1236,7 +1236,7 @@ Download size: %3 - + (None) @@ -1267,17 +1267,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) - - + + Select cheats file Csalásfájl kiválasztása + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1287,43 +1292,43 @@ Download size: %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 Nem sikerült a mentésfájl megnyitása: %1 - + Failed to open game file: %1 Nem sikerült a játékfájl megnyitása: %1 - + Can't yank pack in unexpected platform! A játékkazettát nem lehet kirántani ismeretlen platformon! - + Failed to open snapshot file for reading: %1 A pillanatkép fájljának olvasásra való megnyitása sikertelen: %1 - + Failed to open snapshot file for writing: %1 A pillanatkép fájljának írásra való megnyitása sikertelen: %1 @@ -1336,12 +1341,12 @@ Download size: %3 Nem sikerült a játékfájl megnyitása: %1 - + Could not load game. Are you sure it's in the correct format? A játék betöltése nem sikerült. Biztos vagy benne, hogy a megfelelő formátumú? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3405,27 +3410,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State - + Save State - + Empty - + Corrupted - + Slot %1 @@ -3477,47 +3482,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 - + An error occurred - + DEBUG - + STUB - + INFO - + WARN - + ERROR - + FATAL - + GAME ERROR @@ -3641,42 +3646,42 @@ Download size: %3 Betöltés - + All Mind - + Load TBL TBL betöltése - + Save selected memory - + Failed to open output file: %1 Nem sikerült a kimeneti fájl megnyitása: %1 - + Load memory - + Failed to open input file: %1 - + TBL - + ISO-8859-1 @@ -3854,12 +3859,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -3927,104 +3932,112 @@ Download size: %3 + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None Nincs - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4122,774 +4135,784 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) - + Game Boy ROMs (%1) - + All ROMs (%1) - + %1 Video Logs (*.mvl) - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + + Scripting... + + + + + Game state views + + + + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Napló törlése @@ -5135,6 +5158,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5148,64 +5224,74 @@ Download size: %3 Valósidejű óra - + Fixed time - + System time - + Start time at - + Now - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP - + Light sensor Fényérzékelő - + Brightness - + Tilt sensor - - + + Set Y - - + + Set X - + Gyroscope Giroszkóp - + Sensitivity diff --git a/src/platform/qt/ts/mgba-it.ts b/src/platform/qt/ts/mgba-it.ts index a99033108..11f968726 100644 --- a/src/platform/qt/ts/mgba-it.ts +++ b/src/platform/qt/ts/mgba-it.ts @@ -1185,21 +1185,21 @@ Game Boy Advance è un marchio registrato di Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. Vuoi scaricarlo e installarlo adesso? Dovrai riavviare l'emulatore quando il download sarà completato. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. L'aggiornamento automatico non è disponibile su questa piattaforma. Se vuoi aggiornare dovrai farlo manualmente. - + Current version: %1 New version: %2 Download size: %3 @@ -1208,17 +1208,17 @@ Nuova versione: %2 Dimensione del download: %3 - + Downloading update... Scaricamento aggiornamento in corso... - + Downloading failed. Please update manually. Download fallito. Si prega di aggiornare manualmente. - + Downloading done. Press OK to restart %1 and install the update. Download terminato. Premi OK per riavviare %1 e installare l'aggiornamento. @@ -1241,7 +1241,7 @@ Dimensione del download: %3 Sconosciuto - + (None) (Nessuno) @@ -1272,17 +1272,22 @@ Dimensione del download: %3 QGBA::CheatsView - - + + Autodetect (recommended) Rilevamento automatico (consigliato) - - + + Select cheats file Seleziona il file cheats + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1292,43 +1297,43 @@ Dimensione del download: %3 Reset r%1-%2 %3 - - + + Rewinding not currently enabled Rewind attualmente disattivato - + Reset the game? Resettare il gioco? - + Most games will require a reset to load the new save. Do you want to reset now? La maggior parte dei giochi richiederà un reset per caricare il nuovo salvataggio. Vuoi resettare ora? - + Failed to open save file: %1 Impossibile aprire il file di salvataggio: %1 - + Failed to open game file: %1 Impossibile aprire il file di gioco: %1 - + Can't yank pack in unexpected platform! Non riesco a strappare il pacchetto in una piattaforma inaspettata! - + Failed to open snapshot file for reading: %1 Impossibile aprire il file snapshot per la lettura: %1 - + Failed to open snapshot file for writing: %1 Impossibile aprire il file snapshot per la scrittura: %1 @@ -1341,12 +1346,12 @@ Dimensione del download: %3 Impossibile aprire il file di gioco: %1 - + Could not load game. Are you sure it's in the correct format? Impossibile caricare il gioco. Sei sicuro che sia nel formato corretto? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Impossibile aprire il file di salvataggio; i salvataggi in gioco non possono essere aggiornati. Assicurati che la directory di salvataggio sia scrivibile senza privilegi aggiuntivi (ad esempio UAC su Windows). @@ -3410,27 +3415,27 @@ Dimensione del download: %3 QGBA::LoadSaveState - + Load State Carica stato - + Save State Salva stato - + Empty Vuoto - + Corrupted Corrotto - + Slot %1 Slot %1 @@ -3482,47 +3487,47 @@ Dimensione del download: %3 QGBA::LogController - + [%1] %2: %3 [%1] %2: %3 - + An error occurred È avvenuto un errore - + DEBUG DEBUG - + STUB STUB - + INFO INFORMAZIONI - + WARN AVVERTENZA - + ERROR ERRORE - + FATAL FATALE - + GAME ERROR ERRORE NEL GIOCO @@ -3646,42 +3651,42 @@ Dimensione del download: %3 Carica - + All Tutto - + Load TBL Carica TBL - + Save selected memory Salva la memoria selezionata - + Failed to open output file: %1 Impossibile aprire il file di output: %1 - + Load memory Carica memoria - + Failed to open input file: %1 Impossibile aprire il file di input: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3859,12 +3864,12 @@ Dimensione del download: %3 QGBA::ReportView - + Bug report archive Archivio delle segnalazioni di bug - + ZIP archive (*.zip) Archivio ZIP (*.zip) @@ -3932,97 +3937,105 @@ Dimensione del download: %3 Impossibile convertire salvataggi tra piattaforme + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (forza la versione 1.x) - + None Nessuno - + None (Still Image) nessuno (immagine fissa) - + Keyboard Tastiera - + Controllers Controller - + Shortcuts Scorciatoie - - + + Shaders Shader - + Select BIOS Seleziona BIOS - + Select directory Seleziona directory - + (%1×%2) (%1×%2) - + Never Mai - + Just now Solo adesso - + Less than an hour ago Meno di un ora fa - + %n hour(s) ago %n ora fa @@ -4030,7 +4043,7 @@ Dimensione del download: %3 - + %n day(s) ago %n giorno fa @@ -4129,115 +4142,115 @@ Dimensione del download: %3 QGBA::Window - + Game Boy Advance ROMs (%1) ROM per Game Boy Advance (%1) - + Game Boy ROMs (%1) ROM per Game Boy (%1) - + All ROMs (%1) Tutte le ROM (%1) - + %1 Video Logs (*.mvl) %1 log Video (*.mvl) - + Archives (%1) Archivi (%1) - - - + + + Select ROM Seleziona ROM - - + + Select save Seleziona salvataggio - + Select patch Seleziona patch - + Patches (*.ips *.ups *.bps) Patch (*.ips *.ups *.bps) - + Select e-Reader dotcode Selezione e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) e-Reader card (*.raw *.bin *.bmp) - + Select e-Reader card images Seleziona immagini carte e-Reader - + Image file (*.png *.jpg *.jpeg) File immagine (*.png *.jpg *.jpeg) - + Conversion finished Conversione terminata - + %1 of %2 e-Reader cards converted successfully. %1 di %2 carte e-Reader convertite con successo. - + Select image Seleziona immagine - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) File immagine (*.png *.gif *.jpg *.jpeg);;Tutti i file (*) - + GameShark saves (*.sps *.xps) Salvataggi GameShark (*.sps *.xps) - + Select video log Seleziona log video - + Video logs (*.mvl) Log video (*.mvl) - + Crash Errore fatale - + The game has crashed with the following error: %1 @@ -4246,659 +4259,669 @@ Dimensione del download: %3 %1 - + Unimplemented BIOS call BIOS non implementato - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Questo gioco utilizza una chiamata BIOS non implementata. Utilizza il BIOS ufficiale per una migliore esperienza. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Impossibile creare un dispositivo di visualizzazione appropriato, tornando alla visualizzazione software. I giochi possono funzionare lentamente, specialmente con finestre più grandi. - + Really make portable? Vuoi davvero rendere portatile l'applicazione? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? In questo modo l'emulatore carica la propria configurazione dalla stessa cartella dell'eseguibile. Vuoi continuare? - + Restart needed È necessario riavviare - + Some changes will not take effect until the emulator is restarted. Alcune modifiche non avranno effetto finché l'emulatore non verrà riavviato. - + - Player %1 of %2 - Giocatore %1 di %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File File - + Load &ROM... Carica ROM... - + Load ROM in archive... Carica la ROM in archivio... - + Load &patch... Carica patch... - + Boot BIOS Avvia BIOS - + Replace ROM... Sostituisci la ROM... - + Scan e-Reader dotcodes... Scansiona e-Reader dotcode... - + Convert e-Reader card image to raw... Converti immagini carte e-Reader in raw... - + ROM &info... Informazioni ROM... - + Recent Recenti - + Make portable Rendi portatile - + &Load state Carica stato - + &Save state Salva stato - + Quick load Caricamento rapido - + Quick save Salvataggio rapido - + Load recent Carica recente - + Save recent Salva recente - + Undo load state Annulla il caricamento dello stato - + Undo save state Annulla salvataggio stato - - + + State &%1 Stato %1 - + Load camera image... Carica immagine fotocamera... - + Convert save game... Converti salvataggi... - + GameShark saves (*.gsv *.sps *.xps) Salvataggi GameShark (*.gsv *.sps *.xps) - + Reset needed Reset necessario - + Some changes will not take effect until the game is reset. Alcune modifiche non si applicheranno finché il gioco non viene resettato. - + New multiplayer window Nuova finestra multigiocatore - + Connect to Dolphin... Connessione a Dolphin... - + Report bug... Segnala bug... - + E&xit Esci (&X) - + &Emulation Emulazione - + &Reset Reset - + Sh&utdown Spegni (&U) - + Yank game pak Yank game pak - + &Pause Pausa - + &Next frame Salta il prossimo frame (&N) - + Fast forward (held) Avanzamento rapido (tieni premuto) - + &Fast forward Avanzamento rapido (&F) - + Fast forward speed Velocità di avanzamento rapido - + Unbounded Illimitata - + %0x %0x - + Rewind (held) Riavvolgimento (tieni premuto) - + Re&wind Riavvolgimento (&W) - + Step backwards Torna indietro - + Solar sensor Sensore solare - + Increase solar level Incrementa il livello solare - + Decrease solar level Riduci il livello solare - + Brightest solar level Livello solare massimo - + Darkest solar level Livello solare minimo - + Brightness %1 Luminosità %1 - + Audio/&Video Audio/Video - + Frame size Dimensione frame - + Toggle fullscreen Abilita schermo Intero - + Lock aspect ratio Blocca rapporti aspetto - + Frame&skip Salto frame - + Mute Muto - + FPS target FPS finali - + Take &screenshot Acquisisci schermata - + F12 F12 - + Record GIF/WebP/APNG... Registra GIF / WebP / APNG ... - + Video layers Layers video - + Audio channels Canali audio - + &Tools Strumenti - + View &logs... Visualizza registri (&log)... - + Game &overrides... Valore specifico per il gioco... - + &Cheats... Trucchi... - + Open debugger console... Apri console debugger... - + Start &GDB server... Avvia server GDB... - + Settings... Impostazioni... - + Select folder Seleziona cartella - + Couldn't Start Non è stato possibile avviare - + Could not start game. Non è stato possibile avviare il gioco. - + Add folder to library... Aggiungi cartella alla libreria... - + Load state file... Carica stato di salvataggio... - + Save state file... Salva stato di salvataggio... - + Import GameShark Save... Importa Salvataggio GameShark... - + Export GameShark Save... Esporta Salvataggio GameShark... - + About... Informazioni… - + Force integer scaling Forza ridimensionamento a interi - + Bilinear filtering Filtro bilineare - + Game Boy Printer... Stampante Game Boy... - + Save games (%1) Salvataggi (%1) - + Select save game Seleziona salvataggio - + mGBA save state files (%1) file di stati di salvataggio mGBA (%1) - - + + Select save state Seleziona stato di salvataggio - + Save games Salvataggi - + Load alternate save game... Carica stato di salvataggio alternativo... - + Load temporary save game... Carica salvataggio temporaneo... - + Automatically determine Determina automaticamente - + Use player %0 save game Usa il salvataggio del giocatore %0 - + BattleChip Gate... BattleChip Gate... - + %1× %1x - + Interframe blending Miscelazione dei frame - + Native (59.7275) Nativo (59.7) - + Record A/V... Registra A/V... - + Adjust layer placement... Regola posizionamento layer... - + Game Pak sensors... Sensori Game Pak... - + + Scripting... + + + + + Game state views + + + + View &palette... Mostra palette... - + View &sprites... Mostra sprites... - + View &tiles... Mostra tiles... - + View &map... Mostra mappa... - + &Frame inspector... &Frame inspector... - + View memory... Mostra memoria... - + Search memory... Ricerca memoria... - + View &I/O registers... Mostra registri I/O... - + Record debug video log... Registra video log di debug... - + Stop debug video log Ferma video log di debug - + Exit fullscreen Esci da Schermo Intero - + GameShark Button (held) Pulsante GameShark (tieni premuto) - + Autofire Pulsanti Autofire - + Autofire A Autofire A - + Autofire B Autofire B - + Autofire L Autofire L - + Autofire R Autofire R - + Autofire Start Autofire Start - + Autofire Select Autofire Select - + Autofire Up Autofire Su - + Autofire Right AAutofire Destra - + Autofire Down Autofire Giù - + Autofire Left Autofire Sinistra - + Clear Pulisci @@ -5144,6 +5167,59 @@ Dimensione del download: %3 %1 GameShark Advance SP %2 salvataggio gioco + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + Reset + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5157,64 +5233,74 @@ Dimensione del download: %3 Clock in tempo reale - + Fixed time Ora fissa - + System time Ora del sistema - + Start time at Avvia tempo a - + Now Adesso - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP dd/MM/yy HH:mm:ss - + Light sensor Sensore di luce - + Brightness Luminosità - + Tilt sensor Sensore di inclinazione - - + + Set Y Config. Y - - + + Set X Config. X - + Gyroscope Giroscopio - + Sensitivity Sensibilità diff --git a/src/platform/qt/ts/mgba-ja.ts b/src/platform/qt/ts/mgba-ja.ts index 4b9e82ccf..bb94da436 100644 --- a/src/platform/qt/ts/mgba-ja.ts +++ b/src/platform/qt/ts/mgba-ja.ts @@ -1184,36 +1184,36 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + Current version: %1 New version: %2 Download size: %3 - + Downloading update... - + Downloading failed. Please update manually. - + Downloading done. Press OK to restart %1 and install the update. @@ -1236,7 +1236,7 @@ Download size: %3 不明 - + (None) @@ -1267,17 +1267,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) - - + + Select cheats file チートファイルを選択 + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1287,43 +1292,43 @@ Download size: %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 セーブファイルを開けませんでした: %1 - + Failed to open game file: %1 ゲームファイルを開けませんでした: %1 - + Can't yank pack in unexpected platform! 予期しないプラットフォームでパックをヤンクすることはできません! - + Failed to open snapshot file for reading: %1 読み取り用のスナップショットファイルを開けませんでした: %1 - + Failed to open snapshot file for writing: %1 書き込み用のスナップショットファイルを開けませんでした: %1 @@ -1336,12 +1341,12 @@ Download size: %3 ゲームファイルを開けませんでした: %1 - + Could not load game. Are you sure it's in the correct format? ゲームをロードできませんでした。ゲームのフォーマットが正しいことを確認してください。 - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3405,27 +3410,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State ステートをロード - + Save State ステートをセーブ - + Empty - + Corrupted 破損 - + Slot %1 スロット %1 @@ -3477,47 +3482,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 [%1] %2: %3 - + An error occurred エラーが発生しました - + DEBUG DEBUG - + STUB STUB - + INFO INFO - + WARN WARN - + ERROR ERROR - + FATAL FATAL - + GAME ERROR GAME ERROR @@ -3641,42 +3646,42 @@ Download size: %3 ロード - + All All - + Load TBL TBLをロード - + Save selected memory 選択したメモリを保存 - + Failed to open output file: %1 出力ファイルを開けませんでした: %1 - + Load memory メモリをロード - + Failed to open input file: %1 入力ファイルを開けませんでした: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3854,12 +3859,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive バグレポートアーカイブ - + ZIP archive (*.zip) ZIPアーカイブ (*.zip) @@ -3927,104 +3932,112 @@ Download size: %3 + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) ソフト(Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL(バージョン1.xを強制) - + None なし - + None (Still Image) なし(静止画) - + Keyboard キーボード - + Controllers コントローラー - + Shortcuts ショートカット - - + + Shaders シェーダー - + Select BIOS BIOSを選択 - + Select directory - + (%1×%2) (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4122,100 +4135,100 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) ゲームボーイアドバンスファイル (%1) - + Game Boy ROMs (%1) ゲームボーイファイル (%1) - + All ROMs (%1) すべてのファイル (%1) - + %1 Video Logs (*.mvl) %1ビデオログ (*.mvl) - + Archives (%1) アーカイブファイル (%1) - - - + + + Select ROM ROMを開く - + Select folder フォルダを開く - - + + Select save セーブを開く - + Select patch パッチを開く - + Patches (*.ips *.ups *.bps) パッチファイル (*.ips *.ups *.bps) - + Select e-Reader dotcode カードeを開く - + e-Reader card (*.raw *.bin *.bmp) カードe (*.raw *.bin *.bmp) - + Select image 画像を開く - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) 画像ファイル (*.png *.gif *.jpg *.jpeg);;すべてのファイル (*) - + GameShark saves (*.sps *.xps) GameSharkセーブファイル (*.sps *.xps) - + Select video log ビデオログを開く - + Video logs (*.mvl) ビデオログ (*.mvl) - + Crash クラッシュ - + The game has crashed with the following error: %1 @@ -4224,674 +4237,684 @@ Download size: %3 %1 - + Couldn't Start 起動失敗 - + Could not start game. ゲームを起動できませんでした。 - + Unimplemented BIOS call 未実装のBIOS呼び出し - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. このゲームは実装されていないBIOS呼び出しを使用します。最高のエクスペリエンスを得るには公式のBIOSを使用してください。 - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. 適切なディスプレイデバイスの作成に失敗し、ソフトディスプレイにフォールバックしました。特に大きなウィンドウでは、ゲームの実行が遅い場合があります。 - + Really make portable? 本当にポータブルにしますか? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? これによりエミュレータは実行ファイルと同じディレクトリにある設定ファイルをロードします。続けますか? - + Restart needed 再起動が必要 - + Some changes will not take effect until the emulator is restarted. 一部の変更は、エミュレータを再起動するまで有効になりません。 - + - Player %1 of %2 - プレーヤー %1 of %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &ファイル (&F) - + Load &ROM... ROMをロード... - + Load ROM in archive... アーカイブにROMをロード... - + Add folder to library... ライブラリーにフォルダを追加... - + Load &patch... パッチをロード... (&P) - + Boot BIOS BIOSを起動 - + Replace ROM... ROMを交換... - + Scan e-Reader dotcodes... カードeをスキャン... - + ROM &info... ROM情報... (&I) - + Recent 最近開いたROM - + Make portable ポータブル化 - + &Load state ステートをロード (&L) - + Report bug... バグ報告 - + About... バージョン情報... - + Record GIF/WebP/APNG... GIF/WebP/APNGを記録 - + Game Pak sensors... カートリッジセンサー... - + Clear 消去 - + Load state file... ステートファイルをロード... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Convert e-Reader card image to raw... - + &Save state ステートをセーブ (&S) - + Save state file... ステートファイルをセーブ... - + Quick load クイックロード - + Quick save クイックセーブ - + Load recent 最近使ったクイックロットからロード - + Save recent 最近使ったクイックロットにセーブ - + Undo load state クイックロードを元に戻す - + Undo save state クイックセーブを元に戻す - - + + State &%1 クイックスロット &%1 - + Load camera image... カメラ画像をロード... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Import GameShark Save... GameSharkスナップショットを読み込む - + Export GameShark Save... GameSharkスナップショットを書き出す - + New multiplayer window 新しいウィンドウ(マルチプレイ) - + Connect to Dolphin... - + E&xit 終了 (&X) - + &Emulation エミュレーション (&E) - + &Reset リセット (&R) - + Sh&utdown 閉じる (&U) - + Yank game pak ゲームパックをヤンク - + &Pause 一時停止 (&P) - + &Next frame 次のフレーム (&N) - + Fast forward (held) 早送り(押し) - + &Fast forward 早送り (&F) - + Fast forward speed 早送り速度 - + Unbounded 制限なし - + %0x %0x - + Rewind (held) 巻戻し(押し) - + Re&wind 巻戻し (&R) - + Step backwards 1フレーム巻き戻す - + Solar sensor 太陽センサー - + Increase solar level 明るさを上げる - + Decrease solar level 明るさを下げる - + Brightest solar level 明るさ最高 - + Darkest solar level 明るさ最低 - + Brightness %1 明るさ %1 - + Audio/&Video オーディオ/ビデオ (&V) - + Frame size 画面サイズ - + Toggle fullscreen 全画面表示 - + Lock aspect ratio 縦横比を固定 - + Force integer scaling 整数スケーリングを強制 - + Bilinear filtering バイリニアフィルタリング - + Frame&skip フレームスキップ (&S) - + Mute ミュート - + FPS target FPS - + Native (59.7275) ネイティブ(59.7275) - + Take &screenshot スクリーンショット (&S) - + F12 F12 - + Game Boy Printer... ポケットプリンタ... - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games セーブデータ - + Automatically determine - + Use player %0 save game - + BattleChip Gate... チップゲート... - + %1× %1× - + Interframe blending フレーム間混合 - + Record A/V... ビデオ録画... - + Video layers ビデオレイヤー - + Audio channels オーディオチャンネル - + Adjust layer placement... レイヤーの配置を調整... - + &Tools ツール (&T) - + View &logs... ログビューアー... (&L) - + Game &overrides... ゲーム別設定... (&O) - + &Cheats... チート... (&C) - + Settings... 設定... - + Open debugger console... デバッガコンソールを開く... - + Start &GDB server... GDBサーバを起動... (&G) - + + Scripting... + + + + + Game state views + + + + View &palette... パレットビューアー... (&P) - + View &sprites... スプライトビューアー... (&S) - + View &tiles... タイルビューアー... (&T) - + View &map... マップビューアー... (&M) - + &Frame inspector... フレームインスペクタ... (&F) - + View memory... メモリビューアー... - + Search memory... メモリ検索... - + View &I/O registers... IOビューアー... (&I) - + Record debug video log... デバッグビデオログ... - + Stop debug video log デバッグビデオログを停止 - + Exit fullscreen 全画面表示を終了 - + GameShark Button (held) GameSharkボタン(押し) - + Autofire 連打 - + Autofire A 連打 A - + Autofire B 連打 B - + Autofire L 連打 L - + Autofire R 連打 R - + Autofire Start 連打 Start - + Autofire Select 連打 Select - + Autofire Up 連打 上 - + Autofire Right 連打 右 - + Autofire Down 連打 下 - + Autofire Left 連打 左 @@ -5137,6 +5160,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + リセット (&R) + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5150,64 +5226,74 @@ Download size: %3 リアルタイムクロック - + Fixed time 定刻 - + System time システム日時 - + Start time at 日時指定 - + Now 現在日時 - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP yyyy/MM/dd HH:mm:ss - + Light sensor 光センサー - + Brightness 明るさ - + Tilt sensor 動きセンサー - - + + Set Y 垂直方向 - - + + Set X 水平方向 - + Gyroscope 回転センサー - + Sensitivity 感度 diff --git a/src/platform/qt/ts/mgba-ko.ts b/src/platform/qt/ts/mgba-ko.ts index 5b18c099a..61bf09771 100644 --- a/src/platform/qt/ts/mgba-ko.ts +++ b/src/platform/qt/ts/mgba-ko.ts @@ -1184,36 +1184,36 @@ Game Boy Advance는 Nintendo Co., Ltd.의 등록 상표입니다. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + Current version: %1 New version: %2 Download size: %3 - + Downloading update... - + Downloading failed. Please update manually. - + Downloading done. Press OK to restart %1 and install the update. @@ -1236,7 +1236,7 @@ Download size: %3 - + (None) @@ -1267,17 +1267,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) - - + + Select cheats file 치트 파일 선택 + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1287,43 +1292,43 @@ Download size: %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 저장 파일을 열지 못했습니다: %1 - + Failed to open game file: %1 게임 파일을 열지 못했습니다: %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 읽기 용 스냅샷 파일을 열지 못했습니다: %1 - + Failed to open snapshot file for writing: %1 쓰기 용 스냅샷 파일을 열지 못했습니다: %1 @@ -1336,12 +1341,12 @@ Download size: %3 게임 파일을 열지 못했습니다: %1 - + Could not load game. Are you sure it's in the correct format? 게임을 로드할 수 없습니다. 올바른 형식인지 확인하십시오. - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3405,27 +3410,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State 로드 상태 - + Save State 저장 상태 - + Empty - + Corrupted 손상 - + Slot %1 슬롯 %1 @@ -3477,47 +3482,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 - + An error occurred - + DEBUG 디버그 - + STUB 스텁 - + INFO 정보 - + WARN 경고 - + ERROR 오류 - + FATAL 치명적 - + GAME ERROR 게임 오류 @@ -3641,42 +3646,42 @@ Download size: %3 로드 - + All 모두 - + Load TBL 테이블 로드 - + Save selected memory 선택한 메모리 저장 - + Failed to open output file: %1 출력 파일을 열지 못했습니다: %1 - + Load memory 메모리 로드 - + Failed to open input file: %1 입력 파일을 열지 못했습니다: %1 - + TBL 테이블 - + ISO-8859-1 ISO-8859-1 @@ -3854,12 +3859,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -3927,104 +3932,112 @@ Download size: %3 + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia Qt 멀티미디어 - + SDL SDL - + Software (Qt) 소프트웨어 (Qt) - + OpenGL 오픈GL - + OpenGL (force version 1.x) 오픈GL (버전 1.x 강제) - + None 없음 - + None (Still Image) 없음 (정지 이미지) - + Keyboard 키보드 - + Controllers 컨트롤러 - + Shortcuts 단축키 - - + + Shaders 쉐이더 - + Select BIOS 바이오스 선택 - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4122,115 +4135,115 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) 게임 보이 어드밴스 롬 (%1) - + Game Boy ROMs (%1) 게임 보이 (%1) - + All ROMs (%1) 모든 롬 (%1) - + %1 Video Logs (*.mvl) %1 비디오 로그 (*.mvl) - + Archives (%1) 아카이브 (%1) - - - + + + Select ROM 롬 선택 - - + + Select save 저장 파일 선택 - + Select patch 패치 선택 - + Patches (*.ips *.ups *.bps) 패치 (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Select image 이미지 선택 - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) 이미지 파일 (*.png *.gif *.jpg *.jpeg);;모든 파일 (*) - + GameShark saves (*.sps *.xps) 게임샤크 저장 파일 (*.sps *.xps) - + Select video log 비디오 로그 선택 - + Video logs (*.mvl) 비디오 로그 (*.mvl) - + Crash 치명적인 오류 - + The game has crashed with the following error: %1 @@ -4239,659 +4252,669 @@ Download size: %3 %1 - + Unimplemented BIOS call 구현되지 않은 바이오스 호출 - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. 이 게임은 구현되지 않은 바이오스 호출을 사용합니다. 최상의 성능을 얻으려면 공식 바이오스를 사용하십시오. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? 정말로 휴대용을 만듭니까? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? 이렇게하면 에뮬레이터가 실행 파일과 동일한 디렉토리에서 구성을 로드하게됩니다. 계속 하시겠습니까? - + Restart needed 재시작 필요 - + Some changes will not take effect until the emulator is restarted. 일부 변경 사항은 에뮬레이터가 다시 시작될 때까지 적용되지 않습니다. - + Reset needed - + Some changes will not take effect until the game is reset. - + - Player %1 of %2 - 플레이어 %1 의 %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &파일 - + Load &ROM... 로드 &롬... - + Load ROM in archive... 롬을 아카이브에 로드... - + Save games 게임 저장 - + Automatically determine - + Use player %0 save game - + Load &patch... 로드 &패치... - + Boot BIOS BIOS 부팅 - + Replace ROM... 롬 교체... - + Scan e-Reader dotcodes... - + + Game state views + + + + Convert e-Reader card image to raw... - + ROM &info... 롬 &정보... - + Recent 최근 실행 - + Make portable 휴대용 만들기 - + &Load state &로드 파일 상태 - + &Save state &저장 파일 상태 - + Quick load 빠른 로드 - + Quick save 빠른 저장 - + Load recent 최근 실행 로드 - + Save recent 최근 실행 저장 - + Undo load state 로드 파일 상태 복원 - + Undo save state 저장 파일 상태 복원 - - + + State &%1 상태 &%1 - + Load camera image... 카메라 이미지 로드... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + New multiplayer window 새로운 멀티 플레이어 창 - + Connect to Dolphin... - + E&xit 종&료 - + &Emulation &에뮬레이션 - + &Reset &재설정 - + Sh&utdown 종&료 - + Yank game pak 양키 게임 팩 - + &Pause &정지 - + &Next frame &다음 프레임 - + Fast forward (held) 빨리 감기 (누름) - + &Fast forward &빨리 감기 - + Fast forward speed 빨리 감기 속도 - + Unbounded 무제한 - + %0x %0x - + Rewind (held) 되김기 (누름) - + Re&wind 리&와인드 - + Step backwards 돌아가기 - + Brightest solar level 가장 밝은 태양 수준 - + Darkest solar level 가장 어두운 태양 수준 - + Brightness %1 밝기 %1 - + Audio/&Video 오디오/&비디오 - + Frame size 프레임 크기 - + Toggle fullscreen 전체화면 전환 - + Lock aspect ratio 화면비 잠금 - + Frame&skip 프레임&건너뛰기 - + Mute 무음 - + FPS target FPS 대상 - + Take &screenshot 스크린샷 &찍기 - + F12 F12 - + Video layers 비디오 레이어 - + Audio channels 오디오 채널 - + &Tools &도구 - + View &logs... 로그 &보기... - + Game &overrides... 게임 &오버라이드... - + &Cheats... &치트.. - + Open debugger console... 디버거 콘솔 열기... - + Start &GDB server... GDB 서버 &시작... - + Settings... 설정... - + Select folder 폴더 선택 - + Couldn't Start - + Could not start game. - + Add folder to library... 라이브러리에 폴더 추가... - + Load state file... - + Save state file... - + Import GameShark Save... - + Export GameShark Save... - + Report bug... - + About... - + Solar sensor - + Increase solar level - + Decrease solar level - + Force integer scaling 정수 스케일링 강제 수행 - + Bilinear filtering 이중선형 필터링 - + Game Boy Printer... 게임 보이 프린터... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Load alternate save game... - + Load temporary save game... - + BattleChip Gate... - + %1× %1x {1×?} - + Interframe blending - + Native (59.7275) Nativo (59.7) {59.7275)?} - + Record A/V... - + Record GIF/WebP/APNG... - + Adjust layer placement... 레이어 배치 조정... - + Game Pak sensors... - + + Scripting... + + + + View &palette... 팔레트 &보기... - + View &sprites... 스프라이트 &보기... - + View &tiles... 타일 &보기... - + View &map... 지도 &보기... - + &Frame inspector... - + View memory... 메모리 보기... - + Search memory... 메모리 검색... - + View &I/O registers... I/O 레지스터 &보기... - + Record debug video log... - + Stop debug video log - + Exit fullscreen 전체화면 종료 - + GameShark Button (held) 게임샤크 버튼 (누름) - + Autofire 연사 - + Autofire A 연사 A - + Autofire B 연사 B - + Autofire L 연사 L - + Autofire R 연사 R - + Autofire Start 연사 시작 - + Autofire Select - + Clear 정리 - + Autofire Up 연사 위쪽 - + Autofire Right 연사 오른쪽 - + Autofire Down 연사 아래쪽 - + Autofire Left 연사 왼쪽 @@ -5137,6 +5160,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + &재설정 + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5150,64 +5226,74 @@ Download size: %3 실시간 시계 - + Fixed time 수정된 시간 - + System time 시스템 시간 - + Start time at 시작 시간 - + Now 지금 - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP gg/MM/aa OO:mm:ss - + Light sensor 광센서 - + Brightness 밝기 - + Tilt sensor 기울기 센서 - - + + Set Y Y 설정 - - + + Set X X 설정 - + Gyroscope 자이로스코프 - + Sensitivity 감광도 diff --git a/src/platform/qt/ts/mgba-ms.ts b/src/platform/qt/ts/mgba-ms.ts index f75a32b1f..83e391e03 100644 --- a/src/platform/qt/ts/mgba-ms.ts +++ b/src/platform/qt/ts/mgba-ms.ts @@ -1183,36 +1183,36 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + Current version: %1 New version: %2 Download size: %3 - + Downloading update... - + Downloading failed. Please update manually. - + Downloading done. Press OK to restart %1 and install the update. @@ -1235,7 +1235,7 @@ Download size: %3 - + (None) @@ -1266,17 +1266,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) - - + + Select cheats file Pilih fail tipu + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1286,43 +1291,43 @@ Download size: %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 Gagal membuka fail tersimpan: %1 - + Failed to open game file: %1 Gagal membuka fail permainan: %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 Gagal membuka fail snapshot untuk baca: %1 - + Failed to open snapshot file for writing: %1 Gagal membuka fail snapshot untuk menulis: %1 @@ -1335,12 +1340,12 @@ Download size: %3 Gagal membuka fail permainan: %1 - + Could not load game. Are you sure it's in the correct format? Gagal memuat permainan. Adakah ia dalam format betul? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3404,27 +3409,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State Keadaan Termuat - + Save State Keadaan Tersimpan - + Empty Kosong - + Corrupted Rosak - + Slot %1 Slot %1 @@ -3476,47 +3481,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 [%1] %2: %3 - + An error occurred Ralat dilakukan - + DEBUG NYAHPEPIJAT - + STUB KONTOT - + INFO MAKLUMAT - + WARN AMARAN - + ERROR RALAT - + FATAL - + GAME ERROR RALAT PERMAINAN @@ -3640,42 +3645,42 @@ Download size: %3 Muat - + All Semua - + Load TBL Muat TBL - + Save selected memory Simpan ingatan terpilih - + Failed to open output file: %1 Gagal membuka fail output: %1 - + Load memory Muat ingatan - + Failed to open input file: %1 Gagal membuka fail input: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3853,12 +3858,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive Arkib laporan pepijat - + ZIP archive (*.zip) Arkib ZIP (*.zip) @@ -3926,104 +3931,112 @@ Download size: %3 Tidak boleh menukar simpanan permainan antara platform + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia Multimedia Qt - + SDL SDL - + Software (Qt) Perisian (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (paksa versi 1.x) - + None Tiada - + None (Still Image) Tiada (Gambar Tenang) - + Keyboard Papan Kekunci - + Controllers Pengawal - + Shortcuts Pintas - - + + Shaders - + Select BIOS Pilih BIOS - + Select directory Pilih direktori - + (%1×%2) (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4121,100 +4134,100 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) ROM Game Boy Advance (%1) - + Game Boy ROMs (%1) ROM Game Boy (%1) - + All ROMs (%1) Semua ROM (%1) - + %1 Video Logs (*.mvl) %1 Log Video (*.mvl) - + Archives (%1) Arkib (%1) - - - + + + Select ROM Pilih ROM - + Select folder Pilih folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image Pilih gambar - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Fail gambar (*.png *.gif *.jpg *.jpeg);;Semua fail (*) - + GameShark saves (*.sps *.xps) Simpanan GameShark (*.sps *.xps) - + Select video log Pilih log video - + Video logs (*.mvl) Log video (*.mvl) - + Crash Nahas - + The game has crashed with the following error: %1 @@ -4223,674 +4236,684 @@ Download size: %3 %1 - + Couldn't Start Tidak dapat memula - + Could not start game. Permainan tidak dapat bermula. - + Unimplemented BIOS call Panggilan BIOS yg belum dilaksanakan - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Permainan ini menggunakan panggilan BIOS yang belum dilaksanakan. Sila pakai BIOS rasmi untuk pengalaman yang lebih baik. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Gagal mencipta peranti paparan yang sesuai, berbalik ke paparan perisian. Permainan mungkin menjadi lembap, terutamanya dengan tetingkap besar. - + Really make portable? Betulkah mahu buat jadi mudah alih? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Ini akan menetapkan pelagak untuk muat konfigurasi dari direktori yang sama dengan fail bolehlakunya. Teruskan? - + Restart needed Mula semula diperlukan - + Some changes will not take effect until the emulator is restarted. Beberapa perubahan tidak akan dilaksanakan sehingga pelagak dimula semula. - + - Player %1 of %2 - Pemain %1 dari %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &File - + Load &ROM... Muat %ROM... - + Load ROM in archive... Muat ROM daripada arkib... - + Add folder to library... Tambah folder ke perpustakaan... - + Save games (%1) Simpanan permainan (%1) - + Select save game Pilih simpanan permainan - + mGBA save state files (%1) mGBA fail keadaan tersimpan (%1) - - + + Select save state Pilih keadaan tersimpan - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) Fail gambar (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... Muat simpanan permainan alternatif... - + Load temporary save game... Muat simpanan permainan sementara... - + Load &patch... - + Boot BIOS But BIOS - + Replace ROM... Ganti ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... &Perihal ROM... - + Recent Terkini - + Make portable Buat jadi mudah alih - + &Load state &Muat keadaan - + Load state file... Muat fail keadaan... - + &Save state &Simpan keadaan - + Save state file... Simpan fail keadaan... - + Quick load - + Quick save - + Load recent Muat terkini - + Save recent Simpan terkini - + Undo load state Buat asal keadaan termuat - + Undo save state Buat asal keadaan tersimpan - - + + State &%1 Keadaan &%1 - + Load camera image... Muat gambar kamera... - + Convert save game... Tukar simpanan permainan... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games Simpanan permainan - + Import GameShark Save... Import Simpanan GameShark... - + Export GameShark Save... Eksport Simpanan GameShark... - + Automatically determine - + Use player %0 save game - + New multiplayer window Tetingkap multipemain baru - + Connect to Dolphin... Sambung ke Dolphin... - + Report bug... Laporkan pepijat... - + About... Perihal... - + E&xit &Keluar - + &Emulation Pe&lagak - + &Reset - + Sh&utdown &Matikan - + Yank game pak Alih keluar Game Pak - + &Pause &Jeda - + &Next frame Bingkai se&terusnya - + Fast forward (held) Mundar laju (pegang) - + &Fast forward Mundar &laju - + Fast forward speed Kelajuan mundar laju - + Unbounded Tidak terbatas - + %0x %0x - + Rewind (held) Putar balik (pegang) - + Re&wind Ma&ndir - + Step backwards Langkah belakang - + Solar sensor Pengesan suria - + Increase solar level Meningkatkan aras suria - + Decrease solar level Mengurangkan aras suria - + Brightest solar level Aras suria paling terang - + Darkest solar level Aras suria paling gelap - + Brightness %1 Kecerahan %1 - + Game Boy Printer... Pencetak Game Boy... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Audio/&Video - + Frame size Saiz bingkai - + %1× %1× - + Toggle fullscreen Togol skrinpenuh - + Lock aspect ratio Kekalkan nisbah aspek - + Force integer scaling Paksa skala integer - + Interframe blending Persebatian antarabingkai - + Bilinear filtering Penapisan bilinear - + Frame&skip Langkauan &bingkai - + Mute Senyap - + FPS target Sasaran FPS - + Native (59.7275) Asal (59.7275) - + Take &screenshot Ambil &cekupan skrin - + F12 F12 - + Record A/V... Rakam A/V... - + Record GIF/WebP/APNG... Rakam GIF/WebP/APNG... - + Video layers Lapisan video - + Audio channels Saluran audio - + Adjust layer placement... Melaras peletakan lapisan... - + &Tools &Alat - + View &logs... Lihat &log... - + Game &overrides... - + Game Pak sensors... Pengesan Game Pak... - + &Cheats... &Tipu... - + Settings... Tetapan... - + Open debugger console... Buka konsol penyahpepijat... - + Start &GDB server... Mula pelayan &GDB... - + + Scripting... + + + + + Game state views + + + + View &palette... Pelihat &palet... - + View &sprites... - + View &tiles... Pelihat &jubin... - + View &map... Pelihat pe&ta... - + &Frame inspector... Periksa &bingkai... - + View memory... Lihat ingatan... - + Search memory... Cari ingatan... - + View &I/O registers... Lihat daftar &I/O... - + Record debug video log... Rakam log video nyahpepijat... - + Stop debug video log Henti log video nyahpepijat - + Exit fullscreen Keluar skrinpenuh - + GameShark Button (held) Butang GameShark (pegang) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Kosongkan @@ -5136,6 +5159,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + + + + + 0 + 4K {0?} + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5149,64 +5225,74 @@ Download size: %3 - + Fixed time - + System time - + Start time at - + Now - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP - + Light sensor - + Brightness - + Tilt sensor - - + + Set Y - - + + Set X - + Gyroscope - + Sensitivity diff --git a/src/platform/qt/ts/mgba-nb_NO.ts b/src/platform/qt/ts/mgba-nb_NO.ts index 0c002ff5c..b6ba16304 100644 --- a/src/platform/qt/ts/mgba-nb_NO.ts +++ b/src/platform/qt/ts/mgba-nb_NO.ts @@ -1184,36 +1184,36 @@ Game Boy Advance er et registrert varemerke tilhørende Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + Current version: %1 New version: %2 Download size: %3 - + Downloading update... - + Downloading failed. Please update manually. - + Downloading done. Press OK to restart %1 and install the update. @@ -1236,7 +1236,7 @@ Download size: %3 Ukjent - + (None) @@ -1267,17 +1267,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) - - + + Select cheats file Velg juksekodefil + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1287,43 +1292,43 @@ Download size: %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 - + Failed to open game file: %1 Klarte ikke å åpne spillfil: %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -1336,12 +1341,12 @@ Download size: %3 Klarte ikke å åpne spillfil: %1 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3405,27 +3410,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State - + Save State - + Empty Tom - + Corrupted Skadet - + Slot %1 Plass %1 @@ -3477,47 +3482,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 - + An error occurred En feil inntraff - + DEBUG AVLUSING - + STUB - + INFO INFO - + WARN - + ERROR FEIL - + FATAL KRITISK - + GAME ERROR SPILLFEIL @@ -3641,42 +3646,42 @@ Download size: %3 Last inn - + All - + Load TBL - + Save selected memory Lagre valgt minne - + Failed to open output file: %1 Klarte ikke å åpne utdatafil: %1 - + Load memory Last inn minne - + Failed to open input file: %1 Klarte ikke å åpne inndatafil: %1 - + TBL - + ISO-8859-1 @@ -3854,12 +3859,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) ZIP-arkiv (*.zip) @@ -3927,97 +3932,105 @@ Download size: %3 + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia - + SDL SDL - + Software (Qt) Programvare (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) - + None Ingen - + None (Still Image) - + Keyboard Tastatur - + Controllers Kontrollere - + Shortcuts Snarveier - - + + Shaders Skyggeleggere - + Select BIOS Velg BIOS - + Select directory Velg mappe - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4025,7 +4038,7 @@ Download size: %3 - + %n day(s) ago @@ -4124,774 +4137,784 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) - + Game Boy ROMs (%1) - + All ROMs (%1) - + %1 Video Logs (*.mvl) - + Archives (%1) Arkiv (%1) - - - + + + Select ROM Velg ROM - + Select folder Velg mappe - - + + Select save - + Select patch Velg feilfiks - + Patches (*.ips *.ups *.bps) Feilfikser (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image Velg bilde - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Bildefil (*.png *.gif *.jpg *.jpeg);;All filer (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash Krasj - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + + Scripting... + + + + + Game state views + + + + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Tøm @@ -5137,6 +5160,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5150,64 +5226,74 @@ Download size: %3 Sanntidsklokke - + Fixed time - + System time - + Start time at - + Now - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP - + Light sensor Lyssensor - + Brightness - + Tilt sensor - - + + Set Y - - + + Set X - + Gyroscope Gyroskop - + Sensitivity diff --git a/src/platform/qt/ts/mgba-nl.ts b/src/platform/qt/ts/mgba-nl.ts index 8fa74e37d..ff7c31276 100644 --- a/src/platform/qt/ts/mgba-nl.ts +++ b/src/platform/qt/ts/mgba-nl.ts @@ -1183,36 +1183,36 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + Current version: %1 New version: %2 Download size: %3 - + Downloading update... - + Downloading failed. Please update manually. - + Downloading done. Press OK to restart %1 and install the update. @@ -1235,7 +1235,7 @@ Download size: %3 - + (None) @@ -1266,17 +1266,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) - - + + Select cheats file + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1286,43 +1291,43 @@ Download size: %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 - + Failed to open game file: %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -1335,12 +1340,12 @@ Download size: %3 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3404,27 +3409,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State - + Save State - + Empty - + Corrupted - + Slot %1 @@ -3476,47 +3481,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 - + An error occurred - + DEBUG - + STUB - + INFO - + WARN - + ERROR - + FATAL - + GAME ERROR @@ -3640,42 +3645,42 @@ Download size: %3 Laden - + All - + Load TBL - + Save selected memory - + Failed to open output file: %1 - + Load memory - + Failed to open input file: %1 - + TBL - + ISO-8859-1 @@ -3853,12 +3858,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -3926,97 +3931,105 @@ Download size: %3 + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4024,7 +4037,7 @@ Download size: %3 - + %n day(s) ago @@ -4123,774 +4136,784 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) - + Game Boy ROMs (%1) - + All ROMs (%1) - + %1 Video Logs (*.mvl) - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + + Scripting... + + + + + Game state views + + + + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear @@ -5136,6 +5159,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5149,64 +5225,74 @@ Download size: %3 - + Fixed time - + System time - + Start time at - + Now - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP - + Light sensor - + Brightness - + Tilt sensor - - + + Set Y - - + + Set X - + Gyroscope - + Sensitivity diff --git a/src/platform/qt/ts/mgba-pl.ts b/src/platform/qt/ts/mgba-pl.ts index 0446d7763..d3b4edc8d 100644 --- a/src/platform/qt/ts/mgba-pl.ts +++ b/src/platform/qt/ts/mgba-pl.ts @@ -1185,21 +1185,21 @@ Game Boy Advance jest zarejestrowanym znakiem towarowym Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. Czy chcesz ją teraz pobrać i zainstalować? Po zakończeniu pobierania konieczne będzie ponowne uruchomienie emulatora. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. Automatyczna aktualizacja nie jest dostępna na tej platformie. Jeśli chcesz zaktualizować, musisz to zrobić ręcznie. - + Current version: %1 New version: %2 Download size: %3 @@ -1208,17 +1208,17 @@ Nowa wersja: %2 Rozmiar pobierania: %3 - + Downloading update... Pobieranie aktualizacji... - + Downloading failed. Please update manually. Pobieranie nie powiodło się. Proszę zaktualizować ręcznie. - + Downloading done. Press OK to restart %1 and install the update. Pobieranie zakończone. Naciśnij OK, aby ponownie uruchomić %1 i zainstalować aktualizację. @@ -1241,7 +1241,7 @@ Rozmiar pobierania: %3 Nieznana - + (None) (Żadna) @@ -1272,17 +1272,22 @@ Rozmiar pobierania: %3 QGBA::CheatsView - - + + Autodetect (recommended) Automatyczne wykrywanie (zalecane) - - + + Select cheats file Wybierz plik z kodami + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1292,43 +1297,43 @@ Rozmiar pobierania: %3 Reset r%1-%2 %3 - - + + Rewinding not currently enabled Przewijanie nie jest obecnie włączone - + Reset the game? Zresetować grę? - + Most games will require a reset to load the new save. Do you want to reset now? Większość gier wymaga zresetowania, aby wczytać nowy zapis. Czy chcesz teraz zresetować? - + Failed to open save file: %1 Nie udało się otworzyć pliku zapisu: %1 - + Failed to open game file: %1 Nie udało się otworzyć pliku gry: %1 - + Can't yank pack in unexpected platform! Nie można wyciągnąć pack na nieoczekiwanej platformie! - + Failed to open snapshot file for reading: %1 Nie udało się otworzyć pliku snapshot do odczytu: %1 - + Failed to open snapshot file for writing: %1 Nie udało się otworzyć pliku snapshot do zapisu: %1 @@ -1341,12 +1346,12 @@ Rozmiar pobierania: %3 Nie udało się otworzyć pliku gry: %1 - + Could not load game. Are you sure it's in the correct format? Nie udało się wczytać gry. Czy na pewno jest we właściwym formacie? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Nie udało się otworzyć pliku zapisu; zapisy w grze nie mogą być aktualizowane. Upewnij się, że katalog zapisu jest zapisywalny bez dodatkowych uprawnień (np. UAC w systemie Windows). @@ -3410,27 +3415,27 @@ Rozmiar pobierania: %3 QGBA::LoadSaveState - + Load State Załaduj Stan - + Save State Zapisz Stan - + Empty Pusty - + Corrupted Uszkodzony - + Slot %1 Slot %1 @@ -3482,47 +3487,47 @@ Rozmiar pobierania: %3 QGBA::LogController - + [%1] %2: %3 [%1] %2: %3 - + An error occurred Pojawił się błąd - + DEBUG DEBUG - + STUB ZALĄŻEK - + INFO INFO - + WARN OSTRZEC - + ERROR BŁĄD - + FATAL KRYTYCZNY - + GAME ERROR BŁĄD GRY @@ -3646,42 +3651,42 @@ Rozmiar pobierania: %3 Wgraj - + All Wszystko - + Load TBL Załaduj TBL - + Save selected memory Zapisz wybraną pamięć - + Failed to open output file: %1 Nie udało się otworzyć pliku wyjściowego: %1 - + Load memory Załaduj pamięć - + Failed to open input file: %1 Nie udało się otworzyć pliku wejściowego: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3859,12 +3864,12 @@ Rozmiar pobierania: %3 QGBA::ReportView - + Bug report archive Archiwum raportów o błędach - + ZIP archive (*.zip) Archiwum ZIP (*.zip) @@ -3932,97 +3937,105 @@ Rozmiar pobierania: %3 Nie można konwertować zapisanych gier między platformami + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (wymuś wersję 1.x) - + None Nic - + None (Still Image) Brak (Obraz Nieruchomy) - + Keyboard Klawiatura - + Controllers Kontrolery - + Shortcuts Skróty - - + + Shaders Shadery - + Select BIOS Wybierz BIOS - + Select directory Wybierz katalog - + (%1×%2) (%1×%2) - + Never Nigdy - + Just now Właśnie teraz - + Less than an hour ago Mniej niż godzinę temu - + %n hour(s) ago %n godzinę temu @@ -4031,7 +4044,7 @@ Rozmiar pobierania: %3 - + %n day(s) ago %n dzień temu @@ -4131,100 +4144,100 @@ Rozmiar pobierania: %3 QGBA::Window - + Game Boy Advance ROMs (%1) ROMy Game Boy Advance (%1) - + Game Boy ROMs (%1) ROMy Game Boy (%1) - + All ROMs (%1) Wszystkie ROMy (%1) - + %1 Video Logs (*.mvl) Dzienniki wideo %1 (*.mvl) - + Archives (%1) Archiwa (%1) - - - + + + Select ROM Wybierz ROM - + Select folder Wybierz katalog - - + + Select save Wybierz zapis - + Select patch Wybierz łatkę - + Patches (*.ips *.ups *.bps) Łatki (*.ips *.ups *.bps) - + Select e-Reader dotcode Wybierz kod kropki e-Reader - + e-Reader card (*.raw *.bin *.bmp) Karta e-Reader (*.raw *.bin *.bmp) - + Select image Wybierz obraz - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Plik obrazu (*.png *.gif *.jpg *.jpeg);;Wszystkie pliki (*) - + GameShark saves (*.sps *.xps) Zapisy GameShark (*.sps *.xps) - + Select video log Wybierz dziennik wideo - + Video logs (*.mvl) Dzienniki wideo (*.mvl) - + Crash Crash - + The game has crashed with the following error: %1 @@ -4233,674 +4246,684 @@ Rozmiar pobierania: %3 %1 - + Couldn't Start Nie udało się uruchomić - + Could not start game. Nie udało się rozpocząć gry. - + Unimplemented BIOS call Niewdrożone wywołanie BIOS - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Ta gra używa wywołania BIOS, które nie jest zaimplementowane. Aby uzyskać najlepsze wrażenia, użyj oficjalnego BIOS. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Nie udało się utworzyć odpowiedniego urządzenia wyświetlającego, powracam do wyświetlania programowego. Gry mogą działać wolno, zwłaszcza w przypadku większych okien. - + Really make portable? Naprawdę stworzyć wersję portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? To sprawi, że emulator załaduje swoją konfigurację z tego samego katalogu, co plik wykonywalny. Czy chcesz kontynuować? - + Restart needed Wymagane ponowne uruchomienie - + Some changes will not take effect until the emulator is restarted. Niektóre zmiany nie zaczną obowiązywać, dopóki emulator nie zostanie ponownie uruchomiony. - + - Player %1 of %2 - Gracz %1 z %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 FPS) - %4 - + &File &Plik - + Load &ROM... Załaduj &ROM... - + Load ROM in archive... Załaduj ROM w archiwum... - + Add folder to library... Dodaj folder do biblioteki... - + Save games (%1) Zapisane gry (%1) - + Select save game Wybierz zapis gry - + mGBA save state files (%1) Pliki stanu gry mGBA (%1) - - + + Select save state Wybierz stan - + Select e-Reader card images Wybierz obrazy kart e-Reader - + Image file (*.png *.jpg *.jpeg) Plik graficzny (*.png *.jpg *.jpeg) - + Conversion finished Konwersja zakończona - + %1 of %2 e-Reader cards converted successfully. %1 z %2 kart czytnika e-Reader zostało pomyślnie przekonwertowanych. - + Load alternate save game... Załaduj alternatywny zapis gry... - + Load temporary save game... Załaduj tymczasowy zapis gry... - + Load &patch... Wczytaj &poprawkę... - + Boot BIOS BIOS startowy - + Replace ROM... Wymień ROM... - + Scan e-Reader dotcodes... Skanuj kody kropkowe czytnika e-Reader... - + Convert e-Reader card image to raw... Konwertuj obraz karty czytnika e-Reader na surowy... - + ROM &info... &Informacje o pamięci ROM... - + Recent Ostatnie - + Make portable Stwórz wersję portable - + &Load state &Załaduj stan - + Load state file... Załaduj plik stanu… - + &Save state &Zapisz stan - + Save state file... Zapisz plik stanu... - + Quick load Szybkie załadowanie - + Quick save Szybki zapis - + Load recent Załaduj ostatnie - + Save recent Zapisz ostatnie - + Undo load state Cofnij załadowanie stanu - + Undo save state Cofnij zapisanie stanu - - + + State &%1 Stan &%1 - + Load camera image... Załaduj obraz do kamery... - + Convert save game... Konwertuj zapisaną grę... - + GameShark saves (*.gsv *.sps *.xps) Zapisy GameShark (*.gsv *.sps *.xps) - + Reset needed Wymagane zresetowanie - + Some changes will not take effect until the game is reset. Niektóre zmiany nie zaczną obowiązywać, dopóki gra nie zostanie zresetowana. - + Save games Zapisy gry - + Import GameShark Save... Importuj Zapis GameShark... - + Export GameShark Save... Eksportuj Zapis GameShark... - + Automatically determine Wykryj automatycznie - + Use player %0 save game Użyj zapis gry gracza %0 - + New multiplayer window Nowe okno dla wielu graczy - + Connect to Dolphin... Połącz z Dolphinem... - + Report bug... Zgłoś błąd... - + About... O Aplikacji... - + E&xit Z&akończ - + &Emulation &Emulacja - + &Reset &Resetuj - + Sh&utdown Za&mknij - + Yank game pak Wyciągnij Game Pak - + &Pause &Pauza - + &Next frame &Następna klatka - + Fast forward (held) Przewijanie (przytrzymaj) - + &Fast forward &Przewijanie do przodu - + Fast forward speed Prędkość przewijania do przodu - + Unbounded Bez ograniczeń - + %0x %0x - + Rewind (held) Przewijanie (przytrzymaj) - + Re&wind Pr&zewijanie - + Step backwards Krok w tył - + Solar sensor Czujnik słoneczny - + Increase solar level Zwiększ poziom energii słonecznej - + Decrease solar level Zmniejsz poziom energii słonecznej - + Brightest solar level Najjaśniejszy poziom energii słonecznej - + Darkest solar level Najciemniejszy poziom energii słonecznej - + Brightness %1 Jasność %1 - + Game Boy Printer... Game Boy Printer... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Dźwięk/&Wideo - + Frame size Rozmiar klatki - + %1× %1× - + Toggle fullscreen Przełącz tryb pełnoekranowy - + Lock aspect ratio Zablokuj proporcje - + Force integer scaling Wymuś skalowanie całkowite - + Interframe blending Blendowanie międzyklatkowe - + Bilinear filtering Filtrowanie dwuliniowe - + Frame&skip Klatko&wanie - + Mute Wycisz - + FPS target Cel KL./S - + Native (59.7275) Natywny (59.7275) - + Take &screenshot Wykonaj &zrzut ekranu - + F12 F12 - + Record A/V... Nagraj A/W... - + Record GIF/WebP/APNG... Nagraj GIF/WebP/APNG... - + Video layers Warstwy wideo - + Audio channels Kanały audio - + Adjust layer placement... Dostosuj położenie warstw... - + &Tools &Narzędzia - + View &logs... Wyświetl &logi... - + Game &overrides... Nadpisania &ustawień gry... - + Game Pak sensors... Czujniki Game Pak... - + &Cheats... &Kody... - + Settings... Ustawienia... - + Open debugger console... Otwórz konsolę debugera... - + Start &GDB server... Uruchom serwer &GDB... - + + Scripting... + + + + + Game state views + + + + View &palette... Wyświetl &paletę... - + View &sprites... Wyświetl &sprite'y... - + View &tiles... Wyświetl &kafelki... - + View &map... Wyświetl &mapę... - + &Frame inspector... Inspektor &klatek... - + View memory... Wyświetl pamięć... - + Search memory... Przeszukaj pamięć... - + View &I/O registers... Wyświetl rejestry &we/wy... - + Record debug video log... Nagraj dziennik wideo debugowania... - + Stop debug video log Zatrzymaj dziennik wideo debugowania - + Exit fullscreen Wyłączyć tryb pełnoekranowy - + GameShark Button (held) Przycisk GameShark (przytrzymany) - + Autofire Turbo - + Autofire A Turbo A - + Autofire B Turbo B - + Autofire L Turbo L - + Autofire R Turbo R - + Autofire Start Turbo Start - + Autofire Select Turbo Select - + Autofire Up Turbo Góra - + Autofire Right Turbo Prawo - + Autofire Down Turbo Dół - + Autofire Left Turbo Lewo - + Clear Wyczyść @@ -5146,6 +5169,59 @@ Rozmiar pobierania: %3 %1 GameShark Advance SP %2 zapis gry + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + &Resetuj + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5159,64 +5235,74 @@ Rozmiar pobierania: %3 Zegar czasu rzeczywistego - + Fixed time Ustalony czas - + System time Czas systemu - + Start time at Czas rozpoczęcia o - + Now Teraz - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP yy/MM/DD hh:mm:ss AP - + Light sensor Czujnik światła - + Brightness Jasność - + Tilt sensor Czujnik pochylenia - - + + Set Y Ustaw Y - - + + Set X Ustaw X - + Gyroscope Żyroskop - + Sensitivity Czułość diff --git a/src/platform/qt/ts/mgba-pt_BR.ts b/src/platform/qt/ts/mgba-pt_BR.ts index 44b92c790..3a01b6126 100644 --- a/src/platform/qt/ts/mgba-pt_BR.ts +++ b/src/platform/qt/ts/mgba-pt_BR.ts @@ -1185,21 +1185,21 @@ O Game Boy Advance é uma marca registrada da Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. Você que baixar e instalá-lo agora? Você precisará reiniciar o emulador quando o download estiver completo. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. Uma auto-atualização não está disponível nesta plataforma. Se você deseja atualizar você precisará fazê-lo manualmente. - + Current version: %1 New version: %2 Download size: %3 @@ -1208,17 +1208,17 @@ Nova versão: %2 Tamanho do download: %3 - + Downloading update... Baixando atualização... - + Downloading failed. Please update manually. O download falhou. Por favor atualize manualmente. - + Downloading done. Press OK to restart %1 and install the update. Download concluído. Pressione Ok pra reiniciar o %1 e instalar a atualização. @@ -1241,7 +1241,7 @@ Tamanho do download: %3 Desconhecido - + (None) (Nenhum) @@ -1272,17 +1272,22 @@ Tamanho do download: %3 QGBA::CheatsView - - + + Autodetect (recommended) Auto-detectar (recomendado) - - + + Select cheats file Selecionar o arquivo das trapaças + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1292,43 +1297,43 @@ Tamanho do download: %3 Resetar r%1-%2 %3 - - + + Rewinding not currently enabled O rebobinamento não está ativado atualmente - + Reset the game? Resetar o jogo? - + Most games will require a reset to load the new save. Do you want to reset now? A maioria dos jogos requerirão um reset pra carregar o novo save. Você quer resetar agora? - + Failed to open save file: %1 Falhou em abrir o arquivo do save: %1 - + Failed to open game file: %1 Falhou em abrir o arquivo do jogo: %1 - + Can't yank pack in unexpected platform! Não pode arrancar o pacote numa plataforma inesperada! - + Failed to open snapshot file for reading: %1 Falhou em abrir o arquivo do snapshot pra leitura: %1 - + Failed to open snapshot file for writing: %1 Falhou em abrir o arquivo do snapshot pra gravação: %1 @@ -1341,12 +1346,12 @@ Tamanho do download: %3 Falhou em abrir o arquivo do jogo: %1 - + Could not load game. Are you sure it's in the correct format? Não pôde carregar o jogo. Você tem certeza que está no formato correto? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Falhou em abrir o abrir o arquivo de salvamento; Os salvamentos dentro do jogo não podem ser atualizados. Por favor tenha certeza que o diretório de salvamento seja gravável sem privilégios adicionais (ex: UAC no Windows). @@ -3410,27 +3415,27 @@ Tamanho do download: %3 QGBA::LoadSaveState - + Load State Carregar o State - + Save State Salvar o State - + Empty Vazio - + Corrupted Corrompido - + Slot %1 Slot %1 @@ -3482,47 +3487,47 @@ Tamanho do download: %3 QGBA::LogController - + [%1] %2: %3 [%1] %2: %3 - + An error occurred Um erro ocorreu - + DEBUG DEBUG - + STUB STUB - + INFO INFO - + WARN AVISAR - + ERROR ERRO - + FATAL FATAL - + GAME ERROR ERRO DO JOGO @@ -3646,42 +3651,42 @@ Tamanho do download: %3 Carregar - + All Todos - + Load TBL Carregar TBL - + Save selected memory Salvar a memória selecionada - + Failed to open output file: %1 Falhou em abrir o arquivo de saída: %1 - + Load memory Carregar memória - + Failed to open input file: %1 Falhou em abrir o arquivo de entrada: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3859,12 +3864,12 @@ Tamanho do download: %3 QGBA::ReportView - + Bug report archive Arquivo compactado do relatório dos bugs - + ZIP archive (*.zip) Arquivo compactado ZIP (*.zip) @@ -3932,97 +3937,105 @@ Tamanho do download: %3 Não pôde converter os saves do jogo entre as plataformas + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia Multimídia do Qt - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (forçar a versão 1.x) - + None Nenhum - + None (Still Image) Nenhum (Imagem Parada) - + Keyboard Teclado - + Controllers Controles - + Shortcuts Atalhos - - + + Shaders Shaders - + Select BIOS Selecionar BIOS - + Select directory Selecione o diretório - + (%1×%2) (%1×%2) - + Never Nunca - + Just now Aconteceu agora - + Less than an hour ago Menos do que uma hora atrás - + %n hour(s) ago %n hora atrás @@ -4030,7 +4043,7 @@ Tamanho do download: %3 - + %n day(s) ago %n dia atrás @@ -4129,100 +4142,100 @@ Tamanho do download: %3 QGBA::Window - + Game Boy Advance ROMs (%1) ROMs do Game Boy Advance (%1) - + Game Boy ROMs (%1) ROMs do Game Boy (%1) - + All ROMs (%1) Todas as ROMs (%1) - + %1 Video Logs (*.mvl) %1 Registros do Vídeo (*.mvl) - + Archives (%1) Arquivos Compactados (%1) - - - + + + Select ROM Selecionar ROM - + Select folder Selecionar pasta - - + + Select save Selecionar save - + Select patch Selecionar patch - + Patches (*.ips *.ups *.bps) Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode Selecionar dotcode do e-Reader - + e-Reader card (*.raw *.bin *.bmp) Cartão do e-Reader (*.raw *.bin *.bmp) - + Select image Selecionar imagem - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Arquivo de imagem (*.png *.gif *.jpg *.jpeg);;Todos os arquivos (*) - + GameShark saves (*.sps *.xps) Saves do GameShark (*.sps *.xps) - + Select video log Selecionar registro do vídeo - + Video logs (*.mvl) Registros do vídeo (*.mvl) - + Crash Crash - + The game has crashed with the following error: %1 @@ -4231,674 +4244,684 @@ Tamanho do download: %3 %1 - + Unimplemented BIOS call Chamada da BIOS não implementada - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Este jogo usa uma chamada de BIOS que não está implementada. Por favor use a BIOS oficial pra uma melhor experiência. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Falhou em criar um dispositivo de exibição apropriado, voltando a exibição por software. Os jogos podem executar lentamente, especialmente com janelas maiores. - + Really make portable? Realmente tornar portátil? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Isto fará o emulador carregar sua configuração do mesmo diretório que o executável. Você quer continuar? - + Restart needed Reiniciar é necessário - + Some changes will not take effect until the emulator is restarted. Algumas mudanças não terão efeito até o emulador ser reiniciado. - + - Player %1 of %2 - Jogador %1 de %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Arquivo - + Load &ROM... Carregar &ROM... - + Load ROM in archive... Carregar ROM no arquivo compactado... - + Add folder to library... Adicionar pasta a biblioteca... - + Save games Saves dos jogos - + Automatically determine Determinar automaticamente - + Use player %0 save game Usar o save do jogo %0 do jogador - + Load &patch... Carregar &patch... - + Boot BIOS Dar Boot na BIOS - + Replace ROM... Substituir a ROM... - + ROM &info... Informações da &ROM... - + Recent Recentes - + Make portable Tornar portátil - + &Load state &Carregar state - + Report bug... Reportar bug... - + About... Sobre... - + Game Pak sensors... Sensores do Game Pak... - + Clear Limpar - + Load state file... Carregar arquivo do state... - + Save games (%1) Saves dos jogos (%1) - + Select save game Selecione save do jogo - + mGBA save state files (%1) Arquivos do save state do mGBA (%1) - - + + Select save state Selecione um save state - + Select e-Reader card images Selecionar imagens do cartão do e-Reader - + Image file (*.png *.jpg *.jpeg) Arquivo da imagem (*.png *.jpg *.jpeg) - + Conversion finished Conversão concluída - + %1 of %2 e-Reader cards converted successfully. %1 de %2 cartões do e-Reader convertidos com sucesso. - + Load alternate save game... Carregar save alternativo do jogo... - + Load temporary save game... Carregar save temporário do jogo... - + Convert e-Reader card image to raw... Converter imagem do cartão do e-Reader pro natural... - + &Save state &Salvar o state - + Save state file... Arquivo do save state... - + Quick load Carregamento rápido - + Quick save Salvamento rápido - + Load recent Carregar recentes - + Save recent Salvar recentes - + Undo load state Desfazer o carregamento do state - + Undo save state Desfazer o salvamento do state - - + + State &%1 State &%1 - + Load camera image... Carregar a imagem da câmera... - + Convert save game... Converter o save do jogo... - + GameShark saves (*.gsv *.sps *.xps) Saves do GameShark (*.gsv *.sps *.xps) - + Reset needed É necessário resetar - + Some changes will not take effect until the game is reset. Algumas mudanças não terão efeito até o jogo ser resetado. - + New multiplayer window Nova janela multi-jogador - + Connect to Dolphin... Conectar ao Dolphin... - + E&xit S&air - + &Emulation &Emulação - + &Reset &Resetar - + Sh&utdown De&sligar - + Yank game pak Arrancar o game pak - + &Pause &Pausar - + &Next frame &Próximo frame - + Fast forward (held) Avanço rápido (segurado) - + &Fast forward &Avanço rápido - + Fast forward speed Velocidade do avanço rápido - + Unbounded Ilimitado - + %0x %0x - + Rewind (held) Retroceder (segurado) - + Re&wind Re&troceder - + Step backwards Voltar um passo - + Solar sensor Sensor solar - + Increase solar level Aumentar nível solar - + Decrease solar level Diminuir nível solar - + Brightest solar level Nível solar mais brilhante - + Darkest solar level Nível solar mais escuro - + Brightness %1 Brilho %1 - + Audio/&Video Áudio/&Vídeo - + Frame size Tamanho do frame - + Toggle fullscreen Alternar tela cheia - + Lock aspect ratio Travar a proporção do aspecto - + Force integer scaling Forçar o dimensionamento do inteiro - + Bilinear filtering Filtragem bilinear - + Frame&skip Frame&skip - + Mute Mudo - + FPS target FPS alvo - + Native (59.7275) Nativo (59,7275) - + Take &screenshot Tirar &screenshot - + F12 F12 - + Game Boy Printer... Impressora do Game Boy... - + BattleChip Gate... Portal do BattleChip... - + %1× %1× - + Interframe blending Mistura do interframe - + Record A/V... Gravar A/V... - + Video layers Camadas do vídeo - + Audio channels Canais de áudio - + Adjust layer placement... Ajustar posicionamento da camada... - + &Tools &Ferramentas - + View &logs... Visualizar &registros... - + Game &overrides... Substituições &do jogo... - + Couldn't Start Não Pôde Iniciar - + Could not start game. Não pôde iniciar o jogo. - + Scan e-Reader dotcodes... Escanear dotcodes do e-Reader... - + Import GameShark Save... Importar Save do GameShark... - + Export GameShark Save... Exportar Save do GameShark... - + Record GIF/WebP/APNG... Gravar GIF/WebP/APNG... - + &Cheats... &Trapaças... - + Settings... Configurações... - + Open debugger console... Abrir console do debugger... - + Start &GDB server... Iniciar servidor do &GDB... - + + Scripting... + + + + + Game state views + + + + View &palette... Visualizar &paleta... - + View &sprites... Visualizar &imagens móveis... - + View &tiles... Visualizar &ladrilhos... - + View &map... Visualizar &mapa... - + &Frame inspector... Inspetor dos &frames... - + View memory... Visualizar memória... - + Search memory... Procurar na memória... - + View &I/O registers... Visualizar registros de &E/S... - + Record debug video log... Gravar registro do vídeo de debug... - + Stop debug video log Parar o registro do vídeo de debug - + Exit fullscreen Sair da tela cheia - + GameShark Button (held) Botão do GameShark (segurado) - + Autofire Auto-disparar - + Autofire A Auto-disparar A - + Autofire B Auto-disparar B - + Autofire L Auto-disparar L - + Autofire R Auto-disparar R - + Autofire Start Auto-disparar Start - + Autofire Select Auto-disparar Select - + Autofire Up Auto-disparar Pra Cima - + Autofire Right Auto-disparar Direita - + Autofire Down Auto-disparar Pra Baixo - + Autofire Left Auto-disparar Esquerda @@ -5144,6 +5167,59 @@ Tamanho do download: %3 %1 GameShark Advance SP %2 save do jogo + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + &Resetar + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5157,64 +5233,74 @@ Tamanho do download: %3 Relógio em tempo real - + Fixed time Tempo fixo - + System time Horário do sistema - + Start time at Horário do início em - + Now Agora - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP MM/dd/yy hh:mm:ss AP - + Light sensor Sensor de luz - + Brightness Brilho - + Tilt sensor Sensor de inclinação - - + + Set Y Definir Y - - + + Set X Definir X - + Gyroscope Giroscópio - + Sensitivity Sensibilidade diff --git a/src/platform/qt/ts/mgba-ru.ts b/src/platform/qt/ts/mgba-ru.ts index 062f89450..9bc4fb571 100644 --- a/src/platform/qt/ts/mgba-ru.ts +++ b/src/platform/qt/ts/mgba-ru.ts @@ -1184,21 +1184,21 @@ Game Boy Advance - зарегистрированная торговая мар - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. Вы хотите скачать и установить сейчас? Вам надо будет перезагрузить эмулятор когда загрузка закончится. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. Автообновление не доступно на этой платформе. Если вы хотите обновить эмулятор вам нужно сделать это вручную. - + Current version: %1 New version: %2 Download size: %3 @@ -1207,17 +1207,17 @@ Download size: %3 Размер файла: %3 - + Downloading update... Загрузка обновления... - + Downloading failed. Please update manually. Загрузка не удалась. Пожалуйста, загрузите обновление вручную. - + Downloading done. Press OK to restart %1 and install the update. Загрузка завершена. Нажмите OK для перезапуска и установки обновления. @@ -1240,7 +1240,7 @@ Download size: %3 Неизвестно - + (None) (Нет) @@ -1271,17 +1271,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) Автоопределение (рекомендовано) - - + + Select cheats file Выберите файл с чит-кодами + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1291,43 +1296,43 @@ Download size: %3 Сброс r%1-%2 %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 Не удалось открыть файл сохранения: %1 - + Failed to open game file: %1 Не удалось открыть файл игры: %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 Не удалось открыть файл изображения для считывания: %1 - + Failed to open snapshot file for writing: %1 Не удалось открыть файл изображения для записи: %1 @@ -1340,12 +1345,12 @@ Download size: %3 Не удалось открыть файл игры: %1 - + Could not load game. Are you sure it's in the correct format? Не удалось загрузить игру. Вы уверены, что она в правильном формате? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3409,27 +3414,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State Загрузить состояние - + Save State Сохранить состояние - + Empty - + Corrupted Повреждено - + Slot %1 Слот %1 @@ -3481,47 +3486,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 [%1] %2: %3 - + An error occurred Произошла ошибка - + DEBUG - + STUB - + INFO ИНФОРМАЦИЯ - + WARN - + ERROR - + FATAL - + GAME ERROR @@ -3645,42 +3650,42 @@ Download size: %3 Загрузить - + All Все - + Load TBL Загрузить TBL - + Save selected memory Сохранение выделенной памяти - + Failed to open output file: %1 Не удалось открыть выходной файл: %1 - + Load memory Загрузка памяти - + Failed to open input file: %1 Не удалось загрузить входной файл: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3858,12 +3863,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive Архив отчётов об ошибках - + ZIP archive (*.zip) ZIP архив (*.zip) @@ -3931,97 +3936,105 @@ Download size: %3 Нельзя сконвертировать сохранения для разных платформ + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None Нет - + None (Still Image) Нет (статичное изображение) - + Keyboard Клавиатура - + Controllers Контроллеры - + Shortcuts Сочетания клавиш - - + + Shaders Шейдеры - + Select BIOS Выбор BIOS - + Select directory Выбор папки - + (%1×%2) - + Never Никогда - + Just now Только сейчас - + Less than an hour ago Менее часа назад - + %n hour(s) ago %n час назад @@ -4030,7 +4043,7 @@ Download size: %3 - + %n day(s) ago %n день назад @@ -4130,100 +4143,100 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) Игры Game Boy Advance (%1) - + Game Boy ROMs (%1) Игры Game Boy (%1) - + All ROMs (%1) Все игры (%1) - + %1 Video Logs (*.mvl) - + Archives (%1) Архивы (%1) - - - + + + Select ROM Выбор игры - + Select folder Выбор папки - - + + Select save Выбор сохранения - + Select patch Выбор патча - + Patches (*.ips *.ups *.bps) Патчи (*.ips *.ups *.bps) - + Select e-Reader dotcode Выбор карточки e-Reader - + e-Reader card (*.raw *.bin *.bmp) Карточка e-Reader (*.raw *.bin *.bmp) - + Select image Выбор изображения - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Файл изображения (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) Сохранения GameShark (*.sps *.xps) - + Select video log Выбор видеолога - + Video logs (*.mvl) Видеологи (*.mvl) - + Crash Сбой - + The game has crashed with the following error: %1 @@ -4232,674 +4245,684 @@ Download size: %3 %1 - + Couldn't Start Запуск не удался - + Could not start game. Не удалось запустить игру. - + Unimplemented BIOS call Неизвестный вызов BIOS - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Игра использует нереализованный вызов BIOS. Пожалуйста, воспользуйтесь официальным BIOS для лучшей совместимости. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Не удалось создать устройство отображения, возврат к программному режиму. Игры могут идти медленнее, особенно в окнах больших размеров. - + Really make portable? Перейти в портативный режим? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? После этого эмулятор будет загружать конфигурацию из папки с исполняемым файлом. Продолжить? - + Restart needed Требуется перезапуск - + Some changes will not take effect until the emulator is restarted. Для применения некоторых изменений требуется перезапустить эмулятор. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File &Файл - + Load &ROM... - + Load ROM in archive... Загрузить игру из архива... - + Add folder to library... Добавить папку в библиотеку... - + Save games (%1) Игровые сохранения (%1) - + Select save game Выбор игрового сохранения - + mGBA save state files (%1) Файл сохранения состояния mGBA (%1) - - + + Select save state Выбор сохранения состояния - + Select e-Reader card images Выбор изображения карточки e-Reader - + Image file (*.png *.jpg *.jpeg) Файл изображения (*.png *.jpg *.jpeg) - + Conversion finished Конвертация завершена - + %1 of %2 e-Reader cards converted successfully. %1 из %2 карточек e-Reader успешно сконвертировано. - + Load alternate save game... Загрузить альтернативное сохранение... - + Load temporary save game... Загрузить временное сохранение... - + Load &patch... Загрузить &патч... - + Boot BIOS Загрузиться в BIOS - + Replace ROM... Заменить ROM... - + Scan e-Reader dotcodes... Сканировать dot-коды e-Reader... - + Convert e-Reader card image to raw... Конвертировать карту e-Reader в raw... - + ROM &info... Информация об &игре... - + Recent Недавние - + Make portable Портативный режим - + &Load state &Загрузить состояние - + Load state file... Загрузить состояние из файла... - + &Save state &Сохранить состояние - + Save state file... Сохранить состояние в файл... - + Quick load Быстрая загрузка - + Quick save Быстрое сохранение - + Load recent Загрузить недавнее - + Save recent Сохранить в недавнее - + Undo load state Отмена загрузки состояния - + Undo save state Отмена сохранения состояния - - + + State &%1 Слот &%1 - + Load camera image... Загрузить изображение с камеры... - + Convert save game... Конвертировать игровое сохранение... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... Импорт сохранения GameShark... - + Export GameShark Save... Экспорт сохранения GameShark... - + Automatically determine - + Use player %0 save game - + New multiplayer window Новое окно мультиплеера - + Connect to Dolphin... Соединение с Dolphin... - + Report bug... Сообщить об ошибке... - + About... О программе... - + E&xit &Выход - + &Emulation &Эмуляция - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + + Scripting... + + + + + Game state views + + + + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Очистить @@ -5145,6 +5168,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + + + + + 0 + 0 + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5158,64 +5234,74 @@ Download size: %3 Реальное время - + Fixed time - + System time - + Start time at - + Now - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP - + Light sensor Датчик света - + Brightness - + Tilt sensor - - + + Set Y - - + + Set X - + Gyroscope Гироскоп - + Sensitivity diff --git a/src/platform/qt/ts/mgba-template.ts b/src/platform/qt/ts/mgba-template.ts index cf3eb35d5..6540122ad 100644 --- a/src/platform/qt/ts/mgba-template.ts +++ b/src/platform/qt/ts/mgba-template.ts @@ -1183,36 +1183,36 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + Current version: %1 New version: %2 Download size: %3 - + Downloading update... - + Downloading failed. Please update manually. - + Downloading done. Press OK to restart %1 and install the update. @@ -1235,7 +1235,7 @@ Download size: %3 - + (None) @@ -1266,17 +1266,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) - - + + Select cheats file + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1286,43 +1291,43 @@ Download size: %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 - + Failed to open game file: %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -1335,12 +1340,12 @@ Download size: %3 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3404,27 +3409,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State - + Save State - + Empty - + Corrupted - + Slot %1 @@ -3476,47 +3481,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 - + An error occurred - + DEBUG - + STUB - + INFO - + WARN - + ERROR - + FATAL - + GAME ERROR @@ -3640,42 +3645,42 @@ Download size: %3 - + All - + Load TBL - + Save selected memory - + Failed to open output file: %1 - + Load memory - + Failed to open input file: %1 - + TBL - + ISO-8859-1 @@ -3853,12 +3858,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -3926,104 +3931,112 @@ Download size: %3 + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4121,774 +4134,784 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) - + Game Boy ROMs (%1) - + All ROMs (%1) - + %1 Video Logs (*.mvl) - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + + Scripting... + + + + + Game state views + + + + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear @@ -5134,6 +5157,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + + + + + 0 + + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5147,64 +5223,74 @@ Download size: %3 - + Fixed time - + System time - + Start time at - + Now - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP - + Light sensor - + Brightness - + Tilt sensor - - + + Set Y - - + + Set X - + Gyroscope - + Sensitivity diff --git a/src/platform/qt/ts/mgba-tr.ts b/src/platform/qt/ts/mgba-tr.ts index 040de662b..8171cdcd4 100644 --- a/src/platform/qt/ts/mgba-tr.ts +++ b/src/platform/qt/ts/mgba-tr.ts @@ -1184,36 +1184,36 @@ Game Boy Advance, Nintendo Co., Ltd.'nin tescilli ticari markasıdır. - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + Current version: %1 New version: %2 Download size: %3 - + Downloading update... - + Downloading failed. Please update manually. - + Downloading done. Press OK to restart %1 and install the update. @@ -1236,7 +1236,7 @@ Download size: %3 Bilinmeyen - + (None) @@ -1267,17 +1267,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) - - + + Select cheats file Oyun hileleri seçin + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + QGBA::CoreController @@ -1287,43 +1292,43 @@ Download size: %3 - - + + Rewinding not currently enabled - + Reset the game? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 Kayıt dosyası açılamadı: %1 - + Failed to open game file: %1 Oyun dosyası açılamadı: %1 - + Can't yank pack in unexpected platform! Beklenmedik bir platformda kartı çıkaramazsın! - + Failed to open snapshot file for reading: %1 Anlık görüntü dosyası okuma için açılamadı: %1 - + Failed to open snapshot file for writing: %1 Anlık görüntü dosyası yazma için açılamadı: %1 @@ -1336,12 +1341,12 @@ Download size: %3 Oyun dosyası açılamadı: %1 - + Could not load game. Are you sure it's in the correct format? Oyun yüklenemedi. Doğru formatta olduğundan emin misin? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -3405,27 +3410,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State Durum yükle - + Save State Durumu Kaydet - + Empty Boş - + Corrupted Bozulmuş - + Slot %1 @@ -3477,47 +3482,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 - + An error occurred Bir hata meydana geldi - + DEBUG HATA AYIKLAMA - + STUB - + INFO BİLGİ - + WARN UYARI - + ERROR HATA - + FATAL CİDDİ - + GAME ERROR OYUN HATASI @@ -3641,42 +3646,42 @@ Download size: %3 Yükle - + All Hepsi - + Load TBL TBL yükle - + Save selected memory Seçilen hafızayı kaydet - + Failed to open output file: %1 Çıkış dosyası açılamadı:%1 - + Load memory Hafıza yükle - + Failed to open input file: %1 Giriş dosyası açılamadı:%1 - + TBL - + ISO-8859-1 @@ -3854,12 +3859,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive Hata rapor arşivi - + ZIP archive (*.zip) ZIP arşivi (*.zip) @@ -3927,104 +3932,112 @@ Download size: %3 Kayıtlı oyunlar platformlar arasında dönüştürülemez + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) Yazılım - + OpenGL - + OpenGL (force version 1.x) - + None Hiçbiri - + None (Still Image) - + Keyboard Klavye - + Controllers - + Shortcuts Kısayollar - - + + Shaders Gölgelendiricler - + Select BIOS BIOS seç - + Select directory Yolu seç - + (%1×%2) (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4122,120 +4135,120 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) Game Boy Advance ROMları (%1) - + Game Boy ROMs (%1) Game Boy ROMları (%1) - + All ROMs (%1) Bütün ROMlar (%1) - + %1 Video Logs (*.mvl) - + Archives (%1) Arşivler (%1) - - - + + + Select ROM ROM seç - + Select folder Klasör seç - - + + Select save Kayıt seç - + Select patch Yama seç - + Patches (*.ips *.ups *.bps) Yamalar (*.ips *.ups *.bps) - + Select e-Reader dotcode e-Okuyucu nokta kodunu seç - + e-Reader card (*.raw *.bin *.bmp) e-Okuyucu kart (*.raw *.bin *.bmp) - + Select e-Reader card images e-Okuyucu kartından görüntüleri seç - + Image file (*.png *.jpg *.jpeg) Görüntü dosyası (*.png *.jpg *.jpeg) - + Conversion finished Dönüştürme tamamlandı - + %1 of %2 e-Reader cards converted successfully. %1 / %2 e-Okuyucu kartları dönüştürme tamamlandı. - + Select image Resim seç - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Resim dosyası (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) GameShark kayıtları (*.sps *.xps) - + Select video log Video günlüğü seç - + Video logs (*.mvl) Video günlükleri (*.mvl) - + Crash Çökme - + The game has crashed with the following error: %1 @@ -4244,654 +4257,664 @@ Download size: %3 %1 - + Unimplemented BIOS call Uygulanmamış BIOS girişi - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Oyun BIOS dosyasına ihtiyacı var. Lütfen en iyi deneyim için resmi BIOS'u kullanın. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Uygun görüntü cihazı oluşturma başarısız, yazılım ekranına dönülüyor. Oyunlar özellikle daha büyük ekranlarda yavaş çalışabilir. - + Really make portable? Taşınabilir yapılsın mı? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Emülatörün yapılandırmasını yürütülebilir dosya ile aynı dizinden yüklemesini sağlar. Devam etmek istiyor musun? - + Restart needed Yeniden başlatma gerekli - + Some changes will not take effect until the emulator is restarted. Bazı değişiklikler emülatör yeniden başlatılıncaya kadar etkili olmaz. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... &ROM yükle... - + Load ROM in archive... ROM'u arşivden yükle ... - + Add folder to library... Kütüphaneye klasör ekle ... - + Save games Oyunları kaydet - + Automatically determine - + Use player %0 save game - + Load &patch... &Patch yükle... - + Boot BIOS BIOS boot et - + Replace ROM... ROM değişti... - + + Game state views + + + + Convert e-Reader card image to raw... e-Okuyucu kart resimlerini rawa dönüştür... - + ROM &info... ROM &info... - + Recent Son kullanılanlar - + Make portable Portatif yap - + &Load state &Kaydedilmiş konum yükle - + Load state file... Kaydedilmiş konum dosyası yükle... - + &Save state &Konumu kaydet - + Save state file... Konum dosyasını kaydet... - + Quick load Hızlı Yükle - + Quick save Hızlı kaydet - + Load recent En son yükle - + Save recent Hızlı kaydet - + Undo load state Kaydedilen konum yüklemeyi geri al - + Undo save state Konum kaydetmeyi geri al - - + + State &%1 Konum &%1 - + Load camera image... Kamera resmini yükle ... - + Convert save game... Kayıtlı oyun dömnüştürülüyor... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + New multiplayer window Yeni çokoyunculu ekranı - + Connect to Dolphin... Dolphin'e Bağlan... - + Report bug... Hata rapor et... - + About... Hakkında... - + E&xit Çıkış - + &Emulation Emülasyon - + &Reset &Reset - + Sh&utdown Kapat - + Yank game pak - + &Pause &Durdur - + &Next frame &Sonraki kare - + Fast forward (held) İleriye sar(basılı tutun) - + &Fast forward &İleriye sar - + Fast forward speed İleriye sarma hızı - + Unbounded - + %0x - + Rewind (held) Geri sar (basılı tutun) - + Re&wind Geri sar - + Step backwards Geriye doğru adım - + Solar sensor - + Increase solar level Solar seviyesini arttır - + Decrease solar level Solar seviyesini düşür - + Brightest solar level En parlak solar seviyesi - + Darkest solar level En karanlık solar seviyesi - + Brightness %1 Parlaklık:%1 - + Game Boy Printer... Game Boy yazıcısı... - + BattleChip Gate... - + Audio/&Video Ses/&Video - + Frame size Çerçeve boyutu - + Toggle fullscreen Tamekranı aç/kapa - + Lock aspect ratio En boy oranını kilitle - + Force integer scaling Tamsayılı ölçeklendirmeyi zorla - + Bilinear filtering Bilinear filtreleme - + Frame&skip Kare atlama - + Mute Sessiz - + FPS target FPS hedefi - + Native (59.7275) - + Take &screenshot Ekran görüntüsü al - + F12 - + Video layers - + Audio channels Ses kanalları - + Adjust layer placement... Katman yerleşimini ayarlayın... - + &Tools &Araçlar - + View &logs... Kayıtları görüntüle... - + Game &overrides... & Oyunun üzerine yazılanlar... - + Couldn't Start Başlatılamadı - + Save games (%1) Kayıtlı oyunlar (%1) - + Select save game Kayıtlı oyun seç - + mGBA save state files (%1) mGBA kayıt durum dosyası (%1) - - + + Select save state Kayıt durumu seç - + Could not start game. Oyun başlatılamadı. - + Load alternate save game... Alternatif kayıtlı oyun yükle... - + Load temporary save game... Geçici kayıtlı oyunu yükle... - + Scan e-Reader dotcodes... e-Okuyucu noktakodları tara... - + Import GameShark Save... GameShark kaydını içeri aktar... - + Export GameShark Save... GameShark kaydını dışarı aktar... - + %1× %1× - + Interframe blending Kareler-arası Karıştırma - + Record A/V... A/V Kayıt... - + Record GIF/WebP/APNG... GIF/WebP/APNG Kayıt... - + Game Pak sensors... Oyun Kartuş sensörleri... - + &Cheats... &Hileler... - + Settings... Ayarlar... - + Open debugger console... Hata ayıklayıcı konsolunu aç ... - + Start &GDB server... &GDB sunucusunu başlat... - + + Scripting... + + + + View &palette... &Renk Paletini gör... - + View &sprites... &Spriteları gör... - + View &tiles... &Desenleri gör... - + View &map... &Haritayı gör - + &Frame inspector... &Kare denetçisi... - + View memory... Hafıza gör... - + Search memory... Hafızada ara... - + View &I/O registers... &I/O kayıtlarını görüntüle - + Record debug video log... Hata ayıklama video günlüğünü kaydet... - + Stop debug video log Hata ayıklama video günlüğünü durdur - + Exit fullscreen Tam ekrandan çık - + GameShark Button (held) GameShark Butonu (basılı tutun) - + Autofire Otomatik basma - + Autofire A Otomatik basma A - + Autofire B Otomatik basma B - + Autofire L Otomatik basma L - + Autofire R Otomatik basma R - + Autofire Start Otomatik basma Start - + Autofire Select Otomatik basma Select - + Autofire Up Otomatik basma Up - + Autofire Right Otomatik basma Right - + Autofire Down Otomatik basma Down - + Autofire Left Otomatik basma Sol - + Clear Temizle @@ -5137,6 +5160,59 @@ Download size: %3 + + ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + &Reset + + + + 0 + 4K {0?} + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + SensorView @@ -5150,64 +5226,74 @@ Download size: %3 Gerçek zaman saati - + Fixed time Sabit zaman - + System time Sistem zamanı - + Start time at Başlangıç zamanı - + Now Şimdi - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP - + Light sensor - + Brightness Parlaklık - + Tilt sensor Eğim sensörü - - + + Set Y - - + + Set X - + Gyroscope Jiroskop - + Sensitivity Hassaslık diff --git a/src/platform/qt/ts/mgba-zh_CN.ts b/src/platform/qt/ts/mgba-zh_CN.ts index 0aae8ffbd..450a9329b 100644 --- a/src/platform/qt/ts/mgba-zh_CN.ts +++ b/src/platform/qt/ts/mgba-zh_CN.ts @@ -1185,21 +1185,21 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 - + Do you want to download and install it now? You will need to restart the emulator when the download is complete. 你想现在下载并安装吗?下载完成后,你需要重新启动模拟器。 - + Auto-update is not available on this platform. If you wish to update you will need to do it manually. 自动更新在此平台上不可用。如果您希望更新,则需要手动进行。 - + Current version: %1 New version: %2 Download size: %3 @@ -1208,17 +1208,17 @@ Download size: %3 更新大小:%3 - + Downloading update... 正在下载更新... - + Downloading failed. Please update manually. 下载失败。请手动更新。 - + Downloading done. Press OK to restart %1 and install the update. 下载完成。按确定按钮以重新启动 %1 并安装更新。 @@ -1241,7 +1241,7 @@ Download size: %3 未知 - + (None) (无) @@ -1272,17 +1272,22 @@ Download size: %3 QGBA::CheatsView - - + + Autodetect (recommended) 自动检测(推荐) - - + + Select cheats file 选择作弊码文件 + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + 无法添加某些作弊码。请确认它们的格式是否正确并且/或尝试其它的作弊类型。 + QGBA::CoreController @@ -1292,43 +1297,43 @@ Download size: %3 重置 r%1-%2 %3 - - + + Rewinding not currently enabled 当前未开启倒带 - + Reset the game? 要重置游戏吗? - + Most games will require a reset to load the new save. Do you want to reset now? - 大多数游戏需要重启才能加载新的存档。您要立即重置吗? + 大多数游戏需要重置才能加载新的存档。您要立即重启吗? - + Failed to open save file: %1 打开存档失败: %1 - + Failed to open game file: %1 打开游戏文件失败: %1 - + Can't yank pack in unexpected platform! 无法在意外平台上抽出卡带! - + Failed to open snapshot file for reading: %1 读取快照文件失败: %1 - + Failed to open snapshot file for writing: %1 写入快照文件失败: %1 @@ -1341,12 +1346,12 @@ Download size: %3 打开游戏文件失败: %1 - + Could not load game. Are you sure it's in the correct format? 无法载入游戏。请确认游戏格式是否正确? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). 无法打开存档;游戏内存档无法更新。请确保保存目录是可写的,且没有额外权限(例如 Windows 上的 UAC)。 @@ -1356,7 +1361,7 @@ Download size: %3 Could not open CLI history for writing - 无法打开要写入的 CLI 历史 + 无法打开用于写入的 CLI 历史 @@ -1473,12 +1478,12 @@ Download size: %3 Internal change detection - 检测到内部更改 + 内部改动检测 Break on all writes - 在所有写入时中断 + 中断所有写入 @@ -3081,12 +3086,12 @@ Download size: %3 32× clocking (CGB only) - 32× 时钟(仅 CGB) + 32× 时钟(CGB 特有) Transfer active - 活动传输 + 传输处于活动状态 @@ -3102,13 +3107,13 @@ Download size: %3 LCD STAT - LCD 状态 + LCD 统计 Timer - 计时器 + 定时器 @@ -3222,7 +3227,7 @@ Download size: %3 2: OAM scan - 2: OAM 扫描 + 2:OAM 扫描 @@ -3320,17 +3325,17 @@ Download size: %3 Timing - 定时 + 定时 Write bit - 写位 + 写位 Read bit - 读位 + 读位 @@ -3342,7 +3347,7 @@ Download size: %3 Current index - 当前索引 + 当前索引 @@ -3354,28 +3359,28 @@ Download size: %3 Red - + Blue - + Sprite ordering - 精灵排序 + 精灵排序 OAM order - OAM 顺序 + OAM 顺序 x coordinate sorting - x 坐标排序 + x 坐标排序 @@ -3410,27 +3415,27 @@ Download size: %3 QGBA::LoadSaveState - + Load State 载入即时存档 - + Save State 即时存档 - + Empty - + Corrupted 已损坏 - + Slot %1 插槽 %1 @@ -3482,47 +3487,47 @@ Download size: %3 QGBA::LogController - + [%1] %2: %3 [%1] %2: %3 - + An error occurred 发生错误 - + DEBUG DEBUG - + STUB STUB - + INFO INFO - + WARN WARN - + ERROR ERROR - + FATAL FATAL - + GAME ERROR GAME ERROR @@ -3565,7 +3570,7 @@ Download size: %3 Map Addr. - 映射地址 + 映射地址. @@ -3646,42 +3651,42 @@ Download size: %3 载入 - + All 全部 - + Load TBL 载入 TBL - + Save selected memory 保存所选内存 - + Failed to open output file: %1 打开输出文件失败: %1 - + Load memory 载入内存 - + Failed to open input file: %1 打开输入文件失败: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3859,12 +3864,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive 错误报告存档 - + ZIP archive (*.zip) ZIP 存档 (*.zip) @@ -3874,22 +3879,22 @@ Download size: %3 Save games and save states (%1) - 保存游戏和即时存档(%1) + 保存游戏和即时存档(%1) Select save game or save state - 选择保存游戏或即时存档 + 选择保存游戏或即时存档 Save games (%1) - 保存游戏(%1) + 保存游戏(%1) Select save game - 选择保存游戏 + 选择保存游戏 @@ -3899,12 +3904,12 @@ Download size: %3 Failed to convert the save game. This is probably a bug. - 未能转换保存游戏。这可能是一个错误。 + 未能转换保存游戏。这可能是一个错误。 No file selected - 未选择文件 + 未选择文件 @@ -3924,112 +3929,120 @@ Download size: %3 No valid conversions found - 未发现有效转换 + 未发现有效转换 Cannot convert save games between platforms - 无法在平台之间转换保存游戏 + 无法在平台之间转换保存游戏 + + + + QGBA::ScriptingTextBuffer + + + Untitled buffer + 无标题缓存 QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) 软件渲染 (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (强制版本 1.x) - + None - + - + None (Still Image) 无 (静止图像) - + Keyboard 键盘 - + Controllers 控制器 - + Shortcuts 快捷键 - - + + Shaders 着色器 - + Select BIOS 选择 BIOS - + Select directory 选择目录 - + (%1×%2) (%1×%2) - + Never 从不 - + Just now 刚刚 - + Less than an hour ago 不到一小时前 - + %n hour(s) ago %n 小时前 - + %n day(s) ago %n 天前 @@ -4127,100 +4140,100 @@ Download size: %3 QGBA::Window - + Game Boy Advance ROMs (%1) Game Boy Advance ROM (%1) - + Game Boy ROMs (%1) Game Boy ROM (%1) - + All ROMs (%1) 所有 ROM (%1) - + %1 Video Logs (*.mvl) %1 视频日志 (*.mvl) - + Archives (%1) 压缩文件 (%1) - - - + + + Select ROM 选择 ROM - + Select folder 选择文件夹 - - + + Select save 选择存档 - + Select patch 选择补丁 - + Patches (*.ips *.ups *.bps) 补丁 (*.ips *.ups *.bps) - + Select e-Reader dotcode 选择 e-Reader 点码 - + e-Reader card (*.raw *.bin *.bmp) e-Reader 卡 (*.raw *.bin *.bmp) - + Select image 选择图片 - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) 图像文件 (*.png *.gif *.jpg *.jpeg);;所有文件 (*) - + GameShark saves (*.sps *.xps) GameShark 存档 (*.sps *.xps) - + Select video log 选择视频日志 - + Video logs (*.mvl) 视频日志文件 (*.mvl) - + Crash 崩溃 - + The game has crashed with the following error: %1 @@ -4229,674 +4242,684 @@ Download size: %3 %1 - + Couldn't Start 无法启动 - + Could not start game. 无法启动游戏。 - + Unimplemented BIOS call 未实现的 BIOS 调用 - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. 此游戏使用尚未实现的 BIOS 调用。请使用官方 BIOS 以获得最佳体验。 - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. 无法创建适合的显示设备,正在回滚到软件显示。游戏的运行速度(特别在大窗口的情况下)可能会变慢。 - + Really make portable? 确定进行程序便携化? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? 进行此操作后,模拟器将从其可执行文件所在目录中载入模拟器配置。您想继续吗? - + Restart needed 需要重新启动 - + Some changes will not take effect until the emulator is restarted. 更改将在模拟器下次重新启动时生效。 - + - Player %1 of %2 - 玩家 %1 共 %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - %1 - %2(%3 fps)- %4 + - + &File 文件(&F) - + Load &ROM... 载入 ROM(&R)... - + Load ROM in archive... 从压缩文件中载入 ROM... - + Add folder to library... 将文件夹添加到库中... - + Save games (%1) 保存游戏(%1) - + Select save game - 选择保存游戏 + 选择保存游戏 - + mGBA save state files (%1) mGBA 即时存档文件(%1) - - + + Select save state 选择即时存档 - + Select e-Reader card images 选择 e-Reader 卡片图像 - + Image file (*.png *.jpg *.jpeg) 图像文件(*.png *.jpg *.jpeg) - + Conversion finished 转换完成 - + %1 of %2 e-Reader cards converted successfully. - 成功转换了 %1 张(共 %2 张)e-Reader 卡片。 + 成功转换了 %1 张(共 %2 张)e-Reader 卡片。 - + Load alternate save game... - 加载备用保存游戏... + 加载备用保存游戏... - + Load temporary save game... - 加载临时保存游戏... + 加载临时保存游戏... - + Load &patch... 载入补丁(&P)... - + Boot BIOS 引导 BIOS - + Replace ROM... 替换 ROM... - + Scan e-Reader dotcodes... 扫描 e-Reader 点码... - + Convert e-Reader card image to raw... - 将 e-Reader 卡片图像转换为原始数据... + 将 e-Reader 卡片图像转换为原始数据... - + ROM &info... ROM 信息(&I)... - + Recent 最近打开 - + Make portable 程序便携化 - + &Load state 载入即时存档(&L) - + Load state file... 载入即时存档文件... - + &Save state 保存即时存档(&S) - + Save state file... 保存即时存档文件... - + Quick load 快速读档 - + Quick save 快速存档 - + Load recent 载入最近存档 - + Save recent 保存最近存档 - + Undo load state 撤消读档 - + Undo save state 撤消存档 - - + + State &%1 即时存档 (&%1) - + Load camera image... 载入相机图片... - + Convert save game... - 转换保存游戏... + 转换保存游戏... - + GameShark saves (*.gsv *.sps *.xps) GameShark 存档 (*.gsv *.sps *.xps) - + Reset needed 需要重启 - + Some changes will not take effect until the game is reset. - 某些改动直到游戏重启后才能生效。 + 某些改动需要重新启动才会生效。 - + Save games - 游戏存档 + 游戏存档 - + Import GameShark Save... 导入 GameShark 存档... - + Export GameShark Save... 导出 GameShark 存档... - + Automatically determine - 自动测定 + 自动终止 - + Use player %0 save game - 使用玩家 %0 的存档 + 使用玩家 %0 存档 - + New multiplayer window 新建多人游戏窗口 - + Connect to Dolphin... 连接到 Dolphin... - + Report bug... 报告错误... - + About... 关于... - + E&xit 退出(&X) - + &Emulation 模拟(&E) - + &Reset 重置(&R) - + Sh&utdown 关机(&U) - + Yank game pak 快速抽出游戏卡带 - + &Pause 暂停(&P) - + &Next frame 下一帧(&N) - + Fast forward (held) 快进 (长按) - + &Fast forward 快进(&F) - + Fast forward speed 快进速度 - + Unbounded 不限制 - + %0x %0x - + Rewind (held) 倒带 (长按) - + Re&wind 倒带(&W) - + Step backwards 步退 - + Solar sensor 太阳光传感器 - + Increase solar level 增加太阳光等级 - + Decrease solar level 降低太阳光等级 - + Brightest solar level 太阳光等级为最亮 - + Darkest solar level 太阳光等级为最暗 - + Brightness %1 亮度 %1 - + Game Boy Printer... Game Boy 打印机... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video 音频/视频(&V) - + Frame size 画面大小 - + %1× %1× - + Toggle fullscreen 切换全屏 - + Lock aspect ratio 锁定纵横比 - + Force integer scaling 强制整数缩放 - + Interframe blending 帧间混合 - + Bilinear filtering 双线性过滤 - + Frame&skip 跳帧(&S) - + Mute 静音 - + FPS target 目标 FPS - + Native (59.7275) 原生 (59.7275) - + Take &screenshot 截图(&S) - + F12 F12 - + Record A/V... 录制音频/视频... - + Record GIF/WebP/APNG... 录制 GIF/WebP/APNG... - + Video layers 视频图层 - + Audio channels 音频声道 - + Adjust layer placement... 调整图层布局... - + &Tools 工具(&T) - + View &logs... 查看日志(&L)... - + Game &overrides... 覆写游戏(&O)... - + Game Pak sensors... 游戏卡带传感器... - + &Cheats... 作弊码(&C)... - + Settings... 设置... - + Open debugger console... 打开调试器控制台... - + Start &GDB server... 打开 GDB 服务器(&G)... - + + Scripting... + 脚本... + + + + Game state views + + + + View &palette... 查看调色板(&P)... - + View &sprites... 查看精灵图(&S)... - + View &tiles... 查看图块(&T)... - + View &map... 查看映射(&M)... - + &Frame inspector... 框架检查器(&F)... - + View memory... 查看内存... - + Search memory... 搜索内存... - + View &I/O registers... 查看 I/O 寄存器(&I)... - + Record debug video log... 记录调试视频日志... - + Stop debug video log 停止记录调试视频日志 - + Exit fullscreen 退出全屏 - + GameShark Button (held) GameShark 键 (长按) - + Autofire 连发 - + Autofire A 连发 A - + Autofire B 连发 B - + Autofire L 连发 L - + Autofire R 连发 R - + Autofire Start 连发 Start - + Autofire Select 连发 Select - + Autofire Up 连发 上 - + Autofire Right 连发 右 - + Autofire Down 连发 下 - + Autofire Left 连发 左 - + Clear 清除 @@ -4911,7 +4934,7 @@ Download size: %3 %1 kiB - %1 kiB + @@ -5033,7 +5056,7 @@ Download size: %3 Convert/Extract Save Game - 转换或提取保存游戏 + 转换或提取保存游戏 @@ -5044,7 +5067,7 @@ Download size: %3 Browse - 浏览 + 浏览 @@ -5054,17 +5077,17 @@ Download size: %3 %1 %2 save game - %1 %2 保存游戏 + %1 %2 保存游戏 little endian - 小端 + 小端 big endian - 大端 + 大端 @@ -5074,7 +5097,7 @@ Download size: %3 %1 flash - %1 闪存 + %1 闪存 @@ -5094,17 +5117,17 @@ Download size: %3 packed MBC2 - 包装的MBC2 + 包装的 MBC2 unpacked MBC2 - 未包装的 MBC2 + 未包装的 MBC2 MBC6 flash - MBC6 闪存 + MBC6 闪存 @@ -5129,17 +5152,70 @@ Download size: %3 %1 save state with embedded %2 save game - 带嵌入的 %2 保存游戏的 %1 保存状态 + 带嵌入的 %2 保存游戏的 %1 保存状态 %1 SharkPort %2 save game - %1 SharkPort %2 存档 + %1 SharkPort %2 存档 %1 GameShark Advance SP %2 save game - %1 GameShark Advance SP %2 存档 + %1 GameShark Advance SP %2 存档 + + + + ScriptingView + + + Scripting + 脚本 + + + + Run + 运行 + + + + File + 文件 + + + + Load recent script + 载入历史脚本 + + + + Load script... + 载入脚本... + + + + &Reset + 重置(&R) + + + + 0 + 0 + + + + Select script to load + 选择要载入的脚本 + + + + Lua scripts (*.lua) + Lua 脚本 (*.lua) + + + + All files (*.*) + 所有文件 (*.*) @@ -5155,64 +5231,74 @@ Download size: %3 实时时钟 - + Fixed time 定时 - + System time 系统时间 - + Start time at 开始时间 - + Now 现在 - + + Offset time + + + + + sec + + + + MM/dd/yy hh:mm:ss AP yyyy/MM/dd HH:mm:ss - + Light sensor 光线传感器 - + Brightness 亮度 - + Tilt sensor 倾斜传感器 - - + + Set Y 设定 Y 轴 - - + + Set X 设定 X 轴 - + Gyroscope 陀螺仪 - + Sensitivity 灵敏度 @@ -5387,7 +5473,7 @@ Download size: %3 Currently active player window - 当前活跃的玩家窗口 + 当前活跃的玩家窗口 @@ -5453,7 +5539,7 @@ Download size: %3 Show filename instead of ROM name in library view - 在库视图中显示文件名而不是 ROM 名称 + 库视图中显示文件名替代 ROM 名 @@ -5464,7 +5550,7 @@ Download size: %3 When inactive: - 不活跃时: + 不活跃时: @@ -5539,12 +5625,12 @@ Download size: %3 Driver: - 驱动: + 驱动: Source: - 来源: + 来源: @@ -5644,12 +5730,12 @@ Download size: %3 Show frame count in OSD - 在 OSD 中显示帧数 + OSD 中显示帧数 Show emulation info on reset - 重启时显示模拟信息 + 重置时显示模拟信息 @@ -5710,28 +5796,28 @@ Download size: %3 Save state extra data: - 保存存档附加数据: + 保存存档附加数据: Save game - 保存游戏 + 保存游戏 Load state extra data: - 载入存档附加数据: + 载入存档附加数据: Models - 型号 + 型号 GB only: - 仅 GB: + 仅 GB: @@ -5741,7 +5827,7 @@ Download size: %3 GBC only: - 仅 GBC: + 仅 GBC: @@ -5756,7 +5842,7 @@ Download size: %3 Game Boy palette - Game Boy 调色板 + Game Boy 调色板 @@ -6019,7 +6105,7 @@ Download size: %3 Palette - 调色板 + 调色板 @@ -6039,7 +6125,7 @@ Download size: %3 Displayed tiles - 显示图块 + 已显示的图块 @@ -6054,7 +6140,7 @@ Download size: %3 Both - 两者 + 两者 diff --git a/src/platform/qt/updater-config.qrc.in b/src/platform/qt/updater-config.qrc.in new file mode 100644 index 000000000..edf2cd38e --- /dev/null +++ b/src/platform/qt/updater-config.qrc.in @@ -0,0 +1,5 @@ + + + ${PROJECT_BINARY_DIR}/updater-stub${CMAKE_EXECUTABLE_SUFFIX} + + diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 47f05cc4d..cacf0bc48 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -56,6 +56,9 @@ elseif(APPLE) if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "17.0") # Darwin 17.x is macOS 10.13 list(APPEND SDL_LIBRARY "-framework Metal") endif() + if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "19.0") # Darwin 19.x is macOS 10.15 + list(APPEND SDL_LIBRARY "-framework GameController" "-framework CoreHaptics") + endif() endif() if(NOT SDLMAIN_LIBRARY) diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index b7e8b002b..a675ff2c0 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -199,6 +199,7 @@ bool mSDLAttachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) { player->rotation.gyroSensitivity = 2.2e9f; player->rotation.gyroX = 0; player->rotation.gyroY = 1; + player->rotation.gyroZ = -1; player->rotation.zDelta = 0; CircleBufferInit(&player->rotation.zHistory, sizeof(float) * GYRO_STEPS); player->rotation.p = player; @@ -327,6 +328,13 @@ void mSDLPlayerLoadConfig(struct mSDLPlayer* context, const struct Configuration context->rotation.gyroY = axis; } } + value = mInputGetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroAxisZ", name); + if (value) { + axis = strtol(value, &end, 0); + if (axis >= 0 && axis < numAxes && end && !*end) { + context->rotation.gyroZ = axis; + } + } value = mInputGetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroSensitivity", name); if (value) { float sensitivity = strtof_u(value, &end); @@ -357,6 +365,8 @@ void mSDLPlayerSaveConfig(const struct mSDLPlayer* context, struct Configuration mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroAxisX", value, name); snprintf(value, sizeof(value), "%i", context->rotation.gyroY); mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroAxisY", value, name); + snprintf(value, sizeof(value), "%i", context->rotation.gyroZ); + mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroAxisZ", value, name); snprintf(value, sizeof(value), "%g", context->rotation.gyroSensitivity); mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroSensitivity", value, name); } @@ -750,6 +760,10 @@ static void _mSDLRotationSample(struct mRotationSource* source) { } } #endif + if (rotation->gyroZ >= 0) { + rotation->zDelta = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroZ) / 1.e5f; + return; + } int x = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroX); int y = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroY); diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index 1763865e2..8bdbd7c0b 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -95,6 +95,7 @@ struct mSDLPlayer { // Gyro int gyroX; int gyroY; + int gyroZ; float gyroSensitivity; struct CircleBuffer zHistory; int oldX; diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index 0a40d3a90..25b6f6eca 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -115,7 +115,7 @@ static float gyroZ = 0; static float tiltX = 0; static float tiltY = 0; -static struct GBAStereoSample audioBuffer[N_BUFFERS][BUFFER_SIZE / 4] __attribute__((__aligned__(0x1000))); +static struct mStereoSample audioBuffer[N_BUFFERS][BUFFER_SIZE / 4] __attribute__((__aligned__(0x1000))); static enum ScreenMode { SM_PA, @@ -584,7 +584,7 @@ static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* rig blip_clear(right); return; } - struct GBAStereoSample* samples = audioBuffer[audioBufferActive]; + struct mStereoSample* samples = audioBuffer[audioBufferActive]; blip_read_samples(left, &samples[0].left, SAMPLES, true); blip_read_samples(right, &samples[0].right, SAMPLES, true); audoutAppendAudioOutBuffer(&audoutBuffer[audioBufferActive]); diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 8b3ed8226..36bb9abfc 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -141,7 +141,7 @@ static void* framebuffer[2] = { 0, 0 }; static int whichFb = 0; static struct AudioBuffer { - struct GBAStereoSample samples[SAMPLES] __attribute__((__aligned__(32))); + struct mStereoSample samples[SAMPLES] __attribute__((__aligned__(32))); volatile size_t size; } audioBuffer[BUFFERS] = {0}; static volatile int currentAudioBuffer = 0; @@ -685,8 +685,8 @@ static void _audioDMA(void) { if (buffer->size != SAMPLES) { return; } - DCFlushRange(buffer->samples, SAMPLES * sizeof(struct GBAStereoSample)); - AUDIO_InitDMA((u32) buffer->samples, SAMPLES * sizeof(struct GBAStereoSample)); + DCFlushRange(buffer->samples, SAMPLES * sizeof(struct mStereoSample)); + AUDIO_InitDMA((u32) buffer->samples, SAMPLES * sizeof(struct mStereoSample)); buffer->size = 0; currentAudioBuffer = (currentAudioBuffer + 1) % BUFFERS; } diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index c579ccb9f..e9ecc8c60 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -10,7 +10,9 @@ set(TEST_FILES if(USE_LUA) list(APPEND SOURCE_FILES engines/lua.c) - list(APPEND TEST_FILES test/lua.c) + list(APPEND TEST_FILES + test/stdlib.c + test/lua.c) endif() source_group("Scripting" FILES ${SOURCE_FILES}) diff --git a/src/script/context.c b/src/script/context.c index f2437dc5b..84847983c 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -14,6 +14,11 @@ struct mScriptFileInfo { struct mScriptEngineContext* context; }; +struct mScriptCallbackInfo { + const char* callback; + size_t id; +}; + static void _engineContextDestroy(void* ctx) { struct mScriptEngineContext* context = ctx; context->destroy(context); @@ -56,7 +61,10 @@ void mScriptContextInit(struct mScriptContext* context) { TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref); context->nextWeakref = 1; HashTableInit(&context->callbacks, 0, (void (*)(void*)) mScriptValueDeref); + TableInit(&context->callbackId, 0, free); + context->nextCallbackId = 1; context->constants = NULL; + HashTableInit(&context->docstrings, 0, NULL); } void mScriptContextDeinit(struct mScriptContext* context) { @@ -65,7 +73,9 @@ void mScriptContextDeinit(struct mScriptContext* context) { mScriptContextDrainPool(context); mScriptListDeinit(&context->refPool); HashTableDeinit(&context->callbacks); + TableDeinit(&context->callbackId); HashTableDeinit(&context->engines); + HashTableDeinit(&context->docstrings); } void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* value) { @@ -197,8 +207,11 @@ void mScriptContextTriggerCallback(struct mScriptContext* context, const char* c size_t i; for (i = 0; i < mScriptListSize(list->value.list); ++i) { struct mScriptFrame frame; - mScriptFrameInit(&frame); struct mScriptValue* fn = mScriptListGetPointer(list->value.list, i); + if (!fn->type) { + continue; + } + mScriptFrameInit(&frame); if (fn->type->base == mSCRIPT_TYPE_WRAPPER) { fn = mScriptValueUnwrap(fn); } @@ -207,16 +220,48 @@ void mScriptContextTriggerCallback(struct mScriptContext* context, const char* c } } -void mScriptContextAddCallback(struct mScriptContext* context, const char* callback, struct mScriptValue* fn) { +uint32_t mScriptContextAddCallback(struct mScriptContext* context, const char* callback, struct mScriptValue* fn) { if (fn->type->base != mSCRIPT_TYPE_FUNCTION) { - return; + return 0; } struct mScriptValue* list = HashTableLookup(&context->callbacks, callback); if (!list) { list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); HashTableInsert(&context->callbacks, callback, list); } + struct mScriptCallbackInfo* info = malloc(sizeof(*info)); + // Steal the string from the table key, since it's guaranteed to outlive this struct + struct TableIterator iter; + HashTableIteratorLookup(&context->callbacks, &iter, callback); + info->callback = HashTableIteratorGetKey(&context->callbacks, &iter); + info->id = mScriptListSize(list->value.list); mScriptValueWrap(fn, mScriptListAppend(list->value.list)); + while (true) { + uint32_t id = context->nextCallbackId; + ++context->nextCallbackId; + if (TableLookup(&context->callbackId, id)) { + continue; + } + TableInsert(&context->callbackId, id, info); + return id; + } +} + +void mScriptContextRemoveCallback(struct mScriptContext* context, uint32_t cbid) { + struct mScriptCallbackInfo* info = TableLookup(&context->callbackId, cbid); + if (!info) { + return; + } + struct mScriptValue* list = HashTableLookup(&context->callbacks, info->callback); + if (!list) { + return; + } + if (info->id >= mScriptListSize(list->value.list)) { + return; + } + struct mScriptValue* fn = mScriptValueUnwrap(mScriptListGetPointer(list->value.list, info->id)); + mScriptValueDeref(fn); + mScriptListGetPointer(list->value.list, info->id)->type = NULL; } void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants) { @@ -237,6 +282,26 @@ void mScriptContextExportConstants(struct mScriptContext* context, const char* n mScriptValueDeref(table); } +void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* values) { + struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + size_t i; + for (i = 0; values[i].key; ++i) { + struct mScriptValue* key = mScriptStringCreateFromUTF8(values[i].key); + mScriptTableInsert(table, key, values[i].value); + mScriptValueDeref(key); + mScriptValueDeref(values[i].value); + } + mScriptContextSetGlobal(context, nspace, table); +} + +void mScriptContextSetDocstring(struct mScriptContext* context, const char* key, const char* docstring) { + HashTableInsert(&context->docstrings, key, (char*) docstring); +} + +const char* mScriptContextGetDocstring(struct mScriptContext* context, const char* key) { + return HashTableLookup(&context->docstrings, key); +} + bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) { struct mScriptFileInfo info = { .name = name, diff --git a/src/script/docgen.c b/src/script/docgen.c index bc16bc9b7..ecf19a354 100644 --- a/src/script/docgen.c +++ b/src/script/docgen.c @@ -12,7 +12,7 @@ struct mScriptContext context; struct Table types; FILE* out; -void explainValue(struct mScriptValue* value, int level); +void explainValue(struct mScriptValue* value, const char* name, int level); void explainType(struct mScriptType* type, int level); void addTypesFromTuple(const struct mScriptTypeTuple*); @@ -154,7 +154,7 @@ bool printval(const struct mScriptValue* value, char* buffer, size_t bufferSize) return false; } -void explainTable(struct mScriptValue* value, int level) { +void explainTable(struct mScriptValue* value, const char* name, int level) { char indent[(level + 1) * 2 + 1]; memset(indent, ' ', sizeof(indent) - 1); indent[sizeof(indent) - 1] = '\0'; @@ -167,7 +167,14 @@ void explainTable(struct mScriptValue* value, int level) { printval(k, keyval, sizeof(keyval)); fprintf(out, "%s- key: %s\n", indent, keyval); struct mScriptValue* v = mScriptTableIteratorGetValue(value, &iter); - explainValue(v, level + 1); + + struct mScriptValue string; + if (mScriptCast(mSCRIPT_TYPE_MS_CHARP, k, &string)) { + snprintf(keyval, sizeof(keyval), "%s.%s", name, (const char*) string.value.opaque); + explainValue(v, keyval, level + 1); + } else { + explainValue(v, NULL, level + 1); + } } while (mScriptTableIteratorNext(value, &iter)); } } @@ -234,15 +241,15 @@ void explainObject(struct mScriptValue* value, int level) { struct mScriptValue* unwrappedMember; if (member.type->base == mSCRIPT_TYPE_WRAPPER) { unwrappedMember = mScriptValueUnwrap(&member); - explainValue(unwrappedMember, level + 2); + explainValue(unwrappedMember, NULL, level + 2); } else { - explainValue(&member, level + 2); + explainValue(&member, NULL, level + 2); } } } } -void explainValue(struct mScriptValue* value, int level) { +void explainValue(struct mScriptValue* value, const char* name, int level) { char valstring[1024]; char indent[(level + 1) * 2 + 1]; memset(indent, ' ', sizeof(indent) - 1); @@ -250,10 +257,19 @@ void explainValue(struct mScriptValue* value, int level) { value = mScriptContextAccessWeakref(&context, value); addType(value->type); fprintf(out, "%stype: %s\n", indent, value->type->name); + + const char* docstring = NULL; + if (name) { + docstring = mScriptContextGetDocstring(&context, name); + } + if (docstring) { + fprintf(out, "%scomment: \"%s\"\n", indent, docstring); + } + switch (value->type->base) { case mSCRIPT_TYPE_TABLE: fprintf(out, "%svalue:\n", indent); - explainTable(value, level); + explainTable(value, name, level); break; case mSCRIPT_TYPE_SINT: case mSCRIPT_TYPE_UINT: @@ -375,6 +391,7 @@ void explainCore(struct mCore* core) { fprintf(out, " %s:\n", name->value.string->buffer); value = mScriptContextAccessWeakref(&context, value); + addType(value->type); struct mScriptFrame frame; uint32_t baseVal; @@ -462,7 +479,7 @@ int main(int argc, char* argv[]) { const char* name = HashTableIteratorGetKey(&context.rootScope, &iter); fprintf(out, " %s:\n", name); struct mScriptValue* value = HashTableIteratorGetValue(&context.rootScope, &iter); - explainValue(value, 1); + explainValue(value, name, 1); } while (HashTableIteratorNext(&context.rootScope, &iter)); } fputs("emu:\n", out); diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 5c8906531..90601db94 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -11,6 +11,10 @@ #include #include +#ifdef _WIN32 +#include +#endif + #define MAX_KEY_SIZE 128 static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mScriptContext*); @@ -30,7 +34,7 @@ static bool _luaPushFrame(struct mScriptEngineContextLua*, struct mScriptList*, static bool _luaPopFrame(struct mScriptEngineContextLua*, struct mScriptList*); static bool _luaInvoke(struct mScriptEngineContextLua*, struct mScriptFrame*); -static struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext); +static struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool pop); static bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue*); static void _luaDeref(struct mScriptValue*); @@ -45,10 +49,20 @@ static int _luaPairsTable(lua_State* lua); static int _luaGetList(lua_State* lua); static int _luaLenList(lua_State* lua); +static int _luaRequireShim(lua_State* lua); + #if LUA_VERSION_NUM < 503 #define lua_pushinteger lua_pushnumber #endif +#ifndef LUA_OK +#define LUA_OK 0 +#endif + +#if LUA_VERSION_NUM < 502 +#define luaL_traceback(L, M, S, level) lua_pushstring(L, S) +#endif + const struct mScriptType mSTLuaFunc = { .base = mSCRIPT_TYPE_FUNCTION, .size = 0, @@ -74,6 +88,7 @@ struct mScriptEngineContextLua { struct mScriptEngineContext d; lua_State* lua; int func; + int require; char* lastError; }; @@ -106,6 +121,7 @@ static const luaL_Reg _mSTTable[] = { { "__index", _luaGetTable }, { "__len", _luaLenTable }, { "__pairs", _luaPairsTable }, + { "__gc", _luaGcObject }, { NULL, NULL } }; @@ -158,6 +174,9 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS #endif lua_pop(luaContext->lua, 1); + lua_getglobal(luaContext->lua, "require"); + luaContext->require = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); + return &luaContext->d; } @@ -170,6 +189,9 @@ void _luaDestroy(struct mScriptEngineContext* ctx) { if (luaContext->func > 0) { luaL_unref(luaContext->lua, LUA_REGISTRYINDEX, luaContext->func); } + if (luaContext->require > 0) { + luaL_unref(luaContext->lua, LUA_REGISTRYINDEX, luaContext->require); + } lua_close(luaContext->lua); free(luaContext); } @@ -186,7 +208,7 @@ bool _luaIsScript(struct mScriptEngineContext* ctx, const char* name, struct VFi struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext* ctx, const char* name) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; lua_getglobal(luaContext->lua, name); - return _luaCoerce(luaContext); + return _luaCoerce(luaContext, true); } bool _luaSetGlobal(struct mScriptEngineContext* ctx, const char* name, struct mScriptValue* value) { @@ -212,7 +234,83 @@ struct mScriptValue* _luaCoerceFunction(struct mScriptEngineContextLua* luaConte return value; } -struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { +struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext) { + struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + bool isList = true; + + lua_pushnil(luaContext->lua); + while (lua_next(luaContext->lua, -2) != 0) { + struct mScriptValue* value = NULL; + int type = lua_type(luaContext->lua, -1); + switch (type) { + case LUA_TNUMBER: + case LUA_TBOOLEAN: + case LUA_TSTRING: + case LUA_TFUNCTION: + value = _luaCoerce(luaContext, true); + break; + default: + // Don't let values be something that could contain themselves + break; + } + if (!value) { + lua_pop(luaContext->lua, 3); + mScriptValueDeref(table); + return false; + } + + struct mScriptValue* key = NULL; + type = lua_type(luaContext->lua, -1); + switch (type) { + case LUA_TBOOLEAN: + case LUA_TSTRING: + isList = false; + // Fall through + case LUA_TNUMBER: + key = _luaCoerce(luaContext, false); + break; + default: + // Limit keys to hashable types + break; + } + + if (!key) { + lua_pop(luaContext->lua, 2); + mScriptValueDeref(table); + return false; + } + mScriptTableInsert(table, key, value); + mScriptValueDeref(key); + mScriptValueDeref(value); + } + lua_pop(luaContext->lua, 1); + + size_t len = mScriptTableSize(table); + if (!isList || !len) { + return table; + } + + struct mScriptValue* list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); + size_t i; + for (i = 1; i <= len; ++i) { + struct mScriptValue* value = mScriptTableLookup(table, &mSCRIPT_MAKE_S64(i)); + if (!value) { + mScriptValueDeref(list); + return table; + } + mScriptValueWrap(value, mScriptListAppend(list->value.list)); + } + if (i != len + 1) { + mScriptValueDeref(list); + mScriptContextFillPool(luaContext->d.context, table); + return table; + } + mScriptValueDeref(table); + mScriptContextFillPool(luaContext->d.context, list); + return list; +} + +struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool pop) { if (lua_isnone(luaContext->lua, -1)) { lua_pop(luaContext->lua, 1); return NULL; @@ -222,6 +320,9 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { const void* buffer; struct mScriptValue* value = NULL; switch (lua_type(luaContext->lua, -1)) { + case LUA_TNIL: + value = &mScriptValueNull; + break; case LUA_TNUMBER: #if LUA_VERSION_NUM >= 503 if (lua_isinteger(luaContext->lua, -1)) { @@ -234,8 +335,8 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { value->value.f64 = lua_tonumber(luaContext->lua, -1); break; case LUA_TBOOLEAN: - value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32); - value->value.s32 = lua_toboolean(luaContext->lua, -1); + value = mScriptValueAlloc(mSCRIPT_TYPE_MS_BOOL); + value->value.u32 = lua_toboolean(luaContext->lua, -1); break; case LUA_TSTRING: buffer = lua_tolstring(luaContext->lua, -1, &size); @@ -244,7 +345,16 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { break; case LUA_TFUNCTION: // This function pops the value internally via luaL_ref + if (!pop) { + break; + } return _luaCoerceFunction(luaContext); + case LUA_TTABLE: + // This function pops the value internally + if (!pop) { + break; + } + return _luaCoerceTable(luaContext); case LUA_TUSERDATA: if (!lua_getmetatable(luaContext->lua, -1)) { break; @@ -259,7 +369,9 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { value = mScriptContextAccessWeakref(luaContext->d.context, value); break; } - lua_pop(luaContext->lua, 1); + if (pop) { + lua_pop(luaContext->lua, 1); + } return value; } @@ -289,6 +401,9 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v bool ok = true; struct mScriptValue* newValue; switch (value->type->base) { + case mSCRIPT_TYPE_VOID: + lua_pushnil(luaContext->lua); + break; case mSCRIPT_TYPE_SINT: if (value->type->size <= 4) { lua_pushinteger(luaContext->lua, value->value.s32); @@ -299,7 +414,9 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } break; case mSCRIPT_TYPE_UINT: - if (value->type->size <= 4) { + if (value->type == mSCRIPT_TYPE_MS_BOOL) { + lua_pushboolean(luaContext->lua, !!value->value.u32); + } else if (value->type->size <= 4) { lua_pushinteger(luaContext->lua, value->value.u32); } else if (value->type->size == 8) { lua_pushinteger(luaContext->lua, value->value.u64); @@ -325,9 +442,9 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v *newValue = mSCRIPT_MAKE(WEAKREF, weakref); } else { mScriptValueWrap(value, newValue); - mScriptValueDeref(value); } - luaL_setmetatable(luaContext->lua, "mSTList"); + lua_getfield(luaContext->lua, LUA_REGISTRYINDEX, "mSTList"); + lua_setmetatable(luaContext->lua, -2); break; case mSCRIPT_TYPE_TABLE: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); @@ -335,9 +452,9 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v *newValue = mSCRIPT_MAKE(WEAKREF, weakref); } else { mScriptValueWrap(value, newValue); - mScriptValueDeref(value); } - luaL_setmetatable(luaContext->lua, "mSTTable"); + lua_getfield(luaContext->lua, LUA_REGISTRYINDEX, "mSTTable"); + lua_setmetatable(luaContext->lua, -2); break; case mSCRIPT_TYPE_FUNCTION: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); @@ -353,9 +470,9 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v *newValue = mSCRIPT_MAKE(WEAKREF, weakref); } else { mScriptValueWrap(value, newValue); - mScriptValueDeref(value); } - luaL_setmetatable(luaContext->lua, "mSTStruct"); + lua_getfield(luaContext->lua, LUA_REGISTRYINDEX, "mSTStruct"); + lua_setmetatable(luaContext->lua, -2); break; default: ok = false; @@ -391,7 +508,8 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi free(luaContext->lastError); luaContext->lastError = NULL; } - char name[80]; + char name[PATH_MAX + 1]; + char dirname[PATH_MAX] = {0}; if (filename) { if (*filename == '*') { snprintf(name, sizeof(name), "=%s", filename + 1); @@ -399,23 +517,34 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi const char* lastSlash = strrchr(filename, '/'); const char* lastBackslash = strrchr(filename, '\\'); if (lastSlash && lastBackslash) { - if (lastSlash > lastBackslash) { - filename = lastSlash + 1; - } else { - filename = lastBackslash + 1; + if (lastSlash < lastBackslash) { + lastSlash = lastBackslash; } - } else if (lastSlash) { - filename = lastSlash + 1; } else if (lastBackslash) { - filename = lastBackslash + 1; + lastSlash = lastBackslash; + } + if (lastSlash) { + strncpy(dirname, filename, lastSlash - filename); } snprintf(name, sizeof(name), "@%s", filename); } filename = name; } +#if LUA_VERSION_NUM >= 502 int ret = lua_load(luaContext->lua, _reader, &data, filename, "t"); +#else + int ret = lua_load(luaContext->lua, _reader, &data, filename); +#endif switch (ret) { case LUA_OK: + if (dirname[0]) { + lua_getupvalue(luaContext->lua, -1, 1); + lua_pushliteral(luaContext->lua, "require"); + lua_pushstring(luaContext->lua, dirname); + lua_pushcclosure(luaContext->lua, _luaRequireShim, 1); + lua_rawset(luaContext->lua, -3); + lua_pop(luaContext->lua, 1); + } luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); return true; case LUA_ERRSYNTAX: @@ -430,6 +559,7 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi bool _luaRun(struct mScriptEngineContext* context) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) context; + lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->func); return _luaInvoke(luaContext, NULL); } @@ -467,7 +597,7 @@ bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList if (frame) { int i; for (i = 0; i < count; ++i) { - struct mScriptValue* value = _luaCoerce(luaContext); + struct mScriptValue* value = _luaCoerce(luaContext, true); if (!value) { ok = false; break; @@ -555,8 +685,8 @@ void _luaDeref(struct mScriptValue* value) { static struct mScriptEngineContextLua* _luaGetContext(lua_State* lua) { lua_pushliteral(lua, "mCtx"); - int type = lua_rawget(lua, LUA_REGISTRYINDEX); - if (type != LUA_TLIGHTUSERDATA) { + lua_rawget(lua, LUA_REGISTRYINDEX); + if (lua_type(lua, -1) != LUA_TLIGHTUSERDATA) { lua_pop(lua, 1); lua_pushliteral(lua, "Function called from invalid context"); lua_error(lua); @@ -640,7 +770,7 @@ int _luaSetObject(lua_State* lua) { char key[MAX_KEY_SIZE]; const char* keyPtr = lua_tostring(lua, -2); struct mScriptValue* obj = lua_touserdata(lua, -3); - struct mScriptValue* val = _luaCoerce(luaContext); + struct mScriptValue* val = _luaCoerce(luaContext, true); if (!keyPtr) { lua_pop(lua, 2); @@ -870,3 +1000,68 @@ static int _luaLenList(lua_State* lua) { lua_pushinteger(lua, mScriptListSize(list)); return 1; } + +static int _luaRequireShim(lua_State* lua) { + struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); + + int oldtop = lua_gettop(luaContext->lua); + const char* path = lua_tostring(lua, lua_upvalueindex(1)); + + lua_getglobal(luaContext->lua, "package"); + + lua_pushliteral(luaContext->lua, "path"); + lua_pushstring(luaContext->lua, path); + lua_pushliteral(luaContext->lua, "/?.lua;"); + lua_pushstring(luaContext->lua, path); + lua_pushliteral(luaContext->lua, "/?/init.lua;"); + lua_pushliteral(luaContext->lua, "path"); + lua_gettable(luaContext->lua, -7); + char* oldpath = strdup(lua_tostring(luaContext->lua, -1)); + lua_concat(luaContext->lua, 5); + lua_settable(luaContext->lua, -3); + +#ifdef _WIN32 +#define DLL "dll" +#elif defined(__APPLE__) +#define DLL "dylib" +#else +#define DLL "so" +#endif + lua_pushliteral(luaContext->lua, "cpath"); + lua_pushstring(luaContext->lua, path); + lua_pushliteral(luaContext->lua, "/?." DLL ";"); + lua_pushstring(luaContext->lua, path); + lua_pushliteral(luaContext->lua, "/?/init." DLL ";"); + lua_pushliteral(luaContext->lua, "cpath"); + lua_gettable(luaContext->lua, -7); + char* oldcpath = strdup(lua_tostring(luaContext->lua, -1)); + lua_concat(luaContext->lua, 5); + lua_settable(luaContext->lua, -3); + + lua_pop(luaContext->lua, 1); + + lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->require); + lua_insert(luaContext->lua, -2); + int ret = lua_pcall(luaContext->lua, 1, LUA_MULTRET, 0); + + lua_getglobal(luaContext->lua, "package"); + + lua_pushliteral(luaContext->lua, "path"); + lua_pushstring(luaContext->lua, oldpath); + lua_settable(luaContext->lua, -3); + + lua_pushliteral(luaContext->lua, "cpath"); + lua_pushstring(luaContext->lua, oldcpath); + lua_settable(luaContext->lua, -3); + + lua_pop(luaContext->lua, 1); + + free(oldpath); + free(oldcpath); + if (ret) { + lua_error(luaContext->lua); + } + + int newtop = lua_gettop(luaContext->lua); + return newtop - oldtop + 1; +} diff --git a/src/script/stdlib.c b/src/script/stdlib.c index 35941f6d8..839673cdb 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -19,16 +19,54 @@ struct mScriptCallbackManager { struct mScriptContext* context; }; -static void _mScriptCallbackAdd(struct mScriptCallbackManager* adapter, struct mScriptString* name, struct mScriptValue* fn) { +static uint32_t _mScriptCallbackAdd(struct mScriptCallbackManager* adapter, struct mScriptString* name, struct mScriptValue* fn) { if (fn->type->base == mSCRIPT_TYPE_WRAPPER) { fn = mScriptValueUnwrap(fn); } - mScriptContextAddCallback(adapter->context, name->buffer, fn); + uint32_t id = mScriptContextAddCallback(adapter->context, name->buffer, fn); mScriptValueDeref(fn); + return id; +} + +static void _mScriptCallbackRemove(struct mScriptCallbackManager* adapter, uint32_t id) { + mScriptContextRemoveCallback(adapter->context, id); } mSCRIPT_DECLARE_STRUCT(mScriptCallbackManager); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCallbackManager, U32, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, remove, _mScriptCallbackRemove, 1, U32, cbid); + +static uint64_t mScriptMakeBitmask(struct mScriptList* list) { + size_t i; + uint64_t mask = 0; + for (i = 0; i < mScriptListSize(list); ++i) { + struct mScriptValue bit; + struct mScriptValue* value = mScriptListGetPointer(list, i); + if (value->type->base == mSCRIPT_TYPE_WRAPPER) { + value = mScriptValueUnwrap(value); + } + if (!mScriptCast(mSCRIPT_TYPE_MS_U64, value, &bit)) { + continue; + } + mask |= 1ULL << bit.value.u64; + } + return mask; +} + +static struct mScriptValue* mScriptExpandBitmask(uint64_t mask) { + struct mScriptValue* list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); + size_t i; + for (i = 0; mask; ++i, mask >>= 1) { + if (!(mask & 1)) { + continue; + } + *mScriptListAppend(list->value.list) = mSCRIPT_MAKE_U32(i); + } + return list; +} + +mSCRIPT_BIND_FUNCTION(mScriptMakeBitmask_Binding, U64, mScriptMakeBitmask, 1, LIST, bits); +mSCRIPT_BIND_FUNCTION(mScriptExpandBitmask_Binding, WLIST, mScriptExpandBitmask, 1, U64, mask); mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager) mSCRIPT_DEFINE_CLASS_DOCSTRING( @@ -44,8 +82,10 @@ mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager) "- **start**: The emulation has started\n" "- **stop**: The emulation has voluntarily shut down\n" ) - mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type") + mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type. The returned id can be used to remove it later") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add) + mSCRIPT_DEFINE_DOCSTRING("Remove a callback with the previously retuned id") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, remove) mSCRIPT_DEFINE_END; void mScriptContextAttachStdlib(struct mScriptContext* context) { @@ -58,6 +98,7 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { }; lib->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; mScriptContextSetGlobal(context, "callbacks", lib); + mScriptContextSetDocstring(context, "callbacks", "Singleton instance of struct::mScriptCallbackManager"); mScriptContextExportConstants(context, "SAVESTATE", (struct mScriptKVPair[]) { mSCRIPT_CONSTANT_PAIR(SAVESTATE, SCREENSHOT), @@ -66,17 +107,17 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mSCRIPT_CONSTANT_PAIR(SAVESTATE, RTC), mSCRIPT_CONSTANT_PAIR(SAVESTATE, METADATA), mSCRIPT_CONSTANT_PAIR(SAVESTATE, ALL), - mSCRIPT_CONSTANT_SENTINEL + mSCRIPT_KV_SENTINEL }); mScriptContextExportConstants(context, "PLATFORM", (struct mScriptKVPair[]) { mSCRIPT_CONSTANT_PAIR(mPLATFORM, NONE), mSCRIPT_CONSTANT_PAIR(mPLATFORM, GBA), mSCRIPT_CONSTANT_PAIR(mPLATFORM, GB), - mSCRIPT_CONSTANT_SENTINEL + mSCRIPT_KV_SENTINEL }); mScriptContextExportConstants(context, "CHECKSUM", (struct mScriptKVPair[]) { mSCRIPT_CONSTANT_PAIR(mCHECKSUM, CRC32), - mSCRIPT_CONSTANT_SENTINEL + mSCRIPT_KV_SENTINEL }); #ifdef M_CORE_GBA mScriptContextExportConstants(context, "GBA_KEY", (struct mScriptKVPair[]) { @@ -90,7 +131,7 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mSCRIPT_CONSTANT_PAIR(GBA_KEY, DOWN), mSCRIPT_CONSTANT_PAIR(GBA_KEY, R), mSCRIPT_CONSTANT_PAIR(GBA_KEY, L), - mSCRIPT_CONSTANT_SENTINEL + mSCRIPT_KV_SENTINEL }); #endif #ifdef M_CORE_GB @@ -103,8 +144,18 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mSCRIPT_CONSTANT_PAIR(GB_KEY, LEFT), mSCRIPT_CONSTANT_PAIR(GB_KEY, UP), mSCRIPT_CONSTANT_PAIR(GB_KEY, DOWN), - mSCRIPT_CONSTANT_SENTINEL + mSCRIPT_KV_SENTINEL }); #endif mScriptContextSetGlobal(context, "C", context->constants); + mScriptContextSetDocstring(context, "C", "A table containing the [exported constants](#constants)"); + + mScriptContextExportNamespace(context, "util", (struct mScriptKVPair[]) { + mSCRIPT_KV_PAIR(makeBitmask, &mScriptMakeBitmask_Binding), + mSCRIPT_KV_PAIR(expandBitmask, &mScriptExpandBitmask_Binding), + mSCRIPT_KV_SENTINEL + }); + mScriptContextSetDocstring(context, "util", "Basic utility library"); + mScriptContextSetDocstring(context, "util.makeBitmask", "Compile a list of bit indices into a bitmask"); + mScriptContextSetDocstring(context, "util.expandBitmask", "Expand a bitmask into a list of bit indices"); } diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 71b719966..7790fed60 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -61,8 +61,22 @@ static void testV1(struct Test* a, int b) { a->i += b; } +static int32_t sum(struct mScriptList* list) { + int32_t sum = 0; + size_t i; + for (i = 0; i < mScriptListSize(list); ++i) { + struct mScriptValue value; + if (!mScriptCast(mSCRIPT_TYPE_MS_S32, mScriptListGetPointer(list, i), &value)) { + continue; + } + sum += value.value.s32; + } + return sum; +} + mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, a); mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b); +mSCRIPT_BIND_FUNCTION(boundSum, S32, sum, 1, LIST, list); mSCRIPT_DECLARE_STRUCT(Test); mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn0, 0); @@ -240,7 +254,7 @@ M_TEST_DEFINE(setGlobal) { assert_true(lua->setGlobal(lua, "b", NULL)); val = lua->getGlobal(lua, "b"); - assert_null(val); + assert_ptr_equal(val, &mScriptValueNull); mScriptContextDeinit(&context); } @@ -619,6 +633,24 @@ M_TEST_DEFINE(tableIterate) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(callList) { + SETUP_LUA; + + struct mScriptValue a = mSCRIPT_MAKE_S32(6); + struct mScriptValue* val; + + assert_true(lua->setGlobal(lua, "sum", &boundSum)); + TEST_PROGRAM("a = sum({1, 2, 3})"); + assert_null(lua->getError(lua)); + + val = lua->getGlobal(lua, "a"); + assert_non_null(val); + assert_true(mSCRIPT_TYPE_MS_S32->equal(&a, val)); + mScriptValueDeref(val); + + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(create), cmocka_unit_test(loadGood), @@ -634,4 +666,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(errorReporting), cmocka_unit_test(tableLookup), cmocka_unit_test(tableIterate), + cmocka_unit_test(callList), ) diff --git a/src/script/test/stdlib.c b/src/script/test/stdlib.c new file mode 100644 index 000000000..d4c57605b --- /dev/null +++ b/src/script/test/stdlib.c @@ -0,0 +1,121 @@ +/* Copyright (c) 2013-2022 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/test/suite.h" + +#include +#include +#include +#include + +#define SETUP_LUA \ + struct mScriptContext context; \ + mScriptContextInit(&context); \ + struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA); \ + mScriptContextAttachStdlib(&context) + +#define LOAD_PROGRAM(PROG) \ + do { \ + struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ + assert_true(lua->load(lua, NULL, vf)); \ + vf->close(vf); \ + } while(0) + +#define TEST_PROGRAM(PROG) \ + LOAD_PROGRAM(PROG); \ + assert_true(lua->run(lua)); \ + +#define TEST_VALUE(TYPE, NAME, VALUE) \ + do { \ + struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \ + struct mScriptValue* global = lua->getGlobal(lua, NAME); \ + assert_non_null(global); \ + assert_true(global->type->equal(global, &val)); \ + mScriptValueDeref(global); \ + } while(0) + +M_TEST_SUITE_SETUP(mScriptStdlib) { + if (mSCRIPT_ENGINE_LUA->init) { + mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_SUITE_TEARDOWN(mScriptStdlib) { + if (mSCRIPT_ENGINE_LUA->deinit) { + mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_DEFINE(bitMask) { + SETUP_LUA; + + TEST_PROGRAM("assert(util)"); + TEST_PROGRAM("assert(util.makeBitmask)"); + TEST_PROGRAM("assert(util.makeBitmask{0} == 1)"); + TEST_PROGRAM("assert(util.makeBitmask{1} == 2)"); + TEST_PROGRAM("assert(util.makeBitmask{0, 1} == 3)"); + TEST_PROGRAM("assert(util.makeBitmask{1, 1} == 2)"); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(bitUnmask) { + SETUP_LUA; + + TEST_PROGRAM("assert(util)"); + TEST_PROGRAM("assert(util.expandBitmask)"); + TEST_PROGRAM("assert(#util.expandBitmask(0) == 0)"); + TEST_PROGRAM("assert(#util.expandBitmask(1) == 1)"); + TEST_PROGRAM("assert(util.expandBitmask(1)[1] == 0)"); + TEST_PROGRAM("assert(#util.expandBitmask(2) == 1)"); + TEST_PROGRAM("assert(util.expandBitmask(2)[1] == 1)"); + TEST_PROGRAM("assert(#util.expandBitmask(3) == 2)"); + TEST_PROGRAM("assert(util.expandBitmask(3)[1] == 0 or util.expandBitmask(3)[1] == 1)"); + TEST_PROGRAM("assert(util.expandBitmask(3)[2] == 0 or util.expandBitmask(3)[2] == 1)"); + TEST_PROGRAM("assert(#util.expandBitmask(6) == 2)"); + TEST_PROGRAM("assert(util.expandBitmask(6)[1] == 1 or util.expandBitmask(6)[1] == 2)"); + TEST_PROGRAM("assert(util.expandBitmask(6)[2] == 1 or util.expandBitmask(6)[2] == 2)"); + TEST_PROGRAM("assert(#util.expandBitmask(7) == 3)"); + TEST_PROGRAM("assert(#util.expandBitmask(11) == 3)"); + TEST_PROGRAM("assert(#util.expandBitmask(15) == 4)"); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(callbacks) { + SETUP_LUA; + + TEST_PROGRAM( + "val = 0\n" + "function cb()\n" + " val = val + 1\n" + "end\n" + "id = callbacks:add('test', cb)\n" + "assert(id)" + ); + + TEST_VALUE(S32, "val", 0); + + mScriptContextTriggerCallback(&context, "test"); + TEST_VALUE(S32, "val", 1); + + mScriptContextTriggerCallback(&context, "test"); + TEST_VALUE(S32, "val", 2); + + TEST_PROGRAM("callbacks:remove(id)"); + + mScriptContextTriggerCallback(&context, "test"); + TEST_VALUE(S32, "val", 2); + + mScriptContextDeinit(&context); +} + +M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptStdlib, + cmocka_unit_test(bitMask), + cmocka_unit_test(bitUnmask), + cmocka_unit_test(callbacks), +) diff --git a/src/script/test/types.c b/src/script/test/types.c index 45a9a2d25..6ad38563a 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -52,6 +52,30 @@ static int isHello(const char* str) { return strcmp(str, "hello") == 0; } +static int isSequential(struct mScriptList* list) { + int last; + if (mScriptListSize(list) == 0) { + return true; + } + size_t i; + for (i = 0; i < mScriptListSize(list); ++i) { + struct mScriptValue* value = mScriptListGetPointer(list, i); + struct mScriptValue intValue; + if (!mScriptCast(mSCRIPT_TYPE_MS_S32, value, &intValue)) { + return false; + } + if (!i) { + last = intValue.value.s32; + } else { + if (intValue.value.s32 != last + 1) { + return false; + } + ++last; + } + } + return true; +} + mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32, ignored); mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, in); @@ -61,6 +85,7 @@ mSCRIPT_BIND_FUNCTION(boundIdentityStruct, S(Test), identityStruct, 1, S(Test), mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b); mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, a, S32, b); mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP, str); +mSCRIPT_BIND_FUNCTION(boundIsSequential, S32, isSequential, 1, LIST, list); M_TEST_DEFINE(voidArgs) { struct mScriptFrame frame; @@ -181,41 +206,58 @@ M_TEST_DEFINE(wrongPopType) { uint64_t u64; float f32; double f64; + bool b; mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, S32, 0); assert_false(mScriptPopU32(&frame.arguments, &u32)); assert_false(mScriptPopF32(&frame.arguments, &f32)); + assert_false(mScriptPopBool(&frame.arguments, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, S64, 0); assert_false(mScriptPopU64(&frame.arguments, &u64)); assert_false(mScriptPopF64(&frame.arguments, &f64)); + assert_false(mScriptPopBool(&frame.arguments, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, U32, 0); assert_false(mScriptPopS32(&frame.arguments, &s32)); assert_false(mScriptPopF32(&frame.arguments, &f32)); + assert_false(mScriptPopBool(&frame.arguments, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, U64, 0); assert_false(mScriptPopS64(&frame.arguments, &s64)); assert_false(mScriptPopF64(&frame.arguments, &f64)); + assert_false(mScriptPopBool(&frame.arguments, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, F32, 0); assert_false(mScriptPopS32(&frame.arguments, &s32)); assert_false(mScriptPopU32(&frame.arguments, &u32)); + assert_false(mScriptPopBool(&frame.arguments, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, F64, 0); assert_false(mScriptPopS64(&frame.arguments, &s64)); assert_false(mScriptPopU64(&frame.arguments, &u64)); + assert_false(mScriptPopBool(&frame.arguments, &b)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, BOOL, 0); + assert_false(mScriptPopS32(&frame.arguments, &s32)); + assert_false(mScriptPopU32(&frame.arguments, &u32)); + assert_false(mScriptPopS64(&frame.arguments, &s64)); + assert_false(mScriptPopU64(&frame.arguments, &u64)); + assert_false(mScriptPopF32(&frame.arguments, &f32)); + assert_false(mScriptPopF64(&frame.arguments, &f64)); mScriptFrameDeinit(&frame); } @@ -357,6 +399,100 @@ M_TEST_DEFINE(coerceFromFloat) { mScriptFrameDeinit(&frame); } +M_TEST_DEFINE(coerceToBool) { + struct mScriptValue a; + struct mScriptValue b; + + a = mSCRIPT_MAKE_S32(0); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + + a = mSCRIPT_MAKE_S32(1); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + + a = mSCRIPT_MAKE_S32(-1); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + + a = mSCRIPT_MAKE_S32(INT_MAX); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + + a = mSCRIPT_MAKE_S32(INT_MIN); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + + a = mSCRIPT_MAKE_U32(0); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + + a = mSCRIPT_MAKE_U32(1); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + + a = mSCRIPT_MAKE_U32(UINT_MAX); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + + a = mSCRIPT_MAKE_F32(0); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + + a = mSCRIPT_MAKE_F32(1); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + + a = mSCRIPT_MAKE_F32(1e30f); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); + + a = mSCRIPT_MAKE_F32(1e-30f); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_BOOL, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(true))); + assert_false(mSCRIPT_TYPE_MS_BOOL->equal(&b, &mSCRIPT_MAKE_BOOL(false))); +} + +M_TEST_DEFINE(coerceFromBool) { + struct mScriptValue a; + struct mScriptValue b; + + a = mSCRIPT_MAKE_BOOL(false); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_S32, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_S32->equal(&b, &mSCRIPT_MAKE_S32(0))); + + a = mSCRIPT_MAKE_BOOL(true); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_S32, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_S32->equal(&b, &mSCRIPT_MAKE_S32(1))); + + a = mSCRIPT_MAKE_BOOL(true); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_S32, &a, &b)); + assert_false(mSCRIPT_TYPE_MS_S32->equal(&b, &mSCRIPT_MAKE_S32(-1))); + + a = mSCRIPT_MAKE_BOOL(false); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_U32, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_U32->equal(&b, &mSCRIPT_MAKE_U32(0))); + + a = mSCRIPT_MAKE_BOOL(true); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_U32, &a, &b)); + assert_true(mSCRIPT_TYPE_MS_U32->equal(&b, &mSCRIPT_MAKE_U32(1))); + + a = mSCRIPT_MAKE_BOOL(true); + assert_true(mScriptCast(mSCRIPT_TYPE_MS_U32, &a, &b)); + assert_false(mSCRIPT_TYPE_MS_U32->equal(&b, &mSCRIPT_MAKE_U32(2))); +} + M_TEST_DEFINE(coerceWiden) { struct mScriptFrame frame; mScriptFrameInit(&frame); @@ -446,6 +582,20 @@ M_TEST_DEFINE(s32Equality) { COMPARE_BOOL(false, S32, 0, F64, 0.1); COMPARE_BOOL(true, S32, 0x40000000, F64, 0x40000000); COMPARE_BOOL(true, S32, -0x40000000, F64, -0x40000000); + + // BOOL + COMPARE_BOOL(true, S32, 0, BOOL, false); + COMPARE_BOOL(false, S32, 0, BOOL, true); + COMPARE_BOOL(false, S32, 1, BOOL, false); + COMPARE_BOOL(true, S32, 1, BOOL, true); + COMPARE_BOOL(false, S32, -1, BOOL, false); + COMPARE_BOOL(true, S32, -1, BOOL, true); + COMPARE_BOOL(false, S32, 2, BOOL, false); + COMPARE_BOOL(true, S32, 2, BOOL, true); + COMPARE_BOOL(false, S32, 0x7FFFFFFF, BOOL, false); + COMPARE_BOOL(true, S32, 0x7FFFFFFF, BOOL, true); + COMPARE_BOOL(false, S32, -0x80000000, BOOL, false); + COMPARE_BOOL(true, S32, -0x80000000, BOOL, true); } M_TEST_DEFINE(s64Equality) { @@ -527,6 +677,20 @@ M_TEST_DEFINE(s64Equality) { COMPARE_BOOL(false, S64, 0, F64, 0.1); COMPARE_BOOL(true, S64, 0x4000000000000000LL, F64, 0x4000000000000000LL); COMPARE_BOOL(true, S64, -0x4000000000000000LL, F64, -0x4000000000000000LL); + + // BOOL + COMPARE_BOOL(true, S64, 0, BOOL, false); + COMPARE_BOOL(false, S64, 0, BOOL, true); + COMPARE_BOOL(false, S64, 1, BOOL, false); + COMPARE_BOOL(true, S64, 1, BOOL, true); + COMPARE_BOOL(false, S64, -1, BOOL, false); + COMPARE_BOOL(true, S64, -1, BOOL, true); + COMPARE_BOOL(false, S64, 2, BOOL, false); + COMPARE_BOOL(true, S64, 2, BOOL, true); + COMPARE_BOOL(false, S64, 0x7FFFFFFFFFFFFFFFLL, BOOL, false); + COMPARE_BOOL(true, S64, 0x7FFFFFFFFFFFFFFFLL, BOOL, true); + COMPARE_BOOL(false, S64, -0x8000000000000000LL, BOOL, false); + COMPARE_BOOL(true, S64, -0x8000000000000000LL, BOOL, true); } M_TEST_DEFINE(u32Equality) { @@ -598,6 +762,18 @@ M_TEST_DEFINE(u32Equality) { COMPARE_BOOL(false, U32, 0x80000000U, F64, 0); COMPARE_BOOL(false, U32, 1, F64, 1.1); COMPARE_BOOL(false, U32, 0, F64, 0.1); + + // BOOL + COMPARE_BOOL(true, U32, 0, BOOL, false); + COMPARE_BOOL(false, U32, 0, BOOL, true); + COMPARE_BOOL(false, U32, 1, BOOL, false); + COMPARE_BOOL(true, U32, 1, BOOL, true); + COMPARE_BOOL(false, U32, 2, BOOL, false); + COMPARE_BOOL(true, U32, 2, BOOL, true); + COMPARE_BOOL(false, U32, 0xFFFFFFFFU, BOOL, false); + COMPARE_BOOL(true, U32, 0xFFFFFFFFU, BOOL, true); + COMPARE_BOOL(false, U32, 0x80000000U, BOOL, false); + COMPARE_BOOL(true, U32, 0x80000000U, BOOL, true); } M_TEST_DEFINE(u64Equality) { @@ -676,6 +852,18 @@ M_TEST_DEFINE(u64Equality) { COMPARE_BOOL(false, U64, 0x8000000000000000ULL, F64, 0); COMPARE_BOOL(false, U64, 1, F64, 1.1); COMPARE_BOOL(false, U64, 0, F64, 0.1); + + // BOOL + COMPARE_BOOL(true, U64, 0, BOOL, false); + COMPARE_BOOL(false, U64, 0, BOOL, true); + COMPARE_BOOL(false, U64, 1, BOOL, false); + COMPARE_BOOL(true, U64, 1, BOOL, true); + COMPARE_BOOL(false, U64, 2, BOOL, false); + COMPARE_BOOL(true, U64, 2, BOOL, true); + COMPARE_BOOL(false, U64, 0xFFFFFFFFFFFFFFFFULL, BOOL, false); + COMPARE_BOOL(true, U64, 0xFFFFFFFFFFFFFFFFULL, BOOL, true); + COMPARE_BOOL(false, U64, 0x8000000000000000ULL, BOOL, false); + COMPARE_BOOL(true, U64, 0x8000000000000000ULL, BOOL, true); } M_TEST_DEFINE(f32Equality) { @@ -743,6 +931,18 @@ M_TEST_DEFINE(f32Equality) { COMPARE_BOOL(true, F32, 0x100000000ULL, U64, 0x100000000ULL); COMPARE_BOOL(false, F32, 0x100000000ULL, U64, 0); COMPARE_BOOL(false, F32, 0, U64, 0x100000000ULL); + + // BOOL + COMPARE_BOOL(true, F32, 0, BOOL, false); + COMPARE_BOOL(false, F32, 0, BOOL, true); + COMPARE_BOOL(false, F32, 1, BOOL, false); + COMPARE_BOOL(true, F32, 1, BOOL, true); + COMPARE_BOOL(false, F32, 1.1, BOOL, false); + COMPARE_BOOL(true, F32, 1.1, BOOL, true); + COMPARE_BOOL(false, F32, 0x040000000ULL, BOOL, false); + COMPARE_BOOL(true, F32, 0x040000000ULL, BOOL, true); + COMPARE_BOOL(false, F32, 0x100000000ULL, BOOL, false); + COMPARE_BOOL(true, F32, 0x100000000ULL, BOOL, true); } M_TEST_DEFINE(f64Equality) { @@ -810,6 +1010,107 @@ M_TEST_DEFINE(f64Equality) { COMPARE_BOOL(true, F64, 0x100000000ULL, U64, 0x100000000ULL); COMPARE_BOOL(false, F64, 0x100000000ULL, U64, 0); COMPARE_BOOL(false, F64, 0, U64, 0x100000000ULL); + + // BOOL + COMPARE_BOOL(true, F64, 0, BOOL, false); + COMPARE_BOOL(false, F64, 0, BOOL, true); + COMPARE_BOOL(false, F64, 1, BOOL, false); + COMPARE_BOOL(true, F64, 1, BOOL, true); + COMPARE_BOOL(false, F64, 1.1, BOOL, false); + COMPARE_BOOL(true, F64, 1.1, BOOL, true); + COMPARE_BOOL(false, F64, 0x040000000ULL, BOOL, false); + COMPARE_BOOL(true, F64, 0x040000000ULL, BOOL, true); + COMPARE_BOOL(false, F64, 0x100000000ULL, BOOL, false); + COMPARE_BOOL(true, F64, 0x100000000ULL, BOOL, true); +} + +M_TEST_DEFINE(boolEquality) { + struct mScriptValue a; + struct mScriptValue b; + + // S32 + COMPARE_BOOL(true, BOOL, false, S32, 0); + COMPARE_BOOL(false, BOOL, false, S32, 1); + COMPARE_BOOL(false, BOOL, false, S32, -1); + COMPARE_BOOL(false, BOOL, false, S32, 2); + COMPARE_BOOL(false, BOOL, false, S32, 0x7FFFFFFF); + COMPARE_BOOL(false, BOOL, false, S32, -0x80000000); + COMPARE_BOOL(false, BOOL, true, S32, 0); + COMPARE_BOOL(true, BOOL, true, S32, 1); + COMPARE_BOOL(true, BOOL, true, S32, -1); + COMPARE_BOOL(true, BOOL, true, S32, 2); + COMPARE_BOOL(true, BOOL, true, S32, 0x7FFFFFFF); + COMPARE_BOOL(true, BOOL, true, S32, -0x80000000); + + // S64 + COMPARE_BOOL(true, BOOL, false, S64, 0); + COMPARE_BOOL(false, BOOL, false, S64, 1); + COMPARE_BOOL(false, BOOL, false, S64, -1); + COMPARE_BOOL(false, BOOL, false, S64, 2); + COMPARE_BOOL(false, BOOL, false, S64, INT64_MIN); + COMPARE_BOOL(false, BOOL, false, S64, INT64_MAX); + COMPARE_BOOL(false, BOOL, true, S64, 0); + COMPARE_BOOL(true, BOOL, true, S64, 1); + COMPARE_BOOL(true, BOOL, true, S64, -1); + COMPARE_BOOL(true, BOOL, true, S64, 2); + COMPARE_BOOL(true, BOOL, true, S64, INT64_MIN); + COMPARE_BOOL(true, BOOL, true, S64, INT64_MAX); + + // U32 + COMPARE_BOOL(true, BOOL, false, U32, 0); + COMPARE_BOOL(false, BOOL, false, U32, 1); + COMPARE_BOOL(false, BOOL, false, U32, 2); + COMPARE_BOOL(false, BOOL, false, U32, UINT32_MAX); + COMPARE_BOOL(false, BOOL, true, U32, 0); + COMPARE_BOOL(true, BOOL, true, U32, 1); + COMPARE_BOOL(true, BOOL, true, U32, 2); + COMPARE_BOOL(true, BOOL, true, U32, UINT32_MAX); + + // U64 + COMPARE_BOOL(true, BOOL, false, U64, 0); + COMPARE_BOOL(false, BOOL, false, U64, 1); + COMPARE_BOOL(false, BOOL, false, U64, 2); + COMPARE_BOOL(false, BOOL, false, U64, INT64_MAX); + COMPARE_BOOL(false, BOOL, true, U64, 0); + COMPARE_BOOL(true, BOOL, true, U64, 1); + COMPARE_BOOL(true, BOOL, true, U64, 2); + COMPARE_BOOL(true, BOOL, true, U64, INT64_MAX); + + // F32 + COMPARE_BOOL(true, BOOL, false, F32, 0); + COMPARE_BOOL(false, BOOL, true, F32, 0); + COMPARE_BOOL(false, BOOL, false, F32, 1); + COMPARE_BOOL(true, BOOL, true, F32, 1); + COMPARE_BOOL(false, BOOL, false, F32, 1.1f); + COMPARE_BOOL(true, BOOL, true, F32, 1.1f); + COMPARE_BOOL(false, BOOL, false, F32, 1e30f); + COMPARE_BOOL(true, BOOL, true, F32, 1e30f); + COMPARE_BOOL(false, BOOL, false, F32, -1); + COMPARE_BOOL(true, BOOL, true, F32, -1); + COMPARE_BOOL(false, BOOL, false, F32, -1.1f); + COMPARE_BOOL(true, BOOL, true, F32, -1.1f); + COMPARE_BOOL(false, BOOL, false, F32, -0.1e-30f); + COMPARE_BOOL(true, BOOL, true, F32, -0.1e-30f); + COMPARE_BOOL(false, BOOL, false, F32, -1e30f); + COMPARE_BOOL(true, BOOL, true, F32, -1e30f); + + // F64 + COMPARE_BOOL(true, BOOL, false, F64, 0); + COMPARE_BOOL(false, BOOL, true, F64, 0); + COMPARE_BOOL(false, BOOL, false, F64, 1); + COMPARE_BOOL(true, BOOL, true, F64, 1); + COMPARE_BOOL(false, BOOL, false, F64, 1.1); + COMPARE_BOOL(true, BOOL, true, F64, 1.1); + COMPARE_BOOL(false, BOOL, false, F64, 1e30); + COMPARE_BOOL(true, BOOL, true, F64, 1e30); + COMPARE_BOOL(false, BOOL, false, F64, -1); + COMPARE_BOOL(true, BOOL, true, F64, -1); + COMPARE_BOOL(false, BOOL, false, F64, -1.1); + COMPARE_BOOL(true, BOOL, true, F64, -1.1); + COMPARE_BOOL(false, BOOL, false, F64, -0.1e-300); + COMPARE_BOOL(true, BOOL, true, F64, -0.1e-300); + COMPARE_BOOL(false, BOOL, false, F64, -1e300); + COMPARE_BOOL(true, BOOL, true, F64, -1e300); } M_TEST_DEFINE(stringEquality) { @@ -919,6 +1220,47 @@ M_TEST_DEFINE(stringIsNotHello) { mScriptFrameDeinit(&frame); } +M_TEST_DEFINE(invokeList) { + struct mScriptFrame frame; + struct mScriptList list; + int val; + + mScriptListInit(&list, 0); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, LIST, &list); + assert_true(mScriptInvoke(&boundIsSequential, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 1); + mScriptFrameDeinit(&frame); + + *mScriptListAppend(&list) = mSCRIPT_MAKE_S32(1); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, LIST, &list); + assert_true(mScriptInvoke(&boundIsSequential, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 1); + mScriptFrameDeinit(&frame); + + *mScriptListAppend(&list) = mSCRIPT_MAKE_S32(2); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, LIST, &list); + assert_true(mScriptInvoke(&boundIsSequential, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 1); + mScriptFrameDeinit(&frame); + + *mScriptListAppend(&list) = mSCRIPT_MAKE_S32(4); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, LIST, &list); + assert_true(mScriptInvoke(&boundIsSequential, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 0); + mScriptFrameDeinit(&frame); + + mScriptListDeinit(&list); +} + M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(voidArgs), cmocka_unit_test(voidFunc), @@ -936,6 +1278,8 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(wrongConst), cmocka_unit_test(coerceToFloat), cmocka_unit_test(coerceFromFloat), + cmocka_unit_test(coerceToBool), + cmocka_unit_test(coerceFromBool), cmocka_unit_test(coerceNarrow), cmocka_unit_test(coerceWiden), cmocka_unit_test(s32Equality), @@ -944,8 +1288,11 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(u64Equality), cmocka_unit_test(f32Equality), cmocka_unit_test(f64Equality), + cmocka_unit_test(boolEquality), cmocka_unit_test(stringEquality), cmocka_unit_test(hashTableBasic), cmocka_unit_test(hashTableString), cmocka_unit_test(stringIsHello), - cmocka_unit_test(stringIsNotHello)) + cmocka_unit_test(stringIsNotHello), + cmocka_unit_test(invokeList), +) diff --git a/src/script/types.c b/src/script/types.c index 4ab6653e1..71d2b7e07 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -38,6 +38,7 @@ static bool _f32Equal(const struct mScriptValue*, const struct mScriptValue*); static bool _s64Equal(const struct mScriptValue*, const struct mScriptValue*); static bool _u64Equal(const struct mScriptValue*, const struct mScriptValue*); static bool _f64Equal(const struct mScriptValue*, const struct mScriptValue*); +static bool _boolEqual(const struct mScriptValue*, const struct mScriptValue*); static bool _charpEqual(const struct mScriptValue*, const struct mScriptValue*); static bool _stringEqual(const struct mScriptValue*, const struct mScriptValue*); @@ -162,6 +163,17 @@ const struct mScriptType mSTFloat64 = { .cast = _castScalar, }; +const struct mScriptType mSTBool = { + .base = mSCRIPT_TYPE_UINT, + .size = 1, + .name = "bool", + .alloc = NULL, + .free = NULL, + .hash = _hashScalar, + .equal = _boolEqual, + .cast = _castScalar, +}; + const struct mScriptType mSTString = { .base = mSCRIPT_TYPE_STRING, .size = sizeof(struct mScriptString), @@ -221,6 +233,15 @@ const struct mScriptType mSTStringWrapper = { .hash = NULL, }; +const struct mScriptType mSTListWrapper = { + .base = mSCRIPT_TYPE_WRAPPER, + .size = sizeof(struct mScriptValue), + .name = "wrapper list", + .alloc = NULL, + .free = NULL, + .hash = NULL, +}; + const struct mScriptType mSTWeakref = { .base = mSCRIPT_TYPE_WEAKREF, .size = sizeof(uint32_t), @@ -230,6 +251,11 @@ const struct mScriptType mSTWeakref = { .hash = NULL, }; +struct mScriptValue mScriptValueNull = { + .type = &mSTVoid, + .refs = mSCRIPT_VALUE_UNREF +}; + DEFINE_VECTOR(mScriptList, struct mScriptValue) void _allocList(struct mScriptValue* val) { @@ -240,6 +266,9 @@ void _allocList(struct mScriptValue* val) { void _freeList(struct mScriptValue* val) { size_t i; for (i = 0; i < mScriptListSize(val->value.list); ++i) { + if (val->type) { + continue; + } struct mScriptValue* unwrapped = mScriptValueUnwrap(mScriptListGetPointer(val->value.list, i)); if (unwrapped) { mScriptValueDeref(unwrapped); @@ -343,13 +372,17 @@ uint32_t _hashScalar(const struct mScriptValue* val) { *T = input->value.s32; \ } else if (input->type->size == 8) { \ *T = input->value.s64; \ - } \ + } else { \ + return false; \ + }\ break; \ case mSCRIPT_TYPE_UINT: \ if (input->type->size <= 4) { \ *T = input->value.u32; \ } else if (input->type->size == 8) { \ *T = input->value.u64; \ + } else { \ + return false; \ } \ break; \ case mSCRIPT_TYPE_FLOAT: \ @@ -357,6 +390,8 @@ uint32_t _hashScalar(const struct mScriptValue* val) { *T = input->value.f32; \ } else if (input->type->size == 8) { \ *T = input->value.f64; \ + } else { \ + return false; \ } \ break; \ default: \ @@ -371,6 +406,7 @@ AS(Float32, F32); AS(SInt64, S64); AS(UInt64, U64); AS(Float64, F64); +AS(Bool, BOOL); bool _castScalar(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { switch (type->base) { @@ -388,7 +424,13 @@ bool _castScalar(const struct mScriptValue* input, const struct mScriptType* typ } break; case mSCRIPT_TYPE_UINT: - if (type->size <= 4) { + if (type == mSCRIPT_TYPE_MS_BOOL) { + bool b; + if (!_asBool(input, &b)) { + return false; + } + output->value.u32 = b; + } else if (type->size <= 4) { if (!_asUInt32(input, &output->value.u32)) { return false; } @@ -462,6 +504,9 @@ bool _s32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { } break; case mSCRIPT_TYPE_UINT: + if (b->type == mSCRIPT_TYPE_MS_BOOL) { + return !!a->value.s32 == b->value.u32; + } if (a->value.s32 < 0) { return false; } @@ -509,6 +554,9 @@ bool _u32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { } break; case mSCRIPT_TYPE_UINT: + if (b->type == mSCRIPT_TYPE_MS_BOOL) { + return !!a->value.u32 == b->value.u32; + } if (b->type->size <= 4) { val = b->value.u32; } else if (b->type->size == 8) { @@ -531,8 +579,12 @@ bool _u32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { bool _f32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { float val; switch (b->type->base) { - case mSCRIPT_TYPE_SINT: case mSCRIPT_TYPE_UINT: + if (b->type == mSCRIPT_TYPE_MS_BOOL) { + return (!(uint32_t) !a->value.f32)== b->value.u32; + } + // Fall through + case mSCRIPT_TYPE_SINT: case mSCRIPT_TYPE_FLOAT: if (!_asFloat32(b, &val)) { return false; @@ -559,6 +611,9 @@ bool _s64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { } break; case mSCRIPT_TYPE_UINT: + if (b->type == mSCRIPT_TYPE_MS_BOOL) { + return !!a->value.s64 == b->value.u32; + } if (a->value.s64 < 0) { return false; } @@ -606,6 +661,9 @@ bool _u64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { } break; case mSCRIPT_TYPE_UINT: + if (b->type == mSCRIPT_TYPE_MS_BOOL) { + return !!a->value.u64 == b->value.u32; + } if (b->type->size <= 4) { val = b->value.u32; } else if (b->type->size == 8) { @@ -625,8 +683,12 @@ bool _u64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { bool _f64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { double val; switch (b->type->base) { - case mSCRIPT_TYPE_SINT: case mSCRIPT_TYPE_UINT: + if (b->type == mSCRIPT_TYPE_MS_BOOL) { + return (!(uint32_t) !a->value.f64)== b->value.u32; + } + // Fall through + case mSCRIPT_TYPE_SINT: case mSCRIPT_TYPE_FLOAT: if (!_asFloat64(b, &val)) { return false; @@ -640,6 +702,29 @@ bool _f64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { return a->value.f64 == val; } +bool _boolEqual(const struct mScriptValue* a, const struct mScriptValue* b) { + switch (b->type->base) { + case mSCRIPT_TYPE_SINT: + if (b->type->size <= 4) { + return a->value.u32 == !!b->value.s32; + } else if (b->type->size == 8) { + return a->value.u32 == !!b->value.s64; + } + return false; + case mSCRIPT_TYPE_UINT: + if (b->type->size <= 4) { + return a->value.u32 == !!b->value.u32; + } else if (b->type->size == 8) { + return a->value.u32 == !!b->value.u64; + } + return false; + case mSCRIPT_TYPE_VOID: + return false; + default: + return b->type->equal && b->type->equal(b, a); + } +} + bool _charpEqual(const struct mScriptValue* a, const struct mScriptValue* b) { const char* valA = a->value.opaque; const char* valB; @@ -1345,6 +1430,12 @@ bool mScriptPopF64(struct mScriptList* list, double* out) { return true; } +bool mScriptPopBool(struct mScriptList* list, bool* out) { + mSCRIPT_POP(list, BOOL, val); + *out = val; + return true; +} + bool mScriptPopPointer(struct mScriptList* list, void** out) { mSCRIPT_POP(list, PTR, val); *out = val; diff --git a/src/sm83/debugger/debugger.c b/src/sm83/debugger/debugger.c index edf8afc1e..f6a6f690e 100644 --- a/src/sm83/debugger/debugger.c +++ b/src/sm83/debugger/debugger.c @@ -28,14 +28,12 @@ static struct mBreakpoint* _lookupBreakpoint(struct mBreakpointList* breakpoints static void _destroyBreakpoint(struct mBreakpoint* breakpoint) { if (breakpoint->condition) { parseFree(breakpoint->condition); - free(breakpoint->condition); } } static void _destroyWatchpoint(struct mWatchpoint* watchpoint) { if (watchpoint->condition) { parseFree(watchpoint->condition); - free(watchpoint->condition); } } diff --git a/src/sm83/decoder.c b/src/sm83/decoder.c index 1240d5841..819576c26 100644 --- a/src/sm83/decoder.c +++ b/src/sm83/decoder.c @@ -413,6 +413,9 @@ size_t SM83Decode(uint8_t opcode, struct SM83InstructionInfo* info) { info->op1.immediate |= opcode << ((info->opcodeSize - 2) * 8); } return 0; + default: + // Should never be reached + abort(); } ++info->opcodeSize; return decoder(opcode, info); diff --git a/tools/perf.py b/tools/perf.py index 2793e53be..172cd989b 100755 --- a/tools/perf.py +++ b/tools/perf.py @@ -71,7 +71,7 @@ class GameClockTest(PerfTest): class PerfServer(object): ITERATIONS_PER_INSTANCE = 50 - RETRIES = 5 + RETRIES = 4 def __init__(self, address, root='/', command=None): s = address.rsplit(':', 1) @@ -100,7 +100,15 @@ class PerfServer(object): server_command.append('-N') elif test.renderer == 'threaded-software': server_command.append('-T') - subprocess.check_call(server_command) + for backoff in range(self.RETRIES): + try: + subprocess.check_call(server_command) + break + except subprocess.CalledProcessError as e: + print("Failed to start server:", e, file=sys.stderr) + if backoff == self.RETRIES - 1: + raise + time.sleep(2 ** backoff) time.sleep(3) for backoff in range(self.RETRIES): try: @@ -108,10 +116,9 @@ class PerfServer(object): break except OSError as e: print("Failed to connect:", e, file=sys.stderr) - if backoff < self.RETRIES - 1: - time.sleep(2 ** backoff) - else: + if backoff == self.RETRIES - 1: raise + time.sleep(2 ** backoff) kwargs = {} if sys.version_info[0] >= 3: kwargs["encoding"] = "utf-8"