Merge branch 'feature/input-revamp' into medusa
jump to
@@ -7,6 +7,10 @@ - DS GX: Automatically normalize winding culling calculations (fixes mgba.io/i/699)
Misc: - DS GX: Clean up and unify texture mapping +0.7.0: (Future) +Misc: + - GBA Timer: Use global cycles for timers + 0.6.0: (Future) Features: - GBA: Support printing debug strings from inside a game@@ -34,6 +38,7 @@ - Implement keypad interrupts
- LR35902: Watchpoints - Memory search - Debugger: Execution tracing + - Qt: Italian translation (by theheroGAC) Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior@@ -78,6 +83,14 @@ - GB MBC: Fix swapping carts not detect new MBC
- GB Timer: Fix DIV batching if TAC changes - GB Video: Reset renderer when loading state - GBA BIOS: Fix INT_MIN/-1 crash + - GBA Savedata: Update and fix Sharkport importing (fixes mgba.io/i/658) + - OpenGL: Fix some shaders causing offset graphics + - Qt: Fix game unpausing after frame advancing and refocusing + - GB Timer: Fix sub-M-cycle DIV reset timing and edge triggering + - Core: Fix interrupting a thread while on the thread (fixes mgba.io/i/692) + - Core: Fix directory sets crashing on close if base isn't properly detached + - Qt: Fix window icon being stretched + - Qt: Fix data directory path Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers@@ -142,6 +155,35 @@ - Core: List memory segments in the core
- Core: Move savestate creation time to extdata - Debugger: Add mDebuggerRunFrame convenience function - GBA Memory: Remove unused prefetch cruft + - GB: Trust ROM header for number of SRAM banks (fixes mgba.io/i/726) + - Core: Config values can now be hexadecimal + - GB: Reset with initial state of DIV register + - GB MBC: New MBC7 implementation + - Qt: Better highlight active key in control binding + - Core: Improved threading interrupted detection + - GBA Timer: Improve accuracy of timers + +0.6 beta 2: (Future) +Features: + - Qt: Italian translation (by theheroGAC) + - Qt: Updated German translation +Bugfixes: + - Qt: Fix memory search close button (fixes mgba.io/i/769) + - Qt: Fix window icon being stretched + - Qt: Fix initial window size (fixes mgba.io/i/766) + - Qt: Fix data directory path + - Qt: Fix controls not saving on non-SDL builds + - GB Video: Fix LYC regression + - Qt: Fix translation initialization (fixes mgba.io/i/776) + - PSP2: Use custom localtime_r since newlib version is broken (fixes mgba.io/i/560) +Misc: + - Qt: Add language selector + - GBA Timer: Improve accuracy of timers + - Qt: Minor test fixes + - PSP2: Update toolchain to use vita.cmake + +0.6 beta 1: (2017-06-29) + - Initial beta for 0.6 medusa alpha 2: (2017-04-26) Features:
@@ -280,7 +280,9 @@
include(CheckFunctionExists) check_function_exists(strdup HAVE_STRDUP) check_function_exists(strndup HAVE_STRNDUP) -check_function_exists(localtime_r HAVE_LOCALTIME_R) +if(NOT DEFINED PSP2) + check_function_exists(localtime_r HAVE_LOCALTIME_R) +endif() if(NOT CMAKE_SYSTEM_NAME STREQUAL "Generic") check_function_exists(snprintf_l HAVE_SNPRINTF_L) if(CMAKE_SYSTEM_NAME STREQUAL "Linux")@@ -874,7 +876,6 @@ INCLUDE_DIRECTORIES "${SDL_INCLUDE_DIR};${CMAKE_CURRENT_SOURCE_DIR}/src;${CMAKE_CURRENT_SOURCE_DIR}/include")
endif() endif() -message(STATUS ${USE_PTHREADS}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/core/flags.h.in ${CMAKE_CURRENT_BINARY_DIR}/flags.h) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/flags.h DESTINATION include/mgba COMPONENT lib${BINARY_NAME})@@ -928,6 +929,7 @@ set(SUMMARY_ZIP OFF)
endif() if(NOT QUIET) + message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Platforms:") message(STATUS " Game Boy Advance: ${M_CORE_GBA}") message(STATUS " Game Boy: ${M_CORE_GB}")
@@ -30,7 +30,7 @@ - Video and GIF recording.
- Remappable controls for both keyboards and gamepads. - Loading from ZIP and 7z files. - IPS, UPS and BPS patch support. -- Game debugging via a command-line interface (not available with Qt port) and GDB remote support, compatible with IDA Pro. +- Game debugging via a command-line interface and GDB remote support, compatible with IDA Pro. - Configurable emulation rewinding. - Support for loading and exporting GameShark and Action Replay snapshots. - Cores available for RetroArch/Libretro and OpenEmu.@@ -112,6 +112,16 @@ sudo make install
This will build and install medusa into `/usr/bin` and `/usr/lib`. Dependencies that are installed will be automatically detected, and features that are disabled if the dependencies are not found will be shown after running the `cmake` command after warnings about being unable to find them. +If you are on macOS, the steps are a little different. Assuming you are using the homebrew package manager, the recommended commands to obtain the dependencies and build are: + + brew install cmake ffmpeg imagemagick libzip qt5 sdl2 libedit + mkdir build + cd build + cmake -DCMAKE_PREFIX_PATH=`brew --prefix qt5` .. + make + +Note that you should not do a `make install` on macOS, as it will not work properly. + #### Windows developer building To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MSYS2 MinGW 32-bit") (or the 64-bit version "MSYS2 MinGW 64-bit" if you want to build for x86_64) and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 500MiB of packages, so it will take a long time):@@ -151,7 +161,7 @@ - libzip or zlib: for loading ROMs stored in zip files.
- ImageMagick: for GIF recording. - SQLite3: for game databases. -Both libpng and zlib are included with the emulator, so they do not need to be externally compiled first. +SQLite3, libpng, and zlib are included with the emulator, so they do not need to be externally compiled first. Footnotes ---------@@ -203,7 +213,7 @@ <a name="dscaveat">[2]</a> Many feature are still missing on the DS, including savestates, cheats, rumble, HLE BIOS, and more.
<a name="flashdetect">[3]</a> Flash memory size detection does not work in some cases. These can be configured at runtime, but filing a bug is recommended if such a case is encountered. -<a name="osxver">[4]</a> 10.7 is only needed for the Qt port. The SDL port is known to work on 10.6, and may work on older. +<a name="osxver">[3]</a> 10.7 is only needed for the Qt port. The SDL port is known to work on 10.5, and may work on older. [downloads]: http://mgba.io/downloads.html [source]: https://github.com/mgba-emu/mgba/
@@ -31,6 +31,10 @@ #define M_R8(X) (((((X) << 3) & 0xF8) * 0x21) >> 5)
#define M_G8(X) (((((X) >> 2) & 0xF8) * 0x21) >> 5) #define M_B8(X) (((((X) >> 7) & 0xF8) * 0x21) >> 5) +#define M_RGB5_TO_BGR8(X) ((M_R5(X) << 3) | (M_G5(X) << 11) | (M_B5(X) << 19)) +#define M_RGB8_TO_BGR5(X) ((((X) & 0xF8) >> 3) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 9)) +#define M_RGB8_TO_RGB5(X) ((((X) & 0xF8) << 7) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 19)) + struct blip_t; struct mCoreCallbacks {
@@ -36,6 +36,7 @@
struct VDir; struct VFile; void mLibraryLoadDirectory(struct mLibrary* library, const char* base); +void mLibraryClear(struct mLibrary* library); size_t mLibraryCount(struct mLibrary* library, const struct mLibraryEntry* constraints); size_t mLibraryGetEntries(struct mLibrary* library, struct mLibraryListing* out, size_t numEntries, size_t offset, const struct mLibraryEntry* constraints);
@@ -38,6 +38,7 @@ bool mTimingIsScheduled(const struct mTiming* timing, const struct mTimingEvent*);
int32_t mTimingTick(struct mTiming* timing, int32_t cycles); int32_t mTimingCurrentTime(const struct mTiming* timing); int32_t mTimingNextEvent(struct mTiming* timing); +int32_t mTimingUntil(const struct mTiming* timing, const struct mTimingEvent*); CXX_GUARD_END
@@ -15,7 +15,7 @@
struct ARMCore; struct DS; void DSTimerInit(struct DS* ds); -void DSTimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t control); +void DSTimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, uint16_t* io, uint16_t control); CXX_GUARD_END
@@ -67,21 +67,23 @@ typedef void (*GBMemoryBankControllerWrite)(struct GB*, uint16_t address, uint8_t value);
typedef uint8_t (*GBMemoryBankControllerRead)(struct GBMemory*, uint16_t address); DECL_BITFIELD(GBMBC7Field, uint8_t); -DECL_BIT(GBMBC7Field, SK, 6); DECL_BIT(GBMBC7Field, CS, 7); -DECL_BIT(GBMBC7Field, IO, 1); +DECL_BIT(GBMBC7Field, CLK, 6); +DECL_BIT(GBMBC7Field, DI, 1); +DECL_BIT(GBMBC7Field, DO, 0); enum GBMBC7MachineState { - GBMBC7_STATE_NULL = -1, GBMBC7_STATE_IDLE = 0, GBMBC7_STATE_READ_COMMAND = 1, - GBMBC7_STATE_READ_ADDRESS = 2, - GBMBC7_STATE_COMMAND_0 = 3, - GBMBC7_STATE_COMMAND_SR_WRITE = 4, - GBMBC7_STATE_COMMAND_SR_READ = 5, - GBMBC7_STATE_COMMAND_SR_FILL = 6, - GBMBC7_STATE_READ = 7, - GBMBC7_STATE_WRITE = 8, + GBMBC7_STATE_DO = 2, + + GBMBC7_STATE_EEPROM_EWDS = 0x10, + GBMBC7_STATE_EEPROM_WRAL = 0x11, + GBMBC7_STATE_EEPROM_ERAL = 0x12, + GBMBC7_STATE_EEPROM_EWEN = 0x13, + GBMBC7_STATE_EEPROM_WRITE = 0x14, + GBMBC7_STATE_EEPROM_READ = 0x18, + GBMBC7_STATE_EEPROM_ERASE = 0x1C, }; struct GBMBC1State {@@ -91,12 +93,13 @@ };
struct GBMBC7State { enum GBMBC7MachineState state; - uint32_t sr; + uint16_t sr; uint8_t address; bool writable; int srBits; - int command; - GBMBC7Field field; + uint8_t access; + uint8_t latch; + GBMBC7Field eeprom; }; struct GBPocketCamState {
@@ -16,6 +16,8 @@ struct GBCartridgeOverride {
int headerCrc32; enum GBModel model; enum GBMemoryBankControllerType mbc; + + uint32_t gbColors[4]; }; struct Configuration;
@@ -144,7 +144,7 @@ void GBVideoWriteLYC(struct GBVideo* video, uint8_t value);
void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value); void GBVideoSwitchBank(struct GBVideo* video, uint8_t value); -void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color); +void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color); struct GBSerializedState; void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state);
@@ -264,10 +264,10 @@ } video;
struct { uint16_t reload; - uint16_t oldReload; + uint16_t reserved; uint32_t lastEvent; uint32_t nextEvent; - int32_t overflowInterval; + uint32_t nextIrq; GBATimerFlags flags; } timers[4];
@@ -17,13 +17,13 @@ DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4);
DECL_BIT(GBATimerFlags, CountUp, 4); DECL_BIT(GBATimerFlags, DoIrq, 5); DECL_BIT(GBATimerFlags, Enable, 6); +DECL_BIT(GBATimerFlags, IrqPending, 7); struct GBATimer { uint16_t reload; - uint16_t oldReload; - uint32_t lastEvent; + int32_t lastEvent; struct mTimingEvent event; - int32_t overflowInterval; + struct mTimingEvent irq; GBATimerFlags flags; int forcedPrescale; };@@ -31,13 +31,13 @@
struct ARMCore; struct GBA; void GBATimerInit(struct GBA* gba); -void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload); -void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t control); -void GBATimerUpdateRegister(struct GBA* gba, int timer); -void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, int32_t skew); -void GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate); void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate); +void GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate); +void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate); +void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, uint16_t* io, int32_t skew); +void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload); +void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, uint16_t* io, uint16_t control); CXX_GUARD_END
@@ -3,6 +3,7 @@ Fog
Reilly Grant Philip Horton Jordan Jorgensen +mars Rohit Nirmal Rhys Powell rootfather
@@ -7,5 +7,5 @@
[pass.0] fragmentShader=agb001.fs blend=1 -width=960 -height=640 +width=-4 +height=-4
@@ -7,13 +7,13 @@
[pass.0] fragmentShader=ags001.fs blend=1 -width=960 -height=640 +width=-4 +height=-4 [pass.1] fragmentShader=ags001-light.fs -width=960 -height=640 +width=-4 +height=-4 [pass.1.uniform.lightBrightness] type=float
@@ -23,19 +23,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */ -precision highp float; - varying vec2 texCoord; uniform sampler2D tex; uniform vec2 texSize; uniform float similarity_threshold; -#define screen_res 240,160 - vec4 texel_fetch(sampler2D t, ivec2 c) // because GLSL TexelFetch is not supported { - return texture2D(tex, (2 * vec2(c) + vec2(1,1)) / (2 * vec2(screen_res)) ); + return texture2D(tex, (2 * vec2(c) + vec2(1,1)) / (2 * texSize) ); } float pixel_brightness(vec4 pixel)@@ -140,7 +136,7 @@ }
void main() { - ivec2 pixel_coords2 = ivec2(texCoord * vec2(screen_res) * 2); + ivec2 pixel_coords2 = ivec2(texCoord * texSize * 2); ivec2 pixel_coords = pixel_coords2 / 2; bool x_even = mod(pixel_coords2.x,2) == 0;
@@ -0,0 +1,40 @@
+/* + LCD Shader + + Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +uniform sampler2D tex; +uniform vec2 texSize; +varying vec2 texCoord; + +uniform float boundBrightness; + +void main() +{ + vec4 color = texture2D(tex, texCoord); + + if (int(mod(texCoord.s * texSize.x * 3.0, 3.0)) == 0 || + int(mod(texCoord.t * texSize.y * 3.0, 3.0)) == 0) + { + color.rgb *= vec3(1.0, 1.0, 1.0) * boundBrightness; + } + + gl_FragColor = color; +}
@@ -0,0 +1,18 @@
+[shader] +name=LCD +author=Dominus Iniquitatis +description=Simple LCD emulation. +passes=1 + +[pass.0] +fragmentShader=lcd.fs +blend=1 +width=-3 +height=-3 + +[pass.0.uniform.boundBrightness] +type=float +readableName=Bound brightness +default=0.9 +min=0.0 +max=1.0
@@ -0,0 +1,18 @@
+[shader] +name=Motion Blur +author=Dominus Iniquitatis +description=Simple motion blur. +passes=1 + +[pass.0] +fragmentShader=motion_blur.fs +blend=1 +width=-1 +height=-1 + +[pass.0.uniform.amount] +type=float +readableName=Amount +default=0.3 +min=0.0 +max=1.0
@@ -0,0 +1,35 @@
+/* + Motion Blur Shader + + Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +uniform sampler2D tex; +uniform vec2 texSize; +varying vec2 texCoord; + +uniform float amount; + +void main() +{ + vec4 color = texture2D(tex, texCoord); + color.a = 1.0 - amount; + + gl_FragColor = color; +}
@@ -0,0 +1,18 @@
+[shader] +name=Scanlines +author=Dominus Iniquitatis +description=Simple scanlines. +passes=1 + +[pass.0] +fragmentShader=scanlines.fs +blend=1 +width=-2 +height=-2 + +[pass.0.uniform.lineBrightness] +type=float +readableName=Line brightness +default=0.5 +min=0.0 +max=1.0
@@ -0,0 +1,39 @@
+/* + Scanlines Shader + + Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +uniform sampler2D tex; +uniform vec2 texSize; +varying vec2 texCoord; + +uniform float lineBrightness; + +void main() +{ + vec4 color = texture2D(tex, texCoord); + + if (int(mod(texCoord.t * texSize.y * 2.0, 2.0)) == 0) + { + color.rgb *= vec3(1.0, 1.0, 1.0) * lineBrightness; + } + + gl_FragColor = color; +}
@@ -0,0 +1,18 @@
+[shader] +name=Soften +author=Dominus Iniquitatis +description=Soft image blurring. +passes=1 + +[pass.0] +fragmentShader=soften.fs +blend=1 +width=-1 +height=-1 + +[pass.0.uniform.amount] +type=float +readableName=Amount +default=0.5 +min=0.0 +max=1.0
@@ -0,0 +1,64 @@
+/* + Soften Shader + + Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +uniform sampler2D tex; +uniform vec2 texSize; +varying vec2 texCoord; + +uniform float amount; + +vec2 GetTexelSize() +{ + return vec2(1.0 / texSize.x, 1.0 / texSize.y); +} + +void main() +{ + vec4 color = texture2D(tex, texCoord); + + vec4 northColor = texture2D(tex, texCoord + vec2(0.0, GetTexelSize().y)); + vec4 southColor = texture2D(tex, texCoord - vec2(0.0, GetTexelSize().y)); + vec4 eastColor = texture2D(tex, texCoord + vec2(GetTexelSize().x, 0.0)); + vec4 westColor = texture2D(tex, texCoord - vec2(GetTexelSize().x, 0.0)); + + if (abs(length(color) - length(northColor)) > 0.0) + { + color = mix(color, northColor, amount / 4.0); + } + + if (abs(length(color) - length(southColor)) > 0.0) + { + color = mix(color, southColor, amount / 4.0); + } + + if (abs(length(color) - length(eastColor)) > 0.0) + { + color = mix(color, eastColor, amount / 4.0); + } + + if (abs(length(color) - length(westColor)) > 0.0) + { + color = mix(color, westColor, amount / 4.0); + } + + gl_FragColor = color; +}
@@ -0,0 +1,18 @@
+[shader] +name=VBA Pixelate +author=Dominus Iniquitatis +description=VisualBoyAdvance-style pixelation. +passes=1 + +[pass.0] +fragmentShader=vba_pixelate.fs +blend=1 +width=-2 +height=-2 + +[pass.0.uniform.boundBrightness] +type=float +readableName=Bound brightness +default=0.5 +min=0.0 +max=1.0
@@ -0,0 +1,40 @@
+/* + VBA Pixelate Shader + + Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +uniform sampler2D tex; +uniform vec2 texSize; +varying vec2 texCoord; + +uniform float boundBrightness; + +void main() +{ + vec4 color = texture2D(tex, texCoord); + + if (int(mod(texCoord.s * texSize.x * 2.0, 2.0)) == 0 || + int(mod(texCoord.t * texSize.y * 2.0, 2.0)) == 0) + { + color.rgb *= vec3(1.0, 1.0, 1.0) * boundBrightness; + } + + gl_FragColor = color; +}
@@ -0,0 +1,18 @@
+[shader] +name=Vignette +author=Dominus Iniquitatis +description=Configurable vignette effect. +passes=1 + +[pass.0] +fragmentShader=vignette.fs +blend=1 +width=-1 +height=-1 + +[pass.0.uniform.intensity] +type=float +readableName=Intensity +default=1.0 +min=0.0 +max=1.0
@@ -0,0 +1,35 @@
+/* + Vignette Shader + + Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +uniform sampler2D tex; +uniform vec2 texSize; +varying vec2 texCoord; + +uniform float intensity; + +void main() +{ + vec4 color = texture2D(tex, texCoord); + color = mix(color, vec4(0.0, 0.0, 0.0, 1.0), length(texCoord - 0.5) * intensity); + + gl_FragColor = color; +}
@@ -7,5 +7,5 @@
[pass.0] fragmentShader=wiiu.fs blend=1 -width=960 -height=640 +width=-4 +height=-4
@@ -86,6 +86,9 @@ return false;
} char* end; long value = strtol(charValue, &end, 10); + if (end == &charValue[1] && *end == 'x') { + value = strtol(charValue, &end, 16); + } if (*end) { return false; }
@@ -26,7 +26,7 @@ #ifndef MINIMAL_CORE
#include <mgba/feature/video-logger.h> #endif -const static struct mCoreFilter { +static const struct mCoreFilter { bool (*filter)(struct VFile*); struct mCore* (*open)(void); enum mPlatform platform;
@@ -22,28 +22,58 @@ 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; + } dirs->archive->close(dirs->archive); - dirs->archive = 0; + 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; + } dirs->save->close(dirs->save); - dirs->save = 0; + dirs->save = NULL; } if (dirs->patch) { + if (dirs->patch == dirs->state) { + dirs->state = NULL; + } + if (dirs->patch == dirs->screenshot) { + dirs->screenshot = NULL; + } dirs->patch->close(dirs->patch); - dirs->patch = 0; + dirs->patch = NULL; } if (dirs->state) { + if (dirs->state == dirs->screenshot) { + dirs->state = NULL; + } dirs->state->close(dirs->state); - dirs->state = 0; + dirs->state = NULL; } if (dirs->screenshot) { dirs->screenshot->close(dirs->screenshot); - dirs->screenshot = 0; + dirs->screenshot = NULL; } }@@ -65,21 +95,21 @@ }
void mDirectorySetDetachBase(struct mDirectorySet* dirs) { if (dirs->save == dirs->base) { - dirs->save = 0; + dirs->save = NULL; } if (dirs->patch == dirs->base) { - dirs->patch = 0; + dirs->patch = NULL; } if (dirs->state == dirs->base) { - dirs->state = 0; + dirs->state = NULL; } if (dirs->screenshot == dirs->base) { - dirs->screenshot = 0; + dirs->screenshot = NULL; } if (dirs->base) { dirs->base->close(dirs->base); - dirs->base = 0; + dirs->base = NULL; } }
@@ -339,6 +339,16 @@ sqlite3_bind_text(library->deletePath, 1, entry->filename, -1, SQLITE_TRANSIENT);
sqlite3_step(library->insertPath); } +void mLibraryClear(struct mLibrary* library) { + int result = sqlite3_exec(library->db, + " BEGIN TRANSACTION;" + "\n DELETE FROM roots;" + "\n DELETE FROM roms;" + "\n DELETE FROM paths;" + "\n COMMIT;" + "\n VACUUM;", NULL, NULL, NULL); +} + size_t mLibraryCount(struct mLibrary* library, const struct mLibraryEntry* constraints) { sqlite3_clear_bindings(library->count); sqlite3_reset(library->count);
@@ -272,7 +272,7 @@ mCoreMemorySearchResultsInit(&tmp, 0);
// Decimal: value = strtoull(valueStr, &end, 10); - if (end) { + if (end && !end[0]) { if (value > 0x10000) { found += _search32(mem, size, block, value, out, limit ? limit - found : 0); } else if (value > 0x100) {@@ -305,7 +305,7 @@ }
// Hex: value = strtoull(valueStr, &end, 16); - if (end) { + if (end && !end[0]) { if (value > 0x10000) { found += _search32(mem, size, block, value, out, limit ? limit - found : 0); } else if (value > 0x100) {
@@ -401,11 +401,14 @@ }
MutexLock(&threadContext->stateMutex); ++threadContext->interruptDepth; if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) { + if (threadContext->state == THREAD_INTERRUPTING) { + threadContext->state = THREAD_INTERRUPTED; + } MutexUnlock(&threadContext->stateMutex); return; } threadContext->savedState = threadContext->state; - threadContext->state = THREAD_INTERRUPTING; + threadContext->state = THREAD_INTERRUPTED; ConditionWake(&threadContext->stateCond); MutexUnlock(&threadContext->stateMutex); }@@ -465,7 +468,7 @@
bool mCoreThreadIsPaused(struct mCoreThread* threadContext) { bool isPaused; MutexLock(&threadContext->stateMutex); - if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) { + if (threadContext->interruptDepth) { isPaused = threadContext->savedState == THREAD_PAUSED; } else { isPaused = threadContext->state == THREAD_PAUSED;@@ -495,7 +498,7 @@
void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) { bool frameOn = true; MutexLock(&threadContext->stateMutex); - if (threadContext->state == THREAD_RUNNING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_RUNNING)) { + if (threadContext->state == THREAD_RUNNING || (threadContext->interruptDepth && threadContext->savedState == THREAD_RUNNING)) { threadContext->state = THREAD_PAUSING; frameOn = false; }@@ -506,11 +509,11 @@ }
void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) { MutexLock(&threadContext->stateMutex); - if (rewinding && (threadContext->state == THREAD_REWINDING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_REWINDING))) { + if (rewinding && (threadContext->state == THREAD_REWINDING || (threadContext->interruptDepth && threadContext->savedState == THREAD_REWINDING))) { MutexUnlock(&threadContext->stateMutex); return; } - if (!rewinding && (threadContext->state == THREAD_RUNNING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_RUNNING))) { + if (!rewinding && ((!threadContext->interruptDepth && threadContext->state != THREAD_REWINDING) || (threadContext->interruptDepth && threadContext->savedState != THREAD_REWINDING))) { MutexUnlock(&threadContext->stateMutex); return; }@@ -526,7 +529,7 @@ }
void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) { MutexLock(&threadContext->stateMutex); - if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_RUNNING) { + if (threadContext->interruptDepth && threadContext->savedState == THREAD_RUNNING) { threadContext->savedState = THREAD_WAITING; } else if (threadContext->state == THREAD_RUNNING) { threadContext->state = THREAD_WAITING;@@ -536,7 +539,7 @@ }
void mCoreThreadStopWaiting(struct mCoreThread* threadContext) { MutexLock(&threadContext->stateMutex); - if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_WAITING) { + if (threadContext->interruptDepth && threadContext->savedState == THREAD_WAITING) { threadContext->savedState = THREAD_RUNNING; } else if (threadContext->state == THREAD_WAITING) { threadContext->state = THREAD_RUNNING;
@@ -89,5 +89,9 @@ struct mTimingEvent* next = timing->root;
if (!next) { return INT_MAX; } - return next->when - timing->masterCycles; + return next->when - timing->masterCycles - *timing->relativeCycles; +} + +int32_t mTimingUntil(const struct mTiming* timing, const struct mTimingEvent* event) { + return event->when - timing->masterCycles - *timing->relativeCycles; }
@@ -78,19 +78,19 @@ return 0x20000;
case DS_REG_TM0CNT_HI: value &= 0x00C7; - DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value); + DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value); break; case DS_REG_TM1CNT_HI: value &= 0x00C7; - DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value); + DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value); break; case DS_REG_TM2CNT_HI: value &= 0x00C7; - DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value); + DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value); break; case DS_REG_TM3CNT_HI: value &= 0x00C7; - DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value); + DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value); break; // IPC@@ -243,16 +243,16 @@
static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) { switch (address) { case DS_REG_TM0CNT_LO: - GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0); + GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, &dscore->memory.io[address >> 1], 0); break; case DS_REG_TM1CNT_LO: - GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0); + GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, &dscore->memory.io[address >> 1], 0); break; case DS_REG_TM2CNT_LO: - GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0); + GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, &dscore->memory.io[address >> 1], 0); break; case DS_REG_TM3CNT_LO: - GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0); + GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, &dscore->memory.io[address >> 1], 0); break; } }
@@ -8,42 +8,58 @@
#include <mgba/internal/arm/arm.h> #include <mgba/internal/ds/ds.h> +static void DSTimerIrq(struct DSCommon* dscore, int timerId) { + struct GBATimer* timer = &dscore->timers[timerId]; + if (GBATimerFlagsIsIrqPending(timer->flags)) { + timer->flags = GBATimerFlagsClearIrqPending(timer->flags); + DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_TIMER0 + timerId); + } +} + +static void DSTimerIrq0(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + DSTimerIrq(context, 0); +} + +static void DSTimerIrq1(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + DSTimerIrq(context, 1); +} + +static void DSTimerIrq2(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + DSTimerIrq(context, 2); +} + +static void DSTimerIrq3(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + DSTimerIrq(context, 3); +} + static void DSTimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct DSCommon* dscore = context; - struct GBATimer* timer = &dscore->timers[0]; - if (GBATimerFlagsIsDoIrq(timer->flags)) { - DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_TIMER0); - } GBATimerUpdate(timing, &dscore->timers[0], &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], cyclesLate); GBATimerUpdateCountUp(timing, &dscore->timers[1], &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], cyclesLate); } static void DSTimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct DSCommon* dscore = context; - struct GBATimer* timer = &dscore->timers[1]; - if (GBATimerFlagsIsDoIrq(timer->flags)) { - DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_TIMER1); - } GBATimerUpdate(timing, &dscore->timers[1], &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], cyclesLate); GBATimerUpdateCountUp(timing, &dscore->timers[2], &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], cyclesLate); } static void DSTimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct DSCommon* dscore = context; - struct GBATimer* timer = &dscore->timers[2]; - if (GBATimerFlagsIsDoIrq(timer->flags)) { - DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_TIMER2); - } GBATimerUpdate(timing, &dscore->timers[2], &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], cyclesLate); GBATimerUpdateCountUp(timing, &dscore->timers[3], &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], cyclesLate); } static void DSTimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct DSCommon* dscore = context; - struct GBATimer* timer = &dscore->timers[3]; - if (GBATimerFlagsIsDoIrq(timer->flags)) { - DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_TIMER3); - } GBATimerUpdate(timing, &dscore->timers[3], &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], cyclesLate); }@@ -65,6 +81,22 @@ ds->ds7.timers[3].event.name = "DS7 Timer 3";
ds->ds7.timers[3].event.callback = DSTimerUpdate3; ds->ds7.timers[3].event.context = &ds->ds7; ds->ds7.timers[3].event.priority = 0x23; + ds->ds7.timers[0].irq.name = "DS7 Timer 0 IRQ"; + ds->ds7.timers[0].irq.callback = DSTimerIrq0; + ds->ds7.timers[0].irq.context = &ds->ds7; + ds->ds7.timers[0].irq.priority = 0x28; + ds->ds7.timers[1].irq.name = "DS7 Timer 1 IRQ"; + ds->ds7.timers[1].irq.callback = DSTimerIrq1; + ds->ds7.timers[1].irq.context = &ds->ds7; + ds->ds7.timers[1].irq.priority = 0x29; + ds->ds7.timers[2].irq.name = "DS7 Timer 2 IRQ"; + ds->ds7.timers[2].irq.callback = DSTimerIrq2; + ds->ds7.timers[2].irq.context = &ds->ds7; + ds->ds7.timers[2].irq.priority = 0x2A; + ds->ds7.timers[3].irq.name = "DS7 Timer 3 IRQ"; + ds->ds7.timers[3].irq.callback = DSTimerIrq3; + ds->ds7.timers[3].irq.context = &ds->ds7; + ds->ds7.timers[3].irq.priority = 0x2B; memset(ds->ds9.timers, 0, sizeof(ds->ds9.timers)); ds->ds9.timers[0].event.name = "DS9 Timer 0";@@ -87,9 +119,24 @@ ds->ds9.timers[3].event.callback = DSTimerUpdate3;
ds->ds9.timers[3].event.context = &ds->ds9; ds->ds9.timers[3].event.priority = 0x23; ds->ds9.timers[3].forcedPrescale = 1; + ds->ds9.timers[0].irq.name = "DS9 Timer 0 IRQ"; + ds->ds9.timers[0].irq.callback = DSTimerIrq0; + ds->ds9.timers[0].irq.context = &ds->ds9; + ds->ds9.timers[0].irq.priority = 0x28; + ds->ds9.timers[1].irq.name = "DS9 Timer 1 IRQ"; + ds->ds9.timers[1].irq.callback = DSTimerIrq1; + ds->ds9.timers[1].irq.context = &ds->ds9; + ds->ds9.timers[1].irq.priority = 0x29; + ds->ds9.timers[2].irq.name = "DS9 Timer 2 IRQ"; + ds->ds9.timers[2].irq.callback = DSTimerIrq2; + ds->ds9.timers[2].irq.context = &ds->ds9; + ds->ds9.timers[2].irq.priority = 0x2A; + ds->ds9.timers[3].irq.name = "DS9 Timer 3 IRQ"; + ds->ds9.timers[3].irq.callback = DSTimerIrq3; + ds->ds9.timers[3].irq.context = &ds->ds9; + ds->ds9.timers[3].irq.priority = 0x2B; } -void DSTimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t value) { - GBATimerUpdateRegisterInternal(timer, timing, cpu, io, 0); - GBATimerWriteTMCNT_HI(timer, timing, cpu, io, value); +void DSTimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, uint16_t* io, uint16_t value) { + GBATimerWriteTMCNT_HI(timer, timing, io, value); }
@@ -26,7 +26,7 @@ #define MAX_BLOCK_SIZE 0x800000
const char mVL_MAGIC[] = "mVL\0"; -const static struct mVLDescriptor { +static const struct mVLDescriptor { enum mPlatform platform; struct mCore* (*open)(void); } _descriptors[] = {
@@ -555,7 +555,7 @@ _updateEnvelope(&audio->ch2.envelope);
if (audio->ch2.envelope.dead == 2) { mTimingDeschedule(timing, &audio->ch2Event); } - _updateSquareSample(&audio->ch1); + _updateSquareSample(&audio->ch2); } }@@ -709,7 +709,7 @@ return (envelope->initialVolume || envelope->direction) && envelope->dead != 2;
} static void _updateSquareSample(struct GBAudioSquareChannel* ch) { - ch->sample = (ch->control.hi * ch->envelope.currentVolume - 8) * 0x10; + ch->sample = (ch->control.hi * 2 - 1) * ch->envelope.currentVolume * 0x8; } static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) {
@@ -29,26 +29,26 @@ #ifndef MINIMAL_CORE
#include <mgba/internal/gba/input.h> #endif -const static struct mCoreChannelInfo _GBVideoLayers[] = { +static const struct mCoreChannelInfo _GBVideoLayers[] = { { 0, "bg", "Background", NULL }, { 1, "obj", "Objects", NULL }, { 2, "win", "Window", NULL }, }; -const static struct mCoreChannelInfo _GBAudioChannels[] = { - { 0, "ch0", "Channel 0", "Square/Sweep" }, - { 1, "ch1", "Channel 1", "Square" }, - { 2, "ch2", "Channel 2", "PCM" }, - { 3, "ch3", "Channel 3", "Noise" }, +static const struct mCoreChannelInfo _GBAudioChannels[] = { + { 0, "ch1", "Channel 1", "Square/Sweep" }, + { 1, "ch2", "Channel 2", "Square" }, + { 2, "ch3", "Channel 3", "PCM" }, + { 3, "ch4", "Channel 4", "Noise" }, }; -const static struct LR35902Segment _GBSegments[] = { +static const struct LR35902Segment _GBSegments[] = { { .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM }, { .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 }, { 0 } }; -const static struct LR35902Segment _GBCSegments[] = { +static const struct LR35902Segment _GBCSegments[] = { { .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM }, { .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 }, { .name = "WRAM", .start = GB_BASE_WORKING_RAM_BANK1, .end = 0xE000 },@@ -56,7 +56,7 @@ { .name = "VRAM", .start = GB_BASE_VRAM, .end = GB_BASE_EXTERNAL_RAM },
{ 0 } }; -const static struct mCoreMemoryBlock _GBMemoryBlocks[] = { +static const struct mCoreMemoryBlock _GBMemoryBlocks[] = { { -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL }, { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511 }, { GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },@@ -67,7 +67,7 @@ { GB_BASE_IO, "io", "MMIO", "Memory-Mapped I/O", GB_BASE_IO, GB_BASE_IO + GB_SIZE_IO, GB_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, }; -const static struct mCoreMemoryBlock _GBCMemoryBlocks[] = { +static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = { { -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL }, { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511 }, { GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
@@ -433,6 +433,7 @@ cpu->c = 0x13;
cpu->e = 0xD8; cpu->h = 1; cpu->l = 0x4D; + gb->timer.internalDiv = 0x2AF3; break; case GB_MODEL_AGB: cpu->b = 1;@@ -444,6 +445,7 @@ cpu->c = 0;
cpu->e = 0x08; cpu->h = 0; cpu->l = 0x7C; + gb->timer.internalDiv = 0x7A8; break; }
@@ -79,7 +79,11 @@ }
void GBMBCSwitchSramBank(struct GB* gb, int bank) { size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM; - GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM); + if (bankStart + GB_SIZE_EXTERNAL_RAM > gb->sramSize) { + mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid RAM bank: %0X", bank); + bankStart &= (gb->sramSize - 1); + bank = bankStart / GB_SIZE_EXTERNAL_RAM; + } gb->memory.sramBank = &gb->memory.sram[bankStart]; gb->memory.sramCurrentBank = bank; }@@ -194,7 +198,7 @@ break;
case GB_MBC7: gb->memory.mbcWrite = _GBMBC7; gb->memory.mbcRead = _GBMBC7Read; - gb->sramSize = GB_SIZE_EXTERNAL_RAM; + gb->sramSize = 0x100; break; case GB_MMM01: mLOG(GB_MBC, WARN, "unimplemented MBC: MMM01");@@ -468,12 +472,25 @@
void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) { int bank = value & 0x7F; switch (address >> 13) { + case 0x0: + switch (value) { + default: + case 0: + gb->memory.mbcState.mbc7.access = 0; + break; + case 0xA: + gb->memory.mbcState.mbc7.access |= 1; + break; + } + break; case 0x1: GBMBCSwitchBank(gb, bank); break; case 0x2: - if (value < 0x10) { - GBMBCSwitchSramBank(gb, value); + if (value == 0x40) { + gb->memory.mbcState.mbc7.access |= 2; + } else { + gb->memory.mbcState.mbc7.access &= ~2; } break; default:@@ -485,17 +502,15 @@ }
uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) { struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; + if (mbc7->access != 3) { + return 0xFF; + } switch (address & 0xF0) { - case 0x00: - case 0x10: - case 0x60: - case 0x70: - return 0; case 0x20: if (memory->rotation && memory->rotation->readTiltX) { int32_t x = -memory->rotation->readTiltX(memory->rotation); x >>= 21; - x += 2047; + x += 0x81D0; return x; } return 0xFF;@@ -503,7 +518,7 @@ case 0x30:
if (memory->rotation && memory->rotation->readTiltX) { int32_t x = -memory->rotation->readTiltX(memory->rotation); x >>= 21; - x += 2047; + x += 0x81D0; return x >> 8; } return 7;@@ -511,7 +526,7 @@ case 0x40:
if (memory->rotation && memory->rotation->readTiltY) { int32_t y = -memory->rotation->readTiltY(memory->rotation); y >>= 21; - y += 2047; + y += 0x81D0; return y; } return 0xFF;@@ -519,144 +534,142 @@ case 0x50:
if (memory->rotation && memory->rotation->readTiltY) { int32_t y = -memory->rotation->readTiltY(memory->rotation); y >>= 21; - y += 2047; + y += 0x81D0; return y >> 8; } return 7; + case 0x60: + return 0; case 0x80: - return (mbc7->sr >> 16) & 1; + return mbc7->eeprom; default: return 0xFF; } } void GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) { - if ((address & 0xF0) != 0x80) { + struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; + if (mbc7->access != 3) { + return; + } + switch (address & 0xF0) { + case 0x00: + mbc7->latch = (value & 0x55) == 0x55; + return; + case 0x10: + mbc7->latch |= (value & 0xAA); + if (mbc7->latch == 0xAB && memory->rotation && memory->rotation->sample) { + memory->rotation->sample(memory->rotation); + } + mbc7->latch = 0; + return; + default: + mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value); return; + case 0x80: + break; } - struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; - GBMBC7Field old = memory->mbcState.mbc7.field; - mbc7->field = GBMBC7FieldClearIO(value); + GBMBC7Field old = memory->mbcState.mbc7.eeprom; + value = GBMBC7FieldFillDO(value); // Hi-Z if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) { - if (mbc7->state == GBMBC7_STATE_WRITE) { - if (mbc7->writable) { - memory->sramBank[mbc7->address * 2] = mbc7->sr >> 8; - memory->sramBank[mbc7->address * 2 + 1] = mbc7->sr; - } - mbc7->sr = 0x1FFFF; - mbc7->state = GBMBC7_STATE_NULL; - } else { - mbc7->state = GBMBC7_STATE_IDLE; - } + mbc7->state = GBMBC7_STATE_IDLE; } - if (!GBMBC7FieldIsSK(old) && GBMBC7FieldIsSK(value)) { - if (mbc7->state > GBMBC7_STATE_IDLE && mbc7->state != GBMBC7_STATE_READ) { + if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) { + if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) { mbc7->sr <<= 1; - mbc7->sr |= GBMBC7FieldGetIO(value); + mbc7->sr |= GBMBC7FieldGetDI(value); ++mbc7->srBits; } switch (mbc7->state) { case GBMBC7_STATE_IDLE: - if (GBMBC7FieldIsIO(value)) { + if (GBMBC7FieldIsDI(value)) { mbc7->state = GBMBC7_STATE_READ_COMMAND; mbc7->srBits = 0; mbc7->sr = 0; } break; case GBMBC7_STATE_READ_COMMAND: - if (mbc7->srBits == 2) { - mbc7->state = GBMBC7_STATE_READ_ADDRESS; + if (mbc7->srBits == 10) { + mbc7->state = 0x10 | (mbc7->sr >> 6); + if (mbc7->state & 0xC) { + mbc7->state &= ~0x3; + } mbc7->srBits = 0; - mbc7->command = mbc7->sr; + mbc7->address = mbc7->sr & 0x7F; } break; - case GBMBC7_STATE_READ_ADDRESS: - if (mbc7->srBits == 8) { - mbc7->state = GBMBC7_STATE_COMMAND_0 + mbc7->command; - mbc7->srBits = 0; - mbc7->address = mbc7->sr; - if (mbc7->state == GBMBC7_STATE_COMMAND_0) { - switch (mbc7->address >> 6) { - case 0: - mbc7->writable = false; - mbc7->state = GBMBC7_STATE_NULL; - break; - case 3: - mbc7->writable = true; - mbc7->state = GBMBC7_STATE_NULL; - break; - } - } + case GBMBC7_STATE_DO: + value = GBMBC7FieldSetDO(value, mbc7->sr >> 15); + mbc7->sr <<= 1; + --mbc7->srBits; + if (!mbc7->srBits) { + mbc7->state = GBMBC7_STATE_IDLE; } break; - case GBMBC7_STATE_COMMAND_0: + default: + break; + } + switch (mbc7->state) { + case GBMBC7_STATE_EEPROM_EWEN: + mbc7->writable = true; + mbc7->state = GBMBC7_STATE_IDLE; + break; + case GBMBC7_STATE_EEPROM_EWDS: + mbc7->writable = false; + mbc7->state = GBMBC7_STATE_IDLE; + break; + case GBMBC7_STATE_EEPROM_WRITE: if (mbc7->srBits == 16) { - switch (mbc7->address >> 6) { - case 0: - mbc7->writable = false; - mbc7->state = GBMBC7_STATE_NULL; - break; - case 1: - mbc7->state = GBMBC7_STATE_WRITE; - if (mbc7->writable) { - int i; - for (i = 0; i < 256; ++i) { - memory->sramBank[i * 2] = mbc7->sr >> 8; - memory->sramBank[i * 2 + 1] = mbc7->sr; - } - } - break; - case 2: - mbc7->state = GBMBC7_STATE_WRITE; - if (mbc7->writable) { - int i; - for (i = 0; i < 256; ++i) { - memory->sramBank[i * 2] = 0xFF; - memory->sramBank[i * 2 + 1] = 0xFF; - } - } - break; - case 3: - mbc7->writable = true; - mbc7->state = GBMBC7_STATE_NULL; - break; + if (mbc7->writable) { + memory->sram[mbc7->address * 2] = mbc7->sr >> 8; + memory->sram[mbc7->address * 2 + 1] = mbc7->sr; } + mbc7->state = GBMBC7_STATE_IDLE; } break; - case GBMBC7_STATE_COMMAND_SR_WRITE: - if (mbc7->srBits == 16) { - mbc7->srBits = 0; - mbc7->state = GBMBC7_STATE_WRITE; + case GBMBC7_STATE_EEPROM_ERASE: + if (mbc7->writable) { + memory->sram[mbc7->address * 2] = 0xFF; + memory->sram[mbc7->address * 2 + 1] = 0xFF; } + mbc7->state = GBMBC7_STATE_IDLE; break; - case GBMBC7_STATE_COMMAND_SR_READ: - if (mbc7->srBits == 1) { - mbc7->sr = memory->sramBank[mbc7->address * 2] << 8; - mbc7->sr |= memory->sramBank[mbc7->address * 2 + 1]; - mbc7->srBits = 0; - mbc7->state = GBMBC7_STATE_READ; - } + case GBMBC7_STATE_EEPROM_READ: + mbc7->srBits = 16; + mbc7->sr = memory->sram[mbc7->address * 2] << 8; + mbc7->sr |= memory->sram[mbc7->address * 2 + 1]; + mbc7->state = GBMBC7_STATE_DO; + value = GBMBC7FieldClearDO(value); break; - case GBMBC7_STATE_COMMAND_SR_FILL: + case GBMBC7_STATE_EEPROM_WRAL: if (mbc7->srBits == 16) { - mbc7->sr = 0xFFFF; - mbc7->srBits = 0; - mbc7->state = GBMBC7_STATE_WRITE; + if (mbc7->writable) { + int i; + for (i = 0; i < 128; ++i) { + memory->sram[i * 2] = mbc7->sr >> 8; + memory->sram[i * 2 + 1] = mbc7->sr; + } + } + mbc7->state = GBMBC7_STATE_IDLE; } break; + case GBMBC7_STATE_EEPROM_ERAL: + if (mbc7->writable) { + int i; + for (i = 0; i < 128; ++i) { + memory->sram[i * 2] = 0xFF; + memory->sram[i * 2 + 1] = 0xFF; + } + } + mbc7->state = GBMBC7_STATE_IDLE; + break; default: break; } - } else if (GBMBC7FieldIsSK(old) && !GBMBC7FieldIsSK(value)) { - if (mbc7->state == GBMBC7_STATE_READ) { - mbc7->sr <<= 1; - ++mbc7->srBits; - if (mbc7->srBits == 16) { - mbc7->srBits = 0; - mbc7->state = GBMBC7_STATE_NULL; - } - } + } else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) { + value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old)); } + mbc7->eeprom = value; } void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
@@ -437,6 +437,7 @@ void GBMemoryDMA(struct GB* gb, uint16_t base) {
if (base > 0xF100) { return; } + mTimingDeschedule(&gb->timing, &gb->memory.dmaEvent); mTimingSchedule(&gb->timing, &gb->memory.dmaEvent, 8); if (gb->cpu->cycles + 8 < gb->cpu->nextEvent) { gb->cpu->nextEvent = gb->cpu->cycles + 8;
@@ -13,7 +13,7 @@ #include <mgba-util/crc32.h>
static const struct GBCartridgeOverride _overrides[] = { // None yet - { 0, 0, 0 } + { 0, 0, 0, { 0 } } }; bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverride* override) {@@ -35,6 +35,12 @@ char sectionName[24] = "";
snprintf(sectionName, sizeof(sectionName), "gb.override.%08X", override->headerCrc32); const char* model = ConfigurationGetValue(config, sectionName, "model"); const char* mbc = ConfigurationGetValue(config, sectionName, "mbc"); + const char* pal[4] = { + ConfigurationGetValue(config, sectionName, "pal[0]"), + ConfigurationGetValue(config, sectionName, "pal[1]"), + ConfigurationGetValue(config, sectionName, "pal[2]"), + ConfigurationGetValue(config, sectionName, "pal[3]") + }; if (model) { if (strcasecmp(model, "DMG") == 0) {@@ -63,6 +69,21 @@ override->mbc = type;
found = true; } } + + if (pal[0] && pal[1] && pal[2] && pal[3]) { + int i; + for (i = 0; i < 4; ++i) { + char* end; + unsigned long value = strtoul(pal[i], &end, 10); + if (end == &pal[i][1] && *end == 'x') { + value = strtoul(pal[i], &end, 16); + } + if (*end) { + continue; + } + override->gbColors[i] = value; + } + } } return found; }@@ -89,6 +110,12 @@ break;
} ConfigurationSetValue(config, sectionName, "model", model); + if (override->gbColors[0] | override->gbColors[1] | override->gbColors[2] | override->gbColors[3]) { + ConfigurationSetIntValue(config, sectionName, "pal[0]", override->gbColors[0]); + ConfigurationSetIntValue(config, sectionName, "pal[1]", override->gbColors[1]); + ConfigurationSetIntValue(config, sectionName, "pal[2]", override->gbColors[2]); + ConfigurationSetIntValue(config, sectionName, "pal[3]", override->gbColors[3]); + } if (override->mbc != GB_MBC_AUTODETECT) { ConfigurationSetIntValue(config, sectionName, "mbc", override->mbc); } else {@@ -104,6 +131,13 @@
if (override->mbc != GB_MBC_AUTODETECT) { gb->memory.mbcType = override->mbc; GBMBCInit(gb); + } + + if (override->gbColors[0] | override->gbColors[1] | override->gbColors[2] | override->gbColors[3]) { + GBVideoSetPalette(&gb->video, 0, override->gbColors[0]); + GBVideoSetPalette(&gb->video, 1, override->gbColors[1]); + GBVideoSetPalette(&gb->video, 2, override->gbColors[2]); + GBVideoSetPalette(&gb->video, 3, override->gbColors[3]); } }
@@ -67,8 +67,6 @@ softwareRenderer->wy = 0;
softwareRenderer->currentWy = 0; softwareRenderer->wx = 0; softwareRenderer->model = model; - - _clearScreen(softwareRenderer); } static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) {
@@ -169,8 +169,8 @@ gb->audio.style = GB_AUDIO_CGB;
} GBMemoryDeserialize(gb, state); - GBIODeserialize(gb, state); GBVideoDeserialize(&gb->video, state); + GBIODeserialize(gb, state); GBTimerDeserialize(&gb->timer, state); GBAudioDeserialize(&gb->audio, state);
@@ -5,11 +5,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <mgba/internal/gb/timer.h> +#include <mgba/internal/lr35902/lr35902.h> #include <mgba/internal/gb/gb.h> #include <mgba/internal/gb/io.h> #include <mgba/internal/gb/serialize.h> - -static void _GBTimerUpdateDIV(struct GBTimer* timer, uint32_t cyclesLate); void _GBTimerIRQ(struct mTiming* timing, void* context, uint32_t cyclesLate) { UNUSED(timing);@@ -20,28 +19,28 @@ timer->p->memory.io[REG_IF] |= (1 << GB_IRQ_TIMER);
GBUpdateIRQs(timer->p); } -void _GBTimerIncrement(struct mTiming* timing, void* context, uint32_t cyclesLate) { - struct GBTimer* timer = context; - timer->nextDiv += cyclesLate; - while (timer->nextDiv > 0) { +static void _GBTimerDivIncrement(struct GBTimer* timer, uint32_t cyclesLate) { + while (timer->nextDiv >= GB_DMG_DIV_PERIOD) { timer->nextDiv -= GB_DMG_DIV_PERIOD; // Make sure to trigger when the correct bit is a falling edge if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 1) { ++timer->p->memory.io[REG_TIMA]; if (!timer->p->memory.io[REG_TIMA]) { - mTimingSchedule(timing, &timer->irq, 4 - cyclesLate); + mTimingSchedule(&timer->p->timing, &timer->irq, 4 - cyclesLate); } } ++timer->internalDiv; timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4; } - _GBTimerUpdateDIV(timer, cyclesLate); } -void _GBTimerUpdateDIV(struct GBTimer* timer, uint32_t cyclesLate) { +void _GBTimerUpdate(struct mTiming* timing, void* context, uint32_t cyclesLate) { + struct GBTimer* timer = context; + timer->nextDiv += cyclesLate; + _GBTimerDivIncrement(timer, cyclesLate); // Batch div increments - int divsToGo = 16 - (timer->internalDiv & 15) + (timer->nextDiv / GB_DMG_DIV_PERIOD); + int divsToGo = 16 - (timer->internalDiv & 15); int timaToGo = INT_MAX; if (timer->timaPeriod) { timaToGo = timer->timaPeriod - (timer->internalDiv & (timer->timaPeriod - 1));@@ -49,18 +48,14 @@ }
if (timaToGo < divsToGo) { divsToGo = timaToGo; } - if (divsToGo > 16) { - divsToGo = 16; - } - timer->nextDiv &= GB_DMG_DIV_PERIOD - 1; - timer->nextDiv += GB_DMG_DIV_PERIOD * divsToGo; - mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv - cyclesLate); + timer->nextDiv = GB_DMG_DIV_PERIOD * divsToGo; + mTimingSchedule(timing, &timer->event, timer->nextDiv - cyclesLate); } void GBTimerReset(struct GBTimer* timer) { timer->event.context = timer; timer->event.name = "GB Timer"; - timer->event.callback = _GBTimerIncrement; + timer->event.callback = _GBTimerUpdate; timer->event.priority = 0x20; timer->irq.context = timer; timer->irq.name = "GB Timer IRQ";@@ -73,11 +68,19 @@ timer->internalDiv = 0;
} void GBTimerDivReset(struct GBTimer* timer) { + timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event); + mTimingDeschedule(&timer->p->timing, &timer->event); + _GBTimerDivIncrement(timer, (timer->p->cpu->executionState + 1) & 3); + if (timer->internalDiv & (timer->timaPeriod >> 1)) { + ++timer->p->memory.io[REG_TIMA]; + if (!timer->p->memory.io[REG_TIMA]) { + mTimingSchedule(&timer->p->timing, &timer->irq, 4 - ((timer->p->cpu->executionState + 1) & 3)); + } + } timer->p->memory.io[REG_DIV] = 0; timer->internalDiv = 0; timer->nextDiv = GB_DMG_DIV_PERIOD; - mTimingDeschedule(&timer->p->timing, &timer->event); - mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv); + mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv - ((timer->p->cpu->executionState + 1) & 3)); } uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {@@ -96,11 +99,15 @@ case 3:
timer->timaPeriod = 256 >> 4; break; } + + timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event); + mTimingDeschedule(&timer->p->timing, &timer->event); + _GBTimerDivIncrement(timer, (timer->p->cpu->executionState + 1) & 3); + timer->nextDiv += GB_DMG_DIV_PERIOD; + mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv); } else { timer->timaPeriod = 0; } - mTimingDeschedule(&timer->p->timing, &timer->event); - _GBTimerUpdateDIV(timer, 0); return tac; }
@@ -185,9 +185,6 @@ video->p->memory.io[REG_LY] = video->ly;
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7); video->mode = 2; video->modeEvent.callback = _endMode2; - if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) { - video->p->memory.rotation->sample(video->p->memory.rotation); - } } else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS) { video->p->memory.io[REG_LY] = 0; next = GB_VIDEO_HORIZONTAL_LENGTH - 8;@@ -389,6 +386,7 @@ if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } + video->p->memory.io[REG_STAT] = video->stat; } void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value) {@@ -471,11 +469,11 @@ video->vramBank = &video->vram[value * GB_SIZE_VRAM_BANK0];
video->vramCurrentBank = value; } -void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color) { +void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color) { if (index >= 4) { return; } - video->dmgPalette[index] = color; + video->dmgPalette[index] = M_RGB8_TO_RGB5(color); } static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
@@ -28,7 +28,7 @@ #ifndef MINIMAL_CORE
#include <mgba/internal/gba/input.h> #endif -const static struct mCoreChannelInfo _GBAVideoLayers[] = { +static const struct mCoreChannelInfo _GBAVideoLayers[] = { { 0, "bg0", "Background 0", NULL }, { 1, "bg1", "Background 1", NULL }, { 2, "bg2", "Background 2", NULL },@@ -36,16 +36,16 @@ { 3, "bg3", "Background 3", NULL },
{ 4, "obj", "Objects", NULL }, }; -const static struct mCoreChannelInfo _GBAAudioChannels[] = { - { 0, "ch0", "PSG Channel 0", "Square/Sweep" }, - { 1, "ch1", "PSG Channel 1", "Square" }, - { 2, "ch2", "PSG Channel 2", "PCM" }, - { 3, "ch3", "PSG Channel 3", "Noise" }, +static const struct mCoreChannelInfo _GBAAudioChannels[] = { + { 0, "ch1", "PSG Channel 1", "Square/Sweep" }, + { 1, "ch2", "PSG Channel 2", "Square" }, + { 2, "ch3", "PSG Channel 3", "PCM" }, + { 3, "ch4", "PSG Channel 4", "Noise" }, { 4, "chA", "FIFO Channel A", NULL }, { 5, "chB", "FIFO Channel B", NULL }, }; -const static struct mCoreMemoryBlock _GBAMemoryBlocks[] = { +static const struct mCoreMemoryBlock _GBAMemoryBlocks[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },@@ -59,7 +59,7 @@ { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, }; -const static struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = { +static const struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },@@ -74,7 +74,7 @@ { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART_SRAM, "sram", "SRAM", "Static RAM (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_SRAM, SIZE_CART_SRAM, true }, }; -const static struct mCoreMemoryBlock _GBAMemoryBlocksFlash512[] = { +static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash512[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },@@ -89,7 +89,7 @@ { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, }; -const static struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = { +static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },@@ -104,7 +104,7 @@ { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 }, }; -const static struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = { +static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
@@ -501,23 +501,19 @@ return;
case REG_TM0CNT_HI: value &= 0x00C7; - GBATimerUpdateRegister(gba, 0); - GBATimerWriteTMCNT_HI(&gba->timers[0], &gba->timing, gba->cpu, &gba->memory.io[REG_TM0CNT_LO >> 1], value); + GBATimerWriteTMCNT_HI(&gba->timers[0], &gba->timing, &gba->memory.io[REG_TM0CNT_LO >> 1], value); break; case REG_TM1CNT_HI: value &= 0x00C7; - GBATimerUpdateRegister(gba, 1); - GBATimerWriteTMCNT_HI(&gba->timers[1], &gba->timing, gba->cpu, &gba->memory.io[REG_TM1CNT_LO >> 1], value); + GBATimerWriteTMCNT_HI(&gba->timers[1], &gba->timing, &gba->memory.io[REG_TM1CNT_LO >> 1], value); break; case REG_TM2CNT_HI: value &= 0x00C7; - GBATimerUpdateRegister(gba, 2); - GBATimerWriteTMCNT_HI(&gba->timers[2], &gba->timing, gba->cpu, &gba->memory.io[REG_TM2CNT_LO >> 1], value); + GBATimerWriteTMCNT_HI(&gba->timers[2], &gba->timing, &gba->memory.io[REG_TM2CNT_LO >> 1], value); break; case REG_TM3CNT_HI: value &= 0x00C7; - GBATimerUpdateRegister(gba, 3); - GBATimerWriteTMCNT_HI(&gba->timers[3], &gba->timing, gba->cpu, &gba->memory.io[REG_TM3CNT_LO >> 1], value); + GBATimerWriteTMCNT_HI(&gba->timers[3], &gba->timing, &gba->memory.io[REG_TM3CNT_LO >> 1], value); break; // SIO@@ -711,17 +707,18 @@ gba->haltPending = false;
} switch (address) { + // Reading this takes two cycles (1N+1I), so let's remove them preemptively case REG_TM0CNT_LO: - GBATimerUpdateRegister(gba, 0); + GBATimerUpdateRegister(gba, 0, 2); break; case REG_TM1CNT_LO: - GBATimerUpdateRegister(gba, 1); + GBATimerUpdateRegister(gba, 1, 2); break; case REG_TM2CNT_LO: - GBATimerUpdateRegister(gba, 2); + GBATimerUpdateRegister(gba, 2, 2); break; case REG_TM3CNT_LO: - GBATimerUpdateRegister(gba, 3); + GBATimerUpdateRegister(gba, 3, 2); break; case REG_KEYINPUT:@@ -929,10 +926,9 @@
for (i = 0; i < 4; ++i) { STORE_16(gba->memory.io[(REG_DMA0CNT_LO + i * 12) >> 1], (REG_DMA0CNT_LO + i * 12), state->io); STORE_16(gba->timers[i].reload, 0, &state->timers[i].reload); - STORE_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload); STORE_32(gba->timers[i].lastEvent - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].lastEvent); STORE_32(gba->timers[i].event.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextEvent); - STORE_32(gba->timers[i].overflowInterval, 0, &state->timers[i].overflowInterval); + STORE_32(gba->timers[i].irq.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextIrq); STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags); STORE_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource); STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);@@ -958,8 +954,6 @@
uint32_t when; for (i = 0; i < 4; ++i) { LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload); - LOAD_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload); - LOAD_32(gba->timers[i].overflowInterval, 0, &state->timers[i].overflowInterval); LOAD_32(gba->timers[i].flags, 0, &state->timers[i].flags); if (i > 0 && GBATimerFlagsIsCountUp(gba->timers[i].flags)) { // Overwrite invalid values in savestate@@ -971,6 +965,10 @@ }
LOAD_32(when, 0, &state->timers[i].nextEvent); if (GBATimerFlagsIsEnable(gba->timers[i].flags)) { mTimingSchedule(&gba->timing, &gba->timers[i].event, when); + } + LOAD_32(when, 0, &state->timers[i].nextIrq); + if (GBATimerFlagsIsIrqPending(gba->timers[i].flags)) { + mTimingSchedule(&gba->timing, &gba->timers[i].irq, when); } LOAD_16(gba->memory.dma[i].reg, (REG_DMA0CNT_HI + i * 12), state->io);
@@ -115,24 +115,14 @@ }
uint32_t copySize = size - 0x1C; switch (gba->memory.savedata.type) { - case SAVEDATA_SRAM: - if (copySize > SIZE_CART_SRAM) { - copySize = SIZE_CART_SRAM; - } - break; case SAVEDATA_FLASH512: if (copySize > SIZE_CART_FLASH512) { GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M, gba->memory.savedata.realisticTiming); } // Fall through - case SAVEDATA_FLASH1M: - if (copySize > SIZE_CART_FLASH1M) { - copySize = SIZE_CART_FLASH1M; - } - break; - case SAVEDATA_EEPROM: - if (copySize > SIZE_CART_EEPROM) { - copySize = SAVEDATA_EEPROM; + default: + if (copySize > GBASavedataSize(&gba->memory.savedata)) { + copySize = GBASavedataSize(&gba->memory.savedata); } break; case SAVEDATA_FORCE_NONE:@@ -141,6 +131,7 @@ goto cleanup;
} memcpy(gba->memory.savedata.data, &payload[0x1C], copySize); + gba->memory.savedata.vf && gba->memory.savedata.vf->sync(gba->memory.savedata.vf, gba->memory.savedata.data, size); free(payload); return true;
@@ -8,6 +8,58 @@
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/io.h> +#define TIMER_IRQ_DELAY 7 +#define TIMER_RELOAD_DELAY 0 +#define TIMER_STARTUP_DELAY 2 + +static void GBATimerIrq(struct GBA* gba, int timerId) { + struct GBATimer* timer = &gba->timers[timerId]; + if (GBATimerFlagsIsIrqPending(timer->flags)) { + timer->flags = GBATimerFlagsClearIrqPending(timer->flags); + GBARaiseIRQ(gba, IRQ_TIMER0 + timerId); + } +} + +static void GBATimerIrq0(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + GBATimerIrq(context, 0); +} + +static void GBATimerIrq1(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + GBATimerIrq(context, 1); +} + +static void GBATimerIrq2(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + GBATimerIrq(context, 2); +} + +static void GBATimerIrq3(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + GBATimerIrq(context, 3); +} + +void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate) { + *io = timer->reload; + int32_t currentTime = mTimingCurrentTime(timing) - cyclesLate; + int32_t tickMask = (1 << GBATimerFlagsGetPrescaleBits(timer->flags)) - 1; + currentTime &= ~tickMask; + timer->lastEvent = currentTime; + GBATimerUpdateRegisterInternal(timer, timing, io, 0); + + if (GBATimerFlagsIsDoIrq(timer->flags)) { + timer->flags = GBATimerFlagsFillIrqPending(timer->flags); + if (!mTimingIsScheduled(timing, &timer->irq)) { + mTimingSchedule(timing, &timer->irq, TIMER_IRQ_DELAY - cyclesLate); + } + } +} + static void GBATimerUpdateAudio(struct GBA* gba, int timerId, uint32_t cyclesLate) { if (!gba->audio.enable) { return;@@ -25,28 +77,13 @@ void GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate) {
if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled? ++*io; if (!*io && GBATimerFlagsIsEnable(nextTimer->flags)) { - mTimingSchedule(timing, &nextTimer->event, -cyclesLate); + GBATimerUpdate(timing, nextTimer, io, cyclesLate); } } } -void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate) { - *io = timer->reload; - timer->oldReload = timer->reload; - timer->lastEvent = timing->masterCycles - cyclesLate; - - if (!GBATimerFlagsIsCountUp(timer->flags)) { - uint32_t nextEvent = timer->overflowInterval - cyclesLate; - mTimingSchedule(timing, &timer->event, nextEvent); - } -} - static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBA* gba = context; - struct GBATimer* timer = &gba->timers[0]; - if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER0); - } GBATimerUpdateAudio(gba, 0, cyclesLate); GBATimerUpdate(timing, &gba->timers[0], &gba->memory.io[REG_TM0CNT_LO >> 1], cyclesLate); GBATimerUpdateCountUp(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate);@@ -54,10 +91,6 @@ }
static void GBATimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBA* gba = context; - struct GBATimer* timer = &gba->timers[1]; - if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER1); - } GBATimerUpdateAudio(gba, 1, cyclesLate); GBATimerUpdate(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate); GBATimerUpdateCountUp(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate);@@ -65,20 +98,12 @@ }
static void GBATimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBA* gba = context; - struct GBATimer* timer = &gba->timers[2]; - if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER2); - } GBATimerUpdate(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate); GBATimerUpdateCountUp(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate); } static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBA* gba = context; - struct GBATimer* timer = &gba->timers[3]; - if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER3); - } GBATimerUpdate(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate); }@@ -100,63 +125,97 @@ gba->timers[3].event.name = "GBA Timer 3";
gba->timers[3].event.callback = GBATimerUpdate3; gba->timers[3].event.context = gba; gba->timers[3].event.priority = 0x23; + gba->timers[0].irq.name = "GBA Timer 0 IRQ"; + gba->timers[0].irq.callback = GBATimerIrq0; + gba->timers[0].irq.context = gba; + gba->timers[0].irq.priority = 0x28; + gba->timers[1].irq.name = "GBA Timer 1 IRQ"; + gba->timers[1].irq.callback = GBATimerIrq1; + gba->timers[1].irq.context = gba; + gba->timers[1].irq.priority = 0x29; + gba->timers[2].irq.name = "GBA Timer 2 IRQ"; + gba->timers[2].irq.callback = GBATimerIrq2; + gba->timers[2].irq.context = gba; + gba->timers[2].irq.priority = 0x2A; + gba->timers[3].irq.name = "GBA Timer 3 IRQ"; + gba->timers[3].irq.callback = GBATimerIrq3; + gba->timers[3].irq.context = gba; + gba->timers[3].irq.priority = 0x2B; } -void GBATimerUpdateRegister(struct GBA* gba, int timer) { +void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) { struct GBATimer* currentTimer = &gba->timers[timer]; if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) { - // Reading this takes two cycles (1N+1I), so let's remove them preemptively - int32_t prefetchSkew = -2; + int32_t prefetchSkew = cyclesLate; if (gba->memory.lastPrefetchedPc > (uint32_t) gba->cpu->gprs[ARM_PC]) { prefetchSkew += ((gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * gba->cpu->memory.activeSeqCycles16) / WORD_SIZE_THUMB; } - GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, gba->cpu, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew); + GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew); } } -void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, int32_t skew) { - int32_t diff = cpu->cycles - (timer->lastEvent - timing->masterCycles); - *io = timer->oldReload + ((diff + skew) >> GBATimerFlagsGetPrescaleBits(timer->flags)); +void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, uint16_t* io, int32_t skew) { + if (!GBATimerFlagsIsEnable(timer->flags) || GBATimerFlagsIsCountUp(timer->flags)) { + return; + } + + int prescaleBits = GBATimerFlagsGetPrescaleBits(timer->flags); + int32_t currentTime = mTimingCurrentTime(timing) - skew; + int32_t tickMask = (1 << prescaleBits) - 1; + currentTime &= ~tickMask; + int32_t tickIncrement = currentTime - timer->lastEvent; + timer->lastEvent = currentTime; + tickIncrement >>= prescaleBits; + tickIncrement += *io; + *io = tickIncrement; + if (!mTimingIsScheduled(timing, &timer->event)) { + tickIncrement = (0x10000 - tickIncrement) << prescaleBits; + currentTime -= mTimingCurrentTime(timing) - skew; + mTimingSchedule(timing, &timer->event, TIMER_RELOAD_DELAY + tickIncrement + currentTime); + } } void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) { timer->reload = reload; - timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags); } -void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t control) { +void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, uint16_t* io, uint16_t control) { + GBATimerUpdateRegisterInternal(timer, timing, io, 0); + unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(timer->flags); + unsigned prescaleBits; switch (control & 0x0003) { case 0x0000: - timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, timer->forcedPrescale); + prescaleBits = 0; break; case 0x0001: - timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 6 + timer->forcedPrescale); + prescaleBits = 6; break; case 0x0002: - timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 8 + timer->forcedPrescale); + prescaleBits = 8; break; case 0x0003: - timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 10 + timer->forcedPrescale); + prescaleBits = 10; break; } + prescaleBits += timer->forcedPrescale; + timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, prescaleBits); timer->flags = GBATimerFlagsTestFillCountUp(timer->flags, timer > 0 && (control & 0x0004)); timer->flags = GBATimerFlagsTestFillDoIrq(timer->flags, control & 0x0040); - timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags); bool wasEnabled = GBATimerFlagsIsEnable(timer->flags); timer->flags = GBATimerFlagsTestFillEnable(timer->flags, control & 0x0080); if (!wasEnabled && GBATimerFlagsIsEnable(timer->flags)) { mTimingDeschedule(timing, &timer->event); - if (!GBATimerFlagsIsCountUp(timer->flags)) { - mTimingSchedule(timing, &timer->event, timer->overflowInterval); - } *io = timer->reload; - timer->oldReload = timer->reload; - timer->lastEvent = timing->masterCycles + cpu->cycles; + int32_t tickMask = (1 << prescaleBits) - 1; + timer->lastEvent = (mTimingCurrentTime(timing) - TIMER_STARTUP_DELAY) & ~tickMask; + GBATimerUpdateRegisterInternal(timer, timing, io, TIMER_STARTUP_DELAY); } else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) { mTimingDeschedule(timing, &timer->event); } else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) { mTimingDeschedule(timing, &timer->event); - mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent); + int32_t tickMask = (1 << prescaleBits) - 1; + timer->lastEvent = (mTimingCurrentTime(timing) - TIMER_STARTUP_DELAY) & ~tickMask; + GBATimerUpdateRegisterInternal(timer, timing, io, TIMER_STARTUP_DELAY); } }
@@ -315,24 +315,24 @@ DEFINE_POPPUSH_DECODER_LR35902(HL);
DEFINE_POPPUSH_DECODER_LR35902(AF); #define DEFINE_CB_2_DECODER_LR35902(NAME, BODY) \ - DEFINE_DECODER_LR35902(NAME ## B, info->op1.reg = LR35902_REG_B; BODY) \ - DEFINE_DECODER_LR35902(NAME ## C, info->op1.reg = LR35902_REG_C; BODY) \ - DEFINE_DECODER_LR35902(NAME ## D, info->op1.reg = LR35902_REG_D; BODY) \ - DEFINE_DECODER_LR35902(NAME ## E, info->op1.reg = LR35902_REG_E; BODY) \ - DEFINE_DECODER_LR35902(NAME ## H, info->op1.reg = LR35902_REG_H; BODY) \ - DEFINE_DECODER_LR35902(NAME ## L, info->op1.reg = LR35902_REG_L; BODY) \ - DEFINE_DECODER_LR35902(NAME ## HL, info->op1.reg = LR35902_REG_HL; BODY) \ - DEFINE_DECODER_LR35902(NAME ## A, info->op1.reg = LR35902_REG_A; BODY) + DEFINE_DECODER_LR35902(NAME ## B, info->op2.reg = LR35902_REG_B; BODY) \ + DEFINE_DECODER_LR35902(NAME ## C, info->op2.reg = LR35902_REG_C; BODY) \ + DEFINE_DECODER_LR35902(NAME ## D, info->op2.reg = LR35902_REG_D; BODY) \ + DEFINE_DECODER_LR35902(NAME ## E, info->op2.reg = LR35902_REG_E; BODY) \ + DEFINE_DECODER_LR35902(NAME ## H, info->op2.reg = LR35902_REG_H; BODY) \ + DEFINE_DECODER_LR35902(NAME ## L, info->op2.reg = LR35902_REG_L; BODY) \ + DEFINE_DECODER_LR35902(NAME ## HL, info->op2.reg = LR35902_REG_HL; info->op2.flags = LR35902_OP_FLAG_MEMORY; BODY) \ + DEFINE_DECODER_LR35902(NAME ## A, info->op2.reg = LR35902_REG_A; BODY) #define DEFINE_CB_DECODER_LR35902(NAME, BODY) \ - DEFINE_CB_2_DECODER_LR35902(NAME ## 0, info->op2.immediate = 1; BODY) \ - DEFINE_CB_2_DECODER_LR35902(NAME ## 1, info->op2.immediate = 2; BODY) \ - DEFINE_CB_2_DECODER_LR35902(NAME ## 2, info->op2.immediate = 4; BODY) \ - DEFINE_CB_2_DECODER_LR35902(NAME ## 3, info->op2.immediate = 8; BODY) \ - DEFINE_CB_2_DECODER_LR35902(NAME ## 4, info->op2.immediate = 16; BODY) \ - DEFINE_CB_2_DECODER_LR35902(NAME ## 5, info->op2.immediate = 32; BODY) \ - DEFINE_CB_2_DECODER_LR35902(NAME ## 6, info->op2.immediate = 64; BODY) \ - DEFINE_CB_2_DECODER_LR35902(NAME ## 7, info->op2.immediate = 128; BODY) + DEFINE_CB_2_DECODER_LR35902(NAME ## 0, info->op1.immediate = 0; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 1, info->op1.immediate = 1; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 2, info->op1.immediate = 2; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 3, info->op1.immediate = 3; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 4, info->op1.immediate = 4; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 5, info->op1.immediate = 5; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 6, info->op1.immediate = 6; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 7, info->op1.immediate = 7; BODY) DEFINE_CB_DECODER_LR35902(BIT, info->mnemonic = LR35902_MN_BIT) DEFINE_CB_DECODER_LR35902(RES, info->mnemonic = LR35902_MN_RES)@@ -496,7 +496,7 @@ return 0;
} if (op.flags & LR35902_OP_FLAG_MEMORY) { - strncpy(buffer, "(", blen - 1); + strncpy(buffer, "[", blen - 1); ADVANCE(1); } if (op.reg) {@@ -519,7 +519,7 @@ strncpy(buffer, "-", blen - 1);
ADVANCE(1); } if (op.flags & LR35902_OP_FLAG_MEMORY) { - strncpy(buffer, ")", blen - 1); + strncpy(buffer, "]", blen - 1); ADVANCE(1); } return total;@@ -544,7 +544,7 @@ ADVANCE(2);
} } - if (info->op1.reg || info->op1.immediate) { + if (info->op1.reg || info->op1.immediate || info->op2.reg || info->op2.immediate) { written = _decodeOperand(info->op1, buffer, blen); ADVANCE(written); }
@@ -175,7 +175,7 @@ if (v->lockIntegerScaling) {
drawW -= drawW % v->width; drawH -= drawH % v->height; } - glViewport(0, 0, v->width, v->height); + glViewport(0, 0, w, h); glClearColor(0.f, 0.f, 0.f, 1.f); glClear(GL_COLOR_BUFFER_BIT); glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);@@ -203,13 +203,13 @@ int drawW = shader->width;
int drawH = shader->height; int padW = 0; int padH = 0; - if (!shader->width) { + if (!drawW) { drawW = viewport[2]; padW = viewport[0]; } else if (shader->width < 0) { drawW = context->d.width * -shader->width; } - if (!shader->height) { + if (!drawH) { drawH = viewport[3]; padH = viewport[1]; } else if (shader->height < 0) {@@ -234,7 +234,7 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST); glUseProgram(shader->program); glUniform1i(shader->texLocation, 0); - glUniform2f(shader->texSizeLocation, context->d.width, context->d.height); + glUniform2f(shader->texSizeLocation, context->d.width - padW, context->d.height - padH); glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); glEnableVertexAttribArray(shader->positionLocation); size_t u;@@ -290,7 +290,6 @@ }
} glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glBindTexture(GL_TEXTURE_2D, shader->tex); - glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); } void mGLES2ContextDrawFrame(struct VideoBackend* v) {@@ -298,12 +297,17 @@ struct mGLES2Context* context = (struct mGLES2Context*) v;
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, context->tex); + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + context->finalShader.filter = v->filter; _drawShader(context, &context->initialShader); size_t n; for (n = 0; n < context->nShaders; ++n) { + glViewport(0, 0, viewport[2], viewport[3]); _drawShader(context, &context->shaders[n]); } + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); _drawShader(context, &context->finalShader); glBindFramebuffer(GL_FRAMEBUFFER, 0); glUseProgram(0);
@@ -1,9 +1,7 @@
-find_program(FIXUP vita-elf-create) -find_program(MAKE_FSELF vita-make-fself) -find_program(MAKE_SFO vita-mksfoex) +include("${VITASDK}/share/vita.cmake" REQUIRED) + find_program(OBJCOPY ${cross_prefix}objcopy) find_file(NIDDB db.json PATHS ${VITASDK} ${VITASDK}/bin ${VITASDK}/share) -find_program(STRIP ${cross_prefix}strip) set(OS_DEFINES IOAPI_NO_64) set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE)@@ -37,34 +35,16 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/backdrop.o
COMMAND ${OBJCOPY_CMD} backdrop.png ${CMAKE_CURRENT_BINARY_DIR}/backdrop.o WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -add_custom_target(${BINARY_NAME}.velf ALL - ${STRIP} --strip-unneeded -go ${BINARY_NAME}-stripped.elf ${BINARY_NAME}.elf - COMMAND ${FIXUP} ${BINARY_NAME}-stripped.elf ${BINARY_NAME}.velf ${NIDDB} - DEPENDS ${BINARY_NAME}.elf) - -add_custom_target(sce_sys ${CMAKE_COMMAND} -E make_directory sce_sys) - -add_custom_target(param.sfo - ${MAKE_SFO} ${PROJECT_NAME} -s TITLE_ID=MGBA00001 sce_sys/param.sfo - DEPENDS sce_sys) - -add_custom_target(eboot.bin - ${MAKE_FSELF} -s ${BINARY_NAME}.velf eboot.bin - DEPENDS ${BINARY_NAME}.velf) +vita_create_self(${BINARY_NAME}.self ${BINARY_NAME}.elf) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/template.xml.in ${CMAKE_CURRENT_BINARY_DIR}/template.xml) -add_custom_target(livearea - ${CMAKE_COMMAND} -E make_directory sce_sys/livearea/contents - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/icon0.png sce_sys - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/pic0.png sce_sys - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/template.xml sce_sys/livearea/contents - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bg.png sce_sys/livearea/contents - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/startup.png sce_sys/livearea/contents - DEPENDS sce_sys) - -add_custom_target(${BINARY_NAME}.vpk ALL - zip -qr ${BINARY_NAME}.vpk sce_sys eboot.bin - DEPENDS livearea eboot.bin param.sfo) +vita_create_vpk(${BINARY_NAME}.vpk MGBA00001 ${BINARY_NAME}.self + NAME ${PROJECT_NAME} + FILE ${CMAKE_CURRENT_SOURCE_DIR}/icon0.png sce_sys/icon0.png + FILE ${CMAKE_CURRENT_SOURCE_DIR}/pic0.png sce_sys/pic0.png + FILE ${CMAKE_CURRENT_SOURCE_DIR}/bg.png sce_sys/livearea/contents/bg.png + FILE ${CMAKE_CURRENT_SOURCE_DIR}/startup.png sce_sys/livearea/contents/startup.png + FILE ${CMAKE_CURRENT_BINARY_DIR}/template.xml sce_sys/livearea/contents/template.xml) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.vpk DESTINATION . COMPONENT ${BINARY_NAME}-psp2)
@@ -14,7 +14,7 @@ set(CMAKE_PROGRAM_PATH ${toolchain_dir}/bin)
set(cross_prefix arm-vita-eabi-) set(inc_flags -I${toolchain_dir}/include) -set(link_flags "-L${toolchain_dir}/lib -Wl,-q") +set(link_flags "-L${toolchain_dir}/lib -Wl,-z,nocopyreloc") set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name") set(CMAKE_SYSTEM_PROCESSOR armv7-a CACHE INTERNAL "processor")@@ -26,9 +26,9 @@ find_program(CMAKE_C_COMPILER ${cross_prefix}gcc${extension})
find_program(CMAKE_CXX_COMPILER ${cross_prefix}g++${extension}) find_program(CMAKE_ASM_COMPILER ${cross_prefix}gcc${extension}) find_program(CMAKE_LINKER ${cross_prefix}ld${extension}) -set(CMAKE_C_FLAGS ${inc_flags} CACHE INTERNAL "c compiler flags") +set(CMAKE_C_FLAGS "${inc_flags} -Wl,-q" CACHE INTERNAL "c compiler flags") set(CMAKE_ASM_FLAGS ${inc_flags} CACHE INTERNAL "assembler flags") -set(CMAKE_CXX_FLAGS ${inc_flags} CACHE INTERNAL "cxx compiler flags") +set(CMAKE_CXX_FLAGS "${inc_flags} -Wl,-q" CACHE INTERNAL "cxx compiler flags") set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags") set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags")@@ -40,6 +40,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE INTERNAL "")
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "") set(ENV{PKG_CONFIG_PATH} ${VITASDK}/arm-vita-eabi/lib/pkgconfig) set(ENV{PKG_CONFIG_LIBDIR} ${VITASDK}/arm-vita-eabi/lib/pkgconfig) + +set(VITA_ELF_CREATE "${VITASDK}/bin/vita-elf-create${TOOL_OS_SUFFIX}" CACHE PATH "vita-elf-create") +set(VITA_ELF_EXPORT "${VITASDK}/bin/vita-elf-export${TOOL_OS_SUFFIX}" CACHE PATH "vita-elf-export") +set(VITA_LIBS_GEN "${VITASDK}/bin/vita-libs-gen${TOOL_OS_SUFFIX}" CACHE PATH "vita-libs-gen") +set(VITA_MAKE_FSELF "${VITASDK}/bin/vita-make-fself${TOOL_OS_SUFFIX}" CACHE PATH "vita-make-fself") +set(VITA_MKSFOEX "${VITASDK}/bin/vita-mksfoex${TOOL_OS_SUFFIX}" CACHE PATH "vita-mksfoex") +set(VITA_PACK_VPK "${VITASDK}/bin/vita-pack-vpk${TOOL_OS_SUFFIX}" CACHE PATH "vita-pack-vpk") set(PSP2 ON) add_definitions(-DPSP2)
@@ -30,6 +30,7 @@
#include <mgba/core/core.h> #include <mgba/core/mem-search.h> #include <mgba/core/tile-cache.h> +#include <mgba/core/version.h> #define PYEXPORT extern "Python+C" #include "platform/python/log.h"
@@ -23,6 +23,7 @@ #include <mgba/core/core.h>
#include <mgba/core/log.h> #include <mgba/core/mem-search.h> #include <mgba/core/tile-cache.h> +#include <mgba/core/version.h> #include <mgba/internal/arm/arm.h> #include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/input.h>
@@ -5,6 +5,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. from ._pylib import ffi, lib +from collections import namedtuple + def createCallback(structName, cbName, funcName=None): funcName = funcName or "_py{}{}".format(structName, cbName[0].upper() + cbName[1:]) fullStruct = "struct {}*".format(structName)@@ -13,3 +15,19 @@ h = ffi.cast(fullStruct, handle)
return getattr(ffi.from_handle(h.pyobj), cbName)(*args) return ffi.def_extern(name=funcName)(cb) + +version = ffi.string(lib.projectVersion).decode('utf-8') + +GitInfo = namedtuple("GitInfo", "commit commitShort branch revision") + +git = {} +if lib.gitCommit and lib.gitCommit != "(unknown)": + git['commit'] = ffi.string(lib.gitCommit).decode('utf-8') +if lib.gitCommitShort and lib.gitCommitShort != "(unknown)": + git['commitShort'] = ffi.string(lib.gitCommitShort).decode('utf-8') +if lib.gitBranch and lib.gitBranch != "(unknown)": + git['branch'] = ffi.string(lib.gitBranch).decode('utf-8') +if lib.gitRevision > 0: + git['revision'] = lib.gitRevision + +git = GitInfo(**git)
@@ -35,6 +35,10 @@ def _deinitTileCache(self, cache):
self._native.video.renderer.cache = ffi.NULL lib.mTileCacheDeinit(cache) + def reset(self): + super(GB, self).reset() + self.memory = GBMemory(self._core) + def attachSIO(self, link): lib.GBSIOSetDriver(ffi.addressof(self._native.sio), link._native)@@ -61,21 +65,46 @@ def writeSC(self, value):
return value class GBSIOSimpleDriver(GBSIODriver): - def __init__(self): + def __init__(self, period=0x100): super(GBSIOSimpleDriver, self).__init__() - self.tx = 0xFF - self.rx = 0xFF + self.rx = 0x00 + self._period = period + + def init(self): + self._native.p.period = self._period + return True def writeSB(self, value): self.rx = value - def schedule(self, period=0x100, when=0): + def writeSC(self, value): + self._native.p.period = self._period + if value & 0x80: + lib.mTimingDeschedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event)) + lib.mTimingSchedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event), self._native.p.period) + return value + + def isReady(self): + return not self._native.p.remainingBits + + @property + def tx(self): + self._native.p.pendingSB + + @property + def period(self): + return self._native.p.period + + @tx.setter + def tx(self, newTx): + self._native.p.pendingSB = newTx self._native.p.remainingBits = 8 - self._native.p.period = period - self._native.p.pendingSB = self.tx - self.tx = 0xFF - lib.mTimingDeschedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event)) - lib.mTimingSchedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event), when) + + @period.setter + def period(self, newPeriod): + self._period = newPeriod + if self._native.p: + self._native.p.period = newPeriod class GBMemory(Memory): def __init__(self, core):
@@ -6,64 +6,74 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
from ._pylib import ffi, lib from . import png +try: + import PIL.Image as PImage +except ImportError: + pass + class Image: - def __init__(self, width, height, stride=0): - self.width = width - self.height = height - self.stride = stride - self.constitute() + def __init__(self, width, height, stride=0): + self.width = width + self.height = height + self.stride = stride + self.constitute() - def constitute(self): - if self.stride <= 0: - self.stride = self.width - self.buffer = ffi.new("color_t[{}]".format(self.stride * self.height)) + def constitute(self): + if self.stride <= 0: + self.stride = self.width + self.buffer = ffi.new("color_t[{}]".format(self.stride * self.height)) - def savePNG(self, f): - p = png.PNG(f) - success = p.writeHeader(self) - success = success and p.writePixels(self) - p.writeClose() - return success + def savePNG(self, f): + p = png.PNG(f) + success = p.writeHeader(self) + success = success and p.writePixels(self) + p.writeClose() + return success + + if 'PImage' in globals(): + def toPIL(self): + return PImage.frombytes("RGBX", (self.width, self.height), ffi.buffer(self.buffer), "raw", + "RGBX", self.stride * 4) def u16ToU32(c): - r = c & 0x1F - g = (c >> 5) & 0x1F - b = (c >> 10) & 0x1F - a = (c >> 15) & 1 - abgr = r << 3 - abgr |= g << 11 - abgr |= b << 19 - abgr |= (a * 0xFF) << 24 - return abgr + r = c & 0x1F + g = (c >> 5) & 0x1F + b = (c >> 10) & 0x1F + a = (c >> 15) & 1 + abgr = r << 3 + abgr |= g << 11 + abgr |= b << 19 + abgr |= (a * 0xFF) << 24 + return abgr def u32ToU16(c): - r = (c >> 3) & 0x1F - g = (c >> 11) & 0x1F - b = (c >> 19) & 0x1F - a = c >> 31 - abgr = r - abgr |= g << 5 - abgr |= b << 10 - abgr |= a << 15 - return abgr + r = (c >> 3) & 0x1F + g = (c >> 11) & 0x1F + b = (c >> 19) & 0x1F + a = c >> 31 + abgr = r + abgr |= g << 5 + abgr |= b << 10 + abgr |= a << 15 + return abgr if ffi.sizeof("color_t") == 2: - def colorToU16(c): - return c + def colorToU16(c): + return c - colorToU32 = u16ToU32 + colorToU32 = u16ToU32 - def u16ToColor(c): - return c + def u16ToColor(c): + return c - u32ToColor = u32ToU16 + u32ToColor = u32ToU16 else: - def colorToU32(c): - return c + def colorToU32(c): + return c - colorToU16 = u32ToU16 + colorToU16 = u32ToU16 - def u32ToColor(c): - return c + def u32ToColor(c): + return c - u16ToColor = u16ToU32 + u16ToColor = u16ToU32
@@ -8,29 +8,33 @@ from . import createCallback
createCallback("mLoggerPy", "log", "_pyLog") +defaultLogger = None + def installDefault(logger): - lib.mLogSetDefaultLogger(logger._native) + global defaultLogger + defaultLogger = logger + lib.mLogSetDefaultLogger(logger._native) class Logger(object): - FATAL = lib.mLOG_FATAL - DEBUG = lib.mLOG_DEBUG - INFO = lib.mLOG_INFO - WARN = lib.mLOG_WARN - ERROR = lib.mLOG_ERROR - STUB = lib.mLOG_STUB - GAME_ERROR = lib.mLOG_GAME_ERROR + FATAL = lib.mLOG_FATAL + DEBUG = lib.mLOG_DEBUG + INFO = lib.mLOG_INFO + WARN = lib.mLOG_WARN + ERROR = lib.mLOG_ERROR + STUB = lib.mLOG_STUB + GAME_ERROR = lib.mLOG_GAME_ERROR - def __init__(self): - self._handle = ffi.new_handle(self) - self._native = ffi.gc(lib.mLoggerPythonCreate(self._handle), lib.free) + def __init__(self): + self._handle = ffi.new_handle(self) + self._native = ffi.gc(lib.mLoggerPythonCreate(self._handle), lib.free) - @staticmethod - def categoryName(category): - return ffi.string(lib.mLogCategoryName(category)).decode('UTF-8') + @staticmethod + def categoryName(category): + return ffi.string(lib.mLogCategoryName(category)).decode('UTF-8') - def log(self, category, level, message): - print("{}: {}".format(self.categoryName(category), message)) + def log(self, category, level, message): + print("{}: {}".format(self.categoryName(category), message)) class NullLogger(Logger): - def log(self, category, level, message): - pass + def log(self, category, level, message): + pass
@@ -23,6 +23,7 @@ url="http://github.com/mgba-emu/mgba/",
packages=["mgba"], setup_requires=['cffi>=1.6'], install_requires=['cffi>=1.6', 'cached-property'], + extras_require={'pil': ['Pillow>=2.3']}, cffi_modules=["_builder.py:ffi"], license="MPL 2.0", classifiers=classifiers
@@ -83,7 +83,7 @@ <pointsize>10</pointsize>
</font> </property> <property name="text"> - <string>© 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 + <string>© 2013 – 2017 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 Game Boy and Game Boy Advance are registered trademarks of Nintendo Co., Ltd.</string> </property> <property name="alignment">
@@ -26,6 +26,7 @@ endif()
set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/input) find_package(Qt5Multimedia) find_package(Qt5OpenGL)@@ -76,10 +77,6 @@ GamepadAxisEvent.cpp
GamepadButtonEvent.cpp GamepadHatEvent.cpp IOViewer.cpp - InputController.cpp - InputItem.cpp - InputModel.cpp - InputProfile.cpp KeyEditor.cpp LoadSaveState.cpp LogController.cpp@@ -104,7 +101,12 @@ TileView.cpp
utils.cpp Window.cpp VFileDevice.cpp - VideoView.cpp) + VideoView.cpp + input/InputController.cpp + input/InputIndex.cpp + input/InputItem.cpp + input/InputModel.cpp + input/InputProfile.cpp) set(UI_FILES AboutScreen.ui@@ -215,7 +217,7 @@ endif()
install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/shaders DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) install(FILES ${CMAKE_SOURCE_DIR}/res/nointro.dat DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) if(NOT WIN32 AND NOT APPLE) - list(APPEND QT_DEFINES DATADIR="${DATADIR}") + list(APPEND QT_DEFINES DATADIR="${CMAKE_INSTALL_PREFIX}/${DATADIR}") endif() find_package(Qt5LinguistTools)
@@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GBAApp.h" #include "AudioProcessor.h" +#include "ConfigController.h" #include "Display.h" #include "GameController.h" #include "Window.h"@@ -14,10 +15,8 @@
#include <QFileInfo> #include <QFileOpenEvent> #include <QIcon> -#include <QTranslator> #include <mgba/core/version.h> -#include <mgba/internal/gba/video.h> #include <mgba-util/socket.h> #include <mgba-util/vfs.h>@@ -31,8 +30,9 @@ static GBAApp* g_app = nullptr;
mLOG_DEFINE_CATEGORY(QT, "Qt", "platform.qt"); -GBAApp::GBAApp(int& argc, char* argv[]) +GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config) : QApplication(argc, argv) + , m_configController(config) { g_app = this;@@ -41,15 +41,9 @@ SDL_Init(SDL_INIT_NOPARACHUTE);
#endif #ifndef Q_OS_MAC - setWindowIcon(QIcon(":/res/mgba-1024.png")); + setWindowIcon(QIcon(":/res/mgba-512.png")); #endif - QTranslator* translator = new QTranslator(this); - if (translator->load(QLocale(), QLatin1String(binaryName), QLatin1String("-"), QLatin1String(":/translations"))) { - installTranslator(translator); - } - - SocketSubsystemInit(); qRegisterMetaType<const uint32_t*>("const uint32_t*"); qRegisterMetaType<mCoreThread*>("mCoreThread*");@@ -57,50 +51,15 @@
QApplication::setApplicationName(projectName); QApplication::setApplicationVersion(projectVersion); - if (!m_configController.getQtOption("displayDriver").isNull()) { - Display::setDriver(static_cast<Display::Driver>(m_configController.getQtOption("displayDriver").toInt())); - } - - mArguments args; - mGraphicsOpts graphicsOpts; - mSubParser subparser; - initParserForGraphics(&subparser, &graphicsOpts); - bool loaded = m_configController.parseArguments(&args, argc, argv, &subparser); - if (loaded && args.showHelp) { - usage(argv[0], subparser.usage); - ::exit(0); - return; + if (!m_configController->getQtOption("displayDriver").isNull()) { + Display::setDriver(static_cast<Display::Driver>(m_configController->getQtOption("displayDriver").toInt())); } reloadGameDB(); - if (!m_configController.getQtOption("audioDriver").isNull()) { - AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt())); + if (!m_configController->getQtOption("audioDriver").isNull()) { + AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController->getQtOption("audioDriver").toInt())); } - Window* w = new Window(&m_configController); - connect(w, &Window::destroyed, [this, w]() { - m_windows.removeAll(w); - }); - m_windows.append(w); - - if (loaded) { - w->argumentsPassed(&args); - } else { - w->loadConfig(); - } - freeArguments(&args); - - if (graphicsOpts.multiplier) { - w->resizeFrame(QSize(VIDEO_HORIZONTAL_PIXELS * graphicsOpts.multiplier, VIDEO_VERTICAL_PIXELS * graphicsOpts.multiplier)); - } - if (graphicsOpts.fullscreen) { - w->enterFullScreen(); - } - - w->show(); - - w->controller()->setMultiplayerController(&m_multiplayer); - w->multiplayerChanged(); } GBAApp::~GBAApp() {@@ -122,7 +81,7 @@ Window* GBAApp::newWindow() {
if (m_windows.count() >= MAX_GBAS) { return nullptr; } - Window* w = new Window(&m_configController, m_multiplayer.attached()); + Window* w = new Window(m_configController, m_multiplayer.attached()); int windowId = m_multiplayer.attached(); connect(w, &Window::destroyed, [this, w]() { m_windows.removeAll(w);@@ -165,10 +124,10 @@
QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QString& filter) { QList<Window*> paused; pauseAll(&paused); - QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getOption("lastDirectory"), filter); + QString filename = QFileDialog::getOpenFileName(owner, title, m_configController->getOption("lastDirectory"), filter); continueAll(paused); if (!filename.isEmpty()) { - m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); + m_configController->setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); } return filename; }@@ -176,10 +135,10 @@
QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QString& filter) { QList<Window*> paused; pauseAll(&paused); - QString filename = QFileDialog::getSaveFileName(owner, title, m_configController.getOption("lastDirectory"), filter); + QString filename = QFileDialog::getSaveFileName(owner, title, m_configController->getOption("lastDirectory"), filter); continueAll(paused); if (!filename.isEmpty()) { - m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); + m_configController->setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); } return filename; }@@ -187,10 +146,10 @@
QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title) { QList<Window*> paused; pauseAll(&paused); - QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getOption("lastDirectory")); + QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController->getOption("lastDirectory")); continueAll(paused); if (!filename.isEmpty()) { - m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); + m_configController->setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); } return filename; }
@@ -10,7 +10,6 @@ #include <QApplication>
#include <QFileDialog> #include <QThread> -#include "ConfigController.h" #include "MultiplayerController.h" struct NoIntroDB;@@ -21,6 +20,7 @@ mLOG_DECLARE_CATEGORY(QT);
namespace QGBA { +class ConfigController; class GameController; class Window;@@ -43,7 +43,7 @@ class GBAApp : public QApplication {
Q_OBJECT public: - GBAApp(int& argc, char* argv[]); + GBAApp(int& argc, char* argv[], ConfigController*); ~GBAApp(); static GBAApp* app();@@ -67,7 +67,7 @@
void pauseAll(QList<Window*>* paused); void continueAll(const QList<Window*>& paused); - ConfigController m_configController; + ConfigController* m_configController; QList<Window*> m_windows; MultiplayerController m_multiplayer;
@@ -5,6 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GBAKeyEditor.h" +#include <QApplication> #include <QComboBox> #include <QHBoxLayout> #include <QPaintEvent>@@ -182,10 +183,20 @@ return QWidget::event(event);
} bool GBAKeyEditor::eventFilter(QObject* obj, QEvent* event) { + KeyEditor* keyEditor = static_cast<KeyEditor*>(obj); + if (event->type() == QEvent::FocusOut) { + keyEditor->setPalette(QApplication::palette(keyEditor)); + } if (event->type() != QEvent::FocusIn) { return false; } - findFocus(static_cast<KeyEditor*>(obj)); + + QPalette palette = keyEditor->palette(); + palette.setBrush(keyEditor->backgroundRole(), palette.highlight()); + palette.setBrush(keyEditor->foregroundRole(), palette.highlightedText()); + keyEditor->setPalette(palette); + + findFocus(keyEditor); return true; }
@@ -9,6 +9,7 @@ #include "AudioProcessor.h"
#include "InputController.h" #include "LogController.h" #include "MultiplayerController.h" +#include "Override.h" #include "VFileDevice.h" #include <QCoreApplication>@@ -162,6 +163,7 @@ #endif
default: break; } + mTileCacheDeinit(controller->m_tileCache.get()); controller->m_tileCache.reset(); }@@ -703,6 +705,7 @@
void GameController::frameAdvance() { if (m_pauseAfterFrame.testAndSetRelaxed(false, true)) { setPaused(false); + m_wasPaused = true; } }
@@ -1,867 +0,0 @@
-/* Copyright (c) 2013-2014 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 "InputController.h" - -#include "ConfigController.h" -#include "GameController.h" -#include "GamepadAxisEvent.h" -#include "GamepadButtonEvent.h" -#include "InputModel.h" -#include "InputProfile.h" - -#include <QApplication> -#include <QKeyEvent> -#include <QMenu> -#include <QTimer> -#include <QWidget> - -#include <mgba/core/interface.h> -#include <mgba-util/configuration.h> - -#ifdef M_CORE_GBA -#include <mgba/internal/gba/input.h> -#endif -#ifdef M_CORE_GB -#include <mgba/internal/gb/input.h> -#endif -#ifdef M_CORE_DS -#include <mgba/internal/ds/input.h> -#endif - -using namespace QGBA; - -#ifdef BUILD_SDL -int InputController::s_sdlInited = 0; -mSDLEvents InputController::s_sdlEvents; -#endif - -InputController::InputController(InputModel* model, int playerId, QWidget* topLevel, QObject* parent) - : QObject(parent) - , m_inputModel(model) - , m_platform(PLATFORM_NONE) - , m_playerId(playerId) - , m_topLevel(topLevel) - , m_focusParent(topLevel) -{ -#ifdef BUILD_SDL - if (s_sdlInited == 0) { - mSDLInitEvents(&s_sdlEvents); - } - ++s_sdlInited; - updateJoysticks(); -#endif - -#ifdef BUILD_SDL - connect(&m_gamepadTimer, &QTimer::timeout, [this]() { - testGamepad(SDL_BINDING_BUTTON); - if (m_playerId == 0) { - updateJoysticks(); - } - }); -#endif - m_gamepadTimer.setInterval(50); - m_gamepadTimer.start(); - - m_autofireMenu = std::unique_ptr<QMenu>(new QMenu(tr("Autofire"))); - m_inputModel->addMenu(m_autofireMenu.get()); - - m_inputMenu = std::unique_ptr<QMenu>(new QMenu(tr("Bindings"))); - m_inputModel->addMenu(m_inputMenu.get()); - - connect(m_inputModel, SIGNAL(keyRebound(const QModelIndex&, int)), this, SLOT(bindKey(const QModelIndex&, int))); - connect(m_inputModel, SIGNAL(buttonRebound(const QModelIndex&, int)), this, SLOT(bindButton(const QModelIndex&, int))); - connect(m_inputModel, SIGNAL(axisRebound(const QModelIndex&, int, GamepadAxisEvent::Direction)), this, SLOT(bindAxis(const QModelIndex&, int, GamepadAxisEvent::Direction))); -} - -InputController::~InputController() { - for (auto& inputMap : m_inputMaps) { - mInputMapDeinit(&inputMap); - } - -#ifdef BUILD_SDL - if (m_playerAttached) { - mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer); - } - - --s_sdlInited; - if (s_sdlInited == 0) { - mSDLDeinitEvents(&s_sdlEvents); - } -#endif -} - -void InputController::addPlatform(mPlatform platform, const QString& visibleName, const mInputPlatformInfo* info) { - mInputMap* inputMap = &m_inputMaps[platform]; - mInputMapInit(inputMap, info); - - QMenu* input = m_inputMenu->addMenu(visibleName); - QMenu* autofire = m_autofireMenu->addMenu(visibleName); - m_inputMenuIndices[platform] = m_inputModel->addMenu(input, m_inputMenu.get()); - m_inputModel->addMenu(autofire, m_autofireMenu.get()); - - for (size_t i = 0; i < info->nKeys; ++i) { - m_inputModel->addKey(input, platform, i, 0, info->keyId[i], QString("%1.%2").arg(info->platformName).arg(info->keyId[i])); - m_inputModel->addKey(autofire, platform, i, 0, info->keyId[i], QString("%1.autofire.%2").arg(info->platformName).arg(info->keyId[i])); - } - -#ifdef BUILD_SDL - mSDLInitBindingsGBA(inputMap); -#endif - switch (platform) { -#ifdef M_CORE_GBA - case PLATFORM_GBA: - mInputBindKey(inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT); - break; -#endif -#ifdef M_CORE_GB - case PLATFORM_GB: - mInputBindKey(inputMap, KEYBOARD, Qt::Key_X, GB_KEY_A); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Z, GB_KEY_B); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Return, GB_KEY_START); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Backspace, GB_KEY_SELECT); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Up, GB_KEY_UP); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Down, GB_KEY_DOWN); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Left, GB_KEY_LEFT); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Right, GB_KEY_RIGHT); - break; -#endif -#ifdef M_CORE_DS - case PLATFORM_DS: - mInputBindKey(inputMap, KEYBOARD, Qt::Key_X, DS_KEY_A); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Z, DS_KEY_B); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_A, DS_KEY_X); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_S, DS_KEY_Y); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Q, DS_KEY_L); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_W, DS_KEY_R); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Return, DS_KEY_START); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Backspace, DS_KEY_SELECT); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Up, DS_KEY_UP); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Down, DS_KEY_DOWN); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Left, DS_KEY_LEFT); - mInputBindKey(inputMap, KEYBOARD, Qt::Key_Right, DS_KEY_RIGHT); - break; -#endif - default: - break; - } -} - -void InputController::setPlatform(mPlatform platform) { -#ifdef BUILD_SDL - m_sdlPlayer.bindings = &m_inputMaps[platform]; -#endif - m_platform = platform; -} - -void InputController::setConfiguration(ConfigController* config) { - m_config = config; - setAllowOpposing(config->getOption("allowOpposingDirections").toInt()); - loadConfiguration(KEYBOARD); -#ifdef BUILD_SDL - mSDLEventsLoadConfig(&s_sdlEvents, config->input()); - if (!m_playerAttached) { - m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer); - } - loadConfiguration(SDL_BINDING_BUTTON); - loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); -#endif - restoreModel(); -} - -void InputController::loadConfiguration(uint32_t type) { - for (auto& inputMap : m_inputMaps) { - mInputMapLoad(&inputMap, type, m_config->input()); -#ifdef BUILD_SDL - if (m_playerAttached) { - mInputMap* bindings = m_sdlPlayer.bindings; - m_sdlPlayer.bindings = &inputMap; - mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input()); - m_sdlPlayer.bindings = bindings; - } -#endif - } -} - -void InputController::loadProfile(uint32_t type, const QString& profile) { - for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) { - bool loaded = mInputProfileLoad(&iter.value(), type, m_config->input(), profile.toUtf8().constData()); - if (!loaded) { - const InputProfile* ip = InputProfile::findProfile(iter.key(), profile); - if (ip) { - ip->apply(iter.key(), this); - } - } - } - recalibrateAxes(); - m_inputModel->loadProfile(PLATFORM_NONE, profile); // TODO - emit profileLoaded(profile); -} - -void InputController::saveConfiguration() { - saveConfiguration(KEYBOARD); -#ifdef BUILD_SDL - saveConfiguration(SDL_BINDING_BUTTON); - saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); - if (m_playerAttached) { - mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input()); - } - m_config->write(); -#endif -} - -void InputController::saveConfiguration(uint32_t type) { - for (auto& inputMap : m_inputMaps) { - if (!inputMap.info) { - continue; - } - mInputMapSave(&inputMap, type, m_config->input()); - } - m_config->write(); -} - -void InputController::saveProfile(uint32_t type, const QString& profile) { - for (auto& inputMap : m_inputMaps) { - mInputProfileSave(&inputMap, type, m_config->input(), profile.toUtf8().constData()); - } - m_config->write(); -} - -const char* InputController::profileForType(uint32_t type) { - UNUSED(type); -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { -#if SDL_VERSION_ATLEAST(2, 0, 0) - return SDL_JoystickName(m_sdlPlayer.joystick->joystick); -#else - return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick)); -#endif - } -#endif - return 0; -} - -QStringList InputController::connectedGamepads(uint32_t type) const { - UNUSED(type); - -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - QStringList pads; - for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { - const char* name; -#if SDL_VERSION_ATLEAST(2, 0, 0) - name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick); -#else - name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick)); -#endif - if (name) { - pads.append(QString(name)); - } else { - pads.append(QString()); - } - } - return pads; - } -#endif - - return QStringList(); -} - -int InputController::gamepad(uint32_t type) const { -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; - } -#endif - return 0; -} - -void InputController::setGamepad(uint32_t type, int index) { -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); - } -#endif -} - -void InputController::setPreferredGamepad(uint32_t type, const QString& device) { - if (!m_config) { - return; - } - mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData()); -} - -mRumble* InputController::rumble() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - if (m_playerAttached) { - return &m_sdlPlayer.rumble.d; - } -#endif -#endif - return nullptr; -} - -mRotationSource* InputController::rotationSource() { -#ifdef BUILD_SDL - if (m_playerAttached) { - return &m_sdlPlayer.rotation.d; - } -#endif - return nullptr; -} - -void InputController::registerTiltAxisX(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.axisX = axis; - } -#endif -} - -void InputController::registerTiltAxisY(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.axisY = axis; - } -#endif -} - -void InputController::registerGyroAxisX(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroX = axis; - } -#endif -} - -void InputController::registerGyroAxisY(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroY = axis; - } -#endif -} - -float InputController::gyroSensitivity() const { -#ifdef BUILD_SDL - if (m_playerAttached) { - return m_sdlPlayer.rotation.gyroSensitivity; - } -#endif - return 0; -} - -void InputController::setGyroSensitivity(float sensitivity) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroSensitivity = sensitivity; - } -#endif -} - -void InputController::updateJoysticks() { -#ifdef BUILD_SDL - QString profile = profileForType(SDL_BINDING_BUTTON); - mSDLUpdateJoysticks(&s_sdlEvents, m_config->input()); - QString newProfile = profileForType(SDL_BINDING_BUTTON); - if (profile != newProfile) { - loadProfile(SDL_BINDING_BUTTON, newProfile); - } -#endif -} - -const mInputMap* InputController::map() { - return &m_inputMaps[m_platform]; -} - -int InputController::pollEvents() { - int activeButtons = 0; -#ifdef BUILD_SDL - if (m_playerAttached && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numButtons = SDL_JoystickNumButtons(joystick); - int i; - for (i = 0; i < numButtons; ++i) { - GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i)); - if (key == GBA_KEY_NONE) { - continue; - } - if (hasPendingEvent(key)) { - continue; - } - if (SDL_JoystickGetButton(joystick, i)) { - activeButtons |= 1 << key; - } - } - int numHats = SDL_JoystickNumHats(joystick); - for (i = 0; i < numHats; ++i) { - int hat = SDL_JoystickGetHat(joystick, i); - activeButtons |= mInputMapHat(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, hat); - } - - int numAxes = SDL_JoystickNumAxes(joystick); - for (i = 0; i < numAxes; ++i) { - int value = SDL_JoystickGetAxis(joystick, i); - - enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, value)); - if (key != GBA_KEY_NONE) { - activeButtons |= 1 << key; - } - } - } -#endif - return activeButtons; -} - -QSet<int> InputController::activeGamepadButtons(int type) { - QSet<int> activeButtons; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numButtons = SDL_JoystickNumButtons(joystick); - int i; - for (i = 0; i < numButtons; ++i) { - if (SDL_JoystickGetButton(joystick, i)) { - activeButtons.insert(i); - } - } - } -#endif - return activeButtons; -} - -void InputController::recalibrateAxes() { -#ifdef BUILD_SDL - if (m_playerAttached && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numAxes = SDL_JoystickNumAxes(joystick); - if (numAxes < 1) { - return; - } - m_deadzones.resize(numAxes); - int i; - for (i = 0; i < numAxes; ++i) { - m_deadzones[i] = SDL_JoystickGetAxis(joystick, i); - } - } -#endif -} - -QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) { - QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numAxes = SDL_JoystickNumAxes(joystick); - if (numAxes < 1) { - return activeAxes; - } - m_deadzones.resize(numAxes); - int i; - for (i = 0; i < numAxes; ++i) { - int32_t axis = SDL_JoystickGetAxis(joystick, i); - axis -= m_deadzones[i]; - if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) { - activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE)); - } - } - } -#endif - return activeAxes; -} - -void InputController::bindKey(mPlatform platform, uint32_t type, int key, int coreKey) { - if (m_inputMaps.find(platform) == m_inputMaps.end() || coreKey >= m_inputMaps[platform].info->nKeys) { - return; - } - QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]); - bool signalsBlocked = m_inputModel->blockSignals(true); - if (type != KEYBOARD) { - m_inputModel->updateButton(index, key); - } else { - m_inputModel->updateKey(index, key); - } - m_inputModel->blockSignals(signalsBlocked); - mInputBindKey(&m_inputMaps[platform], type, key, coreKey); -} - -void InputController::bindAxis(mPlatform platform, uint32_t type, int axis, GamepadAxisEvent::Direction direction, int key) { - if (m_inputMaps.find(platform) == m_inputMaps.end() || key >= m_inputMaps[platform].info->nKeys) { - return; - } - QModelIndex index = m_inputModel->index(key, 0, m_inputMenuIndices[platform]); - bool signalsBlocked = m_inputModel->blockSignals(true); - m_inputModel->updateAxis(index, axis, direction); - m_inputModel->blockSignals(signalsBlocked); - - const mInputAxis* old = mInputQueryAxis(&m_inputMaps[platform], type, axis); - mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD }; - if (old) { - description = *old; - } - int deadzone = 0; - if (axis > 0 && m_deadzones.size() > axis) { - deadzone = m_deadzones[axis]; - } - switch (direction) { - case GamepadAxisEvent::NEGATIVE: - description.lowDirection = key; - - description.deadLow = deadzone - AXIS_THRESHOLD; - break; - case GamepadAxisEvent::POSITIVE: - description.highDirection = key; - description.deadHigh = deadzone + AXIS_THRESHOLD; - break; - default: - return; - } - mInputBindAxis(&m_inputMaps[platform], type, axis, &description); -} - -QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) { - QSet<QPair<int, GamepadHatEvent::Direction>> activeHats; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numHats = SDL_JoystickNumHats(joystick); - if (numHats < 1) { - return activeHats; - } - - int i; - for (i = 0; i < numHats; ++i) { - int hat = SDL_JoystickGetHat(joystick, i); - if (hat & GamepadHatEvent::UP) { - activeHats.insert(qMakePair(i, GamepadHatEvent::UP)); - } - if (hat & GamepadHatEvent::RIGHT) { - activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT)); - } - if (hat & GamepadHatEvent::DOWN) { - activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN)); - } - if (hat & GamepadHatEvent::LEFT) { - activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT)); - } - } - } -#endif - return activeHats; -} - -void InputController::bindHat(mPlatform platform, uint32_t type, int hat, GamepadHatEvent::Direction direction, int coreKey) { - if (m_inputMaps.find(platform) == m_inputMaps.end() || coreKey >= m_inputMaps[platform].info->nKeys) { - return; - } - QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]); - //m_inputModel->updateHat(index, hat, direction); - - mInputHatBindings bindings{ -1, -1, -1, -1 }; - mInputQueryHat(&m_inputMaps[platform], type, hat, &bindings); - switch (direction) { - case GamepadHatEvent::UP: - bindings.up = coreKey; - break; - case GamepadHatEvent::RIGHT: - bindings.right = coreKey; - break; - case GamepadHatEvent::DOWN: - bindings.down = coreKey; - break; - case GamepadHatEvent::LEFT: - bindings.left = coreKey; - break; - default: - return; - } - mInputBindHat(&m_inputMaps[platform], type, hat, &bindings); -} - -void InputController::testGamepad(int type) { - auto activeAxes = activeGamepadAxes(type); - auto oldAxes = m_activeAxes; - m_activeAxes = activeAxes; - - auto activeButtons = activeGamepadButtons(type); - auto oldButtons = m_activeButtons; - m_activeButtons = activeButtons; - - auto activeHats = activeGamepadHats(type); - auto oldHats = m_activeHats; - m_activeHats = activeHats; - - if (!QApplication::focusWidget()) { - return; - } - - activeAxes.subtract(oldAxes); - oldAxes.subtract(m_activeAxes); - - for (auto& axis : m_activeAxes) { - bool newlyAboveThreshold = activeAxes.contains(axis); - if (newlyAboveThreshold) { - GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this); - postPendingEvent(event->gbaKey()); - sendGamepadEvent(event); - if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); - } - } - } - for (auto axis : oldAxes) { - GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this); - clearPendingEvent(event->gbaKey()); - sendGamepadEvent(event); - } - - if (!QApplication::focusWidget()) { - return; - } - - activeButtons.subtract(oldButtons); - oldButtons.subtract(m_activeButtons); - - for (int button : activeButtons) { - GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this); - postPendingEvent(event->gbaKey()); - sendGamepadEvent(event); - if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); - } - } - for (int button : oldButtons) { - GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this); - clearPendingEvent(event->gbaKey()); - sendGamepadEvent(event); - } - - activeHats.subtract(oldHats); - oldHats.subtract(m_activeHats); - - for (auto& hat : activeHats) { - GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this); - postPendingEvent(event->gbaKey()); - sendGamepadEvent(event); - if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); - } - } - for (auto& hat : oldHats) { - GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this); - clearPendingEvent(event->gbaKey()); - sendGamepadEvent(event); - } -} - -void InputController::sendGamepadEvent(QEvent* event) { - QWidget* focusWidget = nullptr; - if (m_focusParent) { - focusWidget = m_focusParent->focusWidget(); - if (!focusWidget) { - focusWidget = m_focusParent; - } - } else { - focusWidget = QApplication::focusWidget(); - } - QApplication::sendEvent(focusWidget, event); -} - -void InputController::postPendingEvent(int key) { - m_pendingEvents.insert(key); -} - -void InputController::clearPendingEvent(int key) { - m_pendingEvents.remove(key); -} - -bool InputController::hasPendingEvent(int key) const { - return m_pendingEvents.contains(key); -} - -void InputController::suspendScreensaver() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLSuspendScreensaver(&s_sdlEvents); -#endif -#endif -} - -void InputController::resumeScreensaver() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLResumeScreensaver(&s_sdlEvents); -#endif -#endif -} - -void InputController::setScreensaverSuspendable(bool suspendable) { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable); -#endif -#endif -} - -void InputController::stealFocus(QWidget* focus) { - m_focusParent = focus; -} - -void InputController::releaseFocus(QWidget* focus) { - if (focus == m_focusParent) { - m_focusParent = m_topLevel; - } -} - -void InputController::setupCallback(GameController* controller) { - m_inputModel->setKeyCallback([this, controller](QMenu* menu, int key, bool down) { - if (menu->parent() == m_autofireMenu.get()) { - controller->setAutofire(key, down); - } else { - if (down) { - controller->keyPressed(key); - } else { - controller->keyReleased(key); - } - } - }); -} - -void InputController::bindKey(const QModelIndex& index, int key) { - int coreKey = m_inputModel->keyAt(index); - if (coreKey < 0) { - return; - } - mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE); - bindKey(platform, KEYBOARD, key, coreKey); -} - -#ifdef BUILD_SDL -void InputController::bindButton(const QModelIndex& index, int key) { - int coreKey = m_inputModel->keyAt(index); - if (coreKey < 0) { - return; - } - mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE); - bindKey(platform, SDL_BINDING_BUTTON, key, coreKey); -} - -void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) { - int coreKey = m_inputModel->keyAt(index); - if (coreKey < 0) { - return; - } - mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE); - bindAxis(platform, SDL_BINDING_BUTTON, axis, direction, coreKey); -} - -void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction direction) { - int coreKey = m_inputModel->keyAt(index); - if (coreKey < 0) { - return; - } - mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE); - bindHat(platform, SDL_BINDING_BUTTON, hat, direction, coreKey); -} -#else -void InputController::bindButton(const QModelIndex& index, int key) {} -void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction) {} -void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction) {} -#endif - -bool InputController::eventFilter(QObject*, QEvent* event) { - if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { - QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); - int key = keyEvent->key(); - if (!InputModel::isModifierKey(key)) { - key |= (keyEvent->modifiers() & ~Qt::KeypadModifier); - } else { - key = InputModel::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier)); - } - - if (keyEvent->isAutoRepeat()) { - event->accept(); - return true; - } - - if (m_inputModel->triggerKey(key, event->type() == QEvent::KeyPress, m_platform)) { - event->accept(); - return true; - } - } - - - if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) { - GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event); - if (m_inputModel->triggerButton(gbe->value(), event->type() == GamepadButtonEvent::Down())) { - event->accept(); - return true; - } - } - if (event->type() == GamepadAxisEvent::Type()) { - GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event); - if (m_inputModel->triggerAxis(gae->axis(), gae->direction(), gae->isNew())) { - event->accept(); - return true; - } - } - return false; -} - -void InputController::restoreModel() { - bool signalsBlocked = m_inputModel->blockSignals(true); - for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) { - mPlatform platform = iter.key(); - QModelIndex parent = m_inputMenuIndices[platform]; - int nKeys = iter->info->nKeys; - for (int i = 0; i < nKeys; ++i) { - int key = mInputQueryBinding(&iter.value(), KEYBOARD, i); - if (key >= 0) { - m_inputModel->updateKey(m_inputModel->index(i, 0, parent), key); - } else { - m_inputModel->clearKey(m_inputModel->index(i, 0, parent)); - } -#ifdef BUILD_SDL - key = mInputQueryBinding(&iter.value(), SDL_BINDING_BUTTON, i); - if (key >= 0) { - m_inputModel->updateButton(m_inputModel->index(i, 0, parent), key); - } else { - m_inputModel->clearButton(m_inputModel->index(i, 0, parent)); - } -#endif - } -#ifdef BUILD_SDL - struct Context { - InputModel* model; - QModelIndex parent; - } context{ m_inputModel, parent }; - mInputEnumerateAxes(&iter.value(), SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) { - Context* context = static_cast<Context*>(user); - if (description->highDirection >= 0) { - context->model->updateAxis(context->model->index(description->highDirection, 0, context->parent), axis, GamepadAxisEvent::POSITIVE); - } - if (description->lowDirection >= 0) { - context->model->updateAxis(context->model->index(description->lowDirection, 0, context->parent), axis, GamepadAxisEvent::NEGATIVE); - } - }, &context); -#endif - } - m_inputModel->blockSignals(signalsBlocked); -}
@@ -8,6 +8,7 @@ #define QGBA_INPUT_CONTROLLER_H
#include "GamepadAxisEvent.h" #include "GamepadHatEvent.h" +#include "InputIndex.h" #include <memory>@@ -33,7 +34,7 @@ namespace QGBA {
class ConfigController; class GameController; -class InputModel; +class InputItem; class InputController : public QObject { Q_OBJECT@@ -41,11 +42,17 @@
public: static const uint32_t KEYBOARD = 0x51545F4B; - InputController(InputModel* model, int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr); + InputController(int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr); ~InputController(); - void addPlatform(mPlatform, const QString& visibleName, const mInputPlatformInfo*); + InputIndex* inputIndex() { return &m_inputIndex; } + InputIndex* keyIndex() { return &m_keyIndex; } + void rebuildIndex(const InputIndex* = nullptr); + void rebuildKeyIndex(const InputIndex* = nullptr); + + void addPlatform(mPlatform, const mInputPlatformInfo*); void setPlatform(mPlatform); + void addKey(const QString& name); void setConfiguration(ConfigController* config); void saveConfiguration();@@ -68,9 +75,9 @@ QSet<QPair<int, GamepadAxisEvent::Direction>> activeGamepadAxes(int type);
QSet<QPair<int, GamepadHatEvent::Direction>> activeGamepadHats(int type); void recalibrateAxes(); - void bindKey(mPlatform platform, uint32_t type, int key, int); - void bindAxis(mPlatform platform, uint32_t type, int axis, GamepadAxisEvent::Direction, int); - void bindHat(mPlatform platform, uint32_t type, int hat, GamepadHatEvent::Direction, int); + void bindKey(uint32_t type, int key, const QString&); + void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, const QString&); + void bindHat(uint32_t type, int hat, GamepadHatEvent::Direction, const QString&); QStringList connectedGamepads(uint32_t type) const; int gamepad(uint32_t type) const;@@ -91,10 +98,11 @@
mRumble* rumble(); mRotationSource* rotationSource(); - void setupCallback(GameController* controller); - signals: void profileLoaded(const QString& profile); + void keyPressed(int); + void keyReleased(int); + void keyAutofire(int, bool enabled); public slots: void testGamepad(int type);@@ -105,12 +113,6 @@ void suspendScreensaver();
void resumeScreensaver(); void setScreensaverSuspendable(bool); -private slots: - void bindKey(const QModelIndex&, int key); - void bindButton(const QModelIndex&, int key); - void bindAxis(const QModelIndex&, int axis, GamepadAxisEvent::Direction); - void bindHat(const QModelIndex&, int hat, GamepadHatEvent::Direction); - protected: bool eventFilter(QObject*, QEvent*) override;@@ -120,15 +122,24 @@ void clearPendingEvent(int key);
bool hasPendingEvent(int key) const; void sendGamepadEvent(QEvent*); void restoreModel(); + void rebindKey(const QString& key); + + InputItem* itemForKey(const QString& key); + int keyId(const QString& key); - InputModel* m_inputModel; - mPlatform m_platform; - QMap<mPlatform, mInputMap> m_inputMaps; + InputIndex m_inputIndex; + InputIndex m_keyIndex; + mInputMap m_inputMap; ConfigController* m_config = nullptr; int m_playerId; bool m_allowOpposing = false; QWidget* m_topLevel; QWidget* m_focusParent; + QMap<mPlatform, const mInputPlatformInfo*> m_keyInfo; + const mInputPlatformInfo* m_activeKeyInfo = nullptr; + + std::unique_ptr<QMenu> m_bindings; + std::unique_ptr<QMenu> m_autofire; #ifdef BUILD_SDL static int s_sdlInited;@@ -138,10 +149,6 @@ bool m_playerAttached = false;
#endif QVector<int> m_deadzones; - - std::unique_ptr<QMenu> m_inputMenu; - std::unique_ptr<QMenu> m_autofireMenu; - QMap<mPlatform, QModelIndex> m_inputMenuIndices; QSet<int> m_activeButtons; QSet<QPair<int, GamepadAxisEvent::Direction>> m_activeAxes;
@@ -1,104 +0,0 @@
-/* Copyright (c) 2013-2017 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "InputItem.h" - -#include <QMenu> - -using namespace QGBA; - -InputItem::InputItem(QAction* action, const QString& name, InputItem* parent) - : m_action(action) - , m_shortcut(action->shortcut().isEmpty() ? 0 : action->shortcut()[0]) - , m_keys(-1) - , m_menu(nullptr) - , m_name(name) - , m_button(-1) - , m_axis(-1) - , m_direction(GamepadAxisEvent::NEUTRAL) - , m_platform(PLATFORM_NONE) - , m_parent(parent) -{ - m_visibleName = action->text() - .remove(QRegExp("&(?!&)")) - .remove("..."); -} - -InputItem::InputItem(InputItem::Functions functions, int shortcut, const QString& visibleName, const QString& name, InputItem* parent) - : m_action(nullptr) - , m_shortcut(shortcut) - , m_functions(functions) - , m_keys(-1) - , m_menu(nullptr) - , m_name(name) - , m_visibleName(visibleName) - , m_button(-1) - , m_axis(-1) - , m_direction(GamepadAxisEvent::NEUTRAL) - , m_platform(PLATFORM_NONE) - , m_parent(parent) -{ -} - -InputItem::InputItem(mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name, InputItem* parent) - : m_action(nullptr) - , m_shortcut(shortcut) - , m_keys(key) - , m_menu(nullptr) - , m_name(name) - , m_visibleName(visibleName) - , m_button(-1) - , m_axis(-1) - , m_direction(GamepadAxisEvent::NEUTRAL) - , m_platform(platform) - , m_parent(parent) -{ -} - -InputItem::InputItem(QMenu* menu, InputItem* parent) - : m_action(nullptr) - , m_shortcut(0) - , m_menu(menu) - , m_button(-1) - , m_axis(-1) - , m_direction(GamepadAxisEvent::NEUTRAL) - , m_parent(parent) -{ - if (menu) { - m_visibleName = menu->title() - .remove(QRegExp("&(?!&)")) - .remove("..."); - } -} - -void InputItem::addAction(QAction* action, const QString& name) { - m_items.append(InputItem(action, name, this)); -} - -void InputItem::addFunctions(InputItem::Functions functions, - int shortcut, const QString& visibleName, - const QString& name) { - m_items.append(InputItem(functions, shortcut, visibleName, name, this)); -} - -void InputItem::addKey(mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name) { - m_items.append(InputItem(platform, key, shortcut, visibleName, name, this)); -} - -void InputItem::addSubmenu(QMenu* menu) { - m_items.append(InputItem(menu, this)); -} - -void InputItem::setShortcut(int shortcut) { - m_shortcut = shortcut; - if (m_action) { - m_action->setShortcut(QKeySequence(shortcut)); - } -} - -void InputItem::setAxis(int axis, GamepadAxisEvent::Direction direction) { - m_axis = axis; - m_direction = direction; -}
@@ -1,78 +0,0 @@
-/* Copyright (c) 2013-2017 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef QGBA_INPUT_ITEM -#define QGBA_INPUT_ITEM - -#include "GamepadAxisEvent.h" -#include "GamepadHatEvent.h" - -#include <functional> - -#include <QAction> - -#include <mgba/core/core.h> - -namespace QGBA { - -class InputItem { -public: - typedef QPair<std::function<void ()>, std::function<void ()>> Functions; - - InputItem(QAction* action, const QString& name, InputItem* parent = nullptr); - InputItem(Functions functions, int shortcut, const QString& visibleName, - const QString& name, InputItem* parent = nullptr); - InputItem(mPlatform platform, int key, int shortcut, const QString& name, const QString& visibleName, InputItem* parent = nullptr); - InputItem(QMenu* action, InputItem* parent = nullptr); - - QAction* action() { return m_action; } - const QAction* action() const { return m_action; } - int shortcut() const { return m_shortcut; } - mPlatform platform() const { return m_platform; } - Functions functions() const { return m_functions; } - int key() const { return m_keys; } - QMenu* menu() { return m_menu; } - const QMenu* menu() const { return m_menu; } - const QString& visibleName() const { return m_visibleName; } - const QString& name() const { return m_name; } - QList<InputItem>& items() { return m_items; } - const QList<InputItem>& items() const { return m_items; } - InputItem* parent() { return m_parent; } - const InputItem* parent() const { return m_parent; } - void addAction(QAction* action, const QString& name); - void addFunctions(Functions functions, int shortcut, const QString& visibleName, - const QString& name); - void addKey(mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name); - void addSubmenu(QMenu* menu); - int button() const { return m_button; } - void setShortcut(int sequence); - void setButton(int button) { m_button = button; } - int axis() const { return m_axis; } - GamepadAxisEvent::Direction direction() const { return m_direction; } - void setAxis(int axis, GamepadAxisEvent::Direction direction); - - bool operator==(const InputItem& other) const { - return m_menu == other.m_menu && m_action == other.m_action; - } - -private: - QAction* m_action; - int m_shortcut; - QMenu* m_menu; - Functions m_functions; - QString m_name; - QString m_visibleName; - int m_button; - int m_axis; - int m_keys; - GamepadAxisEvent::Direction m_direction; - mPlatform m_platform; - QList<InputItem> m_items; - InputItem* m_parent; -}; - -} - -#endif
@@ -1,577 +0,0 @@
-/* Copyright (c) 2013-2015 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 "InputModel.h" - -#include "ConfigController.h" -#include "GamepadButtonEvent.h" -#include "InputProfile.h" - -#include <QAction> -#include <QKeyEvent> -#include <QMenu> - -using namespace QGBA; - -InputModel::InputModel(QObject* parent) - : QAbstractItemModel(parent) - , m_rootMenu(nullptr) -{ -} - -void InputModel::setConfigController(ConfigController* controller) { - m_config = controller; -} - -QVariant InputModel::data(const QModelIndex& index, int role) const { - if (role != Qt::DisplayRole || !index.isValid()) { - return QVariant(); - } - int row = index.row(); - const InputItem* item = static_cast<const InputItem*>(index.internalPointer()); - switch (index.column()) { - case 0: - return item->visibleName(); - case 1: - return QKeySequence(item->shortcut()).toString(QKeySequence::NativeText); - case 2: - if (item->button() >= 0) { - return item->button(); - } - if (item->axis() >= 0) { - char d = '\0'; - if (item->direction() == GamepadAxisEvent::POSITIVE) { - d = '+'; - } - if (item->direction() == GamepadAxisEvent::NEGATIVE) { - d = '-'; - } - return QString("%1%2").arg(d).arg(item->axis()); - } - break; - } - return QVariant(); -} - -QVariant InputModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole) { - return QAbstractItemModel::headerData(section, orientation, role); - } - if (orientation == Qt::Horizontal) { - switch (section) { - case 0: - return tr("Action"); - case 1: - return tr("Keyboard"); - case 2: - return tr("Gamepad"); - } - } - return section; -} - -QModelIndex InputModel::index(int row, int column, const QModelIndex& parent) const { - const InputItem* pmenu = &m_rootMenu; - if (parent.isValid()) { - pmenu = static_cast<InputItem*>(parent.internalPointer()); - } - return createIndex(row, column, const_cast<InputItem*>(&pmenu->items()[row])); -} - -QModelIndex InputModel::parent(const QModelIndex& index) const { - if (!index.isValid() || !index.internalPointer()) { - return QModelIndex(); - } - InputItem* item = static_cast<InputItem*>(index.internalPointer()); - return this->index(item->parent()); -} - -QModelIndex InputModel::index(InputItem* item) const { - if (!item || !item->parent()) { - return QModelIndex(); - } - return createIndex(item->parent()->items().indexOf(*item), 0, item); -} - -int InputModel::columnCount(const QModelIndex& index) const { - return 3; -} - -int InputModel::rowCount(const QModelIndex& index) const { - if (!index.isValid()) { - return m_rootMenu.items().count(); - } - const InputItem* item = static_cast<const InputItem*>(index.internalPointer()); - return item->items().count(); -} - -InputItem* InputModel::add(QMenu* menu, std::function<void (InputItem*)> callback) { - InputItem* smenu = m_menuMap[menu]; - if (!smenu) { - return nullptr; - } - QModelIndex parent = index(smenu); - beginInsertRows(parent, smenu->items().count(), smenu->items().count()); - callback(smenu); - endInsertRows(); - InputItem* item = &smenu->items().last(); - emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), - createIndex(smenu->items().count() - 1, 2, item)); - return item; -} - -void InputModel::addAction(QMenu* menu, QAction* action, const QString& name) { - InputItem* item = add(menu, [&](InputItem* smenu) { - smenu->addAction(action, name); - }); - if (!item) { - return; - } - if (m_config) { - loadShortcuts(item); - } -} - -void InputModel::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, - int shortcut, const QString& visibleName, const QString& name) { - InputItem* item = add(menu, [&](InputItem* smenu) { - smenu->addFunctions(qMakePair(press, release), shortcut, visibleName, name); - }); - if (!item) { - return; - } - - bool loadedShortcut = false; - if (m_config) { - loadedShortcut = loadShortcuts(item); - } - if (!loadedShortcut && !m_heldKeys.contains(shortcut)) { - m_heldKeys[shortcut] = item; - } -} - -void InputModel::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, - const QKeySequence& shortcut, const QString& visibleName, const QString& name) { - addFunctions(menu, press, release, shortcut[0], visibleName, name); -} - -void InputModel::addKey(QMenu* menu, mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name) { - InputItem* item = add(menu, [&](InputItem* smenu) { - smenu->addKey(platform, key, shortcut, visibleName, name); - }); - if (!item) { - return; - } - bool loadedShortcut = false; - if (m_config) { - loadedShortcut = loadShortcuts(item); - } - if (!loadedShortcut && !m_keys.contains(qMakePair(platform, shortcut))) { - m_keys[qMakePair(platform, shortcut)] = item; - } -} - -QModelIndex InputModel::addMenu(QMenu* menu, QMenu* parentMenu) { - InputItem* smenu = m_menuMap[parentMenu]; - if (!smenu) { - smenu = &m_rootMenu; - } - QModelIndex parent = index(smenu); - beginInsertRows(parent, smenu->items().count(), smenu->items().count()); - smenu->addSubmenu(menu); - endInsertRows(); - InputItem* item = &smenu->items().last(); - emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), - createIndex(smenu->items().count() - 1, 2, item)); - m_menuMap[menu] = item; - return index(item); -} - -InputItem* InputModel::itemAt(const QModelIndex& index) { - if (!index.isValid()) { - return nullptr; - } - if (index.internalPointer()) { - return static_cast<InputItem*>(index.internalPointer()); - } - if (!index.parent().isValid()) { - return nullptr; - } - InputItem* pmenu = static_cast<InputItem*>(index.parent().internalPointer()); - return &pmenu->items()[index.row()]; -} - -const InputItem* InputModel::itemAt(const QModelIndex& index) const { - if (!index.isValid()) { - return nullptr; - } - if (index.internalPointer()) { - return static_cast<InputItem*>(index.internalPointer()); - } - if (!index.parent().isValid()) { - return nullptr; - } - InputItem* pmenu = static_cast<InputItem*>(index.parent().internalPointer()); - return &pmenu->items()[index.row()]; -} - -int InputModel::shortcutAt(const QModelIndex& index) const { - const InputItem* item = itemAt(index); - if (!item) { - return 0; - } - return item->shortcut(); -} - -int InputModel::keyAt(const QModelIndex& index) const { - const InputItem* item = itemAt(index); - if (!item) { - return -1; - } - return item->key(); -} - -bool InputModel::isMenuAt(const QModelIndex& index) const { - const InputItem* item = itemAt(index); - if (!item) { - return false; - } - return item->menu(); -} - -void InputModel::updateKey(const QModelIndex& index, int keySequence) { - if (!index.isValid()) { - return; - } - const QModelIndex& parent = index.parent(); - if (!parent.isValid()) { - return; - } - InputItem* item = itemAt(index); - updateKey(item, keySequence); - if (m_config) { - m_config->setQtOption(item->name(), QKeySequence(keySequence).toString(), KEY_SECTION); - } -} - -void InputModel::updateKey(InputItem* item, int keySequence) { - int oldShortcut = item->shortcut(); - if (item->functions().first) { - if (oldShortcut > 0) { - m_heldKeys.take(oldShortcut); - } - if (keySequence > 0) { - m_heldKeys[keySequence] = item; - } - } - - if (item->key() >= 0) { - if (oldShortcut > 0) { - m_keys.take(qMakePair(item->platform(), oldShortcut)); - } - if (keySequence > 0) { - m_keys[qMakePair(item->platform(), keySequence)] = item; - } - } - - item->setShortcut(keySequence); - - emit dataChanged(createIndex(index(item).row(), 0, item), - createIndex(index(item).row(), 2, item)); - - emit keyRebound(index(item), keySequence); -} - -void InputModel::updateButton(const QModelIndex& index, int button) { - if (!index.isValid()) { - return; - } - const QModelIndex& parent = index.parent(); - if (!parent.isValid()) { - return; - } - InputItem* item = itemAt(index); - int oldButton = item->button(); - if (oldButton >= 0) { - m_buttons.take(oldButton); - } - updateAxis(index, -1, GamepadAxisEvent::NEUTRAL); - item->setButton(button); - if (button >= 0) { - m_buttons[button] = item; - } - if (m_config) { - m_config->setQtOption(item->name(), button, BUTTON_SECTION); - if (!m_profileName.isNull()) { - m_config->setQtOption(item->name(), button, BUTTON_PROFILE_SECTION + m_profileName); - } - } - emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), - createIndex(index.row(), 2, index.internalPointer())); - - emit buttonRebound(index, button); -} - -void InputModel::updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) { - if (!index.isValid()) { - return; - } - const QModelIndex& parent = index.parent(); - if (!parent.isValid()) { - return; - } - InputItem* item = itemAt(index); - int oldAxis = item->axis(); - GamepadAxisEvent::Direction oldDirection = item->direction(); - if (oldAxis >= 0) { - m_axes.take(qMakePair(oldAxis, oldDirection)); - } - if (axis >= 0 && direction != GamepadAxisEvent::NEUTRAL) { - updateButton(index, -1); - m_axes[qMakePair(axis, direction)] = item; - } - item->setAxis(axis, direction); - if (m_config) { - char d = '\0'; - if (direction == GamepadAxisEvent::POSITIVE) { - d = '+'; - } - if (direction == GamepadAxisEvent::NEGATIVE) { - d = '-'; - } - m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_SECTION); - if (!m_profileName.isNull()) { - m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_PROFILE_SECTION + m_profileName); - } - } - emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), - createIndex(index.row(), 2, index.internalPointer())); - - emit axisRebound(index, axis, direction); -} - -void InputModel::clearKey(const QModelIndex& index) { - updateKey(index, 0); -} - -void InputModel::clearButton(const QModelIndex& index) { - updateButton(index, -1); -} - -bool InputModel::triggerKey(int keySequence, bool down, mPlatform platform) { - auto key = m_keys.find(qMakePair(platform, keySequence)); - if (key != m_keys.end()) { - m_keyCallback(key.value()->parent()->menu(), key.value()->key(), down); - return true; - } - auto heldKey = m_heldKeys.find(keySequence); - if (heldKey != m_heldKeys.end()) { - auto pair = heldKey.value()->functions(); - if (down) { - if (pair.first) { - pair.first(); - } - } else { - if (pair.second) { - pair.second(); - } - } - return true; - } - return false; -} - -bool InputModel::triggerButton(int button, bool down) { - auto item = m_buttons.find(button); - if (item == m_buttons.end()) { - return false; - } - if (down) { - QAction* action = item.value()->action(); - if (action && action->isEnabled()) { - action->trigger(); - } - auto pair = item.value()->functions(); - if (pair.first) { - pair.first(); - } - } else { - auto pair = item.value()->functions(); - if (pair.second) { - pair.second(); - } - } - return true; -} - -bool InputModel::triggerAxis(int axis, GamepadAxisEvent::Direction direction, bool isNew) { - auto item = m_axes.find(qMakePair(axis, direction)); - if (item == m_axes.end()) { - return false; - } - if (isNew) { - QAction* action = item.value()->action(); - if (action && action->isEnabled()) { - action->trigger(); - } - } - auto pair = item.value()->functions(); - if (isNew) { - if (pair.first) { - pair.first(); - } - } else { - if (pair.second) { - pair.second(); - } - } - return true; -} - -bool InputModel::loadShortcuts(InputItem* item) { - if (item->name().isNull()) { - return false; - } - loadGamepadShortcuts(item); - QVariant shortcut = m_config->getQtOption(item->name(), KEY_SECTION); - if (!shortcut.isNull()) { - if (shortcut.toString().endsWith("+")) { - updateKey(item, toModifierShortcut(shortcut.toString())); - } else { - updateKey(item, QKeySequence(shortcut.toString())[0]); - } - return true; - } - return false; -} - -void InputModel::loadGamepadShortcuts(InputItem* item) { - if (item->name().isNull()) { - return; - } - QVariant button = m_config->getQtOption(item->name(), !m_profileName.isNull() ? BUTTON_PROFILE_SECTION + m_profileName : BUTTON_SECTION); - int oldButton = item->button(); - if (oldButton >= 0) { - m_buttons.take(oldButton); - item->setButton(-1); - } - if (button.isNull() && m_profile) { - int buttonInt; - if (m_profile->lookupShortcutButton(item->name(), &buttonInt)) { - button = buttonInt; - } - } - if (!button.isNull()) { - item->setButton(button.toInt()); - m_buttons[button.toInt()] = item; - } - - QVariant axis = m_config->getQtOption(item->name(), !m_profileName.isNull() ? AXIS_PROFILE_SECTION + m_profileName : AXIS_SECTION); - int oldAxis = item->axis(); - GamepadAxisEvent::Direction oldDirection = item->direction(); - if (oldAxis >= 0) { - m_axes.take(qMakePair(oldAxis, oldDirection)); - item->setAxis(-1, GamepadAxisEvent::NEUTRAL); - } - if (axis.isNull() && m_profile) { - int axisInt; - GamepadAxisEvent::Direction direction; - if (m_profile->lookupShortcutAxis(item->name(), &axisInt, &direction)) { - axis = QLatin1String(direction == GamepadAxisEvent::Direction::NEGATIVE ? "-" : "+") + QString::number(axisInt); - } - } - if (!axis.isNull()) { - QString axisDesc = axis.toString(); - if (axisDesc.size() >= 2) { - GamepadAxisEvent::Direction direction = GamepadAxisEvent::NEUTRAL; - if (axisDesc[0] == '-') { - direction = GamepadAxisEvent::NEGATIVE; - } - if (axisDesc[0] == '+') { - direction = GamepadAxisEvent::POSITIVE; - } - bool ok; - int axis = axisDesc.mid(1).toInt(&ok); - if (ok) { - item->setAxis(axis, direction); - m_axes[qMakePair(axis, direction)] = item; - } - } - } -} - -void InputModel::loadProfile(mPlatform platform, const QString& profile) { - m_profileName = profile; - m_profile = InputProfile::findProfile(platform, profile); - onSubitems(&m_rootMenu, [this](InputItem* item) { - loadGamepadShortcuts(item); - }); -} - -void InputModel::onSubitems(InputItem* item, std::function<void(InputItem*)> func) { - for (InputItem& subitem : item->items()) { - func(&subitem); - onSubitems(&subitem, func); - } -} - -int InputModel::toModifierShortcut(const QString& shortcut) { - // Qt doesn't seem to work with raw modifier shortcuts! - QStringList modifiers = shortcut.split('+'); - int value = 0; - for (const auto& mod : modifiers) { - if (mod == QLatin1String("Shift")) { - value |= Qt::ShiftModifier; - continue; - } - if (mod == QLatin1String("Ctrl")) { - value |= Qt::ControlModifier; - continue; - } - if (mod == QLatin1String("Alt")) { - value |= Qt::AltModifier; - continue; - } - if (mod == QLatin1String("Meta")) { - value |= Qt::MetaModifier; - continue; - } - } - return value; -} - -bool InputModel::isModifierKey(int key) { - switch (key) { - case Qt::Key_Shift: - case Qt::Key_Control: - case Qt::Key_Alt: - case Qt::Key_Meta: - return true; - default: - return false; - } -} - -int InputModel::toModifierKey(int key) { - int modifiers = key & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier); - key ^= modifiers; - switch (key) { - case Qt::Key_Shift: - modifiers |= Qt::ShiftModifier; - break; - case Qt::Key_Control: - modifiers |= Qt::ControlModifier; - break; - case Qt::Key_Alt: - modifiers |= Qt::AltModifier; - break; - case Qt::Key_Meta: - modifiers |= Qt::MetaModifier; - break; - default: - break; - } - return modifiers; -}
@@ -1,117 +0,0 @@
-/* Copyright (c) 2013-2017 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef QGBA_INPUT_MODEL -#define QGBA_INPUT_MODEL - -#include <mgba/core/core.h> - -#include "GamepadAxisEvent.h" -#include "InputItem.h" - -#include <QAbstractItemModel> - -#include <functional> - -class QAction; -class QKeyEvent; -class QMenu; -class QString; - -namespace QGBA { - -class ConfigController; -class InputProfile; - -class InputModel : public QAbstractItemModel { -Q_OBJECT - -private: - constexpr static const char* const KEY_SECTION = "shortcutKey"; - constexpr static const char* const BUTTON_SECTION = "shortcutButton"; - constexpr static const char* const AXIS_SECTION = "shortcutAxis"; - constexpr static const char* const BUTTON_PROFILE_SECTION = "shortcutProfileButton."; - constexpr static const char* const AXIS_PROFILE_SECTION = "shortcutProfileAxis."; - -public: - InputModel(QObject* parent = nullptr); - - void setConfigController(ConfigController* controller); - void setProfile(const QString& profile); - void setKeyCallback(std::function<void (QMenu*, int, bool)> callback) { m_keyCallback = callback; } - - virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - - virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override; - virtual QModelIndex parent(const QModelIndex& index) const override; - - virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; - virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; - - void addAction(QMenu* menu, QAction* action, const QString& name); - void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, - int shortcut, const QString& visibleName, const QString& name); - void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, - const QKeySequence& shortcut, const QString& visibleName, const QString& name); - void addKey(QMenu* menu, mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name); - QModelIndex addMenu(QMenu* menu, QMenu* parent = nullptr); - - QAction* getAction(const QString& name); - int shortcutAt(const QModelIndex& index) const; - int keyAt(const QModelIndex& index) const; - bool isMenuAt(const QModelIndex& index) const; - - void updateKey(const QModelIndex& index, int keySequence); - void updateButton(const QModelIndex& index, int button); - void updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction); - void updateHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction); - - void clearKey(const QModelIndex& index); - void clearButton(const QModelIndex& index); - - static int toModifierShortcut(const QString& shortcut); - static bool isModifierKey(int key); - static int toModifierKey(int key); - - void loadProfile(mPlatform platform, const QString& profile); - - bool triggerKey(int keySequence, bool down, mPlatform platform = PLATFORM_NONE); - bool triggerButton(int button, bool down); - bool triggerAxis(int axis, GamepadAxisEvent::Direction, bool isNew); - bool triggerHat(int hat, GamepadHatEvent::Direction); - -signals: - void keyRebound(const QModelIndex&, int keySequence); - void buttonRebound(const QModelIndex&, int button); - void axisRebound(const QModelIndex& index, int axis, GamepadAxisEvent::Direction); - void hatRebound(const QModelIndex& index, int hat, GamepadHatEvent::Direction); - -private: - InputItem* add(QMenu* menu, std::function<void (InputItem*)>); - InputItem* itemAt(const QModelIndex& index); - const InputItem* itemAt(const QModelIndex& index) const; - bool loadShortcuts(InputItem*); - void loadGamepadShortcuts(InputItem*); - void onSubitems(InputItem*, std::function<void(InputItem*)> func); - void updateKey(InputItem* item, int keySequence); - - QModelIndex index(InputItem* item) const; - - InputItem m_rootMenu; - QMap<QMenu*, InputItem*> m_menuMap; - QMap<int, InputItem*> m_buttons; - QMap<QPair<int, GamepadAxisEvent::Direction>, InputItem*> m_axes; - QMap<int, InputItem*> m_heldKeys; - QMap<QPair<mPlatform, int>, InputItem*> m_keys; - ConfigController* m_config; - std::function<void (QMenu*, int key, bool down)> m_keyCallback; - QString m_profileName; - const InputProfile* m_profile; -}; - -} - -#endif
@@ -1,269 +0,0 @@
-/* Copyright (c) 2013-2015 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 "InputProfile.h" - -#include "InputController.h" - -#include <QRegExp> - -using namespace QGBA; - -const InputProfile InputProfile::s_defaultMaps[] = { - { - "XInput Controller #\\d+", // XInput (Windows) - { - /*keyA */ 11, - /*keyB */ 10, - /*keySelect */ 5, - /*keyStart */ 4, - /*keyRight */ 3, - /*keyLeft */ 2, - /*keyUp */ 0, - /*keyDown */ 1, - /*keyR */ 9, - /*keyL */ 8 - }, - { - /*loadState */ 12, - /*saveState */ 13, - /*holdFastForward */ -1, - /*holdRewind */ -1, - }, - { - /*loadState */ {GamepadAxisEvent::Direction::NEUTRAL, -1}, - /*saveState */ {GamepadAxisEvent::Direction::NEUTRAL, -1}, - /*holdFastForward */ {GamepadAxisEvent::Direction::POSITIVE, 5}, - /*holdRewind */ {GamepadAxisEvent::Direction::POSITIVE, 4}, - } - }, - { - "(Microsoft X-Box 360 pad|Xbox Gamepad \\(userspace driver\\))", // Linux - { - /*keyA */ 1, - /*keyB */ 0, - /*keySelect */ 6, - /*keyStart */ 7, - /*keyRight */ -1, - /*keyLeft */ -1, - /*keyUp */ -1, - /*keyDown */ -1, - /*keyR */ 5, - /*keyL */ 4 - }, - { - /*loadState */ 2, - /*saveState */ 3, - /*holdFastForward */ -1, - /*holdRewind */ -1, - }, - { - /*loadState */ {GamepadAxisEvent::Direction::NEUTRAL, -1}, - /*saveState */ {GamepadAxisEvent::Direction::NEUTRAL, -1}, - /*holdFastForward */ {GamepadAxisEvent::Direction::POSITIVE, 5}, - /*holdRewind */ {GamepadAxisEvent::Direction::POSITIVE, 2}, - } - }, - { - "Xbox 360 Wired Controller", // OS X - { - /*keyA */ 1, - /*keyB */ 0, - /*keySelect */ 9, - /*keyStart */ 8, - /*keyRight */ 14, - /*keyLeft */ 13, - /*keyUp */ 11, - /*keyDown */ 12, - /*keyR */ 5, - /*keyL */ 4 - }, - { - /*loadState */ 2, - /*saveState */ 3, - /*holdFastForward */ -1, - /*holdRewind */ -1, - }, - { - /*loadState */ {GamepadAxisEvent::Direction::NEUTRAL, -1}, - /*saveState */ {GamepadAxisEvent::Direction::NEUTRAL, -1}, - /*holdFastForward */ {GamepadAxisEvent::Direction::POSITIVE, 5}, - /*holdRewind */ {GamepadAxisEvent::Direction::POSITIVE, 2}, - } - }, - { - "(Sony Computer Entertainment )?Wireless Controller", // The DualShock 4 device ID is cut off on Windows - { - /*keyA */ 1, - /*keyB */ 2, - /*keySelect */ 8, - /*keyStart */ 9, - /*keyRight */ -1, - /*keyLeft */ -1, - /*keyUp */ -1, - /*keyDown */ -1, - /*keyR */ 5, - /*keyL */ 4 - }, - { - /*loadState */ 0, - /*saveState */ 3, - /*holdFastForward */ 7, - /*holdRewind */ 6, - }, - }, - { - "PLAYSTATION\\(R\\)3 Controller", // DualShock 3 (OS X) - { - /*keyA */ 13, - /*keyB */ 14, - /*keySelect */ 0, - /*keyStart */ 3, - /*keyRight */ 5, - /*keyLeft */ 7, - /*keyUp */ 4, - /*keyDown */ 6, - /*keyR */ 11, - /*keyL */ 10 - }, - { - /*loadState */ 15, - /*saveState */ 12, - /*holdFastForward */ 9, - /*holdRewind */ 8, - }, - }, - { - "Wiimote \\(..-..-..-..-..-..\\)", // WJoy (OS X) - { - /*keyA */ 15, - /*keyB */ 16, - /*keySelect */ 7, - /*keyStart */ 6, - /*keyRight */ 14, - /*keyLeft */ 13, - /*keyUp */ 11, - /*keyDown */ 12, - /*keyR */ 20, - /*keyL */ 19 - }, - { - /*loadState */ 18, - /*saveState */ 17, - /*holdFastForward */ 22, - /*holdRewind */ 21, - }, - }, -}; - -constexpr InputProfile::InputProfile(const char* name, - const KeyList<int> keys, - const Shortcuts<int> shortcutButtons, - const Shortcuts<Axis> shortcutAxes, - const KeyList<AxisValue> axes, - const struct Coord& tiltAxis, - const struct Coord& gyroAxis, - float gyroSensitivity) - : m_profileName(name) - , m_keys { - keys.keyA, - keys.keyB, - keys.keySelect, - keys.keyStart, - keys.keyRight, - keys.keyLeft, - keys.keyUp, - keys.keyDown, - keys.keyR, - keys.keyL, - } - , m_axes { - axes.keyA, - axes.keyB, - axes.keySelect, - axes.keyStart, - axes.keyRight, - axes.keyLeft, - axes.keyUp, - axes.keyDown, - axes.keyR, - axes.keyL, - } - , m_shortcutButtons(shortcutButtons) - , m_shortcutAxes(shortcutAxes) - , m_tiltAxis(tiltAxis) - , m_gyroAxis(gyroAxis) - , m_gyroSensitivity(gyroSensitivity) -{ -} - -const InputProfile* InputProfile::findProfile(mPlatform platform, const QString& name) { - // TODO: Use platform - for (size_t i = 0; i < sizeof(s_defaultMaps) / sizeof(*s_defaultMaps); ++i) { - QRegExp re(s_defaultMaps[i].m_profileName); - if (re.exactMatch(name)) { - return &s_defaultMaps[i]; - } - } - return nullptr; -} - -void InputProfile::apply(mPlatform platform, InputController* controller) const { - for (size_t i = 0; i < GBA_KEY_MAX; ++i) { -#ifdef BUILD_SDL - controller->bindKey(platform, SDL_BINDING_BUTTON, m_keys[i], static_cast<GBAKey>(i)); - controller->bindAxis(platform, SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast<GBAKey>(i)); -#endif - } - controller->registerTiltAxisX(m_tiltAxis.x); - controller->registerTiltAxisY(m_tiltAxis.y); - controller->registerGyroAxisX(m_gyroAxis.x); - controller->registerGyroAxisY(m_gyroAxis.y); - controller->setGyroSensitivity(m_gyroSensitivity); -} - -bool InputProfile::lookupShortcutButton(const QString& shortcutName, int* button) const { - if (shortcutName == QLatin1String("loadState")) { - *button = m_shortcutButtons.loadState; - return true; - } - if (shortcutName == QLatin1String("saveState")) { - *button = m_shortcutButtons.saveState; - return true; - } - if (shortcutName == QLatin1String("holdFastForward")) { - *button = m_shortcutButtons.holdFastForward; - return true; - } - if (shortcutName == QLatin1String("holdRewind")) { - *button = m_shortcutButtons.holdRewind; - return true; - } - return false; -} - -bool InputProfile::lookupShortcutAxis(const QString& shortcutName, int* axis, GamepadAxisEvent::Direction* direction) const { - if (shortcutName == QLatin1String("loadState")) { - *axis = m_shortcutAxes.loadState.axis; - *direction = m_shortcutAxes.loadState.direction; - return true; - } - if (shortcutName == QLatin1String("saveState")) { - *axis = m_shortcutAxes.saveState.axis; - *direction = m_shortcutAxes.saveState.direction; - return true; - } - if (shortcutName == QLatin1String("holdFastForward")) { - *axis = m_shortcutAxes.holdFastForward.axis; - *direction = m_shortcutAxes.holdFastForward.direction; - return true; - } - if (shortcutName == QLatin1String("holdRewind")) { - *axis = m_shortcutAxes.holdRewind.axis; - *direction = m_shortcutAxes.holdRewind.direction; - return true; - } - return false; -}
@@ -1,99 +0,0 @@
-/* Copyright (c) 2013-2015 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef QGBA_INPUT_PROFILE -#define QGBA_INPUT_PROFILE - -#include "GamepadAxisEvent.h" - -#include <mgba/core/core.h> -#include <mgba/gba/interface.h> - -namespace QGBA { - -class InputController; - -class InputProfile { -public: - static const InputProfile* findProfile(mPlatform platform, const QString& name); - - void apply(mPlatform platform, InputController*) const; - bool lookupShortcutButton(const QString& shortcut, int* button) const; - bool lookupShortcutAxis(const QString& shortcut, int* axis, GamepadAxisEvent::Direction* direction) const; - -private: - struct Coord { - int x; - int y; - }; - - struct AxisValue { - GamepadAxisEvent::Direction direction; - int axis; - }; - - template <typename T> struct Shortcuts { - T loadState; - T saveState; - T holdFastForward; - T holdRewind; - }; - - struct Axis { - GamepadAxisEvent::Direction direction; - int axis; - }; - - template <typename T> struct KeyList { - T keyA; - T keyB; - T keySelect; - T keyStart; - T keyRight; - T keyLeft; - T keyUp; - T keyDown; - T keyR; - T keyL; - }; - - constexpr InputProfile(const char* name, - const KeyList<int> keys, - const Shortcuts<int> shortcutButtons = { -1, -1, -1, -1}, - const Shortcuts<Axis> shortcutAxes = { - {GamepadAxisEvent::Direction::NEUTRAL, -1}, - {GamepadAxisEvent::Direction::NEUTRAL, -1}, - {GamepadAxisEvent::Direction::NEUTRAL, -1}, - {GamepadAxisEvent::Direction::NEUTRAL, -1}}, - const KeyList<AxisValue> axes = { - { GamepadAxisEvent::Direction::NEUTRAL, -1 }, - { GamepadAxisEvent::Direction::NEUTRAL, -1 }, - { GamepadAxisEvent::Direction::NEUTRAL, -1 }, - { GamepadAxisEvent::Direction::NEUTRAL, -1 }, - { GamepadAxisEvent::Direction::POSITIVE, 0 }, - { GamepadAxisEvent::Direction::NEGATIVE, 0 }, - { GamepadAxisEvent::Direction::NEGATIVE, 1 }, - { GamepadAxisEvent::Direction::POSITIVE, 1 }, - { GamepadAxisEvent::Direction::NEUTRAL, -1 }, - { GamepadAxisEvent::Direction::NEUTRAL, -1 }}, - const struct Coord& tiltAxis = { 2, 3 }, - const struct Coord& gyroAxis = { 0, 1 }, - float gyroSensitivity = 2e+09f); - - static const InputProfile s_defaultMaps[]; - - const char* m_profileName; - const int m_keys[GBA_KEY_MAX]; - const AxisValue m_axes[GBA_KEY_MAX]; - const Shortcuts<int> m_shortcutButtons; - const Shortcuts<Axis> m_shortcutAxes; - Coord m_tiltAxis; - Coord m_gyroAxis; - float m_gyroSensitivity; -}; - -} - -#endif
@@ -7,7 +7,7 @@ #include "KeyEditor.h"
#include "GamepadAxisEvent.h" #include "GamepadButtonEvent.h" -#include "InputModel.h" +#include "InputIndex.h" #include <QFontMetrics> #include <QKeyEvent>@@ -99,7 +99,7 @@ m_key = 0;
} m_lastKey.start(KEY_TIME); if (m_key) { - if (InputModel::isModifierKey(m_key)) { + if (InputIndex::isModifierKey(m_key)) { switch (event->key()) { case Qt::Key_Shift: setValue(Qt::ShiftModifier);@@ -115,7 +115,7 @@ setValue(Qt::MetaModifier);
break; } } - if (InputModel::isModifierKey(event->key())) { + if (InputIndex::isModifierKey(event->key())) { switch (event->key()) { case Qt::Key_Shift: setValue(m_key | Qt::ShiftModifier);
@@ -9,6 +9,7 @@
#include <mgba/core/core.h> #include "GameController.h" +#include "MemoryView.h" using namespace QGBA;@@ -24,6 +25,7 @@ connect(m_ui.searchWithin, &QPushButton::clicked, this, &MemorySearch::searchWithin);
connect(m_ui.refresh, &QPushButton::clicked, this, &MemorySearch::refresh); connect(m_ui.numHex, &QPushButton::clicked, this, &MemorySearch::refresh); connect(m_ui.numDec, &QPushButton::clicked, this, &MemorySearch::refresh); + connect(m_ui.viewMem, &QPushButton::clicked, this, &MemorySearch::openMemory); } MemorySearch::~MemorySearch() {@@ -47,7 +49,6 @@ if (m_ui.bits32->isChecked()) {
params->type = mCORE_MEMORY_SEARCH_32; } if (m_ui.numHex->isChecked()) { - bool ok; uint32_t v = m_ui.value->text().toUInt(&ok, 16); if (ok) { switch (params->type) {@@ -150,6 +151,7 @@ for (size_t i = 0; i < mCoreMemorySearchResultsSize(&m_results); ++i) {
mCoreMemorySearchResult* result = mCoreMemorySearchResultsGetPointer(&m_results, i); QTableWidgetItem* item = new QTableWidgetItem(QString("%1").arg(result->address, 8, 16, QChar('0'))); m_ui.results->setItem(i, 0, item); + QTableWidgetItem* type; if (m_ui.numHex->isChecked()) { switch (result->type) { case mCORE_MEMORY_SEARCH_8:@@ -181,7 +183,44 @@ case mCORE_MEMORY_SEARCH_STRING:
item = new QTableWidgetItem("?"); // TODO } } + QString divisor; + if (result->guessDivisor > 1) { + divisor = tr(" (⅟%0×)").arg(result->guessDivisor); + } + switch (result->type) { + case mCORE_MEMORY_SEARCH_8: + type = new QTableWidgetItem(tr("1 byte%0").arg(divisor)); + break; + case mCORE_MEMORY_SEARCH_16: + type = new QTableWidgetItem(tr("2 bytes%0").arg(divisor)); + break; + case mCORE_MEMORY_SEARCH_GUESS: + case mCORE_MEMORY_SEARCH_32: + type = new QTableWidgetItem(tr("4 bytes%0").arg(divisor)); + break; + case mCORE_MEMORY_SEARCH_STRING: + item = new QTableWidgetItem("?"); // TODO + } m_ui.results->setItem(i, 1, item); + m_ui.results->setItem(i, 2, type); } m_ui.results->sortItems(0); + m_ui.results->resizeColumnsToContents(); + m_ui.results->resizeRowsToContents(); +} + +void MemorySearch::openMemory() { + auto items = m_ui.results->selectedItems(); + if (items.empty()) { + return; + } + QTableWidgetItem* item = items[0]; + uint32_t address = item->text().toUInt(nullptr, 16); + + MemoryView* memView = new MemoryView(m_controller); + memView->jumpToAddress(address); + + connect(m_controller, &GameController::gameStopped, memView, &QWidget::close); + memView->setAttribute(Qt::WA_DeleteOnClose); + memView->show(); }
@@ -28,6 +28,9 @@ void refresh();
void search(); void searchWithin(); +private slots: + void openMemory(); + private: bool createParams(mCoreMemorySearchParams*);
@@ -17,7 +17,7 @@ <height>241</height>
</size> </property> <property name="windowTitle"> - <string>Form</string> + <string>Memory Search</string> </property> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="1">@@ -27,6 +27,9 @@ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>1</horstretch> <verstretch>0</verstretch> </sizepolicy> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> </property> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum>@@ -194,11 +197,8 @@ </widget>
</item> <item> <widget class="QPushButton" name="viewMem"> - <property name="enabled"> - <bool>false</bool> - </property> <property name="text"> - <string>View in Memory View</string> + <string>Open in Memory Viewer</string> </property> </widget> </item>@@ -214,10 +214,26 @@ </item>
</layout> </widget> <resources/> - <connections/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>MemorySearch</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel"> + <x>315</x> + <y>357</y> + </hint> + <hint type="destinationlabel"> + <x>315</x> + <y>188</y> + </hint> + </hints> + </connection> + </connections> <buttongroups> - <buttongroup name="type"/> <buttongroup name="width"/> - <buttongroup name="numType"/> + <buttongroup name="type"/> </buttongroups> </ui>
@@ -22,6 +22,7 @@ MemoryView(GameController* controller, QWidget* parent = nullptr);
public slots: void update(); + void jumpToAddress(uint32_t address) { m_ui.hexfield->jumpToAddress(address); } private slots: void setIndex(int);
@@ -68,6 +68,7 @@ }
#ifdef M_CORE_GBA void ObjView::updateTilesGBA(bool force) { + m_ui.objId->setMaximum(127); const GBA* gba = static_cast<const GBA*>(m_controller->thread()->core->board); const GBAObj* obj = &gba->video.oam.obj[m_objId];@@ -172,6 +173,7 @@ #endif
#ifdef M_CORE_GB void ObjView::updateTilesGB(bool force) { + m_ui.objId->setMaximum(39); const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board); const GBObj* obj = &gb->video.oam.obj[m_objId];
@@ -5,6 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "OverrideView.h" +#include <QColorDialog> #include <QPushButton> #include "ConfigController.h"@@ -79,6 +80,21 @@
connect(m_ui.gbModel, &QComboBox::currentTextChanged, this, &OverrideView::updateOverrides); connect(m_ui.mbc, &QComboBox::currentTextChanged, this, &OverrideView::updateOverrides); + QPalette palette = m_ui.color0->palette(); + palette.setColor(backgroundRole(), QColor(0xF8, 0xF8, 0xF8)); + m_ui.color0->setPalette(palette); + palette.setColor(backgroundRole(), QColor(0xA8, 0xA8, 0xA8)); + m_ui.color1->setPalette(palette); + palette.setColor(backgroundRole(), QColor(0x50, 0x50, 0x50)); + m_ui.color2->setPalette(palette); + palette.setColor(backgroundRole(), QColor(0x00, 0x00, 0x00)); + m_ui.color3->setPalette(palette); + + m_ui.color0->installEventFilter(this); + m_ui.color1->installEventFilter(this); + m_ui.color2->installEventFilter(this); + m_ui.color3->installEventFilter(this); + connect(m_ui.tabWidget, &QTabWidget::currentChanged, this, &OverrideView::updateOverrides); #ifndef M_CORE_GBA m_ui.tabWidget->removeTab(m_ui.tabWidget->indexOf(m_ui.tabGBA));@@ -96,6 +112,42 @@ gameStarted(controller->thread());
} } +bool OverrideView::eventFilter(QObject* obj, QEvent* event) { +#ifdef M_CORE_GB + if (event->type() != QEvent::MouseButtonRelease) { + return false; + } + int colorId; + if (obj == m_ui.color0) { + colorId = 0; + } else if (obj == m_ui.color1) { + colorId = 1; + } else if (obj == m_ui.color2) { + colorId = 2; + } else if (obj == m_ui.color3) { + colorId = 3; + } else { + return false; + } + + QWidget* swatch = static_cast<QWidget*>(obj); + + QColorDialog* colorPicker = new QColorDialog; + colorPicker->setAttribute(Qt::WA_DeleteOnClose); + colorPicker->open(); + connect(colorPicker, &QColorDialog::colorSelected, [this, swatch, colorId](const QColor& color) { + QPalette palette = swatch->palette(); + palette.setColor(backgroundRole(), color); + swatch->setPalette(palette); + m_gbColors[colorId] = color.rgb(); + updateOverrides(); + }); + return true; +#else + return false; +#endif +} + void OverrideView::saveOverride() { if (!m_config) { return;@@ -155,7 +207,13 @@ if (m_ui.tabWidget->currentWidget() == m_ui.tabGB) {
GBOverride* gb = new GBOverride; gb->override.mbc = s_mbcList[m_ui.mbc->currentIndex()]; gb->override.model = s_gbModelList[m_ui.gbModel->currentIndex()]; - if (gb->override.mbc != GB_MBC_AUTODETECT || gb->override.model != GB_MODEL_AUTODETECT) { + gb->override.gbColors[0] = m_gbColors[0]; + gb->override.gbColors[1] = m_gbColors[1]; + gb->override.gbColors[2] = m_gbColors[2]; + gb->override.gbColors[3] = m_gbColors[3]; + bool hasOverride = gb->override.mbc != GB_MBC_AUTODETECT || gb->override.model != GB_MODEL_AUTODETECT; + hasOverride = hasOverride || (m_gbColors[0] | m_gbColors[1] | m_gbColors[2] | m_gbColors[3]); + if (hasOverride) { m_controller->setOverride(gb); } else { m_controller->clearOverride();
@@ -36,6 +36,9 @@ void updateOverrides();
void gameStarted(mCoreThread*); void gameStopped(); +protected: + bool eventFilter(QObject* obj, QEvent* event) override; + private: Ui::OverrideView m_ui;@@ -43,6 +46,8 @@ GameController* m_controller;
ConfigController* m_config; #ifdef M_CORE_GB + uint32_t m_gbColors[4]{}; + static QList<enum GBModel> s_gbModelList; static QList<enum GBMemoryBankControllerType> s_mbcList; #endif
@@ -6,8 +6,8 @@ <property name="geometry">
<rect> <x>0</x> <y>0</y> - <width>443</width> - <height>282</height> + <width>444</width> + <height>284</height> </rect> </property> <property name="sizePolicy">@@ -325,6 +325,93 @@ <string>HuC-3</string>
</property> </item> </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Colors</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QFrame" name="color0"> + <property name="minimumSize"> + <size> + <width>30</width> + <height>30</height> + </size> + </property> + <property name="autoFillBackground"> + <bool>true</bool> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + </widget> + </item> + <item> + <widget class="QFrame" name="color1"> + <property name="minimumSize"> + <size> + <width>30</width> + <height>30</height> + </size> + </property> + <property name="autoFillBackground"> + <bool>true</bool> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + </widget> + </item> + <item> + <widget class="QFrame" name="color2"> + <property name="minimumSize"> + <size> + <width>30</width> + <height>30</height> + </size> + </property> + <property name="autoFillBackground"> + <bool>true</bool> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + </widget> + </item> + <item> + <widget class="QFrame" name="color3"> + <property name="minimumSize"> + <size> + <width>30</width> + <height>30</height> + </size> + </property> + <property name="autoFillBackground"> + <bool>true</bool> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + </widget> + </item> + </layout> </item> </layout> </widget>
@@ -13,13 +13,15 @@ #include "InputController.h"
#include "ShortcutView.h" #include <mgba/core/serialize.h> +#include <mgba/core/version.h> #include <mgba/internal/gba/gba.h> using namespace QGBA; -SettingsView::SettingsView(ConfigController* controller, InputController* inputController, InputModel* inputModel, QWidget* parent) +SettingsView::SettingsView(ConfigController* controller, InputController* inputController, QWidget* parent) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) + , m_input(inputController) { m_ui.setupUi(this);@@ -88,6 +90,7 @@ m_ui.patchSameDir->setChecked(false);
m_ui.patchPath->setText(path); } }); + connect(m_ui.clearCache, &QAbstractButton::pressed, this, &SettingsView::libraryCleared); // TODO: Move to reloadConfig() QVariant audioDriver = m_controller->getQtOption("audioDriver");@@ -152,11 +155,29 @@ updateConfig();
} }); - ShortcutView* shortcutView = new ShortcutView(); - shortcutView->setModel(inputModel); - shortcutView->setInputController(inputController); - m_ui.stackedWidget->addWidget(shortcutView); - m_ui.tabs->addItem(tr("Bindings")); + m_ui.languages->setItemData(0, QLocale("en")); + QDir ts(":/translations/"); + for (auto name : ts.entryList()) { + if (!name.endsWith(".qm")) { + continue; + } + QLocale locale(name.remove(QString("%0-").arg(binaryName)).remove(".qm")); + m_ui.languages->addItem(locale.nativeLanguageName(), locale); + if (locale == QLocale()) { + m_ui.languages->setCurrentIndex(m_ui.languages->count() - 1); + } + } + + m_keyView = new ShortcutView(); + m_keyView->setModel(inputController->keyIndex()); + m_keyView->setInputController(inputController); + m_shortcutView = new ShortcutView(); + m_shortcutView->setModel(inputController->inputIndex()); + m_shortcutView->setInputController(inputController); + m_ui.stackedWidget->addWidget(m_keyView); + m_ui.tabs->addItem(tr("Controls")); + m_ui.stackedWidget->addWidget(m_shortcutView); + m_ui.tabs->addItem(tr("Shortcuts")); } void SettingsView::selectBios(QLineEdit* bios) {@@ -244,7 +265,17 @@ Display::setDriver(static_cast<Display::Driver>(displayDriver.toInt()));
emit displayDriverChanged(); } + QLocale language = m_ui.languages->itemData(m_ui.languages->currentIndex()).toLocale(); + if (language != m_controller->getQtOption("language").toLocale() && !(language.bcp47Name() == QLocale::system().bcp47Name() && m_controller->getQtOption("language").isNull())) { + m_controller->setQtOption("language", language.bcp47Name()); + emit languageChanged(); + } + m_controller->write(); + + m_input->rebuildIndex(m_shortcutView->root()); + m_input->rebuildKeyIndex(m_keyView->root()); + m_input->saveConfiguration(); emit pathsChanged(); emit biosLoaded(PLATFORM_GBA, m_ui.gbaBios->text());
@@ -16,19 +16,22 @@ namespace QGBA {
class ConfigController; class InputController; -class InputModel; +class InputIndex; +class ShortcutView; class SettingsView : public QDialog { Q_OBJECT public: - SettingsView(ConfigController* controller, InputController* inputController, InputModel* inputModel, QWidget* parent = nullptr); + SettingsView(ConfigController* controller, InputController* inputController, QWidget* parent = nullptr); signals: void biosLoaded(int platform, const QString&); void audioDriverChanged(); void displayDriverChanged(); void pathsChanged(); + void languageChanged(); + void libraryCleared(); private slots: void selectBios(QLineEdit*);@@ -40,6 +43,8 @@ Ui::SettingsView m_ui;
ConfigController* m_controller; InputController* m_input; + ShortcutView* m_shortcutView; + ShortcutView* m_keyView; void saveSetting(const char* key, const QAbstractButton*); void saveSetting(const char* key, const QComboBox*);
@@ -398,17 +398,30 @@ </layout>
</widget> <widget class="QWidget" name="interface_2"> <layout class="QFormLayout" name="formLayout_4"> - <item row="1" column="1"> - <widget class="QCheckBox" name="showLibrary"> + <item row="0" column="0"> + <widget class="QLabel" name="label_26"> <property name="text"> - <string>Show when no game open</string> + <string>Language</string> </property> - <property name="checked"> - <bool>true</bool> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="languages"> + <item> + <property name="text"> + <string>English</string> + </property> + </item> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Library:</string> </property> </widget> </item> - <item row="0" column="1"> + <item row="2" column="1"> <widget class="QComboBox" name="libraryStyle"> <item> <property name="text">@@ -422,38 +435,38 @@ </property>
</item> </widget> </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_6"> + <item row="3" column="1"> + <widget class="QCheckBox" name="showLibrary"> <property name="text"> - <string>Library:</string> + <string>Show when no game open</string> + </property> + <property name="checked"> + <bool>true</bool> </property> </widget> </item> - <item row="2" column="1"> + <item row="4" column="1"> <widget class="QPushButton" name="clearCache"> - <property name="enabled"> - <bool>false</bool> - </property> <property name="text"> <string>Clear cache</string> </property> </widget> </item> - <item row="3" column="0" colspan="2"> + <item row="5" column="0" colspan="2"> <widget class="Line" name="line_8"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> - <item row="4" column="1"> + <item row="6" column="1"> <widget class="QCheckBox" name="allowOpposingDirections"> <property name="text"> <string>Allow opposing input directions</string> </property> </widget> </item> - <item row="5" column="1"> + <item row="7" column="1"> <widget class="QCheckBox" name="suspendScreensaver"> <property name="text"> <string>Suspend screensaver</string>@@ -463,10 +476,17 @@ <bool>true</bool>
</property> </widget> </item> - <item row="6" column="1"> + <item row="8" column="1"> <widget class="QCheckBox" name="pauseOnFocusLost"> <property name="text"> <string>Pause when inactive</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="Line" name="line_10"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> </widget> </item>
@@ -15,9 +15,11 @@ using namespace QGBA;
ShortcutView::ShortcutView(QWidget* parent) : QWidget(parent) + , m_model() { m_ui.setupUi(this); m_ui.keyEdit->setValueKey(0); + m_ui.shortcutTable->setModel(&m_model); connect(m_ui.gamepadButton, &QAbstractButton::pressed, [this]() { bool signalsBlocked = m_ui.keyEdit->blockSignals(true);@@ -33,15 +35,19 @@ connect(m_ui.keyEdit, &KeyEditor::valueChanged, this, &ShortcutView::updateButton);
connect(m_ui.keyEdit, &KeyEditor::axisChanged, this, &ShortcutView::updateAxis); connect(m_ui.shortcutTable, &QAbstractItemView::doubleClicked, this, &ShortcutView::load); connect(m_ui.clearButton, &QAbstractButton::clicked, this, &ShortcutView::clear); +#ifdef BUILD_SDL + connect(m_ui.gamepadName, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this](int index) { + m_input->setGamepad(SDL_BINDING_BUTTON, index); + }); +#endif } ShortcutView::~ShortcutView() { m_input->releaseFocus(this); } -void ShortcutView::setModel(InputModel* model) { - m_controller = model; - m_ui.shortcutTable->setModel(model); +void ShortcutView::setModel(InputIndex* model) { + m_model.clone(*model); } void ShortcutView::setInputController(InputController* controller) {@@ -50,16 +56,33 @@ m_input->releaseFocus(this);
} m_input = controller; m_input->stealFocus(this); + updateGamepads(); } -void ShortcutView::load(const QModelIndex& index) { - if (!m_controller) { +void ShortcutView::updateGamepads() { + if (!m_input) { return; } - if (m_controller->isMenuAt(index)) { +#ifdef BUILD_SDL + m_ui.gamepadName->clear(); + + QStringList gamepads = m_input->connectedGamepads(SDL_BINDING_BUTTON); + int activeGamepad = m_input->gamepad(SDL_BINDING_BUTTON); + + for (const auto& gamepad : gamepads) { + m_ui.gamepadName->addItem(gamepad); + } + m_ui.gamepadName->setCurrentIndex(activeGamepad); +#endif + +} + +void ShortcutView::load(const QModelIndex& index) { + InputItem* item = m_model.itemAt(index); + if (!item) { return; } - int shortcut = m_controller->shortcutAt(index); + int shortcut = item->shortcut(); if (index.column() == 1) { m_ui.keyboardButton->click(); } else if (index.column() == 2) {@@ -76,39 +99,38 @@ m_ui.keyEdit->blockSignals(blockSignals);
} void ShortcutView::clear() { - if (!m_controller) { - return; - } QModelIndex index = m_ui.shortcutTable->selectionModel()->currentIndex(); - if (m_controller->isMenuAt(index)) { + InputItem* item = m_model.itemAt(index); + if (!item) { return; } if (m_ui.gamepadButton->isChecked()) { - m_controller->clearButton(index); + item->clearButton(); m_ui.keyEdit->setValueButton(-1); } else { - m_controller->clearKey(index); + item->clearShortcut(); m_ui.keyEdit->setValueKey(-1); } } void ShortcutView::updateButton(int button) { - if (!m_controller || m_controller->isMenuAt(m_ui.shortcutTable->selectionModel()->currentIndex())) { + InputItem* item = m_model.itemAt(m_ui.shortcutTable->selectionModel()->currentIndex()); + if (!item) { return; } if (m_ui.gamepadButton->isChecked()) { - m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + item->setButton(button); } else { - m_controller->updateKey(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + item->setShortcut(button); } } void ShortcutView::updateAxis(int axis, int direction) { - if (!m_controller || m_controller->isMenuAt(m_ui.shortcutTable->selectionModel()->currentIndex())) { + InputItem* item = m_model.itemAt(m_ui.shortcutTable->selectionModel()->currentIndex()); + if (!item) { return; } - m_controller->updateAxis(m_ui.shortcutTable->selectionModel()->currentIndex(), axis, - static_cast<GamepadAxisEvent::Direction>(direction)); + item->setAxis(axis, static_cast<GamepadAxisEvent::Direction>(direction)); } void ShortcutView::closeEvent(QCloseEvent*) {
@@ -7,6 +7,8 @@ #ifndef QGBA_SHORTCUT_VIEW
#define QGBA_SHORTCUT_VIEW #include "GamepadAxisEvent.h" +#include "InputIndex.h" +#include "InputModel.h" #include <QWidget>@@ -15,7 +17,6 @@
namespace QGBA { class InputController; -class InputModel; class ShortcutView : public QWidget { Q_OBJECT@@ -24,9 +25,11 @@ public:
ShortcutView(QWidget* parent = nullptr); ~ShortcutView(); - void setModel(InputModel* controller); + void setModel(InputIndex* model); void setInputController(InputController* input); + const InputIndex* root() { return m_model.inputIndex(); } + protected: virtual bool event(QEvent*) override; virtual void closeEvent(QCloseEvent*) override;@@ -36,11 +39,12 @@ void load(const QModelIndex&);
void clear(); void updateButton(int button); void updateAxis(int axis, int direction); + void updateGamepads(); private: Ui::ShortcutView m_ui; - InputModel* m_controller = nullptr; + InputModel m_model; InputController* m_input = nullptr; };
@@ -72,6 +72,27 @@ </widget>
</item> </layout> </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Current Gamepad</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="gamepadName"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> </layout> </widget> <customwidgets>
@@ -90,8 +90,7 @@ : QMainWindow(parent)
, m_logView(new LogView(&m_log)) , m_screenWidget(new WindowBackground()) , m_config(config) - , m_inputModel(new InputModel(this)) - , m_inputController(m_inputModel, playerId, this) + , m_inputController(playerId, this) { setFocusPolicy(Qt::StrongFocus); setAcceptDrops(true);@@ -108,7 +107,11 @@ m_logo = m_logo; // Free memory left over in old pixmap
m_screenWidget->setMinimumSize(m_display->minimumSize()); m_screenWidget->setSizePolicy(m_display->sizePolicy()); - int i = 2; +#if defined(M_CORE_GBA) + float i = 2; +#elif defined(M_CORE_GB) + float i = 3; +#endif QVariant multiplier = m_config->getOption("scaleMultiplier"); if (!multiplier.isNull()) { m_savedScale = multiplier.toInt();@@ -142,8 +145,11 @@ QPair<QString, QString> path = m_libraryView->selectedPath();
m_controller->loadGame(output, path.second, path.first); } }); -#elif defined(M_CORE_GBA) - m_screenWidget->setSizeHint(QSize(VIDEO_HORIZONTAL_PIXELS * i, VIDEO_VERTICAL_PIXELS * i)); +#endif +#if defined(M_CORE_GBA) + resizeFrame(QSize(VIDEO_HORIZONTAL_PIXELS * i, VIDEO_VERTICAL_PIXELS * i)); +#elif defined(M_CORE_GB) + resizeFrame(QSize(GB_VIDEO_HORIZONTAL_PIXELS * i, GB_VIDEO_VERTICAL_PIXELS * i)); #endif m_screenWidget->setPixmap(m_logo); m_screenWidget->setCenteredAspectRatio(m_logo.width(), m_logo.height());@@ -197,6 +203,9 @@ connect(this, &Window::shutdown, m_shaderView, &QWidget::hide);
connect(this, &Window::audioBufferSamplesChanged, m_controller, &GameController::setAudioBufferSamples); connect(this, &Window::sampleRateChanged, m_controller, &GameController::setAudioSampleRate); connect(this, &Window::fpsTargetChanged, m_controller, &GameController::setFPSTarget); + connect(&m_inputController, &InputController::keyPressed, m_controller, &GameController::keyPressed); + connect(&m_inputController, &InputController::keyReleased, m_controller, &GameController::keyReleased); + connect(&m_inputController, &InputController::keyAutofire, m_controller, &GameController::setAutofire); connect(&m_fpsTimer, &QTimer::timeout, this, &Window::showFPS); connect(&m_focusCheck, &QTimer::timeout, this, &Window::focusCheck); connect(m_display, &Display::hideCursor, [this]() {@@ -212,19 +221,17 @@ m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL);
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL); m_focusCheck.setInterval(200); - m_inputModel->setConfigController(m_config); setupMenu(menuBar()); #ifdef M_CORE_GBA - m_inputController.addPlatform(PLATFORM_GBA, tr("Game Boy Advance"), &GBAInputInfo); + m_inputController.addPlatform(PLATFORM_GBA, &GBAInputInfo); #endif #ifdef M_CORE_GB - m_inputController.addPlatform(PLATFORM_GB, tr("Game Boy"), &GBInputInfo); + m_inputController.addPlatform(PLATFORM_GB, &GBInputInfo); #endif #ifdef M_CORE_DS - m_inputController.addPlatform(PLATFORM_DS, tr("DS"), &DSInputInfo); + m_inputController.addPlatform(PLATFORM_DS, &DSInputInfo); #endif - m_inputController.setupCallback(m_controller); } Window::~Window() {@@ -266,9 +273,6 @@ }
void Window::resizeFrame(const QSize& size) { QSize newSize(size); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - newSize /= m_screenWidget->devicePixelRatioF(); -#endif m_screenWidget->setSizeHint(newSize); newSize -= m_screenWidget->size(); newSize += this->size();@@ -504,11 +508,13 @@ }
} void Window::openSettingsWindow() { - SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, m_inputModel); + SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController); connect(settingsWindow, &SettingsView::biosLoaded, m_controller, &GameController::loadBIOS); connect(settingsWindow, &SettingsView::audioDriverChanged, m_controller, &GameController::reloadAudioDriver); connect(settingsWindow, &SettingsView::displayDriverChanged, this, &Window::mustRestart); + connect(settingsWindow, &SettingsView::languageChanged, this, &Window::mustRestart); connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig); + connect(settingsWindow, &SettingsView::libraryCleared, m_libraryView, &LibraryController::clear); openView(settingsWindow); }@@ -806,14 +812,14 @@ menuBar()->hide();
} #endif - m_inputController.setPlatform(m_controller->platform()); - m_hitUnimplementedBiosCall = false; m_fpsTimer.start(); m_focusCheck.start(); m_controller->threadInterrupt(); if (m_controller->isLoaded()) { + m_inputController.setPlatform(m_controller->platform()); + mCore* core = m_controller->thread()->core; const mCoreChannelInfo* videoLayers; const mCoreChannelInfo* audioChannels;@@ -1010,9 +1016,9 @@ }
void Window::setupMenu(QMenuBar* menubar) { menubar->clear(); - QMenu* fileMenu = menubar->addMenu(tr("&File")); - m_inputModel->addMenu(fileMenu); installEventFilter(&m_inputController); + + QMenu* fileMenu = menubar->addMenu(tr("&File")); addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open), "loadROM"); #ifdef USE_SQLITE3@@ -1069,8 +1075,6 @@ addControlledAction(fileMenu, saveState, "saveState");
QMenu* quickLoadMenu = fileMenu->addMenu(tr("Quick load")); QMenu* quickSaveMenu = fileMenu->addMenu(tr("Quick save")); - m_inputModel->addMenu(quickLoadMenu); - m_inputModel->addMenu(quickSaveMenu); QAction* quickLoad = new QAction(tr("Load recent"), quickLoadMenu); connect(quickLoad, &QAction::triggered, m_controller, &GameController::loadState);@@ -1162,7 +1166,6 @@ addControlledAction(fileMenu, fileMenu->addAction(tr("E&xit"), this, SLOT(close()), QKeySequence::Quit), "quit");
#endif QMenu* emulationMenu = menubar->addMenu(tr("&Emulation")); - m_inputModel->addMenu(emulationMenu); QAction* reset = new QAction(tr("&Reset"), emulationMenu); reset->setShortcut(tr("Ctrl+R")); connect(reset, &QAction::triggered, m_controller, &GameController::reset);@@ -1203,11 +1206,11 @@ addControlledAction(emulationMenu, frameAdvance, "frameAdvance");
emulationMenu->addSeparator(); - m_inputModel->addFunctions(emulationMenu, [this]() { + m_inputController.inputIndex()->addItem(qMakePair([this]() { m_controller->setTurbo(true, false); }, [this]() { m_controller->setTurbo(false, false); - }, QKeySequence(Qt::Key_Tab), tr("Fast forward (held)"), "holdFastForward"); + }), tr("Fast forward (held)"), "holdFastForward", emulationMenu)->setShortcut(QKeySequence(Qt::Key_Tab)[0]); QAction* turbo = new QAction(tr("&Fast forward"), emulationMenu); turbo->setCheckable(true);@@ -1229,11 +1232,11 @@ ffspeed->addValue(tr("%0x").arg(i), i, ffspeedMenu);
} m_config->updateOption("fastForwardRatio"); - m_inputModel->addFunctions(emulationMenu, [this]() { + m_inputController.inputIndex()->addItem(qMakePair([this]() { m_controller->startRewinding(); }, [this]() { m_controller->stopRewinding(); - }, QKeySequence("`"), tr("Rewind (held)"), "holdRewind"); + }), tr("Rewind (held)"), "holdRewind", emulationMenu)->setShortcut(QKeySequence("`")[0]); QAction* rewind = new QAction(tr("Re&wind"), emulationMenu); rewind->setShortcut(tr("~"));@@ -1270,7 +1273,6 @@
emulationMenu->addSeparator(); QMenu* solarMenu = emulationMenu->addMenu(tr("Solar sensor")); - m_inputModel->addMenu(solarMenu); QAction* solarIncrease = new QAction(tr("Increase solar level"), solarMenu); connect(solarIncrease, &QAction::triggered, m_controller, &GameController::increaseLuminanceLevel); addControlledAction(solarMenu, solarIncrease, "increaseLuminanceLevel");@@ -1297,9 +1299,7 @@ addControlledAction(solarMenu, setSolar, QString("luminanceLevel.%1").arg(QString::number(i)));
} QMenu* avMenu = menubar->addMenu(tr("Audio/&Video")); - m_inputModel->addMenu(avMenu); QMenu* frameMenu = avMenu->addMenu(tr("Frame size")); - m_inputModel->addMenu(frameMenu, avMenu); for (int i = 1; i <= 6; ++i) { QAction* setSize = new QAction(tr("%1x").arg(QString::number(i)), avMenu); setSize->setCheckable(true);@@ -1434,13 +1434,9 @@ m_gameActions.append(stopVL);
avMenu->addSeparator(); m_videoLayers = avMenu->addMenu(tr("Video layers")); - m_inputModel->addMenu(m_videoLayers, avMenu); - m_audioChannels = avMenu->addMenu(tr("Audio channels")); - m_inputModel->addMenu(m_audioChannels, avMenu); QMenu* toolsMenu = menubar->addMenu(tr("&Tools")); - m_inputModel->addMenu(toolsMenu); QAction* viewLogs = new QAction(tr("View &logs..."), toolsMenu); connect(viewLogs, &QAction::triggered, m_logView, &QWidget::show); addControlledAction(toolsMenu, viewLogs, "viewLogs");@@ -1583,72 +1579,11 @@ connect(exitFullScreen, &QAction::triggered, this, &Window::exitFullScreen);
exitFullScreen->setShortcut(QKeySequence("Esc")); addHiddenAction(frameMenu, exitFullScreen, "exitFullScreen"); - QMenu* autofireMenu = new QMenu(tr("Autofire"), this); - m_inputModel->addMenu(autofireMenu); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_A, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_A, false); - }, QKeySequence(), tr("Autofire A"), "autofireA"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_B, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_B, false); - }, QKeySequence(), tr("Autofire B"), "autofireB"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_L, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_L, false); - }, QKeySequence(), tr("Autofire L"), "autofireL"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_R, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_R, false); - }, QKeySequence(), tr("Autofire R"), "autofireR"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_START, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_START, false); - }, QKeySequence(), tr("Autofire Start"), "autofireStart"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_SELECT, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_SELECT, false); - }, QKeySequence(), tr("Autofire Select"), "autofireSelect"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_UP, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_UP, false); - }, QKeySequence(), tr("Autofire Up"), "autofireUp"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_RIGHT, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_RIGHT, false); - }, QKeySequence(), tr("Autofire Right"), "autofireRight"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_DOWN, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_DOWN, false); - }, QKeySequence(), tr("Autofire Down"), "autofireDown"); - - m_inputModel->addFunctions(autofireMenu, [this]() { - m_controller->setAutofire(GBA_KEY_LEFT, true); - }, [this]() { - m_controller->setAutofire(GBA_KEY_LEFT, false); - }, QKeySequence(), tr("Autofire Left"), "autofireLeft"); - for (QAction* action : m_gameActions) { action->setDisabled(true); } + + m_inputController.rebuildIndex(); } void Window::attachWidget(QWidget* widget) {@@ -1701,7 +1636,7 @@ return action;
} QAction* Window::addHiddenAction(QMenu* menu, QAction* action, const QString& name) { - m_inputModel->addAction(menu, action, name); + m_inputController.inputIndex()->addItem(action, name, menu); action->setShortcutContext(Qt::WidgetShortcut); addAction(action); return action;
@@ -28,7 +28,6 @@ class Display;
class GameController; class GDBController; class GIFView; -class InputModel; class LibraryController; class LogView; class ShaderSelector;@@ -171,7 +170,6 @@ LoadSaveState* m_stateWindow = nullptr;
WindowBackground* m_screenWidget; QPixmap m_logo{":/res/medusa-bg.png"}; ConfigController* m_config; - InputModel* m_inputModel; InputController m_inputController; QList<QDateTime> m_frameList; QTimer m_fpsTimer;
@@ -0,0 +1,852 @@
+/* Copyright (c) 2013-2014 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 "InputController.h" + +#include "ConfigController.h" +#include "GameController.h" +#include "GamepadAxisEvent.h" +#include "GamepadButtonEvent.h" +#include "InputItem.h" +#include "InputModel.h" +#include "InputProfile.h" + +#include <QApplication> +#include <QKeyEvent> +#include <QMenu> +#include <QTimer> +#include <QWidget> + +#include <mgba/core/interface.h> +#include <mgba-util/configuration.h> + +#ifdef M_CORE_GBA +#include <mgba/internal/gba/input.h> +#endif +#ifdef M_CORE_GB +#include <mgba/internal/gb/input.h> +#endif +#ifdef M_CORE_DS +#include <mgba/internal/ds/input.h> +#endif +#include <initializer_list> + +using namespace QGBA; + +#ifdef BUILD_SDL +int InputController::s_sdlInited = 0; +mSDLEvents InputController::s_sdlEvents; +#endif + +InputController::InputController(int playerId, QWidget* topLevel, QObject* parent) + : QObject(parent) + , m_playerId(playerId) + , m_topLevel(topLevel) + , m_focusParent(topLevel) + , m_bindings(new QMenu(tr("Controls"))) + , m_autofire(new QMenu(tr("Autofire"))) +{ +#ifdef BUILD_SDL + if (s_sdlInited == 0) { + mSDLInitEvents(&s_sdlEvents); + } + ++s_sdlInited; + updateJoysticks(); +#endif + +#ifdef BUILD_SDL + connect(&m_gamepadTimer, &QTimer::timeout, [this]() { + testGamepad(SDL_BINDING_BUTTON); + if (m_playerId == 0) { + updateJoysticks(); + } + }); +#endif + m_gamepadTimer.setInterval(50); + m_gamepadTimer.start(); + + static QList<QPair<QString, int>> defaultBindings({ + qMakePair(QLatin1String("A"), Qt::Key_Z), + qMakePair(QLatin1String("B"), Qt::Key_X), + qMakePair(QLatin1String("L"), Qt::Key_A), + qMakePair(QLatin1String("R"), Qt::Key_S), + qMakePair(QLatin1String("Start"), Qt::Key_Return), + qMakePair(QLatin1String("Select"), Qt::Key_Backspace), + qMakePair(QLatin1String("Up"), Qt::Key_Up), + qMakePair(QLatin1String("Down"), Qt::Key_Down), + qMakePair(QLatin1String("Left"), Qt::Key_Left), + qMakePair(QLatin1String("Right"), Qt::Key_Right) + }); + + for (auto k : defaultBindings) { + addKey(k.first); + } + m_keyIndex.rebuild(); + for (auto k : defaultBindings) { + bindKey(KEYBOARD, k.second, k.first); + } +} + +void InputController::addKey(const QString& name) { + if (itemForKey(name)) { + return; + } + m_keyIndex.addItem(qMakePair([this, name]() { + emit keyPressed(keyId(name)); + }, [this, name]() { + emit keyReleased(keyId(name)); + }), name, QString("key%0").arg(name), m_bindings.get()); + + m_keyIndex.addItem(qMakePair([this, name]() { + emit keyAutofire(keyId(name), true); + }, [this, name]() { + emit keyAutofire(keyId(name), false); + }), name, QString("autofire%1").arg(name), m_autofire.get()); +} + +void InputController::addPlatform(mPlatform platform, const mInputPlatformInfo* info) { + m_keyInfo[platform] = info; + for (size_t i = 0; i < info->nKeys; ++i) { + addKey(info->keyId[i]); + } +} + +void InputController::setPlatform(mPlatform platform) { + if (m_activeKeyInfo) { + mInputMapDeinit(&m_inputMap); + } + + m_sdlPlayer.bindings = &m_inputMap; + m_activeKeyInfo = m_keyInfo[platform]; + mInputMapInit(&m_inputMap, m_activeKeyInfo); + + loadConfiguration(KEYBOARD); +#ifdef BUILD_SDL + mSDLInitBindingsGBA(&m_inputMap); + loadConfiguration(SDL_BINDING_BUTTON); +#endif + + rebuildKeyIndex(); + restoreModel(); +} + +InputController::~InputController() { + if (m_activeKeyInfo) { + mInputMapDeinit(&m_inputMap); + } + +#ifdef BUILD_SDL + if (m_playerAttached) { + mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer); + } + + --s_sdlInited; + if (s_sdlInited == 0) { + mSDLDeinitEvents(&s_sdlEvents); + } +#endif +} + +void InputController::rebuildIndex(const InputIndex* index) { + m_inputIndex.rebuild(index); +} + +void InputController::rebuildKeyIndex(const InputIndex* index) { + m_keyIndex.rebuild(index); + + for (const InputItem* item : m_keyIndex.items()) { + if (!item->name().startsWith(QLatin1String("key"))) { + rebindKey(item->visibleName()); + } + } +} + +void InputController::setConfiguration(ConfigController* config) { + m_config = config; + m_inputIndex.setConfigController(config); + m_keyIndex.setConfigController(config); + setAllowOpposing(config->getOption("allowOpposingDirections").toInt()); + loadConfiguration(KEYBOARD); + loadProfile(KEYBOARD, profileForType(KEYBOARD)); +#ifdef BUILD_SDL + mSDLEventsLoadConfig(&s_sdlEvents, config->input()); + if (!m_playerAttached) { + m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer); + } + loadConfiguration(SDL_BINDING_BUTTON); + loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); +#endif + restoreModel(); +} + +void InputController::loadConfiguration(uint32_t type) { + if (!m_activeKeyInfo) { + return; + } + mInputMapLoad(&m_inputMap, type, m_config->input()); +#ifdef BUILD_SDL + if (m_playerAttached) { + mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input()); + } +#endif +} + +void InputController::loadProfile(uint32_t type, const QString& profile) { + const InputProfile* ip = InputProfile::findProfile(profile); + if (ip) { + ip->apply(this); + } + recalibrateAxes(); + emit profileLoaded(profile); +} + +void InputController::saveConfiguration() { + saveConfiguration(KEYBOARD); +#ifdef BUILD_SDL + saveConfiguration(SDL_BINDING_BUTTON); + saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); + if (m_playerAttached) { + mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input()); + } +#endif + m_inputIndex.saveConfig(); + m_keyIndex.saveConfig(); + m_config->write(); +} + +void InputController::saveConfiguration(uint32_t type) { + if (m_activeKeyInfo) { + mInputMapSave(&m_inputMap, type, m_config->input()); + } + m_config->write(); +} + +void InputController::saveProfile(uint32_t type, const QString& profile) { + if (m_activeKeyInfo) { + mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); + } + m_config->write(); +} + +const char* InputController::profileForType(uint32_t type) { + UNUSED(type); +#ifdef BUILD_SDL + if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_JoystickName(m_sdlPlayer.joystick->joystick); +#else + return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick)); +#endif + } +#endif + return 0; +} + +QStringList InputController::connectedGamepads(uint32_t type) const { + UNUSED(type); + +#ifdef BUILD_SDL + if (type == SDL_BINDING_BUTTON) { + QStringList pads; + for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { + const char* name; +#if SDL_VERSION_ATLEAST(2, 0, 0) + name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick); +#else + name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick)); +#endif + if (name) { + pads.append(QString(name)); + } else { + pads.append(QString()); + } + } + return pads; + } +#endif + + return QStringList(); +} + +int InputController::gamepad(uint32_t type) const { +#ifdef BUILD_SDL + if (type == SDL_BINDING_BUTTON) { + return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; + } +#endif + return 0; +} + +void InputController::setGamepad(uint32_t type, int index) { +#ifdef BUILD_SDL + if (type == SDL_BINDING_BUTTON) { + mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); + } +#endif +} + +void InputController::setPreferredGamepad(uint32_t type, const QString& device) { + if (!m_config) { + return; + } + mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData()); +} + +mRumble* InputController::rumble() { +#ifdef BUILD_SDL +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (m_playerAttached) { + return &m_sdlPlayer.rumble.d; + } +#endif +#endif + return nullptr; +} + +mRotationSource* InputController::rotationSource() { +#ifdef BUILD_SDL + if (m_playerAttached) { + return &m_sdlPlayer.rotation.d; + } +#endif + return nullptr; +} + +void InputController::registerTiltAxisX(int axis) { +#ifdef BUILD_SDL + if (m_playerAttached) { + m_sdlPlayer.rotation.axisX = axis; + } +#endif +} + +void InputController::registerTiltAxisY(int axis) { +#ifdef BUILD_SDL + if (m_playerAttached) { + m_sdlPlayer.rotation.axisY = axis; + } +#endif +} + +void InputController::registerGyroAxisX(int axis) { +#ifdef BUILD_SDL + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroX = axis; + } +#endif +} + +void InputController::registerGyroAxisY(int axis) { +#ifdef BUILD_SDL + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroY = axis; + } +#endif +} + +float InputController::gyroSensitivity() const { +#ifdef BUILD_SDL + if (m_playerAttached) { + return m_sdlPlayer.rotation.gyroSensitivity; + } +#endif + return 0; +} + +void InputController::setGyroSensitivity(float sensitivity) { +#ifdef BUILD_SDL + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroSensitivity = sensitivity; + } +#endif +} + +void InputController::updateJoysticks() { +#ifdef BUILD_SDL + QString profile = profileForType(SDL_BINDING_BUTTON); + mSDLUpdateJoysticks(&s_sdlEvents, m_config->input()); + QString newProfile = profileForType(SDL_BINDING_BUTTON); + if (profile != newProfile) { + loadProfile(SDL_BINDING_BUTTON, newProfile); + } +#endif +} + +const mInputMap* InputController::map() { + return &m_inputMap; +} + +int InputController::pollEvents() { + int activeButtons = 0; +#ifdef BUILD_SDL + if (m_playerAttached && m_sdlPlayer.joystick) { + SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; + SDL_JoystickUpdate(); + int numButtons = SDL_JoystickNumButtons(joystick); + int i; + for (i = 0; i < numButtons; ++i) { + GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i)); + if (key == GBA_KEY_NONE) { + continue; + } + if (hasPendingEvent(key)) { + continue; + } + if (SDL_JoystickGetButton(joystick, i)) { + activeButtons |= 1 << key; + } + } + int numHats = SDL_JoystickNumHats(joystick); + for (i = 0; i < numHats; ++i) { + int hat = SDL_JoystickGetHat(joystick, i); + activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat); + } + + int numAxes = SDL_JoystickNumAxes(joystick); + for (i = 0; i < numAxes; ++i) { + int value = SDL_JoystickGetAxis(joystick, i); + + enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value)); + if (key != GBA_KEY_NONE) { + activeButtons |= 1 << key; + } + } + } +#endif + return activeButtons; +} + +QSet<int> InputController::activeGamepadButtons(int type) { + QSet<int> activeButtons; +#ifdef BUILD_SDL + if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { + SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; + SDL_JoystickUpdate(); + int numButtons = SDL_JoystickNumButtons(joystick); + int i; + for (i = 0; i < numButtons; ++i) { + if (SDL_JoystickGetButton(joystick, i)) { + activeButtons.insert(i); + } + } + } +#endif + return activeButtons; +} + +void InputController::recalibrateAxes() { +#ifdef BUILD_SDL + if (m_playerAttached && m_sdlPlayer.joystick) { + SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; + SDL_JoystickUpdate(); + int numAxes = SDL_JoystickNumAxes(joystick); + if (numAxes < 1) { + return; + } + m_deadzones.resize(numAxes); + int i; + for (i = 0; i < numAxes; ++i) { + m_deadzones[i] = SDL_JoystickGetAxis(joystick, i); + } + } +#endif +} + +QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) { + QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes; +#ifdef BUILD_SDL + if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { + SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; + SDL_JoystickUpdate(); + int numAxes = SDL_JoystickNumAxes(joystick); + if (numAxes < 1) { + return activeAxes; + } + m_deadzones.resize(numAxes); + int i; + for (i = 0; i < numAxes; ++i) { + int32_t axis = SDL_JoystickGetAxis(joystick, i); + axis -= m_deadzones[i]; + if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) { + activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE)); + } + } + } +#endif + return activeAxes; +} + +void InputController::bindKey(uint32_t type, int key, const QString& keyName) { + InputItem* item = itemForKey(keyName); + if (type != KEYBOARD) { + item->setButton(key); + } else { + item->setShortcut(key); + } + if (m_activeKeyInfo) { + int coreKey = keyId(keyName); + mInputBindKey(&m_inputMap, type, key, coreKey); + } +} + +void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, const QString& key) { + InputItem* item = itemForKey(key); + item->setAxis(axis, direction); + + if (!m_activeKeyInfo) { + return; + } + + const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis); + mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD }; + if (old) { + description = *old; + } + int deadzone = 0; + if (axis > 0 && m_deadzones.size() > axis) { + deadzone = m_deadzones[axis]; + } + switch (direction) { + case GamepadAxisEvent::NEGATIVE: + description.lowDirection = keyId(key); + + description.deadLow = deadzone - AXIS_THRESHOLD; + break; + case GamepadAxisEvent::POSITIVE: + description.highDirection = keyId(key); + description.deadHigh = deadzone + AXIS_THRESHOLD; + break; + default: + return; + } + mInputBindAxis(&m_inputMap, type, axis, &description); +} + +QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) { + QSet<QPair<int, GamepadHatEvent::Direction>> activeHats; +#ifdef BUILD_SDL + if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { + SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; + SDL_JoystickUpdate(); + int numHats = SDL_JoystickNumHats(joystick); + if (numHats < 1) { + return activeHats; + } + + int i; + for (i = 0; i < numHats; ++i) { + int hat = SDL_JoystickGetHat(joystick, i); + if (hat & GamepadHatEvent::UP) { + activeHats.insert(qMakePair(i, GamepadHatEvent::UP)); + } + if (hat & GamepadHatEvent::RIGHT) { + activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT)); + } + if (hat & GamepadHatEvent::DOWN) { + activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN)); + } + if (hat & GamepadHatEvent::LEFT) { + activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT)); + } + } + } +#endif + return activeHats; +} + +void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, const QString& key) { + if (!m_activeKeyInfo) { + return; + } + + mInputHatBindings bindings{ -1, -1, -1, -1 }; + mInputQueryHat(&m_inputMap, type, hat, &bindings); + switch (direction) { + case GamepadHatEvent::UP: + bindings.up = keyId(key); + break; + case GamepadHatEvent::RIGHT: + bindings.right = keyId(key); + break; + case GamepadHatEvent::DOWN: + bindings.down = keyId(key); + break; + case GamepadHatEvent::LEFT: + bindings.left = keyId(key); + break; + default: + return; + } + mInputBindHat(&m_inputMap, type, hat, &bindings); +} + +void InputController::testGamepad(int type) { + auto activeAxes = activeGamepadAxes(type); + auto oldAxes = m_activeAxes; + m_activeAxes = activeAxes; + + auto activeButtons = activeGamepadButtons(type); + auto oldButtons = m_activeButtons; + m_activeButtons = activeButtons; + + auto activeHats = activeGamepadHats(type); + auto oldHats = m_activeHats; + m_activeHats = activeHats; + + if (!QApplication::focusWidget()) { + return; + } + + activeAxes.subtract(oldAxes); + oldAxes.subtract(m_activeAxes); + + for (auto& axis : m_activeAxes) { + bool newlyAboveThreshold = activeAxes.contains(axis); + if (newlyAboveThreshold) { + GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this); + postPendingEvent(event->gbaKey()); + sendGamepadEvent(event); + if (!event->isAccepted()) { + clearPendingEvent(event->gbaKey()); + } + } + } + for (auto axis : oldAxes) { + GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this); + clearPendingEvent(event->gbaKey()); + sendGamepadEvent(event); + } + + if (!QApplication::focusWidget()) { + return; + } + + activeButtons.subtract(oldButtons); + oldButtons.subtract(m_activeButtons); + + for (int button : activeButtons) { + GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this); + postPendingEvent(event->gbaKey()); + sendGamepadEvent(event); + if (!event->isAccepted()) { + clearPendingEvent(event->gbaKey()); + } + } + for (int button : oldButtons) { + GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this); + clearPendingEvent(event->gbaKey()); + sendGamepadEvent(event); + } + + activeHats.subtract(oldHats); + oldHats.subtract(m_activeHats); + + for (auto& hat : activeHats) { + GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this); + postPendingEvent(event->gbaKey()); + sendGamepadEvent(event); + if (!event->isAccepted()) { + clearPendingEvent(event->gbaKey()); + } + } + for (auto& hat : oldHats) { + GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this); + clearPendingEvent(event->gbaKey()); + sendGamepadEvent(event); + } +} + +void InputController::sendGamepadEvent(QEvent* event) { + QWidget* focusWidget = nullptr; + if (m_focusParent) { + focusWidget = m_focusParent->focusWidget(); + if (!focusWidget) { + focusWidget = m_focusParent; + } + } else { + focusWidget = QApplication::focusWidget(); + } + QApplication::sendEvent(focusWidget, event); +} + +void InputController::postPendingEvent(int key) { + m_pendingEvents.insert(key); +} + +void InputController::clearPendingEvent(int key) { + m_pendingEvents.remove(key); +} + +bool InputController::hasPendingEvent(int key) const { + return m_pendingEvents.contains(key); +} + +void InputController::suspendScreensaver() { +#ifdef BUILD_SDL +#if SDL_VERSION_ATLEAST(2, 0, 0) + mSDLSuspendScreensaver(&s_sdlEvents); +#endif +#endif +} + +void InputController::resumeScreensaver() { +#ifdef BUILD_SDL +#if SDL_VERSION_ATLEAST(2, 0, 0) + mSDLResumeScreensaver(&s_sdlEvents); +#endif +#endif +} + +void InputController::setScreensaverSuspendable(bool suspendable) { +#ifdef BUILD_SDL +#if SDL_VERSION_ATLEAST(2, 0, 0) + mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable); +#endif +#endif +} + +void InputController::stealFocus(QWidget* focus) { + m_focusParent = focus; +} + +void InputController::releaseFocus(QWidget* focus) { + if (focus == m_focusParent) { + m_focusParent = m_topLevel; + } +} + +bool InputController::eventFilter(QObject*, QEvent* event) { + event->ignore(); + if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { + QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); + int key = keyEvent->key(); + if (!InputIndex::isModifierKey(key)) { + key |= (keyEvent->modifiers() & ~Qt::KeypadModifier); + } else { + key = InputIndex::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier)); + } + + if (keyEvent->isAutoRepeat()) { + event->accept(); + return true; + } + + event->ignore(); + InputItem* item = m_inputIndex.itemForShortcut(key); + if (item) { + item->trigger(event->type() == QEvent::KeyPress); + event->accept(); + } + item = m_keyIndex.itemForShortcut(key); + if (item) { + item->trigger(event->type() == QEvent::KeyPress); + event->accept(); + } + } + + + if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) { + GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event); + InputItem* item = m_inputIndex.itemForButton(gbe->value()); + if (item) { + item->trigger(event->type() == GamepadButtonEvent::Down()); + event->accept(); + } + item = m_keyIndex.itemForButton(gbe->value()); + if (item) { + item->trigger(event->type() == GamepadButtonEvent::Down()); + event->accept(); + } + } + if (event->type() == GamepadAxisEvent::Type()) { + GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event); + InputItem* item = m_inputIndex.itemForAxis(gae->axis(), gae->direction()); + if (item) { + item->trigger(event->type() == gae->isNew()); + event->accept(); + } + item = m_keyIndex.itemForAxis(gae->axis(), gae->direction()); + if (item) { + item->trigger(event->type() == gae->isNew()); + event->accept(); + } + } + return event->isAccepted(); +} + +InputItem* InputController::itemForKey(const QString& key) { + return m_keyIndex.itemAt(QString("key%0").arg(key)); +} + +int InputController::keyId(const QString& key) { + for (int i = 0; i < m_activeKeyInfo->nKeys; ++i) { + if (m_activeKeyInfo->keyId[i] == key) { + return i; + } + } + return -1; +} + +void InputController::restoreModel() { + if (!m_activeKeyInfo) { + return; + } + int nKeys = m_inputMap.info->nKeys; + for (int i = 0; i < nKeys; ++i) { + const QString& keyName = m_inputMap.info->keyId[i]; + InputItem* item = itemForKey(keyName); + if (item) { + int key = mInputQueryBinding(&m_inputMap, KEYBOARD, i); + if (key >= 0) { + item->setShortcut(key); + } else { + item->clearShortcut(); + } +#ifdef BUILD_SDL + key = mInputQueryBinding(&m_inputMap, SDL_BINDING_BUTTON, i); + if (key >= 0) { + item->setButton(key); + } else { + item->clearButton(); + } +#endif + } + } +#ifdef BUILD_SDL + mInputEnumerateAxes(&m_inputMap, SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) { + InputController* controller = static_cast<InputController*>(user); + InputItem* item; + const mInputPlatformInfo* inputMap = controller->m_inputMap.info; + if (description->highDirection >= 0 && description->highDirection < controller->m_inputMap.info->nKeys) { + int id = description->lowDirection; + if (id >= 0 && id < inputMap->nKeys) { + item = controller->itemForKey(inputMap->keyId[id]); + if (item) { + item->setAxis(axis, GamepadAxisEvent::POSITIVE); + } + } + } + if (description->lowDirection >= 0 && description->lowDirection < controller->m_inputMap.info->nKeys) { + int id = description->highDirection; + if (id >= 0 && id < inputMap->nKeys) { + item = controller->itemForKey(inputMap->keyId[id]); + if (item) { + item->setAxis(axis, GamepadAxisEvent::NEGATIVE); + } + } + } + }, this); +#endif + rebuildKeyIndex(); +} + +void InputController::rebindKey(const QString& key) { + InputItem* item = itemForKey(key); + bindKey(KEYBOARD, item->shortcut(), key); +#ifdef BUILD_SDL + bindKey(SDL_BINDING_BUTTON, item->button(), key); + bindAxis(SDL_BINDING_BUTTON, item->axis(), item->direction(), key); +#endif +}
@@ -0,0 +1,250 @@
+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "InputIndex.h" + +#include "ConfigController.h" + +using namespace QGBA; + +void InputIndex::setConfigController(ConfigController* controller) { + m_config = controller; + for (auto& item : m_items) { + loadShortcuts(item); + } +} + +void InputIndex::clone(InputIndex* root, bool actions) { + if (!actions) { + clone(const_cast<const InputIndex*>(root)); + } else { + qDeleteAll(m_items); + m_items.clear(); + for (auto& item : root->m_items) { + InputItem* newItem = new InputItem(*item); + m_items.append(newItem); + itemAdded(newItem); + } + } + rebuild(); +} + +void InputIndex::clone(const InputIndex* root) { + qDeleteAll(m_items); + m_items.clear(); + for (auto& item : root->m_items) { + InputItem* newItem = new InputItem(*item); + m_items.append(newItem); + itemAdded(newItem); + } + rebuild(); +} + +void InputIndex::rebuild(const InputIndex* root) { + if (!root) { + root = this; + } + + m_names.clear(); + m_menus.clear(); + m_shortcuts.clear(); + m_buttons.clear(); + m_axes.clear(); + + for (auto& item : root->m_items) { + InputItem* newItem = nullptr; + for (auto &iter : m_items) { + if (*iter == *item) { + newItem = iter; + break; + } + } + if (!newItem) { + continue; + } + if (item->hasShortcut()) { + newItem->setShortcut(item->shortcut()); + } + if (item->hasButton()) { + newItem->setButton(item->button()); + } + if (item->hasAxis()) { + newItem->setAxis(item->axis(), item->direction()); + } + + itemAdded(newItem); + } +} + +InputItem* InputIndex::itemAt(const QString& name) { + return m_names[name]; +} + +const InputItem* InputIndex::itemAt(const QString& name) const { + return m_names[name]; +} + +InputItem* InputIndex::itemForMenu(const QMenu* menu) { + InputItem* item = m_menus[menu]; + return item; +} + +const InputItem* InputIndex::itemForMenu(const QMenu* menu) const { + const InputItem* item = m_menus[menu]; + return item; +} + +InputItem* InputIndex::itemForShortcut(int shortcut) { + return m_shortcuts[shortcut]; +} + +InputItem* InputIndex::itemForButton(int button) { + return m_buttons[button]; +} + +InputItem* InputIndex::itemForAxis(int axis, GamepadAxisEvent::Direction direction) { + return m_axes[qMakePair(axis, direction)]; +} + +bool InputIndex::loadShortcuts(InputItem* item) { + if (item->name().isNull()) { + return false; + } + loadGamepadShortcuts(item); + QVariant shortcut = m_config->getQtOption(item->name(), KEY_SECTION); + if (!shortcut.isNull()) { + if (shortcut.toString().endsWith("+")) { + item->setShortcut(toModifierShortcut(shortcut.toString())); + } else { + item->setShortcut(QKeySequence(shortcut.toString())[0]); + } + return true; + } + return false; +} + +void InputIndex::loadGamepadShortcuts(InputItem* item) { + if (item->name().isNull()) { + return; + } + QVariant button = m_config->getQtOption(item->name(), BUTTON_SECTION); + if (!button.isNull()) { + item->setButton(button.toInt()); + } + + QVariant axis = m_config->getQtOption(item->name(), AXIS_SECTION); + int oldAxis = item->axis(); + if (oldAxis >= 0) { + item->setAxis(-1, GamepadAxisEvent::NEUTRAL); + } + if (!axis.isNull()) { + QString axisDesc = axis.toString(); + if (axisDesc.size() >= 2) { + GamepadAxisEvent::Direction direction = GamepadAxisEvent::NEUTRAL; + if (axisDesc[0] == '-') { + direction = GamepadAxisEvent::NEGATIVE; + } + if (axisDesc[0] == '+') { + direction = GamepadAxisEvent::POSITIVE; + } + bool ok; + int axis = axisDesc.mid(1).toInt(&ok); + if (ok) { + item->setAxis(axis, direction); + } + } + } +} + +int InputIndex::toModifierShortcut(const QString& shortcut) { + // Qt doesn't seem to work with raw modifier shortcuts! + QStringList modifiers = shortcut.split('+'); + int value = 0; + for (const auto& mod : modifiers) { + if (mod == QLatin1String("Shift")) { + value |= Qt::ShiftModifier; + continue; + } + if (mod == QLatin1String("Ctrl")) { + value |= Qt::ControlModifier; + continue; + } + if (mod == QLatin1String("Alt")) { + value |= Qt::AltModifier; + continue; + } + if (mod == QLatin1String("Meta")) { + value |= Qt::MetaModifier; + continue; + } + } + return value; +} + +void InputIndex::itemAdded(InputItem* child) { + const QMenu* menu = child->menu(); + if (menu) { + m_menus[menu] = child; + } + m_names[child->name()] = child; + + if (child->shortcut() > 0) { + m_shortcuts[child->shortcut()] = child; + } + if (child->button() >= 0) { + m_buttons[child->button()] = child; + } + if (child->direction() != GamepadAxisEvent::NEUTRAL) { + m_axes[qMakePair(child->axis(), child->direction())] = child; + } +} + +bool InputIndex::isModifierKey(int key) { + switch (key) { + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Alt: + case Qt::Key_Meta: + return true; + default: + return false; + } +} + +int InputIndex::toModifierKey(int key) { + int modifiers = key & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier); + key ^= modifiers; + switch (key) { + case Qt::Key_Shift: + modifiers |= Qt::ShiftModifier; + break; + case Qt::Key_Control: + modifiers |= Qt::ControlModifier; + break; + case Qt::Key_Alt: + modifiers |= Qt::AltModifier; + break; + case Qt::Key_Meta: + modifiers |= Qt::MetaModifier; + break; + default: + break; + } + return modifiers; +} + +void InputIndex::saveConfig() { + for (auto& item : m_items) { + if (item->hasShortcut()) { + m_config->setQtOption(item->name(), QKeySequence(item->shortcut()).toString(), KEY_SECTION); + } + if (item->hasButton()) { + m_config->setQtOption(item->name(), item->button(), BUTTON_SECTION); + } + if (item->hasAxis()) { + m_config->setQtOption(item->name(), QString("%1%2").arg(GamepadAxisEvent::POSITIVE ? '+' : '-').arg(item->axis()), AXIS_SECTION); + } + } +}
@@ -0,0 +1,79 @@
+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_INPUT_INDEX +#define QGBA_INPUT_INDEX + +#include "GamepadAxisEvent.h" +#include "InputItem.h" + +#include <QMap> +#include <QString> + +class QMenu; + +namespace QGBA { + +class ConfigController; + +class InputIndex { +private: + constexpr static const char* const KEY_SECTION = "shortcutKey"; + constexpr static const char* const BUTTON_SECTION = "shortcutButton"; + constexpr static const char* const AXIS_SECTION = "shortcutAxis"; + constexpr static const char* const HAT_SECTION = "shortcutHat"; + +public: + void setConfigController(ConfigController* controller); + + void clone(InputIndex* root, bool actions = false); + void clone(const InputIndex* root); + void rebuild(const InputIndex* root = nullptr); + + const QList<InputItem*>& items() const { return m_items; } + + template<typename... Args> InputItem* addItem(Args... params) { + InputItem* newItem = new InputItem(params...); + m_items.append(newItem); + itemAdded(newItem); + return newItem; + } + + InputItem* itemAt(const QString& name); + const InputItem* itemAt(const QString& name) const; + + InputItem* itemForMenu(const QMenu* menu); + const InputItem* itemForMenu(const QMenu* menu) const; + + InputItem* itemForShortcut(int shortcut); + InputItem* itemForButton(int button); + InputItem* itemForAxis(int axis, GamepadAxisEvent::Direction); + + static int toModifierShortcut(const QString& shortcut); + static bool isModifierKey(int key); + static int toModifierKey(int key); + + void saveConfig(); + +private: + bool loadShortcuts(InputItem*); + void loadGamepadShortcuts(InputItem*); + + void itemAdded(InputItem*); + + QList<InputItem*> m_items; + + QMap<QString, InputItem*> m_names; + QMap<const QMenu*, InputItem*> m_menus; + QMap<int, InputItem*> m_shortcuts; + QMap<int, InputItem*> m_buttons; + QMap<QPair<int, GamepadAxisEvent::Direction>, InputItem*> m_axes; + + ConfigController* m_config = nullptr; +}; + +} + +#endif
@@ -0,0 +1,108 @@
+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "InputItem.h" + +#include <QMenu> + +using namespace QGBA; + +InputItem::InputItem() +{ +} + +InputItem::InputItem(QAction* action, const QString& name, QMenu* parent) + : QObject(parent) + , m_action(action) + , m_shortcut(action->shortcut().isEmpty() ? -2 : action->shortcut()[0]) + , m_name(name) + , m_menu(parent) +{ + m_visibleName = action->text() + .remove(QRegExp("&(?!&)")) + .remove("..."); +} + +InputItem::InputItem(InputItem::Functions functions, const QString& visibleName, const QString& name, QMenu* parent) + : QObject(parent) + , m_functions(functions) + , m_name(name) + , m_visibleName(visibleName) + , m_menu(parent) +{ +} + +InputItem::InputItem(const QString& visibleName, const QString& name, QMenu* parent) + : QObject(parent) + , m_name(name) + , m_visibleName(visibleName) + , m_menu(parent) +{ +} + +InputItem::InputItem(const InputItem& other) + : QObject(other.m_menu) + , m_name(other.m_name) + , m_visibleName(other.m_visibleName) + , m_shortcut(other.m_shortcut) + , m_button(other.m_button) + , m_axis(other.m_axis) + , m_direction(other.m_direction) + , m_menu(other.m_menu) +{ +} + +InputItem::InputItem(InputItem& other) + : QObject(other.m_menu) + , m_action(other.m_action) + , m_functions(other.m_functions) + , m_name(other.m_name) + , m_visibleName(other.m_visibleName) + , m_shortcut(other.m_shortcut) + , m_button(other.m_button) + , m_axis(other.m_axis) + , m_direction(other.m_direction) + , m_menu(other.m_menu) +{ +} + +void InputItem::setShortcut(int shortcut) { + m_shortcut = shortcut; + if (m_action) { + m_action->setShortcut(QKeySequence(shortcut)); + } + emit shortcutBound(this, shortcut); +} + +void InputItem::clearShortcut() { + setShortcut(0); +} + +void InputItem::setButton(int button) { + m_button = button; + emit buttonBound(this, button); +} + +void InputItem::clearButton() { + setButton(-1); +} + +void InputItem::setAxis(int axis, GamepadAxisEvent::Direction direction) { + m_axis = axis; + m_direction = direction; + emit axisBound(this, axis, direction); +} + +void InputItem::trigger(bool active) { + if (active) { + if (m_functions.first) { + m_functions.first(); + } + } else { + if (m_functions.second) { + m_functions.second(); + } + } +}
@@ -0,0 +1,89 @@
+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_INPUT_ITEM +#define QGBA_INPUT_ITEM + +#include "GamepadAxisEvent.h" +#include "GamepadHatEvent.h" + +#include <functional> + +#include <QAction> + +namespace QGBA { + +class InputItem : public QObject { +Q_OBJECT + +public: + typedef QPair<std::function<void ()>, std::function<void ()>> Functions; + + InputItem(QAction* action, const QString& name, QMenu* parent = nullptr); + InputItem(Functions functions, const QString& visibleName, const QString& name, + QMenu* parent = nullptr); + InputItem(const QString& visibleName, const QString& name, QMenu* parent = nullptr); + + InputItem(); + InputItem(InputItem&); + InputItem(const InputItem&); + + QAction* action() { return m_action; } + const QAction* action() const { return m_action; } + Functions functions() const { return m_functions; } + + QMenu* menu() { return m_menu; } + const QMenu* menu() const { return m_menu; } + + const QString& visibleName() const { return m_visibleName; } + const QString& name() const { return m_name; } + + int shortcut() const { return m_shortcut; } + void setShortcut(int sequence); + void clearShortcut(); + bool hasShortcut() { return m_shortcut > -2; } + + int button() const { return m_button; } + void setButton(int button); + void clearButton(); + bool hasButton() { return m_button > -2; } + + int axis() const { return m_axis; } + GamepadAxisEvent::Direction direction() const { return m_direction; } + void setAxis(int axis, GamepadAxisEvent::Direction direction); + bool hasAxis() { return m_axis > -2; } + + bool operator==(const InputItem& other) const { + return m_name == other.m_name; + } + +public slots: + void trigger(bool = true); + +signals: + void shortcutBound(InputItem*, int shortcut); + void buttonBound(InputItem*, int button); + void axisBound(InputItem*, int axis, GamepadAxisEvent::Direction); + void childAdded(InputItem* parent, InputItem* child); + +private: + QAction* m_action = nullptr; + Functions m_functions; + + QMenu* m_menu = nullptr; + QString m_name; + QString m_visibleName; + + int m_shortcut = -2; + int m_button = -2; + int m_axis = -2; + GamepadAxisEvent::Direction m_direction = GamepadAxisEvent::NEUTRAL; +}; + +} + +Q_DECLARE_METATYPE(QGBA::InputItem) + +#endif
@@ -0,0 +1,184 @@
+/* Copyright (c) 2013-2015 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 "InputModel.h" + +#include "GamepadButtonEvent.h" +#include "InputIndex.h" + +#include <QAction> +#include <QKeyEvent> +#include <QMenu> + +using namespace QGBA; + +QString InputModel::InputModelItem::visibleName() const { + if (item) { + return item->visibleName(); + } + if (menu) { + return menu->title() + .remove(QRegExp("&(?!&)")) + .remove("..."); + } + return QString(); +} + +InputModel::InputModel(const InputIndex& index, QObject* parent) + : QAbstractItemModel(parent) +{ + clone(index); +} + +InputModel::InputModel(QObject* parent) + : QAbstractItemModel(parent) +{ +} + +void InputModel::clone(const InputIndex& index) { + emit beginResetModel(); + m_index.clone(&index); + m_menus.clear(); + m_topLevelMenus.clear(); + QList<const QMenu*> menus; + for (auto& item : m_index.items()) { + const QMenu* menu = item->menu(); + if (!menus.contains(menu)) { + menus.append(menu); + m_menus.insert(menu); + } + } + for (auto& menu : menus) { + if (m_menus.contains(menu->parent())) { + m_tree[menu->parent()].append(menu); + } else { + m_topLevelMenus.append(menu); + } + } + for (auto& item : m_index.items()) { + const QMenu* menu = item->menu(); + m_tree[menu].append(item); + } + emit endResetModel(); +} + +QVariant InputModel::data(const QModelIndex& index, int role) const { + if (role != Qt::DisplayRole || !index.isValid()) { + return QVariant(); + } + int row = index.row(); + const InputModelItem* item = static_cast<const InputModelItem*>(index.internalPointer()); + switch (index.column()) { + case 0: + return item->visibleName(); + case 1: + if (item->item && item->item->shortcut() > 0) { + return QKeySequence(item->item->shortcut()).toString(QKeySequence::NativeText); + } + break; + case 2: + if (!item->item) { + break; + } + if (item->item->button() >= 0) { + return item->item->button(); + } + if (item->item->axis() >= 0) { + char d = '\0'; + if (item->item->direction() == GamepadAxisEvent::POSITIVE) { + d = '+'; + } + if (item->item->direction() == GamepadAxisEvent::NEGATIVE) { + d = '-'; + } + return QString("%1%2").arg(d).arg(item->item->axis()); + } + break; + } + return QVariant(); +} + +QVariant InputModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (role != Qt::DisplayRole) { + return QAbstractItemModel::headerData(section, orientation, role); + } + if (orientation == Qt::Horizontal) { + switch (section) { + case 0: + return tr("Action"); + case 1: + return tr("Keyboard"); + case 2: + return tr("Gamepad"); + } + } + return section; +} + +QModelIndex InputModel::index(int row, int column, const QModelIndex& parent) const { + if (parent.isValid()) { + InputModelItem* p = static_cast<InputModelItem*>(parent.internalPointer()); + return createIndex(row, column, const_cast<InputModelItem*>(&m_tree[p->obj][row])); + } + return createIndex(row, column, const_cast<InputModelItem*>(&m_topLevelMenus[row])); +} + +QModelIndex InputModel::parent(const QModelIndex& index) const { + if (!index.isValid() || !index.internalPointer()) { + return QModelIndex(); + } + const QObject* obj = static_cast<const InputModelItem*>(index.internalPointer())->obj; + if (m_menus.contains(obj->parent())) { + return this->index(obj->parent()); + } else { + return QModelIndex(); + } +} + +QModelIndex InputModel::index(const QObject* item, int column) const { + if (!item) { + return QModelIndex(); + } + const QObject* parent = item->parent(); + if (parent && m_tree.contains(parent)) { + int index = m_tree[parent].indexOf(item); + return createIndex(index, column, const_cast<InputModelItem*>(&m_tree[parent][index])); + } + if (m_topLevelMenus.contains(item)) { + int index = m_topLevelMenus.indexOf(item); + return createIndex(index, column, const_cast<InputModelItem*>(&m_topLevelMenus[index])); + } + return QModelIndex(); +} + +int InputModel::columnCount(const QModelIndex& index) const { + return 3; +} + +int InputModel::rowCount(const QModelIndex& index) const { + if (!index.isValid() || !index.internalPointer()) { + return m_topLevelMenus.count(); + } + const QObject* obj = static_cast<const InputModelItem*>(index.internalPointer())->obj; + if (!m_tree.contains(obj)) { + return 0; + } + return m_tree[obj].count(); +} + +InputItem* InputModel::itemAt(const QModelIndex& index) { + if (!index.isValid() || !index.internalPointer()) { + return nullptr; + } + return static_cast<InputModelItem*>(index.internalPointer())->item; +} + +const InputItem* InputModel::itemAt(const QModelIndex& index) const { + if (!index.isValid() || !index.internalPointer()) { + return nullptr; + } + return static_cast<const InputModelItem*>(index.internalPointer())->item; + +}
@@ -0,0 +1,80 @@
+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_INPUT_MODEL +#define QGBA_INPUT_MODEL + +#include <mgba/core/core.h> + +#include "InputIndex.h" + +#include <QAbstractItemModel> +#include <QMenu> +#include <QSet> + +#include <functional> + +class QAction; +class QKeyEvent; +class QMenu; +class QString; + +namespace QGBA { + +class ConfigController; +class InputIndex; +class InputProfile; + +class InputModel : public QAbstractItemModel { +Q_OBJECT + +public: + InputModel(const InputIndex& index, QObject* parent = nullptr); + InputModel(QObject* parent = nullptr); + + void clone(const InputIndex& index); + + void setProfile(const QString& profile); + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override; + virtual QModelIndex parent(const QModelIndex& index) const override; + + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + + InputItem* itemAt(const QModelIndex& index); + const InputItem* itemAt(const QModelIndex& index) const; + + InputIndex* inputIndex() { return &m_index; } + +private: + struct InputModelItem { + InputModelItem(InputItem* i) : item(i), obj(i) {} + InputModelItem(const QMenu* i) : menu(i), obj(i) {} + InputModelItem(const QObject* i) : obj(i) {} + + InputItem* item = nullptr; + const QMenu* menu = nullptr; + const QObject* obj; + + QString visibleName() const; + bool operator==(const InputModelItem& other) { return obj == other.obj; } + }; + + QModelIndex index(const QObject* item, int column = 0) const; + + InputIndex m_index; + QList<InputModelItem> m_topLevelMenus; + QSet<const QObject*> m_menus; + QMap<const QObject*, QList<InputModelItem>> m_tree; + QString m_profileName; +}; + +} + +#endif
@@ -0,0 +1,99 @@
+/* Copyright (c) 2013-2015 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 "InputProfile.h" + +#include "InputController.h" + +#include <QSettings> + +using namespace QGBA; + +QList<InputProfile> InputProfile::s_profiles; + +InputProfile::InputProfile(const QString& name) + : m_profileName(name) +{ +} + +void InputProfile::loadDefaultProfiles() { + loadProfiles(":/input/default-profiles.ini"); +} + +void InputProfile::loadProfiles(const QString& path) { + QSettings profileIni(path, QSettings::IniFormat); + + for (const auto& group : profileIni.childGroups()) { + } + profileIni.beginGroup(PROFILE_SECTION); + for (const auto& group : profileIni.childGroups()) { + loadProfile(profileIni, group); + } + profileIni.endGroup(); +} + +void InputProfile::loadProfile(QSettings& profileIni, const QString& name) { + profileIni.beginGroup(name); + s_profiles.append(name); + InputProfile& profile = s_profiles.last(); + for (const auto& group : profileIni.childGroups()) { + profileIni.beginGroup(group); + if (group == MATCH_SECTION) { + for (const auto& key : profileIni.childKeys()) { + profile.m_match.append(QRegExp(profileIni.value(key).toString())); + } + } + for (const auto& key : profileIni.childKeys()) { + InputItem* item = profile.m_inputIndex.itemAt(key); + if (!item) { + item = profile.m_inputIndex.addItem(QString(), key); + } + if (group == BUTTON_SECTION) { + item->setButton(profileIni.value(key).toInt()); + } + if (group == AXIS_SECTION) { + QString axisDescription = profileIni.value(key).toString(); + GamepadAxisEvent::Direction direction = GamepadAxisEvent::POSITIVE; + int axis = profileIni.value(key).toInt(); + if (axisDescription[0] == '-') { + direction = GamepadAxisEvent::NEGATIVE; + axis = -axis; + } + + item->setAxis(axis, direction); + } + if (group == KEY_SECTION) { + item->setShortcut(profileIni.value(key).toInt()); + } + } + profileIni.endGroup(); + } + profile.m_inputIndex.rebuild(); + profileIni.endGroup(); +} + +const InputProfile* InputProfile::findProfile(const QString& name) { + if (s_profiles.isEmpty()) { + loadDefaultProfiles(); + } + for (const InputProfile& profile : s_profiles) { + for (const auto& match : profile.m_match) { + if (match.exactMatch(name)) { + return &profile; + } + } + } + return nullptr; +} + +void InputProfile::apply(InputController* controller) const { + controller->rebuildIndex(&m_inputIndex); + controller->rebuildKeyIndex(&m_inputIndex); + controller->registerTiltAxisX(m_tiltAxis.x); + controller->registerTiltAxisY(m_tiltAxis.y); + controller->registerGyroAxisX(m_gyroAxis.x); + controller->registerGyroAxisY(m_gyroAxis.y); + controller->setGyroSensitivity(m_gyroSensitivity); +}
@@ -0,0 +1,60 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_INPUT_PROFILE +#define QGBA_INPUT_PROFILE + +#include "GamepadAxisEvent.h" +#include "InputIndex.h" + +#include <mgba/core/core.h> +#include <mgba/gba/interface.h> + +#include <QRegExp> + +class QSettings; + +namespace QGBA { + +class InputController; + +class InputProfile { +public: + constexpr static const char* const PROFILE_SECTION = "profiles"; + constexpr static const char* const MATCH_SECTION = "match"; + constexpr static const char* const KEY_SECTION = "keys"; + constexpr static const char* const BUTTON_SECTION = "buttons"; + constexpr static const char* const AXIS_SECTION = "axes"; + constexpr static const char* const HAT_SECTION = "hats"; + + static const InputProfile* findProfile(const QString& name); + static void loadProfiles(const QString& path); + + void apply(InputController*) const; +private: + InputProfile(const QString&); + + struct Coord { + int x; + int y; + }; + + static void loadDefaultProfiles(); + static void loadProfile(QSettings&, const QString& name); + + static QList<InputProfile> s_profiles; + + QString m_profileName; + QList<QRegExp> m_match; + + Coord m_tiltAxis = { 2, 3 }; + Coord m_gyroAxis = { 0, 1 }; + float m_gyroSensitivity = 2e+09f; + InputIndex m_inputIndex; +}; + +} + +#endif
@@ -0,0 +1,114 @@
+[profiles/XInput (Windows)/match] +0=XInput Controller #\\d+ + +[profiles/XInput (Windows)/buttons] +keyA=11 +keyB=10 +keySelect=5 +keyStart=4 +keyRight=3 +keyLeft=2 +keyUp=0 +keyDown=1 +keyR=9 +keyL=8 +loadState=12 +saveState=13 + +[profiles/XInput (Windows)/axes] +holdFastForward=+5 +holdRewind=+4 + +[profiles/XInput (Linux)/match] +0=Microsoft X-Box 360 pad +1=Xbox Gamepad \\(userspace driver\\) + +[profiles/XInput (Linux)/buttons] +keyA=1 +keyB=0 +keySelect=6 +keyStart=7 +keyR=5 +keyL=4 +loadState=2 +saveState=3 + +[profiles/XInput (Linux)/axes] +holdFastForward=+5 +holdRewind=+2 + +[profiles/XInput (macOS)/match] +0=Xbox 360 Wired Controller + +[profiles/XInput (macOS)/buttons] +keyA=1 +keyB=0 +keySelect=9 +keyStart=8 +keyRight=14 +keyLeft=13 +keyUp=11 +keyDown=12 +keyR=5 +keyL=4 +loadState=2 +saveState=3 + +[profiles/XInput (macOS)/axes] +holdFastForward=+5 +holdRewind=+2 + + +[profiles/DualShock 4/match] +0=Sony Computer Entertainment Wireless Controller +1=Wireless Controller + +[profiles/DualShock 4/buttons] +keyA=1 +keyB=2 +keySelect=8 +keyStart=9 +keyR=5 +keyL=4 +loadState=0 +saveState=3 +holdFastForward=7 +holdRewind=6 + +[profiles/DualShock 3/match] +0=PLAYSTATION\\(R\\)3 Controller + +[profiles/DualShock 3/buttons] +keyA=13 +keyB=14 +keySelect=0 +keyStart=3 +keyRight=5 +keyLeft=7 +keyUp=4 +keyDown=6 +keyR=11 +keyL=10 +loadState=15 +saveState=12 +holdFastForward=9 +holdRewind=8 + +[profiles/Wii Remote/match] +0=Wiimote \\(..-..-..-..-..-..\\) + +[profiles/Wii Remote/buttons] +keyA=15 +keyB=16 +keySelect=7 +keyStart=6 +keyRight=14 +keyLeft=13 +keyUp=11 +keyDown=12 +keyR=20 +keyL=19 +loadState=18 +saveState=17 +holdFastForward=22 +holdRewind=21
@@ -5,7 +5,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "LibraryController.h" -#include "../GBAApp.h" +#include "ConfigController.h" +#include "GBAApp.h" #include "LibraryGrid.h" #include "LibraryTree.h"@@ -24,7 +25,7 @@ }
} void AbstractGameList::removeEntries(QList<LibraryEntryRef> items) { for (LibraryEntryRef o : items) { - addEntry(o); + removeEntry(o); } }@@ -45,8 +46,10 @@ {
mLibraryListingInit(&m_listing, 0); if (!path.isNull()) { + // This can return NULL if the library is already open m_library = mLibraryLoad(path.toUtf8().constData()); - } else { + } + if (!m_library) { m_library = mLibraryCreateEmpty(); }@@ -128,6 +131,20 @@ m_loaderThread.m_library = m_library;
// The m_loaderThread temporarily owns the library m_library = nullptr; m_loaderThread.start(); +} + +void LibraryController::clear() { + if (!m_library) { + if (!m_loaderThread.isRunning() && m_loaderThread.m_library) { + m_library = m_loaderThread.m_library; + m_loaderThread.m_library = nullptr; + } else { + return; + } + } + + mLibraryClear(m_library); + refresh(); } void LibraryController::refresh() {
@@ -100,6 +100,9 @@ void selectLastBootedGame();
void addDirectory(const QString& dir); +public slots: + void clear(); + signals: void startGame(); void doneLoading();
@@ -7,13 +7,15 @@
// This must be defined before anything else is included. #define SDL_MAIN_HANDLED +#include "ConfigController.h" #include "GBAApp.h" #include "Window.h" +#include <mgba/core/version.h> +#include <mgba/internal/gba/video.h> + #include <QLibraryInfo> #include <QTranslator> - -#include <mgba/core/version.h> #ifdef QT_STATIC #include <QtPlugin>@@ -25,13 +27,32 @@ #endif
#endif #endif +using namespace QGBA; + int main(int argc, char* argv[]) { #ifdef BUILD_SDL SDL_SetMainReady(); #endif - QGBA::GBAApp application(argc, argv); - QLocale locale = QLocale::system(); + ConfigController configController; + + QLocale locale; + if (!configController.getQtOption("language").isNull()) { + locale = QLocale(configController.getQtOption("language").toString()); + QLocale::setDefault(locale); + } + + mArguments args; + mGraphicsOpts graphicsOpts; + mSubParser subparser; + initParserForGraphics(&subparser, &graphicsOpts); + bool loaded = configController.parseArguments(&args, argc, argv, &subparser); + if (loaded && args.showHelp) { + usage(argv[0], subparser.usage); + return 0; + } + + GBAApp application(argc, argv, &configController); QTranslator qtTranslator; qtTranslator.load(locale, "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath));@@ -40,6 +61,23 @@
QTranslator langTranslator; langTranslator.load(locale, binaryName, "-", ":/translations/"); application.installTranslator(&langTranslator); + + Window* w = application.newWindow(); + if (loaded) { + w->argumentsPassed(&args); + } else { + w->loadConfig(); + } + freeArguments(&args); + + if (graphicsOpts.multiplier) { + w->resizeFrame(QSize(VIDEO_HORIZONTAL_PIXELS * graphicsOpts.multiplier, VIDEO_VERTICAL_PIXELS * graphicsOpts.multiplier)); + } + if (graphicsOpts.fullscreen) { + w->enterFullScreen(); + } + + w->show(); return application.exec(); }
@@ -2,5 +2,6 @@ <!DOCTYPE RCC><RCC version="1.0">
<qresource> <file>../../../res/medusa-bg.jpg</file> <file>../../../res/patrons.txt</file> + <file>input/default-profiles.ini</file> </qresource> </RCC>
@@ -455,6 +455,105 @@ <translation>Max. Zeilen</translation>
</message> </context> <context> + <name>MemorySearch</name> + <message> + <location filename="../MemorySearch.ui" line="20"/> + <source>Form</source> + <translation>Eingabemaske</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="45"/> + <source>Address</source> + <translation>Adresse</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="50"/> + <source>Current Value</source> + <translation>Aktueller Wert</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="55"/> + <location filename="../MemorySearch.ui" line="75"/> + <source>Type</source> + <translation>Typ</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="65"/> + <source>Value</source> + <translation>Wert</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="82"/> + <source>Numeric</source> + <translation>Numerisch</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="95"/> + <source>Text</source> + <translation>Text</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="105"/> + <source>Width</source> + <translation>Breite</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="112"/> + <source>1 Byte (8-bit)</source> + <translation>1 Byte (8-bit)</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="122"/> + <source>2 Bytes (16-bit)</source> + <translation>2 Bytes (16-bit)</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="132"/> + <source>4 Bytes (32-bit)</source> + <translation>4 Bytes (32-bit)</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="145"/> + <source>Number type</source> + <translation>Zahlensystem</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="152"/> + <source>Hexadecimal</source> + <translation>Hexadezimal</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="162"/> + <source>Decimal</source> + <translation>Dezimal</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="169"/> + <source>Guess</source> + <translation>automatisch</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="187"/> + <source>Search</source> + <translation>Suchen</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="194"/> + <source>Search Within</source> + <translation>Suchen innerhalb</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="201"/> + <source>Open in Memory Viewer</source> + <translation>Im Speicher-Monitor öffnen</translation> + </message> + <message> + <location filename="../MemorySearch.ui" line="208"/> + <source>Refresh</source> + <translation>Aktualisieren</translation> + </message> +</context> +<context> <name>MemoryView</name> <message> <location filename="../MemoryView.ui" line="14"/>@@ -834,6 +933,11 @@ <location filename="../OverrideView.ui" line="324"/>
<source>HuC-3</source> <translation>HuC-3</translation> </message> + <message> + <location filename="../OverrideView.ui" line="332"/> + <source>Colors</source> + <translation>Farben</translation> + </message> </context> <context> <name>PaletteView</name>@@ -1042,28 +1146,28 @@ </context>
<context> <name>QGBA::GameController</name> <message> - <location filename="../GameController.cpp" line="350"/> - <location filename="../GameController.cpp" line="534"/> + <location filename="../GameController.cpp" line="351"/> + <location filename="../GameController.cpp" line="535"/> <source>Failed to open game file: %1</source> <translation>Fehler beim Öffnen der Spieldatei: %1</translation> </message> <message> - <location filename="../GameController.cpp" line="506"/> + <location filename="../GameController.cpp" line="507"/> <source>Failed to open save file: %1</source> <translation>Fehler beim Öffnen der Speicherdatei: %1</translation> </message> <message> - <location filename="../GameController.cpp" line="563"/> + <location filename="../GameController.cpp" line="564"/> <source>Failed to open snapshot file for reading: %1</source> <translation>Konnte Snapshot-Datei %1 nicht zum Lesen öffnen</translation> </message> <message> - <location filename="../GameController.cpp" line="583"/> + <location filename="../GameController.cpp" line="584"/> <source>Failed to open snapshot file for writing: %1</source> <translation>Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen</translation> </message> <message> - <location filename="../GameController.cpp" line="895"/> + <location filename="../GameController.cpp" line="850"/> <source>Failed to start audio processor</source> <translation>Fehler beim Starten des Audio-Prozessors</translation> </message>@@ -2499,7 +2603,7 @@ <source>Corrupted</source>
<translation>Defekt</translation> </message> <message> - <location filename="../LoadSaveState.cpp" line="209"/> + <location filename="../LoadSaveState.cpp" line="215"/> <source>Slot %1</source> <translation>Speicherplatz %1</translation> </message>@@ -2607,56 +2711,79 @@ <translation>ISO-8859-1</translation>
</message> </context> <context> + <name>QGBA::MemorySearch</name> + <message> + <location filename="../MemorySearch.cpp" line="188"/> + <source> (⅟%0×)</source> + <translation> (⅟%0×)</translation> + </message> + <message> + <location filename="../MemorySearch.cpp" line="192"/> + <source>1 byte%0</source> + <translation>1 Byte%0</translation> + </message> + <message> + <location filename="../MemorySearch.cpp" line="195"/> + <source>2 bytes%0</source> + <translation>2 Bytes%0</translation> + </message> + <message> + <location filename="../MemorySearch.cpp" line="199"/> + <source>4 bytes%0</source> + <translation>4 Bytes%0</translation> + </message> +</context> +<context> <name>QGBA::ObjView</name> <message> - <location filename="../ObjView.cpp" line="142"/> - <location filename="../ObjView.cpp" line="234"/> + <location filename="../ObjView.cpp" line="143"/> + <location filename="../ObjView.cpp" line="236"/> <source>0x%0</source> <translation>0x%0</translation> </message> <message> - <location filename="../ObjView.cpp" line="153"/> + <location filename="../ObjView.cpp" line="154"/> <source>Off</source> <translation>Aus</translation> </message> <message> - <location filename="../ObjView.cpp" line="158"/> + <location filename="../ObjView.cpp" line="159"/> <source>Normal</source> <translation>Normal</translation> </message> <message> - <location filename="../ObjView.cpp" line="161"/> + <location filename="../ObjView.cpp" line="162"/> <source>Trans</source> <translation>Trans</translation> </message> <message> - <location filename="../ObjView.cpp" line="164"/> + <location filename="../ObjView.cpp" line="165"/> <source>OBJWIN</source> <translation>OBJWIN</translation> </message> <message> - <location filename="../ObjView.cpp" line="167"/> + <location filename="../ObjView.cpp" line="168"/> <source>Invalid</source> <translation>Ungültig</translation> </message> <message> - <location filename="../ObjView.cpp" line="241"/> - <location filename="../ObjView.cpp" line="242"/> + <location filename="../ObjView.cpp" line="243"/> + <location filename="../ObjView.cpp" line="244"/> <source>N/A</source> <translation>N/A</translation> </message> <message> - <location filename="../ObjView.cpp" line="249"/> + <location filename="../ObjView.cpp" line="251"/> <source>Export sprite</source> <translation>Sprite exportieren</translation> </message> <message> - <location filename="../ObjView.cpp" line="250"/> + <location filename="../ObjView.cpp" line="252"/> <source>Portable Network Graphics (*.png)</source> <translation>Portable Network Graphics (*.png)</translation> </message> <message> - <location filename="../ObjView.cpp" line="253"/> + <location filename="../ObjView.cpp" line="255"/> <source>Failed to open output PNG file: %1</source> <translation>Fehler beim Öffnen der Ausgabe-PNG-Datei: %1</translation> </message>@@ -2716,7 +2843,7 @@ <message>
<location filename="../ROMInfo.cpp" line="57"/> <location filename="../ROMInfo.cpp" line="64"/> <source> bytes</source> - <translation>Bytes</translation> + <translation> Bytes</translation> </message> <message> <location filename="../ROMInfo.cpp" line="83"/>@@ -2727,37 +2854,47 @@ </context>
<context> <name>QGBA::SettingsView</name> <message> - <location filename="../SettingsView.cpp" line="95"/> + <location filename="../SettingsView.cpp" line="98"/> <source>Qt Multimedia</source> <translation>Qt Multimedia</translation> </message> <message> - <location filename="../SettingsView.cpp" line="102"/> + <location filename="../SettingsView.cpp" line="105"/> <source>SDL</source> <translation>SDL</translation> </message> <message> - <location filename="../SettingsView.cpp" line="110"/> + <location filename="../SettingsView.cpp" line="113"/> <source>Software (Qt)</source> <translation>Software (Qt)</translation> </message> <message> - <location filename="../SettingsView.cpp" line="116"/> + <location filename="../SettingsView.cpp" line="119"/> <source>OpenGL</source> <translation>OpenGL</translation> </message> <message> - <location filename="../SettingsView.cpp" line="123"/> + <location filename="../SettingsView.cpp" line="126"/> <source>OpenGL (force version 1.x)</source> <translation>OpenGL (erzwinge Version 1.x)</translation> </message> <message> - <location filename="../SettingsView.cpp" line="154"/> - <source>Bindings</source> - <translation>Tastenbelegung</translation> + <location filename="../SettingsView.cpp" line="144"/> + <source>Keyboard</source> + <translation>Tastatur</translation> </message> <message> - <location filename="../SettingsView.cpp" line="158"/> + <location filename="../SettingsView.cpp" line="153"/> + <source>Controllers</source> + <translation>Gamepads</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="185"/> + <source>Shortcuts</source> + <translation>Tastenkürzel</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="189"/> <source>Select BIOS</source> <translation>BIOS auswählen</translation> </message>@@ -2821,67 +2958,66 @@ </context>
<context> <name>QGBA::Window</name> <message> - <location filename="../Window.cpp" line="369"/> + <location filename="../Window.cpp" line="329"/> <source>Game Boy Advance ROMs (%1)</source> <translation>Game Boy Advance-ROMs (%1)</translation> </message> - <message> <location filename="../Window.cpp" line="385"/> <source>DS ROMs (%1)</source> <translation>DS-ROMs (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="401"/> + <location filename="../Window.cpp" line="345"/> <source>Game Boy ROMs (%1)</source> <translation>Game Boy-ROMs (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="405"/> + <location filename="../Window.cpp" line="349"/> <source>All ROMs (%1)</source> <translation>Alle ROMs (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="345"/> + <location filename="../Window.cpp" line="350"/> <source>%1 Video Logs (*.mvl)</source> <translation>%1 Video-Logs (*.mvl)</translation> </message> <message> - <location filename="../Window.cpp" line="360"/> + <location filename="../Window.cpp" line="365"/> <source>Archives (%1)</source> <translation>Archive (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="425"/> - <location filename="../Window.cpp" line="433"/> - <location filename="../Window.cpp" line="460"/> + <location filename="../Window.cpp" line="370"/> + <location filename="../Window.cpp" line="378"/> + <location filename="../Window.cpp" line="405"/> <source>Select ROM</source> <translation>ROM auswählen</translation> </message> <message> - <location filename="../Window.cpp" line="468"/> + <location filename="../Window.cpp" line="413"/> <source>Game Boy Advance save files (%1)</source> <translation>Game Boy Advance-Speicherdateien (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="469"/> - <location filename="../Window.cpp" line="503"/> - <location filename="../Window.cpp" line="510"/> + <location filename="../Window.cpp" line="414"/> + <location filename="../Window.cpp" line="448"/> + <location filename="../Window.cpp" line="455"/> <source>Select save</source> <translation>Speicherdatei wählen</translation> </message> <message> - <location filename="../Window.cpp" line="489"/> + <location filename="../Window.cpp" line="434"/> <source>Select patch</source> <translation>Patch wählen</translation> </message> <message> - <location filename="../Window.cpp" line="489"/> + <location filename="../Window.cpp" line="434"/> <source>Patches (*.ips *.ups *.bps)</source> <translation>Patches (*.ips *.ups *.bps)</translation> </message> <message> - <location filename="../Window.cpp" line="503"/> - <location filename="../Window.cpp" line="510"/> + <location filename="../Window.cpp" line="448"/> + <location filename="../Window.cpp" line="455"/> <source>GameShark saves (*.sps *.xps)</source> <translation>GameShark-Speicherdaten (*.sps *.xps)</translation> </message>@@ -2896,21 +3032,22 @@ <source>DS support requires dumps of the BIOS and firmware.</source>
<translation>DS-Unterstützung erfordert ein Abbild des BIOS und der Firmware.</translation> </message> <message> + <location filename="../Window.cpp" line="478"/> <source>Select video log</source> <translation>Video-Log auswählen</translation> </message> <message> - <location filename="../Window.cpp" line="471"/> + <location filename="../Window.cpp" line="478"/> <source>Video logs (*.mvl)</source> <translation>Video-Logs (*.mvl)</translation> </message> <message> - <location filename="../Window.cpp" line="813"/> + <location filename="../Window.cpp" line="820"/> <source>Crash</source> <translation>Absturz</translation> </message> <message> - <location filename="../Window.cpp" line="883"/> + <location filename="../Window.cpp" line="821"/> <source>The game has crashed with the following error: %1</source>@@ -2919,523 +3056,523 @@
%1</translation> </message> <message> - <location filename="../Window.cpp" line="890"/> + <location filename="../Window.cpp" line="828"/> <source>Couldn't Load</source> <translation>Konnte nicht geladen werden</translation> </message> <message> - <location filename="../Window.cpp" line="891"/> + <location filename="../Window.cpp" line="829"/> <source>Could not load game. Are you sure it's in the correct format?</source> <translation>Konnte das Spiel nicht laden. Sind Sie sicher, dass es im korrekten Format vorliegt?</translation> </message> <message> - <location filename="../Window.cpp" line="904"/> + <location filename="../Window.cpp" line="842"/> <source>Unimplemented BIOS call</source> <translation>Nicht implementierter BIOS-Aufruf</translation> </message> <message> - <location filename="../Window.cpp" line="905"/> + <location filename="../Window.cpp" line="843"/> <source>This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience.</source> <translation>Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS.</translation> </message> <message> - <location filename="../Window.cpp" line="912"/> + <location filename="../Window.cpp" line="850"/> <source>Really make portable?</source> <translation>Portablen Modus wirklich aktivieren?</translation> </message> <message> - <location filename="../Window.cpp" line="913"/> + <location filename="../Window.cpp" line="851"/> <source>This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?</source> <translation>Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren?</translation> </message> <message> - <location filename="../Window.cpp" line="921"/> + <location filename="../Window.cpp" line="859"/> <source>Restart needed</source> <translation>Neustart benötigt</translation> </message> <message> - <location filename="../Window.cpp" line="922"/> + <location filename="../Window.cpp" line="860"/> <source>Some changes will not take effect until the emulator is restarted.</source> <translation>Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde.</translation> </message> <message> - <location filename="../Window.cpp" line="969"/> + <location filename="../Window.cpp" line="907"/> <source> - Player %1 of %2</source> <translation> - Spieler %1 von %2</translation> </message> <message> - <location filename="../Window.cpp" line="980"/> + <location filename="../Window.cpp" line="918"/> <source>%1 - %2</source> <translation>%1 - %2</translation> </message> <message> - <location filename="../Window.cpp" line="982"/> + <location filename="../Window.cpp" line="920"/> <source>%1 - %2 - %3</source> <translation>%1 - %2 - %3</translation> </message> <message> - <location filename="../Window.cpp" line="984"/> + <location filename="../Window.cpp" line="922"/> <source>%1 - %2 (%3 fps) - %4</source> <translation>%1 - %2 (%3 Bilder/Sekunde) - %4</translation> </message> <message> - <location filename="../Window.cpp" line="1016"/> + <location filename="../Window.cpp" line="954"/> <source>&File</source> <translation>&Datei</translation> </message> <message> - <location filename="../Window.cpp" line="1019"/> + <location filename="../Window.cpp" line="957"/> <source>Load &ROM...</source> <translation>&ROM laden...</translation> </message> <message> - <location filename="../Window.cpp" line="1022"/> + <location filename="../Window.cpp" line="960"/> <source>Load ROM in archive...</source> <translation>ROM aus Archiv laden...</translation> </message> <message> - <location filename="../Window.cpp" line="1028"/> + <location filename="../Window.cpp" line="966"/> <source>Load temporary save...</source> <translation>Temporäre Speicherdatei laden...</translation> </message> <message> - <location filename="../Window.cpp" line="1033"/> + <location filename="../Window.cpp" line="971"/> <source>Load &patch...</source> <translation>&Patch laden...</translation> </message> <message> - <location filename="../Window.cpp" line="1035"/> + <location filename="../Window.cpp" line="973"/> <source>Boot BIOS</source> <translation>BIOS booten</translation> </message> <message> - <location filename="../Window.cpp" line="1042"/> + <location filename="../Window.cpp" line="980"/> <source>Replace ROM...</source> <translation>ROM ersetzen...</translation> </message> <message> - <location filename="../Window.cpp" line="1044"/> + <location filename="../Window.cpp" line="982"/> <source>ROM &info...</source> <translation>ROM-&Informationen...</translation> </message> <message> - <location filename="../Window.cpp" line="1049"/> + <location filename="../Window.cpp" line="987"/> <source>Recent</source> <translation>Zuletzt verwendet</translation> </message> <message> - <location filename="../Window.cpp" line="1053"/> + <location filename="../Window.cpp" line="991"/> <source>Make portable</source> <translation>Portablen Modus aktivieren</translation> </message> <message> - <location filename="../Window.cpp" line="1057"/> + <location filename="../Window.cpp" line="995"/> <source>&Load state</source> <translation>Savestate &laden</translation> </message> <message> - <location filename="../Window.cpp" line="1058"/> + <location filename="../Window.cpp" line="996"/> <source>F10</source> <translation>F10</translation> </message> <message> - <location filename="../Window.cpp" line="1065"/> + <location filename="../Window.cpp" line="1002"/> <source>&Save state</source> <translation>Savestate &speichern</translation> </message> <message> - <location filename="../Window.cpp" line="1066"/> + <location filename="../Window.cpp" line="1003"/> <source>Shift+F10</source> <translation>Umschalt+F10</translation> </message> <message> - <location filename="../Window.cpp" line="1073"/> + <location filename="../Window.cpp" line="1009"/> <source>Quick load</source> <translation>Schnell laden</translation> </message> <message> - <location filename="../Window.cpp" line="1074"/> + <location filename="../Window.cpp" line="1010"/> <source>Quick save</source> <translation>Schnell speichern</translation> </message> <message> - <location filename="../Window.cpp" line="1078"/> + <location filename="../Window.cpp" line="1014"/> <source>Load recent</source> <translation>Lade zuletzt gespeicherten Savestate</translation> </message> <message> - <location filename="../Window.cpp" line="1085"/> + <location filename="../Window.cpp" line="1020"/> <source>Save recent</source> <translation>Speichere aktuellen Stand</translation> </message> <message> - <location filename="../Window.cpp" line="1095"/> + <location filename="../Window.cpp" line="1029"/> <source>Undo load state</source> <translation>Laden des Savestate rückgängig machen</translation> </message> <message> - <location filename="../Window.cpp" line="1096"/> + <location filename="../Window.cpp" line="1030"/> <source>F11</source> <translation>F11</translation> </message> <message> - <location filename="../Window.cpp" line="1103"/> + <location filename="../Window.cpp" line="1036"/> <source>Undo save state</source> <translation>Speichern des Savestate rückgängig machen</translation> </message> <message> - <location filename="../Window.cpp" line="1104"/> + <location filename="../Window.cpp" line="1037"/> <source>Shift+F11</source> <translation>Umschalt+F11</translation> </message> <message> - <location filename="../Window.cpp" line="1116"/> - <location filename="../Window.cpp" line="1124"/> + <location filename="../Window.cpp" line="1048"/> + <location filename="../Window.cpp" line="1055"/> <source>State &%1</source> <translation>Savestate &%1</translation> </message> <message> - <location filename="../Window.cpp" line="1117"/> + <location filename="../Window.cpp" line="1049"/> <source>F%1</source> <translation>F%1</translation> </message> <message> - <location filename="../Window.cpp" line="1125"/> + <location filename="../Window.cpp" line="1056"/> <source>Shift+F%1</source> <translation>Umschalt+F%1</translation> </message> <message> - <location filename="../Window.cpp" line="1135"/> + <location filename="../Window.cpp" line="1065"/> <source>Import GameShark Save</source> <translation>Importiere GameShark-Speicherstand</translation> </message> <message> - <location filename="../Window.cpp" line="1141"/> + <location filename="../Window.cpp" line="1071"/> <source>Export GameShark Save</source> <translation>Exportiere GameShark-Speicherstand</translation> </message> <message> - <location filename="../Window.cpp" line="1149"/> + <location filename="../Window.cpp" line="1079"/> <source>New multiplayer window</source> <translation>Neues Multiplayer-Fenster</translation> </message> <message> - <location filename="../Window.cpp" line="1159"/> + <location filename="../Window.cpp" line="1089"/> <source>About</source> <translation>Über</translation> </message> <message> - <location filename="../Window.cpp" line="1164"/> + <location filename="../Window.cpp" line="1094"/> <source>E&xit</source> <translation>&Beenden</translation> </message> <message> - <location filename="../Window.cpp" line="1167"/> + <location filename="../Window.cpp" line="1097"/> <source>&Emulation</source> <translation>&Emulation</translation> </message> <message> - <location filename="../Window.cpp" line="1169"/> + <location filename="../Window.cpp" line="1099"/> <source>&Reset</source> <translation>Zu&rücksetzen</translation> </message> <message> - <location filename="../Window.cpp" line="1170"/> + <location filename="../Window.cpp" line="1100"/> <source>Ctrl+R</source> <translation>Strg+R</translation> </message> <message> - <location filename="../Window.cpp" line="1175"/> + <location filename="../Window.cpp" line="1105"/> <source>Sh&utdown</source> <translation>B&eenden</translation> </message> <message> - <location filename="../Window.cpp" line="1181"/> + <location filename="../Window.cpp" line="1111"/> <source>Yank game pak</source> <translation>Spielmodul herausziehen</translation> </message> <message> - <location filename="../Window.cpp" line="1189"/> + <location filename="../Window.cpp" line="1119"/> <source>&Pause</source> <translation>&Pause</translation> </message> <message> - <location filename="../Window.cpp" line="1192"/> + <location filename="../Window.cpp" line="1122"/> <source>Ctrl+P</source> <translation>Strg+P</translation> </message> <message> - <location filename="../Window.cpp" line="1201"/> + <location filename="../Window.cpp" line="1131"/> <source>&Next frame</source> <translation>&Nächstes Bild</translation> </message> <message> - <location filename="../Window.cpp" line="1202"/> + <location filename="../Window.cpp" line="1132"/> <source>Ctrl+N</source> <translation>Strg+N</translation> </message> <message> - <location filename="../Window.cpp" line="1213"/> + <location filename="../Window.cpp" line="1143"/> <source>Fast forward (held)</source> <translation>Schneller Vorlauf (gehalten)</translation> </message> <message> - <location filename="../Window.cpp" line="1215"/> + <location filename="../Window.cpp" line="1145"/> <source>&Fast forward</source> <translation>Schneller &Vorlauf</translation> </message> <message> - <location filename="../Window.cpp" line="1218"/> + <location filename="../Window.cpp" line="1148"/> <source>Shift+Tab</source> <translation>Umschalt+Tab</translation> </message> <message> - <location filename="../Window.cpp" line="1222"/> + <location filename="../Window.cpp" line="1152"/> <source>Fast forward speed</source> <translation>Vorlauf-Geschwindigkeit</translation> </message> <message> - <location filename="../Window.cpp" line="1227"/> + <location filename="../Window.cpp" line="1157"/> <source>Unbounded</source> <translation>Unbegrenzt</translation> </message> <message> - <location filename="../Window.cpp" line="1231"/> + <location filename="../Window.cpp" line="1161"/> <source>%0x</source> <translation>%0x</translation> </message> <message> - <location filename="../Window.cpp" line="1239"/> + <location filename="../Window.cpp" line="1169"/> <source>Rewind (held)</source> <translation>Zurückspulen (gehalten)</translation> </message> <message> - <location filename="../Window.cpp" line="1241"/> + <location filename="../Window.cpp" line="1171"/> <source>Re&wind</source> <translation>Zur&ückspulen</translation> </message> <message> - <location filename="../Window.cpp" line="1242"/> + <location filename="../Window.cpp" line="1172"/> <source>~</source> <translation>~</translation> </message> <message> - <location filename="../Window.cpp" line="1249"/> + <location filename="../Window.cpp" line="1178"/> <source>Step backwards</source> <translation>Schrittweiser Rücklauf</translation> </message> <message> - <location filename="../Window.cpp" line="1250"/> + <location filename="../Window.cpp" line="1179"/> <source>Ctrl+B</source> <translation>Strg+B</translation> </message> <message> - <location filename="../Window.cpp" line="1260"/> + <location filename="../Window.cpp" line="1188"/> <source>Sync to &video</source> <translation>Mit &Video synchronisieren</translation> </message> <message> - <location filename="../Window.cpp" line="1267"/> + <location filename="../Window.cpp" line="1195"/> <source>Sync to &audio</source> <translation>Mit &Audio synchronisieren</translation> </message> <message> - <location filename="../Window.cpp" line="1275"/> + <location filename="../Window.cpp" line="1203"/> <source>Solar sensor</source> <translation>Solar-Sensor</translation> </message> <message> - <location filename="../Window.cpp" line="1277"/> + <location filename="../Window.cpp" line="1205"/> <source>Increase solar level</source> <translation>Sonnen-Level erhöhen</translation> </message> <message> - <location filename="../Window.cpp" line="1281"/> + <location filename="../Window.cpp" line="1209"/> <source>Decrease solar level</source> <translation>Sonnen-Level verringern</translation> </message> <message> - <location filename="../Window.cpp" line="1285"/> + <location filename="../Window.cpp" line="1213"/> <source>Brightest solar level</source> <translation>Hellster Sonnen-Level</translation> </message> <message> - <location filename="../Window.cpp" line="1289"/> + <location filename="../Window.cpp" line="1217"/> <source>Darkest solar level</source> <translation>Dunkelster Sonnen-Level</translation> </message> <message> - <location filename="../Window.cpp" line="1295"/> + <location filename="../Window.cpp" line="1223"/> <source>Brightness %1</source> <translation>Helligkeit %1</translation> </message> <message> - <location filename="../Window.cpp" line="1302"/> + <location filename="../Window.cpp" line="1230"/> <source>Audio/&Video</source> <translation>Audio/&Video</translation> </message> <message> - <location filename="../Window.cpp" line="1304"/> + <location filename="../Window.cpp" line="1232"/> <source>Frame size</source> <translation>Bildgröße</translation> </message> <message> - <location filename="../Window.cpp" line="1307"/> + <location filename="../Window.cpp" line="1235"/> <source>%1x</source> <translation>%1x</translation> </message> <message> - <location filename="../Window.cpp" line="1335"/> + <location filename="../Window.cpp" line="1263"/> <source>Toggle fullscreen</source> <translation>Vollbildmodus umschalten</translation> </message> <message> - <location filename="../Window.cpp" line="1338"/> + <location filename="../Window.cpp" line="1266"/> <source>Lock aspect ratio</source> <translation>Seitenverhältnis korrigieren</translation> </message> <message> - <location filename="../Window.cpp" line="1345"/> + <location filename="../Window.cpp" line="1273"/> <source>Force integer scaling</source> <translation>Pixelgenaue Skalierung (Integer scaling)</translation> </message> <message> - <location filename="../Window.cpp" line="1361"/> + <location filename="../Window.cpp" line="1289"/> <source>Frame&skip</source> <translation>Frame&skip</translation> </message> <message> - <location filename="../Window.cpp" line="1371"/> + <location filename="../Window.cpp" line="1299"/> <source>Shader options...</source> <translation>Shader-Optionen...</translation> </message> <message> - <location filename="../Window.cpp" line="1381"/> + <location filename="../Window.cpp" line="1309"/> <source>Mute</source> <translation>Stummschalten</translation> </message> <message> - <location filename="../Window.cpp" line="1388"/> + <location filename="../Window.cpp" line="1316"/> <source>FPS target</source> <translation>Bildwiederholrate</translation> </message> <message> - <location filename="../Window.cpp" line="1393"/> + <location filename="../Window.cpp" line="1321"/> <source>15</source> <translation>15</translation> </message> <message> - <location filename="../Window.cpp" line="1394"/> + <location filename="../Window.cpp" line="1322"/> <source>30</source> <translation>30</translation> </message> <message> - <location filename="../Window.cpp" line="1395"/> + <location filename="../Window.cpp" line="1323"/> <source>45</source> <translation>45</translation> </message> <message> - <location filename="../Window.cpp" line="1396"/> + <location filename="../Window.cpp" line="1324"/> <source>Native (59.7)</source> <translation>Nativ (59.7)</translation> </message> <message> - <location filename="../Window.cpp" line="1397"/> + <location filename="../Window.cpp" line="1325"/> <source>60</source> <translation>60</translation> </message> <message> - <location filename="../Window.cpp" line="1398"/> + <location filename="../Window.cpp" line="1326"/> <source>90</source> <translation>90</translation> </message> <message> - <location filename="../Window.cpp" line="1399"/> + <location filename="../Window.cpp" line="1327"/> <source>120</source> <translation>120</translation> </message> <message> - <location filename="../Window.cpp" line="1400"/> + <location filename="../Window.cpp" line="1328"/> <source>240</source> <translation>240</translation> </message> <message> - <location filename="../Window.cpp" line="1408"/> + <location filename="../Window.cpp" line="1336"/> <source>Take &screenshot</source> <translation>&Screenshot erstellen</translation> </message> <message> - <location filename="../Window.cpp" line="1409"/> + <location filename="../Window.cpp" line="1337"/> <source>F12</source> <translation>F12</translation> </message> <message> - <location filename="../Window.cpp" line="1416"/> + <location filename="../Window.cpp" line="1344"/> <source>Record output...</source> <translation>Ausgabe aufzeichen...</translation> </message> <message> - <location filename="../Window.cpp" line="1423"/> + <location filename="../Window.cpp" line="1351"/> <source>Record GIF...</source> <translation>GIF aufzeichen...</translation> </message> <message> - <location filename="../Window.cpp" line="1430"/> + <location filename="../Window.cpp" line="1356"/> <source>Record video log...</source> <translation>Video-Log aufzeichnen...</translation> </message> <message> - <location filename="../Window.cpp" line="1354"/> + <location filename="../Window.cpp" line="1361"/> <source>Stop video log</source> <translation>Video-Log beenden</translation> </message> <message> - <location filename="../Window.cpp" line="1360"/> + <location filename="../Window.cpp" line="1367"/> <source>Video layers</source> <translation>Video-Ebenen</translation> </message> <message> - <location filename="../Window.cpp" line="1433"/> + <location filename="../Window.cpp" line="1370"/> <source>Audio channels</source> <translation>Audio-Kanäle</translation> </message> <message> - <location filename="../Window.cpp" line="1436"/> + <location filename="../Window.cpp" line="1373"/> <source>&Tools</source> <translation>&Werkzeuge</translation> </message> <message> - <location filename="../Window.cpp" line="1438"/> + <location filename="../Window.cpp" line="1375"/> <source>View &logs...</source> <translation>&Logs ansehen...</translation> </message> <message> - <location filename="../Window.cpp" line="1442"/> + <location filename="../Window.cpp" line="1379"/> <source>Game &overrides...</source> <translation>Spiel-&Überschreibungen...</translation> </message> <message> - <location filename="../Window.cpp" line="1446"/> + <location filename="../Window.cpp" line="1383"/> <source>Game &Pak sensors...</source> <translation>Game &Pak-Sensoren...</translation> </message> <message> - <location filename="../Window.cpp" line="1450"/> + <location filename="../Window.cpp" line="1387"/> <source>&Cheats...</source> <translation>&Cheats...</translation> </message> <message> - <location filename="../Window.cpp" line="1463"/> + <location filename="../Window.cpp" line="1399"/> <source>Open debugger console...</source> <translation>Debugger-Konsole äffnen...</translation> </message> <message> - <location filename="../Window.cpp" line="1469"/> + <location filename="../Window.cpp" line="1405"/> <source>Start &GDB server...</source> <translation>&GDB-Server starten...</translation> </message> <message> - <location filename="../Window.cpp" line="1457"/> + <location filename="../Window.cpp" line="1393"/> <source>Settings...</source> <translation>Einstellungen...</translation> </message>@@ -3455,37 +3592,37 @@ <source>DS</source>
<translation>DS</translation> </message> <message> - <location filename="../Window.cpp" line="451"/> + <location filename="../Window.cpp" line="396"/> <source>Select folder</source> <translation>Ordner auswählen</translation> </message> <message> - <location filename="../Window.cpp" line="1024"/> + <location filename="../Window.cpp" line="962"/> <source>Add folder to library...</source> <translation>Ordner zur Bibliothek hinzufügen...</translation> </message> <message> - <location filename="../Window.cpp" line="1355"/> + <location filename="../Window.cpp" line="1283"/> <source>Bilinear filtering</source> <translation>Bilineare Filterung</translation> </message> <message> - <location filename="../Window.cpp" line="1476"/> + <location filename="../Window.cpp" line="1412"/> <source>View &palette...</source> <translation>&Palette betrachten...</translation> </message> <message> - <location filename="../Window.cpp" line="1482"/> + <location filename="../Window.cpp" line="1417"/> <source>View &sprites...</source> <translation>&Sprites betrachten...</translation> </message> <message> - <location filename="../Window.cpp" line="1488"/> + <location filename="../Window.cpp" line="1422"/> <source>View &tiles...</source> <translation>&Tiles betrachten...</translation> </message> <message> - <location filename="../Window.cpp" line="1494"/> + <location filename="../Window.cpp" line="1427"/> <source>View memory...</source> <translation>Speicher betrachten...</translation> </message>@@ -3495,62 +3632,72 @@ <source>View &I/O registers...</source>
<translation>&I/O-Register betrachten...</translation> </message> <message> - <location filename="../Window.cpp" line="1496"/> + <location filename="../Window.cpp" line="1432"/> + <source>Search memory...</source> + <translation>Speicher durchsuchen...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1438"/> + <source>View &I/O registers...</source> + <translation>&I/O-Register betrachten...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1508"/> <source>Exit fullscreen</source> <translation>Vollbildmodus beenden</translation> </message> <message> - <location filename="../Window.cpp" line="1501"/> + <location filename="../Window.cpp" line="1513"/> <source>Autofire</source> <translation>Autofeuer</translation> </message> <message> - <location filename="../Window.cpp" line="1508"/> + <location filename="../Window.cpp" line="1520"/> <source>Autofire A</source> <translation>Autofeuer A</translation> </message> <message> - <location filename="../Window.cpp" line="1514"/> + <location filename="../Window.cpp" line="1526"/> <source>Autofire B</source> <translation>Autofeuer B</translation> </message> <message> - <location filename="../Window.cpp" line="1520"/> + <location filename="../Window.cpp" line="1532"/> <source>Autofire L</source> <translation>Autofeuer L</translation> </message> <message> - <location filename="../Window.cpp" line="1526"/> + <location filename="../Window.cpp" line="1538"/> <source>Autofire R</source> <translation>Autofeuer R</translation> </message> <message> - <location filename="../Window.cpp" line="1532"/> + <location filename="../Window.cpp" line="1544"/> <source>Autofire Start</source> <translation>Autofeuer Start</translation> </message> <message> - <location filename="../Window.cpp" line="1538"/> + <location filename="../Window.cpp" line="1550"/> <source>Autofire Select</source> <translation>Autofeuer Select</translation> </message> <message> - <location filename="../Window.cpp" line="1544"/> + <location filename="../Window.cpp" line="1556"/> <source>Autofire Up</source> <translation>Autofeuer nach oben</translation> </message> <message> - <location filename="../Window.cpp" line="1550"/> + <location filename="../Window.cpp" line="1562"/> <source>Autofire Right</source> <translation>Autofeuer rechts</translation> </message> <message> - <location filename="../Window.cpp" line="1556"/> + <location filename="../Window.cpp" line="1568"/> <source>Autofire Down</source> <translation>Autofeuer nach unten</translation> </message> <message> - <location filename="../Window.cpp" line="1562"/> + <location filename="../Window.cpp" line="1574"/> <source>Autofire Left</source> <translation>Autofeuer links</translation> </message>@@ -3842,7 +3989,7 @@ <translation>Alle</translation>
</message> <message> <location filename="../SettingsView.ui" line="307"/> - <location filename="../SettingsView.ui" line="556"/> + <location filename="../SettingsView.ui" line="576"/> <source>frames</source> <translation>Bilder</translation> </message>@@ -3883,55 +4030,65 @@ <translation>Erzwinge pixelgenaue Skalierung
(Integer scaling)</translation> </message> <message> - <location filename="../SettingsView.ui" line="415"/> + <location filename="../SettingsView.ui" line="404"/> + <source>Language</source> + <translation>Sprache</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="412"/> + <source>English</source> + <translation>Englisch</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="428"/> <source>List view</source> <translation>Listenansicht</translation> </message> <message> - <location filename="../SettingsView.ui" line="420"/> + <location filename="../SettingsView.ui" line="433"/> <source>Tree view</source> <translation>Baumansicht</translation> </message> <message> - <location filename="../SettingsView.ui" line="428"/> + <location filename="../SettingsView.ui" line="420"/> <source>Library:</source> <translation>Bibliothek:</translation> </message> <message> - <location filename="../SettingsView.ui" line="404"/> + <location filename="../SettingsView.ui" line="441"/> <source>Show when no game open</source> <translation>Anzeigen, wenn kein Spiel geöffnet ist</translation> </message> <message> - <location filename="../SettingsView.ui" line="438"/> + <location filename="../SettingsView.ui" line="451"/> <source>Clear cache</source> <translation>Cache leeren</translation> </message> <message> - <location filename="../SettingsView.ui" line="483"/> + <location filename="../SettingsView.ui" line="503"/> <source>Fast forward speed:</source> <translation>Vorlauf-Geschwindigkeit:</translation> </message> <message> - <location filename="../SettingsView.ui" line="680"/> + <location filename="../SettingsView.ui" line="700"/> <source>Rewind affects save data</source> <translation>Rücklauf beeinflusst Speicherdaten</translation> </message> <message> - <location filename="../SettingsView.ui" line="690"/> + <location filename="../SettingsView.ui" line="710"/> <source>Preload entire ROM into memory</source> <translation>ROM-Datei vollständig in Arbeitsspeicher vorladen</translation> </message> <message> - <location filename="../SettingsView.ui" line="720"/> - <location filename="../SettingsView.ui" line="758"/> - <location filename="../SettingsView.ui" line="793"/> - <location filename="../SettingsView.ui" line="834"/> - <location filename="../SettingsView.ui" line="882"/> - <location filename="../SettingsView.ui" line="930"/> - <location filename="../SettingsView.ui" line="978"/> + <location filename="../SettingsView.ui" line="740"/> + <location filename="../SettingsView.ui" line="778"/> + <location filename="../SettingsView.ui" line="813"/> + <location filename="../SettingsView.ui" line="854"/> + <location filename="../SettingsView.ui" line="902"/> + <location filename="../SettingsView.ui" line="950"/> + <location filename="../SettingsView.ui" line="998"/> <source>Browse</source> <translation>Durchsuchen</translation> </message>@@ -3946,28 +4103,28 @@ <source>DS BIOS 9 file:</source>
<translation>DS-BIOS 9:</translation> </message> <message> - <location filename="../SettingsView.ui" line="848"/> + <location filename="../SettingsView.ui" line="749"/> <source>Use BIOS file if found</source> <translation>BIOS-Datei verwenden, wenn vorhanden</translation> </message> <message> - <location filename="../SettingsView.ui" line="858"/> + <location filename="../SettingsView.ui" line="759"/> <source>Skip BIOS intro</source> <translation>BIOS-Intro überspringen</translation> </message> <message> - <location filename="../SettingsView.ui" line="495"/> + <location filename="../SettingsView.ui" line="515"/> <source>×</source> <translation>×</translation> </message> <message> - <location filename="../SettingsView.ui" line="514"/> + <location filename="../SettingsView.ui" line="534"/> <source>Unbounded</source> <translation>unbegrenzt</translation> </message> <message> - <location filename="../SettingsView.ui" line="459"/> + <location filename="../SettingsView.ui" line="472"/> <source>Suspend screensaver</source> <translation>Bildschirmschoner deaktivieren</translation> </message>@@ -3977,50 +4134,50 @@ <source>BIOS</source>
<translation>BIOS</translation> </message> <message> - <location filename="../SettingsView.ui" line="469"/> + <location filename="../SettingsView.ui" line="482"/> <source>Pause when inactive</source> <translation>Pause, wenn inaktiv</translation> </message> <message> - <location filename="../SettingsView.ui" line="580"/> + <location filename="../SettingsView.ui" line="600"/> <source>Run all</source> <translation>Alle ausführen</translation> </message> <message> - <location filename="../SettingsView.ui" line="585"/> + <location filename="../SettingsView.ui" line="605"/> <source>Remove known</source> <translation>Bekannte entfernen</translation> </message> <message> - <location filename="../SettingsView.ui" line="590"/> + <location filename="../SettingsView.ui" line="610"/> <source>Detect and remove</source> <translation>Erkennen und entfernen</translation> </message> <message> - <location filename="../SettingsView.ui" line="452"/> + <location filename="../SettingsView.ui" line="465"/> <source>Allow opposing input directions</source> <translation>Gegensätzliche Eingaberichtungen erlauben</translation> </message> <message> - <location filename="../SettingsView.ui" line="612"/> - <location filename="../SettingsView.ui" line="649"/> + <location filename="../SettingsView.ui" line="632"/> + <location filename="../SettingsView.ui" line="669"/> <source>Screenshot</source> <translation>Screenshot</translation> </message> <message> - <location filename="../SettingsView.ui" line="622"/> - <location filename="../SettingsView.ui" line="659"/> + <location filename="../SettingsView.ui" line="642"/> + <location filename="../SettingsView.ui" line="679"/> <source>Save data</source> <translation>Speicherdaten</translation> </message> <message> - <location filename="../SettingsView.ui" line="632"/> - <location filename="../SettingsView.ui" line="666"/> + <location filename="../SettingsView.ui" line="652"/> + <location filename="../SettingsView.ui" line="686"/> <source>Cheat codes</source> <translation>Cheat-Codes</translation> </message> <message> - <location filename="../SettingsView.ui" line="533"/> + <location filename="../SettingsView.ui" line="553"/> <source>Enable rewind</source> <translation>Rücklauf aktivieren</translation> </message>@@ -4030,37 +4187,37 @@ <source>Bilinear filtering</source>
<translation>Bilineare Filterung</translation> </message> <message> - <location filename="../SettingsView.ui" line="540"/> + <location filename="../SettingsView.ui" line="560"/> <source>Rewind history:</source> <translation>Rücklauf-Verlauf:</translation> </message> <message> - <location filename="../SettingsView.ui" line="572"/> + <location filename="../SettingsView.ui" line="592"/> <source>Idle loops:</source> <translation>Leerlaufprozesse:</translation> </message> <message> - <location filename="../SettingsView.ui" line="605"/> + <location filename="../SettingsView.ui" line="625"/> <source>Savestate extra data:</source> <translation>Zusätzliche Savestate-Daten:</translation> </message> <message> - <location filename="../SettingsView.ui" line="642"/> + <location filename="../SettingsView.ui" line="662"/> <source>Load extra data:</source> <translation>Lade zusätzliche Daten:</translation> </message> <message> - <location filename="../SettingsView.ui" line="687"/> + <location filename="../SettingsView.ui" line="721"/> <source>GB BIOS file:</source> <translation>Datei mit GB-BIOS:</translation> </message> <message> - <location filename="../SettingsView.ui" line="743"/> + <location filename="../SettingsView.ui" line="787"/> <source>GBA BIOS file:</source> <translation>Datei mit GBA-BIOS:</translation> </message> <message> - <location filename="../SettingsView.ui" line="774"/> + <location filename="../SettingsView.ui" line="794"/> <source>GBC BIOS file:</source> <translation>Datei mit GBC-BIOS:</translation> </message>@@ -4070,30 +4227,30 @@ <source>DS firmware file:</source>
<translation>DS-Firmware:</translation> </message> <message> - <location filename="../SettingsView.ui" line="879"/> + <location filename="../SettingsView.ui" line="829"/> <source>Save games</source> <translation>Spielstände</translation> </message> <message> - <location filename="../SettingsView.ui" line="913"/> - <location filename="../SettingsView.ui" line="961"/> - <location filename="../SettingsView.ui" line="1009"/> - <location filename="../SettingsView.ui" line="1057"/> + <location filename="../SettingsView.ui" line="863"/> + <location filename="../SettingsView.ui" line="911"/> + <location filename="../SettingsView.ui" line="959"/> + <location filename="../SettingsView.ui" line="1007"/> <source>Same directory as the ROM</source> <translation>Verzeichnis der ROM-Datei</translation> </message> <message> - <location filename="../SettingsView.ui" line="927"/> + <location filename="../SettingsView.ui" line="877"/> <source>Save states</source> <translation>Savestates</translation> </message> <message> - <location filename="../SettingsView.ui" line="975"/> + <location filename="../SettingsView.ui" line="925"/> <source>Screenshots</source> <translation>Screenshots</translation> </message> <message> - <location filename="../SettingsView.ui" line="1023"/> + <location filename="../SettingsView.ui" line="973"/> <source>Patches</source> <translation>Patches</translation> </message>
@@ -0,0 +1,4305 @@
+<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="it" sourcelanguage="en_US"> +<context> + <name>AboutScreen</name> + <message> + <location filename="../AboutScreen.ui" line="14"/> + <source>About</source> + <translation>About</translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="23"/> + <source><a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a></source> + <translation><a href="http://mgba.io/">Sitio web</a> • <a href="https://forums.mgba.io/">Foros / Supporto</a> • <a href="https://patreon.com/mgba">Donar</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Codice sorgente</a></translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="58"/> + <source>{projectName}</source> + <translation>{projectName}</translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="68"/> + <source>{projectName} would like to thank the following patrons from Patreon:</source> + <translation>{projectName} desidera ringraziare i seguenti sponsor di Patreon:</translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="86"/> + <source>© 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 +Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source> + <translation>© 2013 - 2016 Jeffrey Pfau, sotto licenza Mozilla Public License, versione 2.0 +Game Boy Advance è un marchio registrato di Nintendo Co., Ltd.</translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="190"/> + <source>{patrons}</source> + <translation>{patrons}</translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="106"/> + <source>{projectVersion}</source> + <translation>{projectVersion}</translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="155"/> + <source>{logo}</source> + <translation>{logo}</translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="177"/> + <source>{projectName} is an open-source Game Boy Advance emulator</source> + <translation>{projectName} è un emulatore open-source del Game Boy Advance</translation> + </message> + <message> + <location filename="../AboutScreen.ui" line="41"/> + <source>Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt></source> + <translation>Ramo Git: <tt>{gitBranch}</tt><br/>Revisione: <tt>{gitCommit}</tt></translation> + </message> +</context> +<context> + <name>ArchiveInspector</name> + <message> + <location filename="../ArchiveInspector.ui" line="14"/> + <source>Open in archive...</source> + <translation>Apri il file in ...</translation> + </message> + <message> + <location filename="../ArchiveInspector.ui" line="20"/> + <source>Loading...</source> + <translation>Caricamento in corso...</translation> + </message> +</context> +<context> + <name>AssetTile</name> + <message> + <location filename="../AssetTile.ui" line="12"/> + <source>AssetTile</source> + <translation>AssetTile</translation> + </message> + <message> + <location filename="../AssetTile.ui" line="33"/> + <source>Tile #</source> + <translation>Tile Nº</translation> + </message> + <message> + <location filename="../AssetTile.ui" line="40"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../AssetTile.ui" line="54"/> + <source>Address</source> + <translation>Indirizzo</translation> + </message> + <message> + <location filename="../AssetTile.ui" line="61"/> + <source>0x06000000</source> + <translation>0x06000000</translation> + </message> + <message> + <location filename="../AssetTile.ui" line="100"/> + <source>Red</source> + <translation>Rosso</translation> + </message> + <message> + <location filename="../AssetTile.ui" line="107"/> + <source>Green</source> + <translation>Verde</translation> + </message> + <message> + <location filename="../AssetTile.ui" line="114"/> + <source>Blue</source> + <translation>Blue</translation> + </message> + <message> + <location filename="../AssetTile.ui" line="128"/> + <location filename="../AssetTile.ui" line="135"/> + <location filename="../AssetTile.ui" line="142"/> + <source>0x00 (00)</source> + <translation>0x00 (00)</translation> + </message> +</context> +<context> + <name>CheatsView</name> + <message> + <location filename="../CheatsView.ui" line="14"/> + <source>Cheats</source> + <translation>Trucchi</translation> + </message> + <message> + <location filename="../CheatsView.ui" line="20"/> + <source>Remove</source> + <translation>Rimuovere</translation> + </message> + <message> + <location filename="../CheatsView.ui" line="34"/> + <source>Save</source> + <translation>Salva</translation> + </message> + <message> + <location filename="../CheatsView.ui" line="41"/> + <source>Load</source> + <translation>Carica</translation> + </message> + <message> + <location filename="../CheatsView.ui" line="69"/> + <source>Add New Set</source> + <translation>Aggiungere Nuovo Set</translation> + </message> + <message> + <location filename="../CheatsView.ui" line="76"/> + <source>Add</source> + <translation>Aggiungi</translation> + </message> +</context> +<context> + <name>DebuggerConsole</name> + <message> + <location filename="../DebuggerConsole.ui" line="14"/> + <source>Debugger</source> + <translation>Debugger</translation> + </message> + <message> + <location filename="../DebuggerConsole.ui" line="25"/> + <source>Enter command (try `help` for more info)</source> + <translation>Inserire un comando (premere `help` per maggiori informazioni)</translation> + </message> + <message> + <location filename="../DebuggerConsole.ui" line="32"/> + <source>Break</source> + <translation>Pausa</translation> + </message> +</context> +<context> + <name>GIFView</name> + <message> + <location filename="../GIFView.ui" line="14"/> + <source>Record GIF</source> + <translation>Registra GIF</translation> + </message> + <message> + <location filename="../GIFView.ui" line="34"/> + <source>Start</source> + <translation>Avvia</translation> + </message> + <message> + <location filename="../GIFView.ui" line="50"/> + <source>Stop</source> + <translation>Stop</translation> + </message> + <message> + <location filename="../GIFView.ui" line="63"/> + <source>Select File</source> + <translation>Seleziona File</translation> + </message> + <message> + <location filename="../GIFView.ui" line="101"/> + <source>Frameskip</source> + <translation>Salta Frame</translation> + </message> + <message> + <location filename="../GIFView.ui" line="115"/> + <source>Frame delay (ms)</source> + <translation>Ritardo Frame (ms)</translation> + </message> + <message> + <location filename="../GIFView.ui" line="122"/> + <source>Automatic</source> + <translation>Automatico</translation> + </message> +</context> +<context> + <name>IOViewer</name> + <message> + <location filename="../IOViewer.ui" line="14"/> + <source>I/O Viewer</source> + <translation>Visualizzatore I/O</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="26"/> + <source>0x0000</source> + <translation>0x0000</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="58"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="82"/> + <source>5</source> + <translation>5</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="100"/> + <source>4</source> + <translation>4</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="118"/> + <source>7</source> + <translation>7</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="136"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="157"/> + <source>9</source> + <translation>9</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="178"/> + <source>1</source> + <translation>1</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="196"/> + <source>3</source> + <translation>3</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="217"/> + <source>8</source> + <translation>8</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="241"/> + <source>C</source> + <translation>C</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="259"/> + <source>E</source> + <translation>E</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="283"/> + <source>6</source> + <translation>6</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="310"/> + <source>D</source> + <translation>D</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="328"/> + <source>F</source> + <translation>F</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="346"/> + <source>A</source> + <translation>A</translation> + </message> + <message> + <location filename="../IOViewer.ui" line="364"/> + <source>B</source> + <translation>B</translation> + </message> +</context> +<context> + <name>LibraryView</name> + <message> + <location filename="../LibraryView.ui" line="14"/> + <source>Library</source> + <translation>Biblioteca</translation> + </message> +</context> +<context> + <name>LoadSaveState</name> + <message> + <location filename="../LoadSaveState.ui" line="14"/> + <location filename="../LoadSaveState.ui" line="88"/> + <source>%1 State</source> + <translation>%1 cattura stato</translation> + </message> + <message> + <location filename="../LoadSaveState.ui" line="41"/> + <location filename="../LoadSaveState.ui" line="63"/> + <location filename="../LoadSaveState.ui" line="107"/> + <location filename="../LoadSaveState.ui" line="129"/> + <location filename="../LoadSaveState.ui" line="151"/> + <location filename="../LoadSaveState.ui" line="173"/> + <location filename="../LoadSaveState.ui" line="195"/> + <location filename="../LoadSaveState.ui" line="217"/> + <location filename="../LoadSaveState.ui" line="239"/> + <source>No Save</source> + <translation>Senza Salvare</translation> + </message> + <message> + <location filename="../LoadSaveState.ui" line="50"/> + <source>1</source> + <translation>1</translation> + </message> + <message> + <location filename="../LoadSaveState.ui" line="72"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../LoadSaveState.ui" line="116"/> + <source>3</source> + <translation>3</translation> + </message> + <message> + <location filename="../LoadSaveState.ui" line="138"/> + <source>4</source> + <translation>4</translation> + </message> + <message> + <location filename="../LoadSaveState.ui" line="160"/> + <source>5</source> + <translation>5</translation> + </message> + <message> + <location filename="../LoadSaveState.ui" line="182"/> + <source>6</source> + <translation>6</translation> + </message> + <message> + <location filename="../LoadSaveState.ui" line="204"/> + <source>7</source> + <translation>7</translation> + </message> + <message> + <location filename="../LoadSaveState.ui" line="226"/> + <source>8</source> + <translation>8</translation> + </message> + <message> + <location filename="../LoadSaveState.ui" line="248"/> + <source>9</source> + <translation>9</translation> + </message> +</context> +<context> + <name>LogView</name> + <message> + <location filename="../LogView.ui" line="14"/> + <source>Logs</source> + <translation>Logs</translation> + </message> + <message> + <location filename="../LogView.ui" line="22"/> + <source>Enabled Levels</source> + <translation>Abilita Livelli</translation> + </message> + <message> + <location filename="../LogView.ui" line="28"/> + <source>Debug</source> + <translation>Debug</translation> + </message> + <message> + <location filename="../LogView.ui" line="38"/> + <source>Stub</source> + <translation>Stub</translation> + </message> + <message> + <location filename="../LogView.ui" line="48"/> + <source>Info</source> + <translation>Informazioni</translation> + </message> + <message> + <location filename="../LogView.ui" line="58"/> + <source>Warning</source> + <translation>Avvertenze</translation> + </message> + <message> + <location filename="../LogView.ui" line="68"/> + <source>Error</source> + <translation>Errore</translation> + </message> + <message> + <location filename="../LogView.ui" line="78"/> + <source>Fatal</source> + <translation>Fatale</translation> + </message> + <message> + <location filename="../LogView.ui" line="95"/> + <source>Game Error</source> + <translation>Errore del gioco</translation> + </message> + <message> + <location filename="../LogView.ui" line="121"/> + <source>Clear</source> + <translation>Pulisci</translation> + </message> + <message> + <location filename="../LogView.ui" line="130"/> + <source>Max Lines</source> + <translation>Linee di massima</translation> + </message> +</context> +<context> + <name>MemoryView</name> + <message> + <location filename="../MemoryView.ui" line="14"/> + <source>Memory</source> + <translation>Memoria</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="38"/> + <source>Inspect Address:</source> + <translation>Ispezionare indirizzo:</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="61"/> + <source>0x</source> + <translation>0x</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="81"/> + <source>Set Alignment:</source> + <translation>Set di allignamento:</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="101"/> + <source>1 Byte</source> + <translation>1 byte</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="124"/> + <source>2 Bytes</source> + <translation>2 bytes</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="144"/> + <source>4 Bytes</source> + <translation>4 bytes</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="199"/> + <source>Signed Integer:</source> + <translation>Integer Signato:</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="217"/> + <source>String:</source> + <translation>Stringa:</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="231"/> + <source>Load TBL</source> + <translation>Carica TBL</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="244"/> + <source>Copy Selection</source> + <translation>Copia la selezione</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="251"/> + <source>Paste</source> + <translation>Incolla</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="271"/> + <source>Save Selection</source> + <translation>Salva Selezione</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="278"/> + <source>Load</source> + <translation>Carica</translation> + </message> + <message> + <location filename="../MemoryView.ui" line="181"/> + <source>Unsigned Integer:</source> + <translation>Integer non signato:</translation> + </message> +</context> +<context> + <name>ObjView</name> + <message> + <location filename="../ObjView.ui" line="14"/> + <source>Sprites</source> + <translation>Sprites</translation> + </message> + <message> + <location filename="../ObjView.ui" line="56"/> + <location filename="../ObjView.ui" line="499"/> + <source>×</source> + <translation>×</translation> + </message> + <message> + <location filename="../ObjView.ui" line="69"/> + <source>Magnification</source> + <translation>Magnification</translation> + </message> + <message> + <location filename="../ObjView.ui" line="78"/> + <source>Attributes</source> + <translation>Attributi</translation> + </message> + <message> + <location filename="../ObjView.ui" line="86"/> + <source>Transform</source> + <translation>Transformazione</translation> + </message> + <message> + <location filename="../ObjView.ui" line="106"/> + <source>Off</source> + <translation>No</translation> + </message> + <message> + <location filename="../ObjView.ui" line="117"/> + <source>Palette</source> + <translation>Palette</translation> + </message> + <message> + <location filename="../ObjView.ui" line="137"/> + <location filename="../ObjView.ui" line="367"/> + <location filename="../ObjView.ui" line="423"/> + <location filename="../ObjView.ui" line="449"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../ObjView.ui" line="151"/> + <source>Double Size</source> + <translation>Doppia Dimensione</translation> + </message> + <message> + <location filename="../ObjView.ui" line="177"/> + <location filename="../ObjView.ui" line="221"/> + <location filename="../ObjView.ui" line="234"/> + <location filename="../ObjView.ui" line="302"/> + <source>Return, Ctrl+R</source> + <translation>Return, Ctrl+R</translation> + </message> + <message> + <location filename="../ObjView.ui" line="195"/> + <source>Flipped</source> + <translation>Flippato</translation> + </message> + <message> + <location filename="../ObjView.ui" line="218"/> + <source>H</source> + <translation>H</translation> + </message> + <message> + <location filename="../ObjView.ui" line="231"/> + <source>V</source> + <translation>V</translation> + </message> + <message> + <location filename="../ObjView.ui" line="245"/> + <source>Mode</source> + <translation>Modalità</translation> + </message> + <message> + <location filename="../ObjView.ui" line="265"/> + <source>Normal</source> + <translation>Normale</translation> + </message> + <message> + <location filename="../ObjView.ui" line="276"/> + <source>Mosaic</source> + <translation>Mosaico</translation> + </message> + <message> + <location filename="../ObjView.ui" line="313"/> + <source>Enabled</source> + <translation>Abilitato</translation> + </message> + <message> + <location filename="../ObjView.ui" line="347"/> + <source>Priority</source> + <translation>Priorità</translation> + </message> + <message> + <location filename="../ObjView.ui" line="382"/> + <source>Tile</source> + <translation>Tile</translation> + </message> + <message> + <location filename="../ObjView.ui" line="389"/> + <source>Geometry</source> + <translation>Geometria</translation> + </message> + <message> + <location filename="../ObjView.ui" line="397"/> + <source>Position</source> + <translation>Posizione</translation> + </message> + <message> + <location filename="../ObjView.ui" line="433"/> + <source>, </source> + <translation>, </translation> + </message> + <message> + <location filename="../ObjView.ui" line="463"/> + <source>Dimensions</source> + <translation>Dimensione</translation> + </message> + <message> + <location filename="../ObjView.ui" line="489"/> + <location filename="../ObjView.ui" line="515"/> + <source>8</source> + <translation>8</translation> + </message> + <message> + <location filename="../ObjView.ui" line="545"/> + <source>Address</source> + <translation>Indirizzo</translation> + </message> + <message> + <location filename="../ObjView.ui" line="552"/> + <source>0x07000000</source> + <translation>0x07000000</translation> + </message> +</context> +<context> + <name>OverrideView</name> + <message> + <location filename="../OverrideView.ui" line="20"/> + <source>Game Overrides</source> + <translation>Valori specifici per gioco</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="30"/> + <source>Game Boy Advance</source> + <translation>Game Boy Advance</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="42"/> + <location filename="../OverrideView.ui" line="121"/> + <location filename="../OverrideView.ui" line="248"/> + <location filename="../OverrideView.ui" line="279"/> + <source>Autodetect</source> + <translation>Rilevamento automatico</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="55"/> + <source>Realtime clock</source> + <translation>RealTime clock</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="65"/> + <source>Gyroscope</source> + <translation>Giroscopio</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="75"/> + <source>Tilt</source> + <translation>Inclinazione</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="85"/> + <source>Light sensor</source> + <translation>Sensore di luce</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="95"/> + <source>Rumble</source> + <translation>Vibrazione</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="113"/> + <source>Save type</source> + <translation>Tipo di salvataggio</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="126"/> + <location filename="../OverrideView.ui" line="284"/> + <source>None</source> + <translation>Nessuno</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="131"/> + <source>SRAM</source> + <translation>SRAM</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="136"/> + <source>Flash 512kb</source> + <translation>Flash 512kb</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="141"/> + <source>Flash 1Mb</source> + <translation>Flash 1Mb</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="146"/> + <source>EEPROM</source> + <translation>EEPROM</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="154"/> + <source>Idle loop</source> + <translation>Idle loop</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="195"/> + <source>Game Boy Player features</source> + <translation>Caratteristiche Game Boy Player</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="234"/> + <source>Game Boy</source> + <translation>Game Boy</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="240"/> + <source>Game Boy model</source> + <translation>Modello del Game Boy</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="253"/> + <source>Game Boy (DMG)</source> + <translation>Game Boy (DMG)</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="258"/> + <source>Game Boy Color (CGB)</source> + <translation>Game Boy Color (CGB)</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="263"/> + <source>Game Boy Advance (AGB)</source> + <translation>Game Boy Advance (AGB)</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="271"/> + <source>Memory bank controller</source> + <translation>Controller del banco di memoria</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="289"/> + <source>MBC1</source> + <translation>MBC1</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="294"/> + <source>MBC2</source> + <translation>MBC2</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="299"/> + <source>MBC3</source> + <translation>MBC3</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="304"/> + <source>MBC3 + RTC</source> + <translation>MBC3 + Reloj</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="309"/> + <source>MBC5</source> + <translation>MBC5</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="314"/> + <source>MBC5 + Rumble</source> + <translation>MBC5 + Vibrazione</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="319"/> + <source>MBC7</source> + <translation>MBC7</translation> + </message> + <message> + <location filename="../OverrideView.ui" line="324"/> + <source>HuC-3</source> + <translation>HuC-3</translation> + </message> +</context> +<context> + <name>PaletteView</name> + <message> + <location filename="../PaletteView.ui" line="14"/> + <source>Palette</source> + <translation>Palette</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="44"/> + <source>Background</source> + <translation>SFondo (BG)</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="94"/> + <source>Objects</source> + <translation>Oggetti (OBJ)</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="153"/> + <source>Selection</source> + <translation>Selezione</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="184"/> + <source>Red</source> + <translation>Rosso</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="191"/> + <source>Green</source> + <translation>Verde</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="198"/> + <source>Blue</source> + <translation>Blue</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="212"/> + <location filename="../PaletteView.ui" line="219"/> + <location filename="../PaletteView.ui" line="226"/> + <source>0x00 (00)</source> + <translation>0x00 (00)</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="244"/> + <source>16-bit value</source> + <translation>Valore in 16 bits</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="251"/> + <source>Hex code</source> + <translation>Codice esadecimale</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="258"/> + <source>Palette index</source> + <translation>Indice palette</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="272"/> + <source>0x0000</source> + <translation>0x0000</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="282"/> + <source>#000000</source> + <translation>#000000</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="292"/> + <source>000</source> + <translation>000</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="322"/> + <source>Export BG</source> + <translation>Esporta BG</translation> + </message> + <message> + <location filename="../PaletteView.ui" line="342"/> + <source>Export OBJ</source> + <translation>Esporta OBJ</translation> + </message> +</context> +<context> + <name>QGBA::AssetTile</name> + <message> + <location filename="../AssetTile.cpp" line="107"/> + <source>%0%1%2</source> + <translation>%0%1%2</translation> + </message> + <message> + <location filename="../AssetTile.cpp" line="136"/> + <location filename="../AssetTile.cpp" line="137"/> + <location filename="../AssetTile.cpp" line="138"/> + <source>0x%0 (%1)</source> + <translation>0x%0 (%1)</translation> + </message> +</context> +<context> + <name>QGBA::CheatsModel</name> + <message> + <location filename="../CheatsModel.cpp" line="54"/> + <source>(untitled)</source> + <translation>(senza titolo)</translation> + </message> + <message> + <location filename="../CheatsModel.cpp" line="209"/> + <source>Failed to open cheats file: %1</source> + <translation>Impossibile aprire il file cheats: %1</translation> + </message> +</context> +<context> + <name>QGBA::CheatsView</name> + <message> + <location filename="../CheatsView.cpp" line="49"/> + <location filename="../CheatsView.cpp" line="74"/> + <source>Add GameShark</source> + <translation>Aggiungi GameShark</translation> + </message> + <message> + <location filename="../CheatsView.cpp" line="55"/> + <source>Add Pro Action Replay</source> + <translation>Aggiungi Pro Action Replay</translation> + </message> + <message> + <location filename="../CheatsView.cpp" line="61"/> + <source>Add CodeBreaker</source> + <translation>Aggiungi CodeBreaker</translation> + </message> + <message> + <location filename="../CheatsView.cpp" line="80"/> + <source>Add GameGenie</source> + <translation>Aggiungi GameGenie</translation> + </message> + <message> + <location filename="../CheatsView.cpp" line="112"/> + <location filename="../CheatsView.cpp" line="119"/> + <source>Select cheats file</source> + <translation>Seleziona il file cheats</translation> + </message> +</context> +<context> + <name>QGBA::GBAKeyEditor</name> + <message> + <location filename="../GBAKeyEditor.cpp" line="68"/> + <source>Clear Button</source> + <translation>Pulisci bottoni</translation> + </message> + <message> + <location filename="../GBAKeyEditor.cpp" line="80"/> + <source>Clear Analog</source> + <translation>Pulisci analogici</translation> + </message> + <message> + <location filename="../GBAKeyEditor.cpp" line="91"/> + <source>Refresh</source> + <translation>Aggiornare</translation> + </message> + <message> + <location filename="../GBAKeyEditor.cpp" line="101"/> + <source>Set all</source> + <translation>Impostare tutti</translation> + </message> +</context> +<context> + <name>QGBA::GDBWindow</name> + <message> + <location filename="../GDBWindow.cpp" line="28"/> + <source>Server settings</source> + <translation>Impostazioni Server</translation> + </message> + <message> + <location filename="../GDBWindow.cpp" line="34"/> + <source>Local port</source> + <translation>Porta locale</translation> + </message> + <message> + <location filename="../GDBWindow.cpp" line="36"/> + <source>Bind address</source> + <translation>Indirizzo Bind</translation> + </message> + <message> + <location filename="../GDBWindow.cpp" line="55"/> + <source>Break</source> + <translation>Break</translation> + </message> + <message> + <location filename="../GDBWindow.cpp" line="104"/> + <source>Stop</source> + <translation>Stop</translation> + </message> + <message> + <location filename="../GDBWindow.cpp" line="114"/> + <source>Start</source> + <translation>Avvia</translation> + </message> + <message> + <location filename="../GDBWindow.cpp" line="122"/> + <source>Crash</source> + <translation>Errore</translation> + </message> + <message> + <location filename="../GDBWindow.cpp" line="122"/> + <source>Could not start GDB server</source> + <translation>Impossibile avviare il server GDB</translation> + </message> +</context> +<context> + <name>QGBA::GIFView</name> + <message> + <location filename="../GIFView.cpp" line="45"/> + <source>Failed to open output GIF file: %1</source> + <translation>Impossibile aprire il file GIF di output: %1</translation> + </message> + <message> + <location filename="../GIFView.cpp" line="63"/> + <source>Select output file</source> + <translation>Seleziona file di output</translation> + </message> + <message> + <location filename="../GIFView.cpp" line="63"/> + <source>Graphics Interchange Format (*.gif)</source> + <translation>Formato di interconnessione grafica (*.gif)</translation> + </message> +</context> +<context> + <name>QGBA::GameController</name> + <message> + <location filename="../GameController.cpp" line="397"/> + <location filename="../GameController.cpp" line="572"/> + <source>Failed to open game file: %1</source> + <translation>Impossibile aprire il file di gioco: %1</translation> + </message> + <message> + <location filename="../GameController.cpp" line="544"/> + <source>Failed to open save file: %1</source> + <translation>Impossibile aprire il file di salvataggio: %1</translation> + </message> + <message> + <location filename="../GameController.cpp" line="601"/> + <source>Failed to open snapshot file for reading: %1</source> + <translation>Impossibile aprire il file snapshot per la lettura: %1</translation> + </message> + <message> + <location filename="../GameController.cpp" line="621"/> + <source>Failed to open snapshot file for writing: %1</source> + <translation>Impossibile aprire il file snapshot per la scrittura: %1</translation> + </message> + <message> + <location filename="../GameController.cpp" line="914"/> + <source>Failed to start audio processor</source> + <translation>Impossibile avviare il processore audio</translation> + </message> +</context> +<context> + <name>QGBA::IOViewer</name> + <message> + <location filename="../IOViewer.cpp" line="30"/> + <source>Background mode</source> + <translation>Modalità Background (BG)</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="31"/> + <source>Mode 0: 4 tile layers</source> + <translation>Modalità 0: 4 tiles layers</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="32"/> + <source>Mode 1: 2 tile layers + 1 rotated/scaled tile layer</source> + <translation>Modalità 1: 2 tile layers + 1 ruotato/scalato tile layer</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="33"/> + <source>Mode 2: 2 rotated/scaled tile layers</source> + <translation>Modalità 2: 2 ruotato/scalato tile layers</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="34"/> + <source>Mode 3: Full 15-bit bitmap</source> + <translation>Modalità 3: completo 15 bitmap</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="35"/> + <source>Mode 4: Full 8-bit bitmap</source> + <translation>Modalità 4: completo 8 bits</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="36"/> + <source>Mode 5: Small 15-bit bitmap</source> + <translation>Modalità 5: basso 15-bit bitmap</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="40"/> + <source>CGB Mode</source> + <translation>Modalità CGB</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="41"/> + <source>Frame select</source> + <translation>Seleziona Frame</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="42"/> + <source>Unlocked HBlank</source> + <translation>HBlank sbloccato</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="43"/> + <source>Linear OBJ tile mapping</source> + <translation>Mappatura lineare tile OBJ</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="44"/> + <source>Force blank screen</source> + <translation>Forza schermo bianco</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="45"/> + <source>Enable background 0</source> + <translation>Abilitare sfondo 0</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="46"/> + <source>Enable background 1</source> + <translation>Abilitare sfondo 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="47"/> + <source>Enable background 2</source> + <translation>Abilitare sfondo 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="48"/> + <source>Enable background 3</source> + <translation>Abilitare sfondo 3</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="49"/> + <source>Enable OBJ</source> + <translation>Abilitare OBJ</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="50"/> + <source>Enable Window 0</source> + <translation>Abilitare Window 0</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="51"/> + <source>Enable Window 1</source> + <translation>Abilitare Window 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="52"/> + <source>Enable OBJ Window</source> + <translation>Abilitare Window OBJ</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="58"/> + <source>Currently in VBlank</source> + <translation>Attualmente in VBlank</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="59"/> + <source>Currently in HBlank</source> + <translation>Attualmente in HBlank</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="60"/> + <source>Currently in VCounter</source> + <translation>Attualmente in VCounter</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="61"/> + <source>Enable VBlank IRQ generation</source> + <translation>Abilita VBlank</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="62"/> + <source>Enable HBlank IRQ generation</source> + <translation>Abilita HBlank generazione IRQ</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="63"/> + <source>Enable VCounter IRQ generation</source> + <translation>Abilita generazione IRQ VCounter</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="64"/> + <source>VCounter scanline</source> + <translation>VCounter scanline</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="68"/> + <source>Current scanline</source> + <translation>Scanline corrente</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="72"/> + <location filename="../IOViewer.cpp" line="81"/> + <location filename="../IOViewer.cpp" line="90"/> + <location filename="../IOViewer.cpp" line="100"/> + <source>Priority</source> + <translation>Priorità</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="73"/> + <location filename="../IOViewer.cpp" line="82"/> + <location filename="../IOViewer.cpp" line="91"/> + <location filename="../IOViewer.cpp" line="101"/> + <source>Tile data base (* 16kB)</source> + <translation>Tile data base (* 16kB)</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="74"/> + <location filename="../IOViewer.cpp" line="83"/> + <location filename="../IOViewer.cpp" line="92"/> + <location filename="../IOViewer.cpp" line="102"/> + <source>Enable mosaic</source> + <translation>Abilita Mosaico</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="75"/> + <location filename="../IOViewer.cpp" line="84"/> + <location filename="../IOViewer.cpp" line="93"/> + <location filename="../IOViewer.cpp" line="103"/> + <source>Enable 256-color</source> + <translation>Abilita 256 colori</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="76"/> + <location filename="../IOViewer.cpp" line="85"/> + <location filename="../IOViewer.cpp" line="94"/> + <location filename="../IOViewer.cpp" line="104"/> + <source>Tile map base (* 2kB)</source> + <translation>Tile map base (* 2kB)</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="77"/> + <location filename="../IOViewer.cpp" line="86"/> + <location filename="../IOViewer.cpp" line="96"/> + <location filename="../IOViewer.cpp" line="106"/> + <source>Background dimensions</source> + <translation>Dimensioni dello sfondo</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="95"/> + <location filename="../IOViewer.cpp" line="105"/> + <source>Overflow wraps</source> + <translation>Overflow wraps</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="110"/> + <location filename="../IOViewer.cpp" line="118"/> + <location filename="../IOViewer.cpp" line="126"/> + <location filename="../IOViewer.cpp" line="134"/> + <source>Horizontal offset</source> + <translation>Compensazione orizzontale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="114"/> + <location filename="../IOViewer.cpp" line="122"/> + <location filename="../IOViewer.cpp" line="130"/> + <location filename="../IOViewer.cpp" line="138"/> + <source>Vertical offset</source> + <translation>Compensazione verticale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="142"/> + <location filename="../IOViewer.cpp" line="147"/> + <location filename="../IOViewer.cpp" line="152"/> + <location filename="../IOViewer.cpp" line="157"/> + <location filename="../IOViewer.cpp" line="162"/> + <location filename="../IOViewer.cpp" line="171"/> + <location filename="../IOViewer.cpp" line="180"/> + <location filename="../IOViewer.cpp" line="185"/> + <location filename="../IOViewer.cpp" line="190"/> + <location filename="../IOViewer.cpp" line="195"/> + <location filename="../IOViewer.cpp" line="200"/> + <location filename="../IOViewer.cpp" line="209"/> + <source>Fractional part</source> + <translation>Parte frazionale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="143"/> + <location filename="../IOViewer.cpp" line="148"/> + <location filename="../IOViewer.cpp" line="153"/> + <location filename="../IOViewer.cpp" line="158"/> + <location filename="../IOViewer.cpp" line="181"/> + <location filename="../IOViewer.cpp" line="186"/> + <location filename="../IOViewer.cpp" line="191"/> + <location filename="../IOViewer.cpp" line="196"/> + <source>Integer part</source> + <translation>Parte Integer</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="163"/> + <location filename="../IOViewer.cpp" line="172"/> + <location filename="../IOViewer.cpp" line="201"/> + <location filename="../IOViewer.cpp" line="210"/> + <source>Integer part (bottom)</source> + <translation>Parte Integer (inferiore)</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="167"/> + <location filename="../IOViewer.cpp" line="176"/> + <location filename="../IOViewer.cpp" line="205"/> + <location filename="../IOViewer.cpp" line="214"/> + <source>Integer part (top)</source> + <translation>Parte Integer (superiore)</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="218"/> + <location filename="../IOViewer.cpp" line="223"/> + <source>End x</source> + <translation>Fine x</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="219"/> + <location filename="../IOViewer.cpp" line="224"/> + <source>Start x</source> + <translation>Inizio x</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="228"/> + <location filename="../IOViewer.cpp" line="233"/> + <source>End y</source> + <translation>Fine y</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="229"/> + <location filename="../IOViewer.cpp" line="234"/> + <source>Start y</source> + <translation>Inizio y</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="238"/> + <source>Window 0 enable BG 0</source> + <translation>Window 0 BG 0</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="239"/> + <source>Window 0 enable BG 1</source> + <translation>Window 0 BG 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="240"/> + <source>Window 0 enable BG 2</source> + <translation>Window 0 BG 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="241"/> + <source>Window 0 enable BG 3</source> + <translation>Window 0 BG 3</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="242"/> + <source>Window 0 enable OBJ</source> + <translation>Window 0 OBJ</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="243"/> + <source>Window 0 enable blend</source> + <translation>Abilita Window 0 miscelato</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="244"/> + <source>Window 1 enable BG 0</source> + <translation>Abilita Window 1 BG 0</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="245"/> + <source>Window 1 enable BG 1</source> + <translation>Abilita Window 1 BG 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="246"/> + <source>Window 1 enable BG 2</source> + <translation>Abilita Window 1 BG 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="247"/> + <source>Window 1 enable BG 3</source> + <translation>AbilitaWindow 1 BG 3</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="248"/> + <source>Window 1 enable OBJ</source> + <translation>Abilita Window 1 OBJ</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="249"/> + <source>Window 1 enable blend</source> + <translation>Abilita Window 1 miscelato</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="253"/> + <source>Outside window enable BG 0</source> + <translation>Abilita Outside window BG 0</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="254"/> + <source>Outside window enable BG 1</source> + <translation>Abilita Outside window BG 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="255"/> + <source>Outside window enable BG 2</source> + <translation>Abilita Outside window BG 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="256"/> + <source>Outside window enable BG 3</source> + <translation>Abilita Outside window BG 3</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="257"/> + <source>Outside window enable OBJ</source> + <translation>Abilita Outside window OBJ</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="258"/> + <source>Outside window enable blend</source> + <translation>Outside window mezcla</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="259"/> + <source>OBJ window enable BG 0</source> + <translation>Abilita OBJ window BG 0</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="260"/> + <source>OBJ window enable BG 1</source> + <translation>Abilita OBJ window BG 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="261"/> + <source>OBJ window enable BG 2</source> + <translation>Abilita OBJ window BG 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="262"/> + <source>OBJ window enable BG 3</source> + <translation>Abilita OBJ window BG 3</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="263"/> + <source>OBJ window enable OBJ</source> + <translation>Abilita OBJ window OBJ</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="264"/> + <source>OBJ window enable blend</source> + <translation>Abilita OBJ window blend</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="268"/> + <source>Background mosaic size vertical</source> + <translation>Sfondo mosaico verticale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="269"/> + <source>Background mosaic size horizontal</source> + <translation>Sfondo mosaico orizzontale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="270"/> + <source>Object mosaic size vertical</source> + <translation>Sfondo mosaico oggetto verticale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="271"/> + <source>Object mosaic size horizontal</source> + <translation>Sfondo mosaico oggetto orizzontale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="277"/> + <source>BG 0 target 1</source> + <translation>BG 0 target 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="278"/> + <source>BG 1 target 1</source> + <translation>BG 1 target 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="279"/> + <source>BG 2 target 1</source> + <translation>BG 2 target 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="280"/> + <source>BG 3 target 1</source> + <translation>BG 3 target 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="281"/> + <source>OBJ target 1</source> + <translation>OBJ target 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="282"/> + <source>Backdrop target 1</source> + <translation>Backdrop target 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="283"/> + <source>Blend mode</source> + <translation>Modalità miscelato</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="284"/> + <source>Disabled</source> + <translation>Disabilitato</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="285"/> + <source>Additive blending</source> + <translation>Miscelazione dell'additivo</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="286"/> + <source>Brighten</source> + <translation>Schiarire</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="287"/> + <source>Darken</source> + <translation>Oscurire</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="289"/> + <source>BG 0 target 2</source> + <translation>BG 0 target 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="290"/> + <source>BG 1 target 2</source> + <translation>BG 1 target 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="291"/> + <source>BG 2 target 2</source> + <translation>BG 2 target 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="292"/> + <source>BG 3 target 2</source> + <translation>BG 3 target 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="293"/> + <source>OBJ target 2</source> + <translation>OBJ target 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="294"/> + <source>Backdrop target 2</source> + <translation>Backdrop target 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="298"/> + <source>Blend A (target 1)</source> + <translation>Miscelato A (target 1)</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="299"/> + <source>Blend B (target 2)</source> + <translation>Miscelato B (target 2)</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="303"/> + <source>Blend Y</source> + <translation>Miscelato Y</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="317"/> + <source>Sweep shifts</source> + <translation>Sweep shifts</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="318"/> + <source>Sweep subtract</source> + <translation>Sposta subastratto</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="319"/> + <source>Sweep time (in 1/128s)</source> + <translation>Tempo di sweep (in 1/128seg)</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="323"/> + <location filename="../IOViewer.cpp" line="339"/> + <location filename="../IOViewer.cpp" line="363"/> + <location filename="../IOViewer.cpp" line="385"/> + <source>Sound length</source> + <translation>Lunghezza del suono</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="324"/> + <location filename="../IOViewer.cpp" line="340"/> + <source>Duty cycle</source> + <translation>Ciclo di lavoro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="325"/> + <location filename="../IOViewer.cpp" line="341"/> + <location filename="../IOViewer.cpp" line="386"/> + <source>Envelope step time</source> + <translation>Envelope step time</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="326"/> + <location filename="../IOViewer.cpp" line="342"/> + <location filename="../IOViewer.cpp" line="387"/> + <source>Envelope increase</source> + <translation>Aumento envelope</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="327"/> + <location filename="../IOViewer.cpp" line="343"/> + <location filename="../IOViewer.cpp" line="388"/> + <source>Initial volume</source> + <translation>Volume iniziale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="331"/> + <location filename="../IOViewer.cpp" line="349"/> + <location filename="../IOViewer.cpp" line="377"/> + <source>Sound frequency</source> + <translation>Frequenza del suono</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="332"/> + <location filename="../IOViewer.cpp" line="350"/> + <location filename="../IOViewer.cpp" line="378"/> + <location filename="../IOViewer.cpp" line="400"/> + <source>Timed</source> + <translation>Temporizzato</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="333"/> + <location filename="../IOViewer.cpp" line="351"/> + <location filename="../IOViewer.cpp" line="379"/> + <location filename="../IOViewer.cpp" line="401"/> + <source>Reset</source> + <translation>Reset</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="357"/> + <source>Double-size wave table</source> + <translation>Tavola delle onde a doppia dimensione</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="358"/> + <source>Active wave table</source> + <translation>Tavola delle onde attive</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="359"/> + <source>Enable channel 3</source> + <translation>Canale 3 attivo</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="364"/> + <source>Volume</source> + <translation>Volume</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="365"/> + <source>0%</source> + <translation>0%</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="366"/> + <location filename="../IOViewer.cpp" line="423"/> + <source>100%</source> + <translation>100%</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="367"/> + <location filename="../IOViewer.cpp" line="422"/> + <source>50%</source> + <translation>50%</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="368"/> + <location filename="../IOViewer.cpp" line="421"/> + <source>25%</source> + <translation>25%</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="369"/> + <location filename="../IOViewer.cpp" line="370"/> + <location filename="../IOViewer.cpp" line="371"/> + <location filename="../IOViewer.cpp" line="372"/> + <source>75%</source> + <translation>75%</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="394"/> + <source>Clock divider</source> + <translation>Divisore dell'orologio</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="395"/> + <source>Register stages</source> + <translation>Stadi di registrazione</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="396"/> + <source>15</source> + <translation>15</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="397"/> + <source>7</source> + <translation>7</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="399"/> + <source>Shifter frequency</source> + <translation>Cambio di frequenza</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="407"/> + <source>PSG volume right</source> + <translation>PSG volume destro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="408"/> + <source>PSG volume left</source> + <translation>PSG volume sinistro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="409"/> + <source>Enable channel 1 right</source> + <translation>Abilita Canale 1 destro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="410"/> + <source>Enable channel 2 right</source> + <translation>Abilita Canale 2 destro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="411"/> + <source>Enable channel 3 right</source> + <translation>Abilita Canale 3 destro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="412"/> + <source>Enable channel 4 right</source> + <translation>Abilita Canale 4 destro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="413"/> + <source>Enable channel 1 left</source> + <translation>Abilita Canale 1 sinistro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="414"/> + <source>Enable channel 2 left</source> + <translation>Abilita Canale 2 sinistro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="415"/> + <source>Enable channel 3 left</source> + <translation>Abilita Canale 3 sinistro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="416"/> + <source>Enable channel 4 left</source> + <translation>Abilita Canale 4 sinistro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="420"/> + <source>PSG master volume</source> + <translation>PSG volume master</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="426"/> + <source>Loud channel A</source> + <translation>Canale A forte</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="427"/> + <source>Loud channel B</source> + <translation>Canale B forte</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="428"/> + <source>Enable channel A right</source> + <translation>Abilita Canale A destro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="429"/> + <source>Enable channel A left</source> + <translation>Abilta Canale A sinistro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="430"/> + <source>Channel A timer</source> + <translation>Canale A temporizzato</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="431"/> + <location filename="../IOViewer.cpp" line="438"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="432"/> + <location filename="../IOViewer.cpp" line="439"/> + <location filename="../IOViewer.cpp" line="768"/> + <location filename="../IOViewer.cpp" line="783"/> + <location filename="../IOViewer.cpp" line="799"/> + <location filename="../IOViewer.cpp" line="815"/> + <location filename="../IOViewer.cpp" line="987"/> + <location filename="../IOViewer.cpp" line="997"/> + <location filename="../IOViewer.cpp" line="1007"/> + <source>1</source> + <translation>1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="434"/> + <source>Channel A reset</source> + <translation>Resetta canale A</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="435"/> + <source>Enable channel B right</source> + <translation>Abilita Canale B destro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="436"/> + <source>Enable channel B left</source> + <translation>Abilita Canale B sinistro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="437"/> + <source>Channel B timer</source> + <translation>Canale B temporizzato</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="441"/> + <source>Channel B reset</source> + <translation>Resetta canale B</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="445"/> + <source>Active channel 1</source> + <translation>Canale 1 attivo</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="446"/> + <source>Active channel 2</source> + <translation>Canale 2 attivo</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="447"/> + <source>Active channel 3</source> + <translation>Canale 3 attivo</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="448"/> + <source>Active channel 4</source> + <translation>Canale 4 attivo</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="449"/> + <source>Enable audio</source> + <translation>Abilitare audio</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="455"/> + <source>Bias</source> + <translation>Polarizzazione</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="456"/> + <source>Resolution</source> + <translation>Risoluzione</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="466"/> + <location filename="../IOViewer.cpp" line="467"/> + <location filename="../IOViewer.cpp" line="468"/> + <location filename="../IOViewer.cpp" line="469"/> + <location filename="../IOViewer.cpp" line="473"/> + <location filename="../IOViewer.cpp" line="474"/> + <location filename="../IOViewer.cpp" line="475"/> + <location filename="../IOViewer.cpp" line="476"/> + <location filename="../IOViewer.cpp" line="480"/> + <location filename="../IOViewer.cpp" line="481"/> + <location filename="../IOViewer.cpp" line="482"/> + <location filename="../IOViewer.cpp" line="483"/> + <location filename="../IOViewer.cpp" line="487"/> + <location filename="../IOViewer.cpp" line="488"/> + <location filename="../IOViewer.cpp" line="489"/> + <location filename="../IOViewer.cpp" line="490"/> + <location filename="../IOViewer.cpp" line="494"/> + <location filename="../IOViewer.cpp" line="495"/> + <location filename="../IOViewer.cpp" line="496"/> + <location filename="../IOViewer.cpp" line="497"/> + <location filename="../IOViewer.cpp" line="501"/> + <location filename="../IOViewer.cpp" line="502"/> + <location filename="../IOViewer.cpp" line="503"/> + <location filename="../IOViewer.cpp" line="504"/> + <location filename="../IOViewer.cpp" line="508"/> + <location filename="../IOViewer.cpp" line="509"/> + <location filename="../IOViewer.cpp" line="510"/> + <location filename="../IOViewer.cpp" line="511"/> + <location filename="../IOViewer.cpp" line="515"/> + <location filename="../IOViewer.cpp" line="516"/> + <location filename="../IOViewer.cpp" line="517"/> + <location filename="../IOViewer.cpp" line="518"/> + <location filename="../IOViewer.cpp" line="522"/> + <location filename="../IOViewer.cpp" line="523"/> + <location filename="../IOViewer.cpp" line="527"/> + <location filename="../IOViewer.cpp" line="528"/> + <location filename="../IOViewer.cpp" line="532"/> + <location filename="../IOViewer.cpp" line="533"/> + <location filename="../IOViewer.cpp" line="537"/> + <location filename="../IOViewer.cpp" line="538"/> + <source>Sample</source> + <translation>Mostra</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="550"/> + <location filename="../IOViewer.cpp" line="558"/> + <location filename="../IOViewer.cpp" line="595"/> + <location filename="../IOViewer.cpp" line="603"/> + <location filename="../IOViewer.cpp" line="640"/> + <location filename="../IOViewer.cpp" line="648"/> + <location filename="../IOViewer.cpp" line="685"/> + <location filename="../IOViewer.cpp" line="693"/> + <source>Address (bottom)</source> + <translation>Indirizzo (inferiore)</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="554"/> + <location filename="../IOViewer.cpp" line="562"/> + <location filename="../IOViewer.cpp" line="599"/> + <location filename="../IOViewer.cpp" line="607"/> + <location filename="../IOViewer.cpp" line="644"/> + <location filename="../IOViewer.cpp" line="652"/> + <location filename="../IOViewer.cpp" line="689"/> + <location filename="../IOViewer.cpp" line="697"/> + <source>Address (top)</source> + <translation>Indirizzo (superiore)</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="566"/> + <location filename="../IOViewer.cpp" line="611"/> + <location filename="../IOViewer.cpp" line="656"/> + <location filename="../IOViewer.cpp" line="701"/> + <source>Word count</source> + <translation>Contatore di parole</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="570"/> + <location filename="../IOViewer.cpp" line="615"/> + <location filename="../IOViewer.cpp" line="660"/> + <location filename="../IOViewer.cpp" line="705"/> + <source>Destination offset</source> + <translation>Compensazione offset</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="571"/> + <location filename="../IOViewer.cpp" line="577"/> + <location filename="../IOViewer.cpp" line="616"/> + <location filename="../IOViewer.cpp" line="622"/> + <location filename="../IOViewer.cpp" line="661"/> + <location filename="../IOViewer.cpp" line="667"/> + <location filename="../IOViewer.cpp" line="706"/> + <location filename="../IOViewer.cpp" line="712"/> + <source>Increment</source> + <translation>Incremento</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="572"/> + <location filename="../IOViewer.cpp" line="578"/> + <location filename="../IOViewer.cpp" line="617"/> + <location filename="../IOViewer.cpp" line="623"/> + <location filename="../IOViewer.cpp" line="662"/> + <location filename="../IOViewer.cpp" line="668"/> + <location filename="../IOViewer.cpp" line="707"/> + <location filename="../IOViewer.cpp" line="713"/> + <source>Decrement</source> + <translation>Decremento</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="573"/> + <location filename="../IOViewer.cpp" line="579"/> + <location filename="../IOViewer.cpp" line="618"/> + <location filename="../IOViewer.cpp" line="624"/> + <location filename="../IOViewer.cpp" line="663"/> + <location filename="../IOViewer.cpp" line="669"/> + <location filename="../IOViewer.cpp" line="708"/> + <location filename="../IOViewer.cpp" line="714"/> + <source>Fixed</source> + <translation>Fissato</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="574"/> + <location filename="../IOViewer.cpp" line="619"/> + <location filename="../IOViewer.cpp" line="664"/> + <location filename="../IOViewer.cpp" line="709"/> + <source>Increment and reload</source> + <translation>Incremento e ricarica</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="576"/> + <location filename="../IOViewer.cpp" line="621"/> + <location filename="../IOViewer.cpp" line="666"/> + <location filename="../IOViewer.cpp" line="711"/> + <source>Source offset</source> + <translation>Compensazione di origine</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="582"/> + <location filename="../IOViewer.cpp" line="627"/> + <location filename="../IOViewer.cpp" line="672"/> + <location filename="../IOViewer.cpp" line="718"/> + <source>Repeat</source> + <translation>Ripeti</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="583"/> + <location filename="../IOViewer.cpp" line="628"/> + <location filename="../IOViewer.cpp" line="673"/> + <location filename="../IOViewer.cpp" line="719"/> + <source>32-bit</source> + <translation>32 bit</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="584"/> + <location filename="../IOViewer.cpp" line="629"/> + <location filename="../IOViewer.cpp" line="674"/> + <location filename="../IOViewer.cpp" line="720"/> + <source>Start timing</source> + <translation>Avvia temporizzatore</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="585"/> + <location filename="../IOViewer.cpp" line="630"/> + <location filename="../IOViewer.cpp" line="675"/> + <location filename="../IOViewer.cpp" line="721"/> + <source>Immediate</source> + <translation>Immediato</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="586"/> + <location filename="../IOViewer.cpp" line="631"/> + <location filename="../IOViewer.cpp" line="676"/> + <location filename="../IOViewer.cpp" line="722"/> + <location filename="../IOViewer.cpp" line="939"/> + <location filename="../IOViewer.cpp" line="956"/> + <source>VBlank</source> + <translation>VBlank</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="587"/> + <location filename="../IOViewer.cpp" line="632"/> + <location filename="../IOViewer.cpp" line="677"/> + <location filename="../IOViewer.cpp" line="723"/> + <location filename="../IOViewer.cpp" line="940"/> + <location filename="../IOViewer.cpp" line="957"/> + <source>HBlank</source> + <translation>HBlank</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="590"/> + <location filename="../IOViewer.cpp" line="635"/> + <location filename="../IOViewer.cpp" line="680"/> + <location filename="../IOViewer.cpp" line="726"/> + <location filename="../IOViewer.cpp" line="773"/> + <location filename="../IOViewer.cpp" line="789"/> + <location filename="../IOViewer.cpp" line="805"/> + <location filename="../IOViewer.cpp" line="821"/> + <location filename="../IOViewer.cpp" line="881"/> + <source>IRQ</source> + <translation>IRQ</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="591"/> + <location filename="../IOViewer.cpp" line="636"/> + <location filename="../IOViewer.cpp" line="681"/> + <location filename="../IOViewer.cpp" line="727"/> + <location filename="../IOViewer.cpp" line="774"/> + <location filename="../IOViewer.cpp" line="790"/> + <location filename="../IOViewer.cpp" line="806"/> + <location filename="../IOViewer.cpp" line="822"/> + <source>Enable</source> + <translation>Abilitare</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="633"/> + <location filename="../IOViewer.cpp" line="678"/> + <location filename="../IOViewer.cpp" line="724"/> + <source>Audio FIFO</source> + <translation>Audio FIFO</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="715"/> + <source>Video Capture</source> + <translation>Cattura video</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="717"/> + <source>DRQ</source> + <translation>DRQ</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="763"/> + <location filename="../IOViewer.cpp" line="778"/> + <location filename="../IOViewer.cpp" line="794"/> + <location filename="../IOViewer.cpp" line="810"/> + <source>Value</source> + <translation>Valore</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="767"/> + <location filename="../IOViewer.cpp" line="782"/> + <location filename="../IOViewer.cpp" line="798"/> + <location filename="../IOViewer.cpp" line="814"/> + <source>Scale</source> + <translation>Scala</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="769"/> + <location filename="../IOViewer.cpp" line="784"/> + <location filename="../IOViewer.cpp" line="800"/> + <location filename="../IOViewer.cpp" line="816"/> + <source>1/64</source> + <translation>1/64</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="770"/> + <location filename="../IOViewer.cpp" line="785"/> + <location filename="../IOViewer.cpp" line="801"/> + <location filename="../IOViewer.cpp" line="817"/> + <source>1/256</source> + <translation>1/256</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="771"/> + <location filename="../IOViewer.cpp" line="786"/> + <location filename="../IOViewer.cpp" line="802"/> + <location filename="../IOViewer.cpp" line="818"/> + <source>1/1024</source> + <translation>1/1024</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="788"/> + <location filename="../IOViewer.cpp" line="804"/> + <location filename="../IOViewer.cpp" line="820"/> + <source>Cascade</source> + <translation>Cascata</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="858"/> + <location filename="../IOViewer.cpp" line="871"/> + <source>A</source> + <translation>A</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="859"/> + <location filename="../IOViewer.cpp" line="872"/> + <source>B</source> + <translation>B</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="860"/> + <location filename="../IOViewer.cpp" line="873"/> + <source>Select</source> + <translation>Seleziona</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="861"/> + <location filename="../IOViewer.cpp" line="874"/> + <source>Start</source> + <translation>Avvia</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="862"/> + <location filename="../IOViewer.cpp" line="875"/> + <source>Right</source> + <translation>Destro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="863"/> + <location filename="../IOViewer.cpp" line="876"/> + <source>Left</source> + <translation>Sinistro</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="864"/> + <location filename="../IOViewer.cpp" line="877"/> + <source>Up</source> + <translation>Sù</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="865"/> + <location filename="../IOViewer.cpp" line="878"/> + <source>Down</source> + <translation>Giù</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="866"/> + <location filename="../IOViewer.cpp" line="879"/> + <source>R</source> + <translation>R</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="867"/> + <location filename="../IOViewer.cpp" line="880"/> + <source>L</source> + <translation>L</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="882"/> + <source>Condition</source> + <translation>Condizione</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="886"/> + <source>SC</source> + <translation>SC</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="887"/> + <source>SD</source> + <translation>SD</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="888"/> + <source>SI</source> + <translation>SI</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="889"/> + <source>SO</source> + <translation>SO</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="941"/> + <location filename="../IOViewer.cpp" line="958"/> + <source>VCounter</source> + <translation>VCounter</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="942"/> + <location filename="../IOViewer.cpp" line="959"/> + <source>Timer 0</source> + <translation>Timer 0</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="943"/> + <location filename="../IOViewer.cpp" line="960"/> + <source>Timer 1</source> + <translation>Timer 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="944"/> + <location filename="../IOViewer.cpp" line="961"/> + <source>Timer 2</source> + <translation>Timer 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="945"/> + <location filename="../IOViewer.cpp" line="962"/> + <source>Timer 3</source> + <translation>Timer 3</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="946"/> + <location filename="../IOViewer.cpp" line="963"/> + <source>SIO</source> + <translation>SIO</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="947"/> + <location filename="../IOViewer.cpp" line="964"/> + <source>DMA 0</source> + <translation>DMA 0</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="948"/> + <location filename="../IOViewer.cpp" line="965"/> + <source>DMA 1</source> + <translation>DMA 1</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="949"/> + <location filename="../IOViewer.cpp" line="966"/> + <source>DMA 2</source> + <translation>DMA 2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="950"/> + <location filename="../IOViewer.cpp" line="967"/> + <source>DMA 3</source> + <translation>DMA 3</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="951"/> + <location filename="../IOViewer.cpp" line="968"/> + <source>Keypad</source> + <translation>Tastiera</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="952"/> + <location filename="../IOViewer.cpp" line="969"/> + <source>Gamepak</source> + <translation>Gamepak</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="973"/> + <source>SRAM wait</source> + <translation>Attesa SRAM</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="974"/> + <location filename="../IOViewer.cpp" line="980"/> + <location filename="../IOViewer.cpp" line="990"/> + <location filename="../IOViewer.cpp" line="996"/> + <location filename="../IOViewer.cpp" line="1000"/> + <source>4</source> + <translation>4</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="975"/> + <location filename="../IOViewer.cpp" line="981"/> + <location filename="../IOViewer.cpp" line="991"/> + <location filename="../IOViewer.cpp" line="1001"/> + <source>3</source> + <translation>3</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="976"/> + <location filename="../IOViewer.cpp" line="982"/> + <location filename="../IOViewer.cpp" line="986"/> + <location filename="../IOViewer.cpp" line="992"/> + <location filename="../IOViewer.cpp" line="1002"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="977"/> + <location filename="../IOViewer.cpp" line="983"/> + <location filename="../IOViewer.cpp" line="993"/> + <location filename="../IOViewer.cpp" line="1003"/> + <location filename="../IOViewer.cpp" line="1006"/> + <source>8</source> + <translation>8</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="979"/> + <source>Cart 0 non-sequential</source> + <translation>Cart 0 non sequenziale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="985"/> + <source>Cart 0 sequential</source> + <translation>Cart 0 sequenziale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="989"/> + <source>Cart 1 non-sequential</source> + <translation>Cart 1 non sequenziale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="995"/> + <source>Cart 1 sequential</source> + <translation>Cart 1 sequenziale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="999"/> + <source>Cart 2 non-sequential</source> + <translation>Cart 2 non sequenziale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="1005"/> + <source>Cart 2 sequential</source> + <translation>Cart 2 sequenziale</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="1009"/> + <source>PHI terminal</source> + <translation>Terminale PHI</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="1010"/> + <source>Disable</source> + <translation>Disabilita</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="1011"/> + <source>4.19MHz</source> + <translation>4.19MHz</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="1012"/> + <source>8.38MHz</source> + <translation>8.38MHz</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="1013"/> + <source>16.78MHz</source> + <translation>16.78MHz</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="1015"/> + <source>Gamepak prefetch</source> + <translation>Gamepak prefetch</translation> + </message> + <message> + <location filename="../IOViewer.cpp" line="1021"/> + <source>Enable IRQs</source> + <translation>Abilitare IRQs</translation> + </message> +</context> +<context> + <name>QGBA::KeyEditor</name> + <message> + <location filename="../KeyEditor.cpp" line="37"/> + <location filename="../KeyEditor.cpp" line="211"/> + <source>---</source> + <translation>---</translation> + </message> +</context> +<context> + <name>QGBA::LibraryModel</name> + <message> + <location filename="../LibraryModel.cpp" line="24"/> + <source>Name</source> + <translation>Nome</translation> + </message> + <message> + <location filename="../LibraryModel.cpp" line="33"/> + <source>Filename</source> + <translation>Nome del file</translation> + </message> + <message> + <location filename="../LibraryModel.cpp" line="39"/> + <source>Size</source> + <translation>Dimensione</translation> + </message> + <message> + <location filename="../LibraryModel.cpp" line="56"/> + <source>Platform</source> + <translation>Piattaforma</translation> + </message> + <message> + <location filename="../LibraryModel.cpp" line="62"/> + <source>GBA</source> + <translation>GBA</translation> + </message> + <message> + <location filename="../LibraryModel.cpp" line="66"/> + <source>GB</source> + <translation>GB</translation> + </message> + <message> + <location filename="../LibraryModel.cpp" line="69"/> + <source>?</source> + <translation>?</translation> + </message> + <message> + <location filename="../LibraryModel.cpp" line="74"/> + <source>Location</source> + <translation>Posizione</translation> + </message> + <message> + <location filename="../LibraryModel.cpp" line="80"/> + <source>CRC32</source> + <translation>CRC32</translation> + </message> +</context> +<context> + <name>QGBA::LoadSaveState</name> + <message> + <location filename="../LoadSaveState.cpp" line="68"/> + <source>Load State</source> + <translation>Carica stato</translation> + </message> + <message> + <location filename="../LoadSaveState.cpp" line="68"/> + <source>Save State</source> + <translation>Salva stato</translation> + </message> + <message> + <location filename="../LoadSaveState.cpp" line="177"/> + <source>Empty</source> + <translation>Vuoto</translation> + </message> + <message> + <location filename="../LoadSaveState.cpp" line="186"/> + <source>Corrupted</source> + <translation>Corrotto</translation> + </message> + <message> + <location filename="../LoadSaveState.cpp" line="209"/> + <source>Slot %1</source> + <translation>Slot %1</translation> + </message> +</context> +<context> + <name>QGBA::LogController</name> + <message> + <location filename="../LogController.cpp" line="57"/> + <source>DEBUG</source> + <translation>DEBUG</translation> + </message> + <message> + <location filename="../LogController.cpp" line="59"/> + <source>STUB</source> + <translation>STUB</translation> + </message> + <message> + <location filename="../LogController.cpp" line="61"/> + <source>INFO</source> + <translation>INFORMAZIONI</translation> + </message> + <message> + <location filename="../LogController.cpp" line="63"/> + <source>WARN</source> + <translation>AVVERTENZA</translation> + </message> + <message> + <location filename="../LogController.cpp" line="65"/> + <source>ERROR</source> + <translation>ERRORE</translation> + </message> + <message> + <location filename="../LogController.cpp" line="67"/> + <source>FATAL</source> + <translation>FATALE</translation> + </message> + <message> + <location filename="../LogController.cpp" line="69"/> + <source>GAME ERROR</source> + <translation>ERRORE NEL GIOCO</translation> + </message> +</context> +<context> + <name>QGBA::MemoryModel</name> + <message> + <location filename="../MemoryModel.cpp" line="50"/> + <source>Copy selection</source> + <translation>Copia selezionato</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="55"/> + <source>Save selection</source> + <translation>Salva selezionato</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="60"/> + <source>Paste</source> + <translation>Incolla</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="65"/> + <source>Load</source> + <translation>Carica</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="97"/> + <location filename="../MemoryModel.cpp" line="162"/> + <source>All</source> + <translation>Tutto</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="142"/> + <source>Load TBL</source> + <translation>Carica TBL</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="202"/> + <source>Save selected memory</source> + <translation>Salva la memoria selezionate</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="208"/> + <source>Failed to open output file: %1</source> + <translation>Impossibile aprire il file di output: %1</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="216"/> + <source>Load memory</source> + <translation>Carica memoria</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="222"/> + <source>Failed to open input file: %1</source> + <translation>Impossibile aprire il file di input: %1</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="338"/> + <source>TBL</source> + <translation>TBL</translation> + </message> + <message> + <location filename="../MemoryModel.cpp" line="338"/> + <source>ISO-8859-1</source> + <translation>ISO-8859-1</translation> + </message> +</context> +<context> + <name>QGBA::ObjView</name> + <message> + <location filename="../ObjView.cpp" line="135"/> + <location filename="../ObjView.cpp" line="221"/> + <source>0x%0</source> + <translation>0x%0</translation> + </message> + <message> + <location filename="../ObjView.cpp" line="146"/> + <source>Off</source> + <translation>No</translation> + </message> + <message> + <location filename="../ObjView.cpp" line="151"/> + <source>Normal</source> + <translation>Normale</translation> + </message> + <message> + <location filename="../ObjView.cpp" line="154"/> + <source>Trans</source> + <translation>Trans</translation> + </message> + <message> + <location filename="../ObjView.cpp" line="157"/> + <source>OBJWIN</source> + <translation>OBJWIN</translation> + </message> + <message> + <location filename="../ObjView.cpp" line="160"/> + <source>Invalid</source> + <translation>Invalido</translation> + </message> + <message> + <location filename="../ObjView.cpp" line="228"/> + <location filename="../ObjView.cpp" line="229"/> + <source>N/A</source> + <translation>n/d</translation> + </message> +</context> +<context> + <name>QGBA::PaletteView</name> + <message> + <location filename="../PaletteView.cpp" line="120"/> + <source>#%0</source> + <translation>#%0</translation> + </message> + <message> + <location filename="../PaletteView.cpp" line="121"/> + <source>0x%0</source> + <translation>0x%0</translation> + </message> + <message> + <location filename="../PaletteView.cpp" line="122"/> + <source>%0</source> + <translation>%0</translation> + </message> + <message> + <location filename="../PaletteView.cpp" line="123"/> + <location filename="../PaletteView.cpp" line="124"/> + <location filename="../PaletteView.cpp" line="125"/> + <source>0x%0 (%1)</source> + <translation>0x%0 (%1)</translation> + </message> + <message> + <location filename="../PaletteView.cpp" line="137"/> + <source>Export palette</source> + <translation>Esporta palette</translation> + </message> + <message> + <location filename="../PaletteView.cpp" line="138"/> + <source>Windows PAL (*.pal);;Adobe Color Table (*.act)</source> + <translation>WIndows PAL (*.pal);;Tabella dei colori Adobe (*.act)</translation> + </message> + <message> + <location filename="../PaletteView.cpp" line="145"/> + <source>Failed to open output palette file: %1</source> + <translation>Errore nell'aprire il file palette di output : %1</translation> + </message> +</context> +<context> + <name>QGBA::ROMInfo</name> + <message> + <location filename="../ROMInfo.cpp" line="48"/> + <location filename="../ROMInfo.cpp" line="69"/> + <location filename="../ROMInfo.cpp" line="80"/> + <location filename="../ROMInfo.cpp" line="89"/> + <location filename="../ROMInfo.cpp" line="90"/> + <source>(unknown)</source> + <translation>(sconosciuto)</translation> + </message> + <message> + <location filename="../ROMInfo.cpp" line="57"/> + <location filename="../ROMInfo.cpp" line="64"/> + <source> bytes</source> + <translation> bytes</translation> + </message> + <message> + <location filename="../ROMInfo.cpp" line="83"/> + <source>(no database present)</source> + <translation>(nessun database presente)</translation> + </message> +</context> +<context> + <name>QGBA::SettingsView</name> + <message> + <location filename="../SettingsView.cpp" line="96"/> + <source>Qt Multimedia</source> + <translation>Qt Multimedia</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="103"/> + <source>SDL</source> + <translation>SDL</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="111"/> + <source>Software (Qt)</source> + <translation>Software (Qt)</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="117"/> + <source>OpenGL</source> + <translation>OpenGL</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="124"/> + <source>OpenGL (force version 1.x)</source> + <translation>OpenGL (forza la versione 1.x)</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="142"/> + <source>Keyboard</source> + <translation>Tastiera</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="151"/> + <source>Controllers</source> + <translation>Controllers</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="170"/> + <source>Shortcuts</source> + <translation>Tasti di scelta rapida</translation> + </message> + <message> + <location filename="../SettingsView.cpp" line="174"/> + <source>Select BIOS</source> + <translation>Seleziona BIOS</translation> + </message> +</context> +<context> + <name>QGBA::ShaderSelector</name> + <message> + <location filename="../ShaderSelector.cpp" line="50"/> + <source>No shader active</source> + <translation>Nessun shader attivo</translation> + </message> + <message> + <location filename="../ShaderSelector.cpp" line="63"/> + <source>Load shader</source> + <translation>Carica shader</translation> + </message> + <message> + <location filename="../ShaderSelector.cpp" line="63"/> + <source>%1 Shader (%.shader)</source> + <translation>%1 Shader (%.shader)</translation> + </message> + <message> + <location filename="../ShaderSelector.cpp" line="102"/> + <source>No shader loaded</source> + <translation>Nessun shader caricato</translation> + </message> + <message> + <location filename="../ShaderSelector.cpp" line="110"/> + <source>by %1</source> + <translation>por %1</translation> + </message> + <message> + <location filename="../ShaderSelector.cpp" line="121"/> + <source>Preprocessing</source> + <translation>Preprocesso</translation> + </message> + <message> + <location filename="../ShaderSelector.cpp" line="128"/> + <source>Pass %1</source> + <translation>Pass %1</translation> + </message> +</context> +<context> + <name>QGBA::ShortcutController</name> + <message> + <location filename="../ShortcutController.cpp" line="67"/> + <source>Action</source> + <translation>Azione</translation> + </message> + <message> + <location filename="../ShortcutController.cpp" line="69"/> + <source>Keyboard</source> + <translation>Tastiera</translation> + </message> + <message> + <location filename="../ShortcutController.cpp" line="71"/> + <source>Gamepad</source> + <translation>Gamepad</translation> + </message> +</context> +<context> + <name>QGBA::VideoView</name> + <message> + <location filename="../VideoView.cpp" line="208"/> + <source>Failed to open output video file: %1</source> + <translation>Errore durante l'archiviazione del video: %1</translation> + </message> + <message> + <location filename="../VideoView.cpp" line="226"/> + <source>Native (%0x%1)</source> + <translation>Nativo (%0x%1)</translation> + </message> + <message> + <location filename="../VideoView.cpp" line="241"/> + <source>Select output file</source> + <translation>Seleziona file di output</translation> + </message> +</context> +<context> + <name>QGBA::Window</name> + <message> + <location filename="../Window.cpp" line="340"/> + <source>Game Boy Advance ROMs (%1)</source> + <translation>ROM di Game Boy Advance (%1)</translation> + </message> + <message> + <location filename="../Window.cpp" line="356"/> + <source>Game Boy ROMs (%1)</source> + <translation>ROMs del Game Boy (%1)</translation> + </message> + <message> + <location filename="../Window.cpp" line="360"/> + <source>All ROMs (%1)</source> + <translation>Tutte le ROM (%1)</translation> + </message> + <message> + <location filename="../Window.cpp" line="375"/> + <source>Archives (%1)</source> + <translation>Archivio (%1)</translation> + </message> + <message> + <location filename="../Window.cpp" line="380"/> + <location filename="../Window.cpp" line="388"/> + <location filename="../Window.cpp" line="415"/> + <source>Select ROM</source> + <translation>Seleziona ROM</translation> + </message> + <message> + <location filename="../Window.cpp" line="423"/> + <source>Game Boy Advance save files (%1)</source> + <translation>Game Boy Advance file di salvataggio (%1)</translation> + </message> + <message> + <location filename="../Window.cpp" line="424"/> + <location filename="../Window.cpp" line="457"/> + <location filename="../Window.cpp" line="464"/> + <source>Select save</source> + <translation>Seleziona salvataggio</translation> + </message> + <message> + <location filename="../Window.cpp" line="444"/> + <source>Select patch</source> + <translation>Seleziona patch</translation> + </message> + <message> + <location filename="../Window.cpp" line="444"/> + <source>Patches (*.ips *.ups *.bps)</source> + <translation>Patches (*.ips *.ups *.bps)</translation> + </message> + <message> + <location filename="../Window.cpp" line="457"/> + <location filename="../Window.cpp" line="464"/> + <source>GameShark saves (*.sps *.xps)</source> + <translation>Salvataggi GameShark (*.sps *.xps)</translation> + </message> + <message> + <location filename="../Window.cpp" line="782"/> + <source>Crash</source> + <translation>Errore fatale</translation> + </message> + <message> + <location filename="../Window.cpp" line="783"/> + <source>The game has crashed with the following error: + +%1</source> + <translation>Il gioco è andato in crash con il seguente errore:: + +%1</translation> + </message> + <message> + <location filename="../Window.cpp" line="790"/> + <source>Couldn't Load</source> + <translation>Non è possibile caricare</translation> + </message> + <message> + <location filename="../Window.cpp" line="791"/> + <source>Could not load game. Are you sure it's in the correct format?</source> + <translation>Impossibile caricare il gioco. Sei sicuro che sia nel formato corretto?</translation> + </message> + <message> + <location filename="../Window.cpp" line="804"/> + <source>Unimplemented BIOS call</source> + <translation>BIOS non implementato</translation> + </message> + <message> + <location filename="../Window.cpp" line="805"/> + <source>This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience.</source> + <translation>Questo gioco utilizza una chiamata BIOS non implementata. Utilizza il BIOS ufficiale per una migliore esperienza</translation> + </message> + <message> + <location filename="../Window.cpp" line="812"/> + <source>Really make portable?</source> + <translation>Davvero rendere portatile?</translation> + </message> + <message> + <location filename="../Window.cpp" line="813"/> + <source>This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?</source> + <translation>In questo modo l'emulatore carica la propria configurazione dalla stessa directory dell'eseguibile. Vuoi continuare?</translation> + </message> + <message> + <location filename="../Window.cpp" line="821"/> + <source>Restart needed</source> + <translation>È necessario riavviare</translation> + </message> + <message> + <location filename="../Window.cpp" line="822"/> + <source>Some changes will not take effect until the emulator is restarted.</source> + <translation>Alcune modifiche non avranno effetto finché l'emulatore non viene riavviato.</translation> + </message> + <message> + <location filename="../Window.cpp" line="869"/> + <source> - Player %1 of %2</source> + <translation> - Giocatore %1 di %2</translation> + </message> + <message> + <location filename="../Window.cpp" line="880"/> + <source>%1 - %2</source> + <translation>%1 - %2</translation> + </message> + <message> + <location filename="../Window.cpp" line="882"/> + <source>%1 - %2 - %3</source> + <translation>%1 - %2 - %3</translation> + </message> + <message> + <location filename="../Window.cpp" line="884"/> + <source>%1 - %2 (%3 fps) - %4</source> + <translation>%1 - %2 (%3 fps) - %4</translation> + </message> + <message> + <location filename="../Window.cpp" line="916"/> + <source>&File</source> + <translation>&File</translation> + </message> + <message> + <location filename="../Window.cpp" line="919"/> + <source>Load &ROM...</source> + <translation>Carica &ROM...</translation> + </message> + <message> + <location filename="../Window.cpp" line="922"/> + <source>Load ROM in archive...</source> + <translation>Carica la ROM in archivio...</translation> + </message> + <message> + <location filename="../Window.cpp" line="928"/> + <source>Load temporary save...</source> + <translation>Carica il salvataggio temporaneo..</translation> + </message> + <message> + <location filename="../Window.cpp" line="933"/> + <source>Load &patch...</source> + <translation>Carica &patch...</translation> + </message> + <message> + <location filename="../Window.cpp" line="935"/> + <source>Boot BIOS</source> + <translation>Boot BIOS</translation> + </message> + <message> + <location filename="../Window.cpp" line="942"/> + <source>Replace ROM...</source> + <translation>Sostituire la ROM...</translation> + </message> + <message> + <location filename="../Window.cpp" line="944"/> + <source>ROM &info...</source> + <translation>ROM &info...</translation> + </message> + <message> + <location filename="../Window.cpp" line="949"/> + <source>Recent</source> + <translation>Recente</translation> + </message> + <message> + <location filename="../Window.cpp" line="953"/> + <source>Make portable</source> + <translation>Rendi portatile</translation> + </message> + <message> + <location filename="../Window.cpp" line="957"/> + <source>&Load state</source> + <translation>&Carica stato</translation> + </message> + <message> + <location filename="../Window.cpp" line="958"/> + <source>F10</source> + <translation>F10</translation> + </message> + <message> + <location filename="../Window.cpp" line="964"/> + <source>&Save state</source> + <translation>&Salva stato</translation> + </message> + <message> + <location filename="../Window.cpp" line="965"/> + <source>Shift+F10</source> + <translatorcomment>DO NOT TRANSLATE</translatorcomment> + <translation>Shift+F10</translation> + </message> + <message> + <location filename="../Window.cpp" line="971"/> + <source>Quick load</source> + <translation>Caricamento rapido</translation> + </message> + <message> + <location filename="../Window.cpp" line="972"/> + <source>Quick save</source> + <translation>Salvataggio rapido</translation> + </message> + <message> + <location filename="../Window.cpp" line="976"/> + <source>Load recent</source> + <translation>Carica recente</translation> + </message> + <message> + <location filename="../Window.cpp" line="982"/> + <source>Save recent</source> + <translation>Salva recente</translation> + </message> + <message> + <location filename="../Window.cpp" line="991"/> + <source>Undo load state</source> + <translation>Annulla il caricamento dello stato</translation> + </message> + <message> + <location filename="../Window.cpp" line="992"/> + <source>F11</source> + <translatorcomment>DO NOT TRANSLATE</translatorcomment> + <translation>F11</translation> + </message> + <message> + <location filename="../Window.cpp" line="998"/> + <source>Undo save state</source> + <translation>Annulla salva stato</translation> + </message> + <message> + <location filename="../Window.cpp" line="999"/> + <source>Shift+F11</source> + <translatorcomment>DO NOT TRANSLATE</translatorcomment> + <translation>Shift+F11</translation> + </message> + <message> + <location filename="../Window.cpp" line="1010"/> + <location filename="../Window.cpp" line="1017"/> + <source>State &%1</source> + <translation>Stato &%1</translation> + </message> + <message> + <location filename="../Window.cpp" line="1011"/> + <source>F%1</source> + <translation>F%1</translation> + </message> + <message> + <location filename="../Window.cpp" line="1018"/> + <source>Shift+F%1</source> + <translatorcomment>DO NOT TRANSLATE</translatorcomment> + <translation>Shift+F%1</translation> + </message> + <message> + <location filename="../Window.cpp" line="1027"/> + <source>Import GameShark Save</source> + <translation>Importa il salvataggio del GameShark</translation> + </message> + <message> + <location filename="../Window.cpp" line="1033"/> + <source>Export GameShark Save</source> + <translation>Esporta salvataggio dal GameShark</translation> + </message> + <message> + <location filename="../Window.cpp" line="1041"/> + <source>New multiplayer window</source> + <translation>Nuova finestra multigiocatore</translation> + </message> + <message> + <location filename="../Window.cpp" line="1051"/> + <source>About</source> + <translation>About</translation> + </message> + <message> + <location filename="../Window.cpp" line="1056"/> + <source>E&xit</source> + <translation>Uscire (&X)</translation> + </message> + <message> + <location filename="../Window.cpp" line="1059"/> + <source>&Emulation</source> + <translation>&Emulazione</translation> + </message> + <message> + <location filename="../Window.cpp" line="1061"/> + <source>&Reset</source> + <translation>&Reset</translation> + </message> + <message> + <location filename="../Window.cpp" line="1062"/> + <source>Ctrl+R</source> + <translation>Ctrl+R</translation> + </message> + <message> + <location filename="../Window.cpp" line="1067"/> + <source>Sh&utdown</source> + <translation>Spegni (&U)</translation> + </message> + <message> + <location filename="../Window.cpp" line="1073"/> + <source>Yank game pak</source> + <translation>Yank game pak</translation> + </message> + <message> + <location filename="../Window.cpp" line="1081"/> + <source>&Pause</source> + <translation>&Pausa</translation> + </message> + <message> + <location filename="../Window.cpp" line="1084"/> + <source>Ctrl+P</source> + <translation>Ctrl+P</translation> + </message> + <message> + <location filename="../Window.cpp" line="1093"/> + <source>&Next frame</source> + <translation>Salta il prossimo frame (&N)</translation> + </message> + <message> + <location filename="../Window.cpp" line="1094"/> + <source>Ctrl+N</source> + <translation>Ctrl+N</translation> + </message> + <message> + <location filename="../Window.cpp" line="1105"/> + <source>Fast forward (held)</source> + <translation>Avanzamento rapido (sostenuto)</translation> + </message> + <message> + <location filename="../Window.cpp" line="1107"/> + <source>&Fast forward</source> + <translation>Avanzamento rapido (&F)</translation> + </message> + <message> + <location filename="../Window.cpp" line="1110"/> + <source>Shift+Tab</source> + <translation>Shift+Tab</translation> + </message> + <message> + <location filename="../Window.cpp" line="1114"/> + <source>Fast forward speed</source> + <translation>Velocità di avanzamento rapido</translation> + </message> + <message> + <location filename="../Window.cpp" line="1119"/> + <source>Unbounded</source> + <translation>Illimitato</translation> + </message> + <message> + <location filename="../Window.cpp" line="1123"/> + <source>%0x</source> + <translation>%0x</translation> + </message> + <message> + <location filename="../Window.cpp" line="1131"/> + <source>Rewind (held)</source> + <translation>Riavvolgi (sostenuto)</translation> + </message> + <message> + <location filename="../Window.cpp" line="1133"/> + <source>Re&wind</source> + <translation>Riavvolgi (&W)</translation> + </message> + <message> + <location filename="../Window.cpp" line="1134"/> + <source>~</source> + <translation>~</translation> + </message> + <message> + <location filename="../Window.cpp" line="1140"/> + <source>Step backwards</source> + <translation>Torna indietro</translation> + </message> + <message> + <location filename="../Window.cpp" line="1141"/> + <source>Ctrl+B</source> + <translation>Ctrl+B</translation> + </message> + <message> + <location filename="../Window.cpp" line="1150"/> + <source>Sync to &video</source> + <translation>Sincronizzare su &video</translation> + </message> + <message> + <location filename="../Window.cpp" line="1157"/> + <source>Sync to &audio</source> + <translation>Sincronizzare su &audio</translation> + </message> + <message> + <location filename="../Window.cpp" line="1165"/> + <source>Solar sensor</source> + <translation>Sensore solare</translation> + </message> + <message> + <location filename="../Window.cpp" line="1167"/> + <source>Increase solar level</source> + <translation>Aumenta il livello solare</translation> + </message> + <message> + <location filename="../Window.cpp" line="1171"/> + <source>Decrease solar level</source> + <translation>Riduce il livello solare</translation> + </message> + <message> + <location filename="../Window.cpp" line="1175"/> + <source>Brightest solar level</source> + <translation>Livello solare brillante</translation> + </message> + <message> + <location filename="../Window.cpp" line="1179"/> + <source>Darkest solar level</source> + <translation>Livello solare più scuro</translation> + </message> + <message> + <location filename="../Window.cpp" line="1185"/> + <source>Brightness %1</source> + <translation>Luminosità %1</translation> + </message> + <message> + <location filename="../Window.cpp" line="1192"/> + <source>Audio/&Video</source> + <translation>Audio/&Video</translation> + </message> + <message> + <location filename="../Window.cpp" line="1194"/> + <source>Frame size</source> + <translation>Dimensione Frame</translation> + </message> + <message> + <location filename="../Window.cpp" line="1197"/> + <source>%1x</source> + <translation>%1x</translation> + </message> + <message> + <location filename="../Window.cpp" line="1225"/> + <source>Toggle fullscreen</source> + <translation>Abilita schermo intero</translation> + </message> + <message> + <location filename="../Window.cpp" line="1228"/> + <source>Lock aspect ratio</source> + <translation>Blocca aspect ratio</translation> + </message> + <message> + <location filename="../Window.cpp" line="1235"/> + <source>Resample video</source> + <translation>Rimostra video</translation> + </message> + <message> + <location filename="../Window.cpp" line="1241"/> + <source>Frame&skip</source> + <translation>Frame&skip</translation> + </message> + <message> + <location filename="../Window.cpp" line="1251"/> + <source>Shader options...</source> + <translation>Opzioni shader...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1261"/> + <source>Mute</source> + <translation>Muto</translation> + </message> + <message> + <location filename="../Window.cpp" line="1267"/> + <source>FPS target</source> + <translation>FPS mirato</translation> + </message> + <message> + <location filename="../Window.cpp" line="1272"/> + <source>15</source> + <translation>15</translation> + </message> + <message> + <location filename="../Window.cpp" line="1273"/> + <source>30</source> + <translation>30</translation> + </message> + <message> + <location filename="../Window.cpp" line="1274"/> + <source>45</source> + <translation>45</translation> + </message> + <message> + <location filename="../Window.cpp" line="1275"/> + <source>Native (59.7)</source> + <translation>Nativo (59.7)</translation> + </message> + <message> + <location filename="../Window.cpp" line="1276"/> + <source>60</source> + <translation>60</translation> + </message> + <message> + <location filename="../Window.cpp" line="1277"/> + <source>90</source> + <translation>90</translation> + </message> + <message> + <location filename="../Window.cpp" line="1278"/> + <source>120</source> + <translation>120</translation> + </message> + <message> + <location filename="../Window.cpp" line="1279"/> + <source>240</source> + <translation>240</translation> + </message> + <message> + <location filename="../Window.cpp" line="1287"/> + <source>Take &screenshot</source> + <translation>Effettua &screenshot</translation> + </message> + <message> + <location filename="../Window.cpp" line="1288"/> + <source>F12</source> + <translation>F12</translation> + </message> + <message> + <location filename="../Window.cpp" line="1295"/> + <source>Record output...</source> + <translation>Registra salida...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1302"/> + <source>Record GIF...</source> + <translation>Registra GIF...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1308"/> + <source>Video layers</source> + <translation>Layers video</translation> + </message> + <message> + <location filename="../Window.cpp" line="1312"/> + <source>Background %0</source> + <translation>Sfondo %0</translation> + </message> + <message> + <location filename="../Window.cpp" line="1319"/> + <source>OBJ (sprites)</source> + <translation>OBJ (sprites)</translation> + </message> + <message> + <location filename="../Window.cpp" line="1325"/> + <source>Audio channels</source> + <translation>Canali audio</translation> + </message> + <message> + <location filename="../Window.cpp" line="1329"/> + <source>Channel %0</source> + <translation>Canale %0</translation> + </message> + <message> + <location filename="../Window.cpp" line="1336"/> + <source>Channel A</source> + <translation>Canale A</translation> + </message> + <message> + <location filename="../Window.cpp" line="1342"/> + <source>Channel B</source> + <translation>Canale B</translation> + </message> + <message> + <location filename="../Window.cpp" line="1348"/> + <source>&Tools</source> + <translation>&Strumenti</translation> + </message> + <message> + <location filename="../Window.cpp" line="1350"/> + <source>View &logs...</source> + <translation>Visualizza registri... (&L)</translation> + </message> + <message> + <location filename="../Window.cpp" line="1354"/> + <source>Game &overrides...</source> + <translation>Val&specifico per il gioco...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1358"/> + <source>Game &Pak sensors...</source> + <translation>Sensori di gioco &Pak...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1362"/> + <source>&Cheats...</source> + <translation>&Trucchi...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1374"/> + <source>Open debugger console...</source> + <translation>Apri debugger console...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1380"/> + <source>Start &GDB server...</source> + <translation>Avvia server &GDB...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1368"/> + <source>Settings...</source> + <translation>Impostazioni...</translation> + </message> + <message> + <location filename="../Window.cpp" line="406"/> + <source>Select folder</source> + <translation>Seleziona cartella</translation> + </message> + <message> + <location filename="../Window.cpp" line="924"/> + <source>Add folder to library...</source> + <translation>Aggiungi cartella alla libreria...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1387"/> + <source>View &palette...</source> + <translation>Ver &palette...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1392"/> + <source>View &sprites...</source> + <translation>Ver &sprites...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1397"/> + <source>View &tiles...</source> + <translation>Ver &tiles...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1402"/> + <source>View memory...</source> + <translation>Ver memoria...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1408"/> + <source>View &I/O registers...</source> + <translation>Ver reg&registri I/O...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1465"/> + <source>Exit fullscreen</source> + <translation>Esci da schermo intero</translation> + </message> + <message> + <location filename="../Window.cpp" line="1470"/> + <source>Autofire</source> + <translation>Pulsanti Auto fuoco</translation> + </message> + <message> + <location filename="../Window.cpp" line="1477"/> + <source>Autofire A</source> + <translation>Auto fuoco A</translation> + </message> + <message> + <location filename="../Window.cpp" line="1483"/> + <source>Autofire B</source> + <translation>Auto fuoco B</translation> + </message> + <message> + <location filename="../Window.cpp" line="1489"/> + <source>Autofire L</source> + <translation>Auto fuoco L</translation> + </message> + <message> + <location filename="../Window.cpp" line="1495"/> + <source>Autofire R</source> + <translation>Auto fuoco R</translation> + </message> + <message> + <location filename="../Window.cpp" line="1501"/> + <source>Autofire Start</source> + <translation>Avvia Auto fuoco</translation> + </message> + <message> + <location filename="../Window.cpp" line="1507"/> + <source>Autofire Select</source> + <translation>Seleziona Auto fuoco</translation> + </message> + <message> + <location filename="../Window.cpp" line="1513"/> + <source>Autofire Up</source> + <translation>Auto fuoco sù</translation> + </message> + <message> + <location filename="../Window.cpp" line="1519"/> + <source>Autofire Right</source> + <translation>Auto fuoco destro</translation> + </message> + <message> + <location filename="../Window.cpp" line="1525"/> + <source>Autofire Down</source> + <translation>Auto fuoco giù</translation> + </message> + <message> + <location filename="../Window.cpp" line="1531"/> + <source>Autofire Left</source> + <translation>Auto fuoco sinistro</translation> + </message> +</context> +<context> + <name>ROMInfo</name> + <message> + <location filename="../ROMInfo.ui" line="14"/> + <source>ROM Info</source> + <translation>Informazioni della ROM</translation> + </message> + <message> + <location filename="../ROMInfo.ui" line="26"/> + <source>Game name:</source> + <translation>Nome del gioco:</translation> + </message> + <message> + <location filename="../ROMInfo.ui" line="33"/> + <source>{NAME}</source> + <translation>{NAME}</translation> + </message> + <message> + <location filename="../ROMInfo.ui" line="46"/> + <source>Internal name:</source> + <translation>Nome interno:</translation> + </message> + <message> + <location filename="../ROMInfo.ui" line="53"/> + <source>{TITLE}</source> + <translation>{TITLE}</translation> + </message> + <message> + <location filename="../ROMInfo.ui" line="63"/> + <source>Game ID:</source> + <translation>ID del gioco:</translation> + </message> + <message> + <location filename="../ROMInfo.ui" line="70"/> + <source>{ID}</source> + <translation>{ID}</translation> + </message> + <message> + <location filename="../ROMInfo.ui" line="80"/> + <source>File size:</source> + <translation>Dimensione del file:</translation> + </message> + <message> + <location filename="../ROMInfo.ui" line="87"/> + <source>{SIZE}</source> + <translation>{SIZE}</translation> + </message> + <message> + <location filename="../ROMInfo.ui" line="97"/> + <source>CRC32:</source> + <translation>CRC32:</translation> + </message> + <message> + <location filename="../ROMInfo.ui" line="104"/> + <source>{CRC}</source> + <translation>{CRC}</translation> + </message> +</context> +<context> + <name>SensorView</name> + <message> + <location filename="../SensorView.ui" line="20"/> + <source>Sensors</source> + <translation>Sensori</translation> + </message> + <message> + <location filename="../SensorView.ui" line="31"/> + <source>Realtime clock</source> + <translation>Realtime clock</translation> + </message> + <message> + <location filename="../SensorView.ui" line="37"/> + <source>Fixed time</source> + <translation>Ora fissa</translation> + </message> + <message> + <location filename="../SensorView.ui" line="47"/> + <source>System time</source> + <translation>Ora del sistema</translation> + </message> + <message> + <location filename="../SensorView.ui" line="60"/> + <source>Start time at</source> + <translation>Avvia tempo a</translation> + </message> + <message> + <location filename="../SensorView.ui" line="70"/> + <source>Now</source> + <translation>Adesso</translation> + </message> + <message> + <location filename="../SensorView.ui" line="97"/> + <source>MM/dd/yy hh:mm:ss AP</source> + <translation>dd/MM/yy HH:mm:ss</translation> + </message> + <message> + <location filename="../SensorView.ui" line="110"/> + <source>Light sensor</source> + <translation>Sensore di luce</translation> + </message> + <message> + <location filename="../SensorView.ui" line="116"/> + <source>Brightness</source> + <translation>Luminosità</translation> + </message> + <message> + <location filename="../SensorView.ui" line="153"/> + <source>Tilt sensor</source> + <translation>Sensore di inclinazione</translation> + </message> + <message> + <location filename="../SensorView.ui" line="161"/> + <location filename="../SensorView.ui" line="250"/> + <source>Set Y</source> + <translation>Config. Y</translation> + </message> + <message> + <location filename="../SensorView.ui" line="171"/> + <location filename="../SensorView.ui" line="260"/> + <source>Set X</source> + <translation>Config. X</translation> + </message> + <message> + <location filename="../SensorView.ui" line="242"/> + <source>Gyroscope</source> + <translation>Giroscopio</translation> + </message> + <message> + <location filename="../SensorView.ui" line="270"/> + <source>Sensitivity</source> + <translation>Sensibilità</translation> + </message> +</context> +<context> + <name>SettingsView</name> + <message> + <location filename="../SettingsView.ui" line="20"/> + <source>Settings</source> + <translation>Impostazioni</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="45"/> + <source>Audio/Video</source> + <translation>Audio/Video</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="50"/> + <source>Interface</source> + <translation>Interfaccia</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="55"/> + <source>Emulation</source> + <translation>Emulazione</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="65"/> + <source>Paths</source> + <translation>Paths</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="90"/> + <source>Audio driver:</source> + <translation>Audio driver:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="107"/> + <source>Audio buffer:</source> + <translation>Buffer audio:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="119"/> + <location filename="../SettingsView.ui" line="141"/> + <source>1536</source> + <translation>1536</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="126"/> + <source>512</source> + <translation>512</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="131"/> + <source>768</source> + <translation>768</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="136"/> + <source>1024</source> + <translation>1024</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="146"/> + <source>2048</source> + <translation>2048</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="151"/> + <source>3072</source> + <translation>3072</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="156"/> + <source>4096</source> + <translation>4096</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="164"/> + <source>samples</source> + <translation>samples</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="173"/> + <source>Sample rate:</source> + <translation>Sample rate:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="185"/> + <location filename="../SettingsView.ui" line="202"/> + <source>44100</source> + <translation>44100</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="192"/> + <source>22050</source> + <translation>22050</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="197"/> + <source>32000</source> + <translation>32000</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="207"/> + <source>48000</source> + <translation>48000</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="215"/> + <source>Hz</source> + <translation>Hz</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="224"/> + <source>Volume:</source> + <translation>Volume:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="255"/> + <source>Mute</source> + <translation>Muto</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="271"/> + <source>Display driver:</source> + <translation>Visualizza driver:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="288"/> + <source>Frameskip:</source> + <translation>Frameskip:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="297"/> + <source>Skip every</source> + <translation>Salta ognuno</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="307"/> + <location filename="../SettingsView.ui" line="532"/> + <source>frames</source> + <translation>frames</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="316"/> + <source>FPS target:</source> + <translation>FPS mirato:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="338"/> + <source>frames per second</source> + <translation>frame per secondo</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="354"/> + <source>Sync:</source> + <translation>Sincronizzare:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="363"/> + <source>Video</source> + <translation>Video</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="370"/> + <source>Audio</source> + <translation>Audio</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="379"/> + <source>Lock aspect ratio</source> + <translation>Blocca aspect ratio</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="386"/> + <source>Resample video</source> + <translation>Rimostrare video</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="421"/> + <source>Library:</source> + <translation>Biblioteca:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="428"/> + <source>Show when no game open</source> + <translation>Mostra quando nessun gioco è aperto</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="445"/> + <source>Clear cache</source> + <translation>Cancella cache</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="459"/> + <source>Fast forward speed:</source> + <translation>Velocità di avanzamento rapido:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="679"/> + <location filename="../SettingsView.ui" line="717"/> + <location filename="../SettingsView.ui" line="752"/> + <location filename="../SettingsView.ui" line="793"/> + <location filename="../SettingsView.ui" line="841"/> + <location filename="../SettingsView.ui" line="889"/> + <location filename="../SettingsView.ui" line="937"/> + <source>Browse</source> + <translation>Sfoglia</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="688"/> + <source>Use BIOS file if found</source> + <translation>Utilizzare il file del BIOS se è stato trovato</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="698"/> + <source>Skip BIOS intro</source> + <translation>Salta BIOS intro</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="471"/> + <source>×</source> + <translation>×</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="490"/> + <source>Unbounded</source> + <translation>Illimitato</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="404"/> + <source>Suspend screensaver</source> + <translation>Sospendi screensaver</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="60"/> + <source>BIOS</source> + <translation>BIOS</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="414"/> + <source>Pause when inactive</source> + <translation>Pausa se inattivo</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="556"/> + <source>Run all</source> + <translation>Avviare tutto</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="561"/> + <source>Remove known</source> + <translation>Rimuovi conosciuto</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="566"/> + <source>Detect and remove</source> + <translation>Rileva e rimuovi</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="397"/> + <source>Allow opposing input directions</source> + <translation>Consenti direzioni opposte</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="588"/> + <location filename="../SettingsView.ui" line="625"/> + <source>Screenshot</source> + <translation>Screenshot</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="598"/> + <location filename="../SettingsView.ui" line="635"/> + <source>Save data</source> + <translation>Salva dati</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="608"/> + <location filename="../SettingsView.ui" line="642"/> + <source>Cheat codes</source> + <translation>Trucchi</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="509"/> + <source>Enable rewind</source> + <translation>Abilita riavvolgi</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="516"/> + <source>Rewind history:</source> + <translation>Riavvolgi storia:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="548"/> + <source>Idle loops:</source> + <translation>Idle loops:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="581"/> + <source>Savestate extra data:</source> + <translation>Salva dati extra:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="618"/> + <source>Load extra data:</source> + <translation>Carica dati extra:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="660"/> + <source>GB BIOS file:</source> + <translation>File GB BIOS:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="726"/> + <source>GBA BIOS file:</source> + <translation>File GBA BIOS:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="733"/> + <source>GBC BIOS file:</source> + <translation>File GBC BIOS:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="768"/> + <source>Save games</source> + <translation>Salva il gioco</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="802"/> + <location filename="../SettingsView.ui" line="850"/> + <location filename="../SettingsView.ui" line="898"/> + <location filename="../SettingsView.ui" line="946"/> + <source>Same directory as the ROM</source> + <translation>Stessa directory della ROM</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="816"/> + <source>Save states</source> + <translation>Salva Stato</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="864"/> + <source>Screenshots</source> + <translation>Screenshots</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="912"/> + <source>Patches</source> + <translation>Patches</translation> + </message> +</context> +<context> + <name>ShaderSelector</name> + <message> + <location filename="../ShaderSelector.ui" line="14"/> + <source>Shaders</source> + <translation>Shaders</translation> + </message> + <message> + <location filename="../ShaderSelector.ui" line="28"/> + <source>Active Shader:</source> + <translation>Attiva Shader:</translation> + </message> + <message> + <location filename="../ShaderSelector.ui" line="35"/> + <source>Name</source> + <translation>Nome</translation> + </message> + <message> + <location filename="../ShaderSelector.ui" line="45"/> + <source>Author</source> + <translation>Autore</translation> + </message> + <message> + <location filename="../ShaderSelector.ui" line="62"/> + <source>Description</source> + <translation>Descrizione</translation> + </message> + <message> + <location filename="../ShaderSelector.ui" line="88"/> + <source>Unload Shader</source> + <translation>Non caricare shader</translation> + </message> + <message> + <location filename="../ShaderSelector.ui" line="95"/> + <source>Load New Shader</source> + <translation>Carica Nuovo shader</translation> + </message> +</context> +<context> + <name>ShortcutView</name> + <message> + <location filename="../ShortcutView.ui" line="14"/> + <source>Edit Shortcuts</source> + <translation>Edita Shortcuts</translation> + </message> + <message> + <location filename="../ShortcutView.ui" line="29"/> + <source>Keyboard</source> + <translation>Tastiera</translation> + </message> + <message> + <location filename="../ShortcutView.ui" line="39"/> + <source>Gamepad</source> + <translation>Gamepad</translation> + </message> + <message> + <location filename="../ShortcutView.ui" line="46"/> + <source>Clear</source> + <translation>Cancella</translation> + </message> +</context> +<context> + <name>TileView</name> + <message> + <location filename="../TileView.ui" line="14"/> + <source>Tiles</source> + <translation>Tiles</translation> + </message> + <message> + <location filename="../TileView.ui" line="20"/> + <source>256 colors</source> + <translation>256 colori</translation> + </message> + <message> + <location filename="../TileView.ui" line="70"/> + <source>×</source> + <translation>×</translation> + </message> + <message> + <location filename="../TileView.ui" line="83"/> + <source>Magnification</source> + <translation>Magnification</translation> + </message> +</context> +<context> + <name>VideoView</name> + <message> + <location filename="../VideoView.ui" line="20"/> + <source>Record Video</source> + <translation>Registra video</translation> + </message> + <message> + <location filename="../VideoView.ui" line="40"/> + <source>Start</source> + <translation>Avvia</translation> + </message> + <message> + <location filename="../VideoView.ui" line="56"/> + <source>Stop</source> + <translation>Stop</translation> + </message> + <message> + <location filename="../VideoView.ui" line="69"/> + <source>Select File</source> + <translation>Seleziona File</translation> + </message> + <message> + <location filename="../VideoView.ui" line="101"/> + <source>Presets</source> + <translation>Presets</translation> + </message> + <message> + <location filename="../VideoView.ui" line="109"/> + <source>High Quality</source> + <translation>Alta Qualità</translation> + </message> + <message> + <location filename="../VideoView.ui" line="119"/> + <source>YouTube</source> + <translation>YouTube</translation> + </message> + <message> + <location filename="../VideoView.ui" line="129"/> + <location filename="../VideoView.ui" line="237"/> + <source>WebM</source> + <translation>WebM</translation> + </message> + <message> + <location filename="../VideoView.ui" line="139"/> + <source>Lossless</source> + <translation>Senza perdite</translation> + </message> + <message> + <location filename="../VideoView.ui" line="156"/> + <source>1080p</source> + <translation>1080p</translation> + </message> + <message> + <location filename="../VideoView.ui" line="166"/> + <source>720p</source> + <translation>720p</translation> + </message> + <message> + <location filename="../VideoView.ui" line="176"/> + <source>480p</source> + <translation>480p</translation> + </message> + <message> + <location filename="../VideoView.ui" line="189"/> + <source>Native</source> + <translation>Nativo</translation> + </message> + <message> + <location filename="../VideoView.ui" line="222"/> + <source>Format</source> + <translation>Formato</translation> + </message> + <message> + <location filename="../VideoView.ui" line="232"/> + <source>MKV</source> + <translation>MKV</translation> + </message> + <message> + <location filename="../VideoView.ui" line="242"/> + <source>AVI</source> + <translation>AVI</translation> + </message> + <message> + <location filename="../VideoView.ui" line="247"/> + <source>MP4</source> + <translation>MP4</translation> + </message> + <message> + <location filename="../VideoView.ui" line="259"/> + <source>PNG</source> + <translation>PNG</translation> + </message> + <message> + <location filename="../VideoView.ui" line="264"/> + <source>h.264</source> + <translation>h.264</translation> + </message> + <message> + <location filename="../VideoView.ui" line="269"/> + <source>VP8</source> + <translation></translation> + </message> + <message> + <location filename="../VideoView.ui" line="274"/> + <source>Xvid</source> + <translation>Xvid</translation> + </message> + <message> + <location filename="../VideoView.ui" line="279"/> + <source>FFV1</source> + <translation>FFV1</translation> + </message> + <message> + <location filename="../VideoView.ui" line="291"/> + <source>FLAC</source> + <translation>FLAC</translation> + </message> + <message> + <location filename="../VideoView.ui" line="296"/> + <source>Opus</source> + <translation>Opus</translation> + </message> + <message> + <location filename="../VideoView.ui" line="301"/> + <source>Vorbis</source> + <translation>Vorbis</translation> + </message> + <message> + <location filename="../VideoView.ui" line="306"/> + <source>MP3</source> + <translation>MP3</translation> + </message> + <message> + <location filename="../VideoView.ui" line="311"/> + <source>AAC</source> + <translation>AAC</translation> + </message> + <message> + <location filename="../VideoView.ui" line="316"/> + <source>Uncompressed</source> + <translation>Senza compressione</translation> + </message> + <message> + <location filename="../VideoView.ui" line="327"/> + <source> Bitrate (kbps)</source> + <translation>Bitrate (kbps)</translation> + </message> + <message> + <location filename="../VideoView.ui" line="333"/> + <source>VBR </source> + <translation>VBR </translation> + </message> + <message> + <location filename="../VideoView.ui" line="381"/> + <source>ABR</source> + <translation>ABR</translation> + </message> + <message> + <location filename="../VideoView.ui" line="397"/> + <source>Dimensions</source> + <translation>Dimensioni</translation> + </message> + <message> + <location filename="../VideoView.ui" line="403"/> + <source>:</source> + <translation>:</translation> + </message> + <message> + <location filename="../VideoView.ui" line="413"/> + <source>×</source> + <translation>×</translation> + </message> + <message> + <location filename="../VideoView.ui" line="463"/> + <source>Lock aspect ratio</source> + <translation>Blocca aspect ratio</translation> + </message> + <message> + <location filename="../VideoView.ui" line="478"/> + <source>Show advanced</source> + <translation>Mostra avanzato</translation> + </message> +</context> +</TS>
@@ -189,11 +189,8 @@ claimed = true;
break; } } - if (claimed) { - continue; - } - if (firstUnclaimed == SIZE_MAX) { + if (!claimed && firstUnclaimed == SIZE_MAX) { firstUnclaimed = i; }@@ -353,7 +350,7 @@ continue;
} if (events->preferredJoysticks[i] && strcmp(events->preferredJoysticks[i], joystickName) == 0) { events->players[i]->joystick = joystick; - if (config) { + if (config && events->players[i]->bindings) { mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_BUTTON, config, joystickName); } return;@@ -364,7 +361,7 @@ if (events->players[i]->joystick) {
continue; } events->players[i]->joystick = joystick; - if (config) { + if (config && events->players[i]->bindings) { mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_BUTTON, config, joystickName); } break;