Merge branch 'master' (early part) into medusa
jump to
@@ -28,6 +28,8 @@ - GB: Expose platform information to CLI debugger
- Support Discord Rich Presence - Debugger: Add tracing to file - Map viewer supports bitmapped GBA modes + - OpenGL renderer with high-resolution upscaling support + - Experimental high level "XQ" audio for most GBA games Emulation fixes: - GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208) - GBA: Reset now reloads multiboot ROMs@@ -43,6 +45,9 @@ - GB Timer: Fix timing adjustments when writing to TAC (fixes mgba.io/i/1340)
- GBA Memory: Fix writing to OBJ memory in modes 3 and 5 - GBA: Fix RTC on non-standard sized ROMs (fixes mgba.io/i/1400) - GBA Memory: Prevent writing to mirrored BG VRAM (fixes mgba.io/i/743) + - GBA Video: Fix sprite mosaic clamping (fixes mgba.io/i/1008) + - GB: Fix HALT when IE and IF unused bits are set (fixes mgba.io/i/1349) + - GBA Video: Implement mosaic on transformed sprites (fixes mgba.io/b/9) Other fixes: - Qt: More app metadata fixes - Qt: Fix load recent from archive (fixes mgba.io/i/1325)@@ -60,6 +65,10 @@ - FFmpeg: Improve initialization reliability and cleanup
- Wii: Fix aspect ratio (fixes mgba.io/i/500) - Qt: Fix some Qt display driver race conditions - FFmpeg: Fix audio conversion producing gaps + - Core: Improved lockstep driver reliability (Le Hoang Quyen) + - GBA: Fix skipping BIOS on irregularly sized ROMs + - Qt: Fix bounded fast forward with Qt Multimedia + - Qt: Fix saving settings with native FPS target Misc: - GBA Savedata: EEPROM performance fixes - GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash@@ -79,6 +88,9 @@ - Debugger: Print breakpoint/watchpoint number when inserting
- Qt: Open a message box for Qt frontend errors - GBA Video: Clean up dead code in sprite rendering loop - FFmpeg: Support audio-only recording + - Qt: Increase maximum magnifications and scaling + - Qt: Add native FPS button to settings view + - Qt: Improve sync code 0.7.1: (2019-02-24) Bugfixes:
@@ -90,6 +90,7 @@ file(GLOB GB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/*.c)
file(GLOB GB_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/test/*.c) file(GLOB DS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/ds/*.c) file(GLOB GBA_CHEATS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/cheats/*.c) +file(GLOB GBA_EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/extra/audio-mixer.c) file(GLOB GBA_RR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/rr/*.c) file(GLOB CORE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/core/*.c) file(GLOB CORE_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/core/test/*.c)@@ -261,14 +262,9 @@ set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
endif() elseif(UNIX) set(USE_PTHREADS ON) - add_definitions(-DUSE_PTHREADS) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") add_definitions(-D_GNU_SOURCE) - endif() - if(NOT APPLE AND NOT HAIKU) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-dirent.c)@@ -355,6 +351,7 @@ if(3DS OR WII)
add_definitions(-D_GNU_SOURCE) endif() +include(CheckCCompilerFlag) include(CheckFunctionExists) include(CheckIncludeFiles) check_function_exists(strdup HAVE_STRDUP)@@ -406,6 +403,27 @@
check_function_exists(chmod HAVE_CHMOD) check_function_exists(umask HAVE_UMASK) +if(USE_PTHREADS) + check_include_files("pthread.h" HAVE_PTHREAD_H) + if(HAVE_PTHREAD_H) + check_c_compiler_flag(-pthread HAVE_PTHREAD) + if(HAVE_PTHREAD AND NOT APPLE AND NOT HAIKU) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + endif() + + check_function_exists(pthread_create HAVE_PTHREAD_CREATE) + if(HAVE_PTHREAD_CREATE) + add_definitions(-DUSE_PTHREADS) + + check_include_files("pthread_np.h" HAVE_PTHREAD_NP_H) + + check_function_exists(pthread_setname_np HAVE_PTHREAD_SETNAME_NP) + check_function_exists(pthread_set_name_np HAVE_PTHREAD_SET_NAME_NP) + endif() + endif() +endif() + set(FUNCTION_DEFINES) if(HAVE_STRDUP)@@ -445,6 +463,18 @@ endif()
if(HAVE_UMASK) list(APPEND FUNCTION_DEFINES HAVE_UMASK) +endif() + +if(HAVE_PTHREAD_NP_H) + list(APPEND FUNCTION_DEFINES HAVE_PTHREAD_NP_H) +endif() + +if(HAVE_PTHREAD_SETNAME_NP) + list(APPEND FUNCTION_DEFINES HAVE_PTHREAD_SETNAME_NP) +endif() + +if(HAVE_PTHREAD_SET_NAME_NP) + list(APPEND FUNCTION_DEFINES HAVE_PTHREAD_SET_NAME_NP) endif() # Feature dependencies
@@ -215,7 +215,6 @@
<a name="missing">[1]</a> Zurzeit fehlende Features sind - OBJ-Fenster für die Modi 3, 4 und 5 ([Bug #5](http://mgba.io/b/5)) -- Mosaik-Effekt für umgewandelte OBJs ([Bug #9](http://mgba.io/b/9)) <a name="flashdetect">[2]</a> In manchen Fällen ist es nicht möglich, die Größe des Flash-Speichers automatisch zu ermitteln. Diese kann dann zur Laufzeit konfiguriert werden, es wird jedoch empfohlen, den Fehler zu melden.
@@ -12,7 +12,7 @@ CXX_GUARD_START
#include <pthread.h> #include <sys/time.h> -#if defined(__FreeBSD__) || defined(__OpenBSD__) +#ifdef HAVE_PTHREAD_NP_H #include <pthread_np.h> #elif defined(__HAIKU__) #include <OS.h>@@ -85,20 +85,15 @@ return pthread_join(thread, 0);
} static inline int ThreadSetName(const char* name) { -#ifdef __APPLE__ -#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 +#if defined(__APPLE__) && defined(HAVE_PTHREAD_SETNAME_NP) return pthread_setname_np(name); -#else - UNUSED(name); - return 0; -#endif -#elif defined(__FreeBSD__) || defined(__OpenBSD__) +#elif defined(HAVE_PTHREAD_SET_NAME_NP) pthread_set_name_np(pthread_self(), name); return 0; #elif defined(__HAIKU__) rename_thread(find_thread(NULL), name); return 0; -#elif !defined(BUILD_PANDORA) // Pandora's glibc is too old +#elif defined(HAVE_PTHREAD_SETNAME_NP) return pthread_setname_np(pthread_self(), name); #else UNUSED(name);
@@ -13,6 +13,10 @@
enum mCPUComponentType { CPU_COMPONENT_DEBUGGER, CPU_COMPONENT_CHEAT_DEVICE, + CPU_COMPONENT_MISC_1, + CPU_COMPONENT_MISC_2, + CPU_COMPONENT_MISC_3, + CPU_COMPONENT_MISC_4, CPU_COMPONENT_MAX };
@@ -10,10 +10,14 @@ #include <mgba-util/common.h>
CXX_GUARD_START +#include <mgba/core/cpu.h> #include <mgba/core/log.h> #include <mgba/internal/gb/audio.h> #include <mgba-util/circle-buffer.h> +#define MP2K_MAGIC 0x68736D53 +#define MP2K_MAX_SOUND_CHANNELS 12 + mLOG_DECLARE_CATEGORY(GBA_AUDIO); struct GBADMA;@@ -44,6 +48,7 @@ DECL_BITFIELD(GBARegisterSOUNDBIAS, uint16_t);
DECL_BITS(GBARegisterSOUNDBIAS, Bias, 0, 10); DECL_BITS(GBARegisterSOUNDBIAS, Resolution, 14, 2); +struct GBAAudioMixer; struct GBAAudio { struct GBA* p;@@ -71,6 +76,8 @@ unsigned sampleRate;
GBARegisterSOUNDBIAS soundbias; + struct GBAAudioMixer* mixer; + bool externalMixing; int32_t sampleInterval; bool forceDisableChA;@@ -83,6 +90,188 @@
struct GBAStereoSample { int16_t left; int16_t right; +}; + +struct GBAMP2kADSR { + uint8_t attack; + uint8_t decay; + uint8_t sustain; + uint8_t release; +}; + +struct GBAMP2kSoundChannel { + uint8_t status; + uint8_t type; + uint8_t rightVolume; + uint8_t leftVolume; + struct GBAMP2kADSR adsr; + uint8_t ky; + uint8_t envelopeV; + uint8_t envelopeRight; + uint8_t envelopeLeft; + uint8_t echoVolume; + uint8_t echoLength; + uint8_t d1; + uint8_t d2; + uint8_t gt; + uint8_t midiKey; + uint8_t ve; + uint8_t pr; + uint8_t rp; + uint8_t d3[3]; + uint32_t ct; + uint32_t fw; + uint32_t freq; + uint32_t waveData; + uint32_t cp; + uint32_t track; + uint32_t pp; + uint32_t np; + uint32_t d4; + uint16_t xpi; + uint16_t xpc; +}; + +struct GBAMP2kContext { + uint32_t magic; + uint8_t pcmDmaCounter; + uint8_t reverb; + uint8_t maxChans; + uint8_t masterVolume; + uint8_t freq; + uint8_t mode; + uint8_t c15; + uint8_t pcmDmaPeriod; + uint8_t maxLines; + uint8_t gap[3]; + int32_t pcmSamplesPerVBlank; + int32_t pcmFreq; + int32_t divFreq; + uint32_t cgbChans; + uint32_t func; + uint32_t intp; + uint32_t cgbSound; + uint32_t cgbOscOff; + uint32_t midiKeyToCgbFreq; + uint32_t mPlayJumpTable; + uint32_t plynote; + uint32_t extVolPit; + uint8_t gap2[16]; + struct GBAMP2kSoundChannel chans[MP2K_MAX_SOUND_CHANNELS]; +}; + +struct GBAMP2kMusicPlayerInfo { + uint32_t songHeader; + uint32_t status; + uint8_t trackCount; + uint8_t priority; + uint8_t cmd; + uint8_t unk_B; + uint32_t clock; + uint8_t gap[8]; + uint32_t memAccArea; + uint16_t tempoD; + uint16_t tempoU; + uint16_t tempoI; + uint16_t tempoC; + uint16_t fadeOI; + uint16_t fadeOC; + uint16_t fadeOV; + uint32_t tracks; + uint32_t tone; + uint32_t magic; + uint32_t func; + uint32_t intp; +}; + +struct GBAMP2kInstrument { + uint8_t type; + uint8_t key; + uint8_t length; + union { + uint8_t pan; + uint8_t sweep; + } ps; + union { + uint32_t waveData; + uint32_t subTable; + } data; + union { + struct GBAMP2kADSR adsr; + uint32_t map; + } extInfo; +}; + +struct GBAMP2kMusicPlayerTrack { + uint8_t flags; + uint8_t wait; + uint8_t patternLevel; + uint8_t repN; + uint8_t gateTime; + uint8_t key; + uint8_t velocity; + uint8_t runningStatus; + uint8_t keyM; + uint8_t pitM; + int8_t keyShift; + int8_t keyShiftX; + int8_t tune; + uint8_t pitX; + int8_t bend; + uint8_t bendRange; + uint8_t volMR; + uint8_t volML; + uint8_t vol; + uint8_t volX; + int8_t pan; + int8_t panX; + int8_t modM; + uint8_t mod; + uint8_t modT; + uint8_t lfoSpeed; + uint8_t lfoSpeedC; + uint8_t lfoDelay; + uint8_t lfoDelayC; + uint8_t priority; + uint8_t echoVolume; + uint8_t echoLength; + uint32_t chan; + struct GBAMP2kInstrument instrument; + uint8_t gap[10]; + uint16_t unk_3A; + uint32_t unk_3C; + uint32_t cmdPtr; + uint32_t patternStack[3]; +}; + +struct GBAMP2kTrack { + struct GBAMP2kMusicPlayerTrack track; + struct GBAMP2kSoundChannel* channel; + uint8_t lastCommand; + struct CircleBuffer buffer; + uint32_t samplePlaying; + float currentOffset; + bool waiting; +}; + +struct GBAAudioMixer { + struct mCPUComponent d; + struct GBAAudio* p; + + uint32_t contextAddress; + + bool (*engage)(struct GBAAudioMixer* mixer, uint32_t address); + void (*vblank)(struct GBAAudioMixer* mixer); + void (*step)(struct GBAAudioMixer* mixer); + + struct GBAMP2kContext context; + struct GBAMP2kMusicPlayerInfo player; + struct GBAMP2kTrack activeTracks[MP2K_MAX_SOUND_CHANNELS]; + + double tempo; + double frame; + + struct GBAStereoSample last; }; void GBAAudioInit(struct GBAAudio* audio, size_t samples);
@@ -0,0 +1,19 @@
+/* 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 GBA_AUDIO_MIXER_H +#define GBA_AUDIO_MIXER_H + +#include <mgba-util/common.h> + +CXX_GUARD_START + +#include <mgba/internal/gba/audio.h> + +void GBAAudioMixerCreate(struct GBAAudioMixer* mixer); + +CXX_GUARD_END + +#endif
@@ -62,20 +62,25 @@ uint16_t y;
int32_t refx; int32_t refy; - struct GBAVideoGLAffine affine[4]; + struct GBAVideoGLAffine affine; }; enum { GBA_GL_FBO_OBJ = 0, - GBA_GL_FBO_WINDOW = 1, - GBA_GL_FBO_OUTPUT = 2, + GBA_GL_FBO_BACKDROP, + GBA_GL_FBO_WINDOW, + GBA_GL_FBO_OUTPUT, GBA_GL_FBO_MAX }; enum { GBA_GL_TEX_OBJ_COLOR = 0, - GBA_GL_TEX_OBJ_FLAGS = 1, - GBA_GL_TEX_WINDOW = 6, + GBA_GL_TEX_OBJ_FLAGS, + GBA_GL_TEX_BACKDROP_COLOR, + GBA_GL_TEX_BACKDROP_FLAGS, + GBA_GL_TEX_WINDOW, + GBA_GL_TEX_AFFINE_2, + GBA_GL_TEX_AFFINE_3, GBA_GL_TEX_MAX };@@ -91,6 +96,8 @@ GBA_GL_BG_SIZE,
GBA_GL_BG_OFFSET, GBA_GL_BG_INFLAGS, GBA_GL_BG_TRANSFORM, + GBA_GL_BG_RANGE, + GBA_GL_BG_MOSAIC, GBA_GL_OBJ_VRAM = 2, GBA_GL_OBJ_PALETTE,@@ -101,6 +108,7 @@ GBA_GL_OBJ_INFLAGS,
GBA_GL_OBJ_TRANSFORM, GBA_GL_OBJ_DIMS, GBA_GL_OBJ_OBJWIN, + GBA_GL_OBJ_MOSAIC, GBA_GL_FINALIZE_SCALE = 2, GBA_GL_FINALIZE_LAYERS,@@ -124,6 +132,7 @@
uint32_t* temporaryBuffer; struct GBAVideoGLBackground bg[4]; + struct GBAVideoGLAffine affine[2][GBA_VIDEO_VERTICAL_PIXELS]; int oamMax; bool oamDirty;@@ -144,6 +153,9 @@
GLuint vramTex; unsigned vramDirty; + uint16_t shadowRegs[0x30]; + uint64_t regsDirty; + struct GBAVideoGLShader bgShader[6]; struct GBAVideoGLShader objShader[2]; struct GBAVideoGLShader finalizeShader;@@ -162,7 +174,7 @@
GBAMosaicControl mosaic; struct GBAVideoGLWindowN { - struct GBAVideoWindowRegion h; + struct GBAVideoWindowRegion h[2]; struct GBAVideoWindowRegion v; GBAWindowControl control; } winN[2];@@ -171,6 +183,7 @@ GBAWindowControl winout;
GBAWindowControl objwin; int firstAffine; + int firstY; int scale; };
@@ -153,10 +153,12 @@ mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
_proxyThreadRecover(proxyRenderer); return; } + MutexLock(&proxyRenderer->mutex); while (RingFIFOSize(&proxyRenderer->dirtyQueue)) { ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); } + MutexUnlock(&proxyRenderer->mutex); } static void _unlock(struct mVideoLogger* logger) {
@@ -250,31 +250,21 @@ }
void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) { struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; - if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { - proxyRenderer->logger->lock(proxyRenderer->logger); - } if (!proxyRenderer->logger->block) { proxyRenderer->backend->finishFrame(proxyRenderer->backend); } mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger); - if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { - proxyRenderer->logger->unlock(proxyRenderer->logger); - } } static void GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer* renderer, bool enable) { struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { - proxyRenderer->logger->lock(proxyRenderer->logger); // Insert an extra item into the queue to make sure it gets flushed mVideoLoggerRendererFlush(proxyRenderer->logger); proxyRenderer->logger->wait(proxyRenderer->logger); } proxyRenderer->backend->enableSGBBorder(proxyRenderer->backend, enable); - if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { - proxyRenderer->logger->unlock(proxyRenderer->logger); - } } static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) {@@ -297,9 +287,6 @@ static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) {
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { proxyRenderer->logger->lock(proxyRenderer->logger); - // Insert an extra item into the queue to make sure it gets flushed - mVideoLoggerRendererFlush(proxyRenderer->logger); - proxyRenderer->logger->wait(proxyRenderer->logger); } proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
@@ -723,7 +723,7 @@ }
void GBHalt(struct LR35902Core* cpu) { struct GB* gb = (struct GB*) cpu->master; - if (!(gb->memory.ie & gb->memory.io[REG_IF])) { + if (!(gb->memory.ie & gb->memory.io[REG_IF] & 0x1F)) { cpu->cycles = cpu->nextEvent; cpu->halted = true; } else if (gb->model < GB_MODEL_CGB) {
@@ -14,6 +14,8 @@ #include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/serialize.h> #include <mgba/internal/gba/video.h> +#define MP2K_LOCK_MAX 8 + #ifdef _3DS #define blip_add_delta blip_add_delta_fast #endif@@ -24,7 +26,7 @@ const unsigned GBA_AUDIO_SAMPLES = 2048;
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); const int GBA_AUDIO_VOLUME_MAX = 0x100; -static const int CLOCKS_PER_FRAME = 0x400; +static const int CLOCKS_PER_FRAME = 0x800; static int _applyBias(struct GBAAudio* audio, int sample); static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate);@@ -49,9 +51,11 @@ blip_set_rates(audio->psg.right, GBA_ARM7TDMI_FREQUENCY, 96000);
CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE); CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE); + audio->externalMixing = false; audio->forceDisableChA = false; audio->forceDisableChB = false; audio->masterVolume = GBA_AUDIO_VOLUME_MAX; + audio->mixer = NULL; } void GBAAudioReset(struct GBAAudio* audio) {@@ -110,6 +114,20 @@ break;
default: mLOG(GBA_AUDIO, GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest); return; + } + uint32_t source = info->source; + uint32_t magic[2] = { + audio->p->cpu->memory.load32(audio->p->cpu, source - 0x350, NULL), + audio->p->cpu->memory.load32(audio->p->cpu, source - 0x980, NULL) + }; + if (audio->mixer) { + if (magic[0] - MP2K_MAGIC <= MP2K_LOCK_MAX) { + audio->mixer->engage(audio->mixer, source - 0x350); + } else if (magic[1] - MP2K_MAGIC <= MP2K_LOCK_MAX) { + audio->mixer->engage(audio->mixer, source - 0x980); + } else { + audio->externalMixing = false; + } } info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED); info->reg = GBADMARegisterSetWidth(info->reg, 1);@@ -265,23 +283,28 @@ GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight);
sampleLeft >>= psgShift; sampleRight >>= psgShift; - if (!audio->forceDisableChA) { - if (audio->chALeft) { - sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA; - } + if (audio->mixer) { + audio->mixer->step(audio->mixer); + } + if (!audio->externalMixing) { + if (!audio->forceDisableChA) { + if (audio->chALeft) { + sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA; + } - if (audio->chARight) { - sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA; + if (audio->chARight) { + sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA; + } } - } - if (!audio->forceDisableChB) { - if (audio->chBLeft) { - sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB; - } + if (!audio->forceDisableChB) { + if (audio->chBLeft) { + sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB; + } - if (audio->chBRight) { - sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB; + if (audio->chBRight) { + sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB; + } } }
@@ -12,6 +12,7 @@ #include <mgba/internal/debugger/symbols.h>
#include <mgba/internal/gba/cheats.h> #include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/io.h> +#include <mgba/internal/gba/extra/audio-mixer.h> #include <mgba/internal/gba/extra/cli.h> #include <mgba/internal/gba/overrides.h> #ifndef DISABLE_THREADING@@ -127,6 +128,9 @@ { REGION_CART_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, SIZE_CART_EEPROM, SIZE_CART_EEPROM, mCORE_MEMORY_RW },
}; struct mVideoLogContext; + +#define CPU_COMPONENT_AUDIO_MIXER CPU_COMPONENT_MISC_1 + struct GBACore { struct mCore d; struct GBAVideoSoftwareRenderer renderer;@@ -144,6 +148,7 @@ struct mCPUComponent* components[CPU_COMPONENT_MAX];
const struct Configuration* overrides; struct mDebuggerPlatform* debuggerPlatform; struct mCheatDevice* cheatDevice; + struct GBAAudioMixer* audioMixer; }; static bool _GBACoreInit(struct mCore* core) {@@ -166,6 +171,7 @@ gbacore->overrides = NULL;
gbacore->debuggerPlatform = NULL; gbacore->cheatDevice = NULL; gbacore->logContext = NULL; + gbacore->audioMixer = NULL; GBACreate(gba); // TODO: Restore cheats@@ -217,6 +223,7 @@ if (gbacore->cheatDevice) {
mCheatDeviceDestroy(gbacore->cheatDevice); } free(gbacore->cheatDevice); + free(gbacore->audioMixer); mCoreConfigFreeOpts(&core->opts); free(core); }@@ -280,6 +287,7 @@ gba->allowOpposingDirections = fakeBool;
mCoreConfigCopyValue(&core->config, config, "allowOpposingDirections"); mCoreConfigCopyValue(&core->config, config, "gba.bios"); + mCoreConfigCopyValue(&core->config, config, "gba.audioHle"); #ifndef DISABLE_THREADING mCoreConfigCopyValue(&core->config, config, "threadedVideo");@@ -475,6 +483,16 @@ }
GBAVideoAssociateRenderer(&gba->video, renderer); } +#ifndef MINIMAL_CORE + int useAudioMixer; + if (!gbacore->audioMixer && mCoreConfigGetIntValue(&core->config, "gba.audioHle", &useAudioMixer) && useAudioMixer) { + gbacore->audioMixer = malloc(sizeof(*gbacore->audioMixer)); + GBAAudioMixerCreate(gbacore->audioMixer); + ((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_AUDIO_MIXER] = &gbacore->audioMixer->d; + ARMHotplugAttach(core->cpu, CPU_COMPONENT_AUDIO_MIXER); + } +#endif + GBAOverrideApplyDefaults(gba, gbacore->overrides); #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2@@ -521,7 +539,7 @@ }
#endif ARMReset(core->cpu); - if (core->opts.skipBios && gba->isPristine) { + if (core->opts.skipBios && (gba->romVf || gba->memory.rom)) { GBASkipBIOS(core->board); } }
@@ -0,0 +1,307 @@
+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <mgba/internal/gba/extra/audio-mixer.h> + +#include <mgba/core/blip_buf.h> +#include <mgba/internal/gba/gba.h> +#include <mgba/internal/gba/video.h> + +#define OVERSAMPLE 2 + +static void _mp2kInit(void* cpu, struct mCPUComponent* component); +static void _mp2kDeinit(struct mCPUComponent* component); + +static bool _mp2kEngage(struct GBAAudioMixer* mixer, uint32_t address); +static void _mp2kVblank(struct GBAAudioMixer* mixer); +static void _mp2kStep(struct GBAAudioMixer* mixer); + +void GBAAudioMixerCreate(struct GBAAudioMixer* mixer) { + mixer->d.init = _mp2kInit; + mixer->d.deinit = _mp2kDeinit; + mixer->engage = _mp2kEngage; + mixer->vblank = _mp2kVblank; + mixer->step = _mp2kStep; +} + +void _mp2kInit(void* cpu, struct mCPUComponent* component) { + struct ARMCore* arm = cpu; + struct GBA* gba = (struct GBA*) arm->master; + struct GBAAudioMixer* mixer = (struct GBAAudioMixer*) component; + gba->audio.mixer = mixer; + mixer->p = &gba->audio; + mixer->contextAddress = 0; + mixer->tempo = 120.0 / 75.0; + mixer->frame = 0; + mixer->last.left = 0; + mixer->last.right = 0; + memset(&mixer->context, 0, sizeof(mixer->context)); + memset(&mixer->activeTracks, 0, sizeof(mixer->activeTracks)); + + size_t i; + for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) { + mixer->activeTracks[i].channel = &mixer->context.chans[i]; + CircleBufferInit(&mixer->activeTracks[i].buffer, 0x10000); + } +} + +void _mp2kDeinit(struct mCPUComponent* component) { + struct GBAAudioMixer* mixer = (struct GBAAudioMixer*) component; + size_t i; + for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) { + CircleBufferDeinit(&mixer->activeTracks[i].buffer); + } +} + +static void _loadInstrument(struct ARMCore* cpu, struct GBAMP2kInstrument* instrument, uint32_t base) { + struct ARMMemory* memory = &cpu->memory; + instrument->type = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, type), 0); + instrument->key = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, key), 0); + instrument->length = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, length), 0); + instrument->ps.pan = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, ps.pan), 0); + if (instrument->type == 0x40 || instrument->type == 0x80) { + instrument->data.subTable = memory->load32(cpu, base + offsetof(struct GBAMP2kInstrument, data.subTable), 0); + instrument->extInfo.map = memory->load32(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.map), 0); + } else { + instrument->data.waveData = memory->load32(cpu, base + offsetof(struct GBAMP2kInstrument, data.waveData), 0); + instrument->extInfo.adsr.attack = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.attack), 0); + instrument->extInfo.adsr.decay = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.decay), 0); + instrument->extInfo.adsr.sustain = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.sustain), 0); + instrument->extInfo.adsr.release = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.release), 0); + } +} + +static void _lookupInstrument(struct ARMCore* cpu, struct GBAMP2kInstrument* instrument, uint8_t key) { + struct ARMMemory* memory = &cpu->memory; + if (instrument->type == 0x40) { + uint32_t subInstrumentBase = instrument->data.subTable; + uint32_t keyTable = instrument->extInfo.map; + uint8_t id = memory->load8(cpu, keyTable + key, 0); + subInstrumentBase += 12 * id; + _loadInstrument(cpu, instrument, subInstrumentBase); + } + if (instrument->type == 0x80) { + uint32_t subInstrumentBase = instrument->data.subTable; + subInstrumentBase += 12 * key; + _loadInstrument(cpu, instrument, subInstrumentBase); + } +} + +static void _stepSample(struct GBAAudioMixer* mixer, struct GBAMP2kTrack* track) { + struct ARMCore* cpu = mixer->p->p->cpu; + struct ARMMemory* memory = &cpu->memory; + uint32_t headerAddress; + struct GBAMP2kInstrument instrument = track->track.instrument; + + uint8_t note = track->track.key; + _lookupInstrument(cpu, &instrument, note); + double freq; + + switch (instrument.type) { + case 0x00: + case 0x08: + case 0x40: + case 0x80: + freq = GBA_ARM7TDMI_FREQUENCY / (double) track->channel->freq; + break; + default: + // We don't care about PSG channels + return; + } + headerAddress = instrument.data.waveData; + if (headerAddress < 0x20) { + mLOG(GBA_AUDIO, ERROR, "Audio track has invalid instrument"); + return; + } + uint32_t loopOffset = memory->load32(cpu, headerAddress + 0x8, 0); + uint32_t endOffset = memory->load32(cpu, headerAddress + 0xC, 0); + uint32_t sampleBase = headerAddress + 0x10; + uint32_t sampleI = track->samplePlaying; + double sampleOffset = track->currentOffset; + double updates = VIDEO_TOTAL_LENGTH / (mixer->tempo * mixer->p->sampleInterval / OVERSAMPLE); + int nSample; + for (nSample = 0; nSample < updates; ++nSample) { + int8_t sample = memory->load8(cpu, sampleBase + sampleI, 0); + + struct GBAStereoSample stereo = { + (sample * track->channel->leftVolume * track->channel->envelopeV) >> 9, + (sample * track->channel->rightVolume * track->channel->envelopeV) >> 9 + }; + + CircleBufferWrite16(&track->buffer, stereo.left); + CircleBufferWrite16(&track->buffer, stereo.right); + + sampleOffset += mixer->p->sampleInterval / OVERSAMPLE; + while (sampleOffset > freq) { + sampleOffset -= freq; + ++sampleI; + if (sampleI >= endOffset) { + sampleI = loopOffset; + } + } + } + + track->samplePlaying = sampleI; + track->currentOffset = sampleOffset; +} + +static void _mp2kReload(struct GBAAudioMixer* mixer) { + struct ARMCore* cpu = mixer->p->p->cpu; + struct ARMMemory* memory = &cpu->memory; + mixer->context.magic = memory->load32(cpu, mixer->contextAddress + offsetof(struct GBAMP2kContext, magic), 0); + int i; + for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) { + struct GBAMP2kSoundChannel* ch = &mixer->context.chans[i]; + struct GBAMP2kTrack* track = &mixer->activeTracks[i]; + track->waiting = false; + uint32_t base = mixer->contextAddress + offsetof(struct GBAMP2kContext, chans[i]); + + ch->status = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, status), 0); + ch->type = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, type), 0); + ch->rightVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, rightVolume), 0); + ch->leftVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, leftVolume), 0); + ch->adsr.attack = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.attack), 0); + ch->adsr.decay = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.decay), 0); + ch->adsr.sustain = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.sustain), 0); + ch->adsr.release = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.release), 0); + ch->ky = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, ky), 0); + ch->envelopeV = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, envelopeV), 0); + ch->envelopeRight = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, envelopeRight), 0); + ch->envelopeLeft = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, envelopeLeft), 0); + ch->echoVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, echoVolume), 0); + ch->echoLength = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, echoLength), 0); + ch->d1 = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d1), 0); + ch->d2 = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d2), 0); + ch->gt = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, gt), 0); + ch->midiKey = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, midiKey), 0); + ch->ve = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, ve), 0); + ch->pr = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, pr), 0); + ch->rp = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, rp), 0); + ch->d3[0] = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d3[0]), 0); + ch->d3[1] = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d3[1]), 0); + ch->d3[2] = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d3[2]), 0); + ch->ct = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, ct), 0); + ch->fw = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, fw), 0); + ch->freq = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, freq), 0); + ch->waveData = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, waveData), 0); + ch->cp = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, cp), 0); + ch->track = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, track), 0); + ch->pp = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, pp), 0); + ch->np = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, np), 0); + ch->d4 = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, d4), 0); + ch->xpi = memory->load16(cpu, base + offsetof(struct GBAMP2kSoundChannel, xpi), 0); + ch->xpc = memory->load16(cpu, base + offsetof(struct GBAMP2kSoundChannel, xpc), 0); + + base = ch->track; + if (base) { + track->track.flags = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, flags), 0); + track->track.wait = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, wait), 0); + track->track.patternLevel = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternLevel), 0); + track->track.repN = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, repN), 0); + track->track.gateTime = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, gateTime), 0); + track->track.key = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, key), 0); + track->track.velocity = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, velocity), 0); + track->track.runningStatus = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, runningStatus), 0); + track->track.keyM = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, keyM), 0); + track->track.pitM = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, pitM), 0); + track->track.keyShift = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, keyShift), 0); + track->track.keyShiftX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, keyShiftX), 0); + track->track.tune = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, tune), 0); + track->track.pitX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, pitX), 0); + track->track.bend = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, bend), 0); + track->track.bendRange = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, bendRange), 0); + track->track.volMR = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, volMR), 0); + track->track.volML = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, volML), 0); + track->track.vol = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, vol), 0); + track->track.volX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, volX), 0); + track->track.pan = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, pan), 0); + track->track.panX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, panX), 0); + track->track.modM = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, modM), 0); + track->track.mod = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, mod), 0); + track->track.modT = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, modT), 0); + track->track.lfoSpeed = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoSpeed), 0); + track->track.lfoSpeedC = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoSpeedC), 0); + track->track.lfoDelay = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoDelay), 0); + track->track.lfoDelayC = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoDelayC), 0); + track->track.priority = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, priority), 0); + track->track.echoVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, echoVolume), 0); + track->track.echoLength = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, echoLength), 0); + track->track.chan = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, chan), 0); + _loadInstrument(cpu, &track->track.instrument, base + offsetof(struct GBAMP2kMusicPlayerTrack, instrument)); + track->track.cmdPtr = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, cmdPtr), 0); + track->track.patternStack[0] = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternStack[0]), 0); + track->track.patternStack[1] = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternStack[1]), 0); + track->track.patternStack[2] = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternStack[2]), 0); + } else { + memset(&track->track, 0, sizeof(track->track)); + } + if (track->track.runningStatus == 0xCD) { + // XCMD isn't supported + mixer->p->externalMixing = false; + } + } +} + +bool _mp2kEngage(struct GBAAudioMixer* mixer, uint32_t address) { + if (address < BASE_WORKING_RAM) { + return false; + } + if (address != mixer->contextAddress) { + mixer->contextAddress = address; + mixer->p->externalMixing = true; + _mp2kReload(mixer); + } + return true; +} + +void _mp2kStep(struct GBAAudioMixer* mixer) { + mixer->frame += mixer->p->sampleInterval; + + while (mixer->frame >= VIDEO_TOTAL_LENGTH / mixer->tempo) { + int i; + for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) { + struct GBAMP2kTrack* track = &mixer->activeTracks[i]; + if (track->channel->status > 0) { + _stepSample(mixer, track); + } else { + track->currentOffset = 0; + track->samplePlaying = 0; + CircleBufferClear(&track->buffer); + } + } + mixer->frame -= VIDEO_TOTAL_LENGTH / mixer->tempo; + } + + uint32_t interval = mixer->p->sampleInterval / OVERSAMPLE; + int i; + for (i = 0; i < OVERSAMPLE; ++i) { + struct GBAStereoSample sample = {0}; + size_t track; + for (track = 0; track < MP2K_MAX_SOUND_CHANNELS; ++track) { + if (!mixer->activeTracks[track].channel->status) { + continue; + } + int16_t value; + CircleBufferRead16(&mixer->activeTracks[track].buffer, &value); + sample.left += value; + CircleBufferRead16(&mixer->activeTracks[track].buffer, &value); + sample.right += value; + } + if (mixer->p->externalMixing) { + blip_add_delta(mixer->p->psg.left, mixer->p->clock + i * interval, sample.left - mixer->last.left); + blip_add_delta(mixer->p->psg.right, mixer->p->clock + i * interval, sample.left - mixer->last.left); + } + mixer->last = sample; + } +} + +void _mp2kVblank(struct GBAAudioMixer* mixer) { + if (!mixer->contextAddress) { + return; + } + mLOG(GBA_AUDIO, DEBUG, "Frame"); + mixer->p->externalMixing = true; + _mp2kReload(mixer); +}
@@ -147,6 +147,7 @@ if (!proxyRenderer->logger->block) {
proxyRenderer->backend->deinit(proxyRenderer->backend); } else { proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT); + mVideoLoggerRendererFlush(proxyRenderer->logger); } mVideoLoggerRendererDeinit(proxyRenderer->logger);@@ -307,28 +308,20 @@ }
void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { - proxyRenderer->logger->lock(proxyRenderer->logger); - } if (!proxyRenderer->logger->block) { proxyRenderer->backend->finishFrame(proxyRenderer->backend); } mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger); - if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { - proxyRenderer->logger->unlock(proxyRenderer->logger); - } } static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { - proxyRenderer->logger->lock(proxyRenderer->logger); // Insert an extra item into the queue to make sure it gets flushed mVideoLoggerRendererFlush(proxyRenderer->logger); proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS); mVideoLoggerRendererFlush(proxyRenderer->logger); - proxyRenderer->logger->unlock(proxyRenderer->logger); *pixels = proxyRenderer->logger->pixelBuffer; *stride = proxyRenderer->logger->pixelStride; } else {@@ -340,8 +333,6 @@ static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { proxyRenderer->logger->lock(proxyRenderer->logger); - // Insert an extra item into the queue to make sure it gets flushed - mVideoLoggerRendererFlush(proxyRenderer->logger); } proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
@@ -241,10 +241,6 @@ memset(gba->debugString, 0, sizeof(gba->debugString));
if (gba->pristineRomSize > SIZE_CART0) { GBAMatrixReset(gba); } - - if (!gba->romVf && gba->memory.rom) { - GBASkipBIOS(gba); - } } void GBASkipBIOS(struct GBA* gba) {@@ -787,6 +783,10 @@ }
void GBAFrameStarted(struct GBA* gba) { GBATestKeypadIRQ(gba); + + if (gba->audio.mixer) { + gba->audio.mixer->vblank(gba->audio.mixer); + } size_t c; for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
@@ -13,6 +13,8 @@ #include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/renderers/cache-set.h> #include <mgba-util/memory.h> +#define FLAG_CONST "const vec4 flagCoeff = vec4(64., 32., 16., 1.);\n" + static void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer); static void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer); static void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer);@@ -41,15 +43,20 @@ static void GBAVideoGLRendererDrawBackgroundMode4(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
static void GBAVideoGLRendererDrawBackgroundMode5(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y); static void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y); -static void _finalizeLayers(struct GBAVideoGLRenderer* renderer, int y); +static void _cleanRegister(struct GBAVideoGLRenderer* renderer, int address, uint16_t value); +static void _drawScanlines(struct GBAVideoGLRenderer* renderer, int lastY); +static void _finalizeLayers(struct GBAVideoGLRenderer* renderer); -#define TEST_LAYER_ENABLED(X) !renderer->disableBG[X] && glRenderer->bg[X].enabled == 4 +#define TEST_LAYER_ENABLED(X) !glRenderer->d.disableBG[X] && glRenderer->bg[X].enabled == 4 struct GBAVideoGLUniform { const char* name; int type; }; +static const GLchar* const _gles3Header = + "#version 300\n"; + static const GLchar* const _gl3Header = "#version 130\n";@@ -102,6 +109,7 @@ { "charBase", GBA_GL_BG_CHARBASE, },
{ "size", GBA_GL_BG_SIZE, }, { "offset", GBA_GL_BG_OFFSET, }, { "inflags", GBA_GL_BG_INFLAGS, }, + { "mosaic", GBA_GL_BG_MOSAIC, }, { 0 } };@@ -114,14 +122,22 @@ "uniform int charBase;\n"
"uniform int size;\n" "uniform ivec2 offset;\n" "uniform ivec4 inflags;\n" + "uniform ivec2 mosaic;\n" "out vec4 color;\n" "out vec4 flags;\n" - "const vec4 flagCoeff = vec4(32., 32., 16., 16.);\n" + FLAG_CONST "vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n" "void main() {\n" - " ivec2 coord = ivec2(texCoord) + offset;\n" + " ivec2 coord = ivec2(texCoord);\n" + " if (mosaic.x > 1) {\n" + " coord.x -= int(mod(coord.x, mosaic.x));\n" + " }\n" + " if (mosaic.y > 1) {\n" + " coord.y -= int(mod(coord.y, mosaic.y));\n" + " }\n" + " coord += offset;\n" " if ((size & 1) == 1) {\n" " coord.y += coord.x & 256;\n" " }\n"@@ -168,9 +184,79 @@ { "size", GBA_GL_BG_SIZE, },
{ "inflags", GBA_GL_BG_INFLAGS, }, { "offset", GBA_GL_BG_OFFSET, }, { "transform", GBA_GL_BG_TRANSFORM, }, + { "range", GBA_GL_BG_RANGE, }, + { "mosaic", GBA_GL_BG_MOSAIC, }, { 0 } }; +static const char* const _interpolate = + "vec2 interpolate(ivec2 arr[4], float x) {\n" + " float x1m = 1. - x;\n" + " return x1m * x1m * x1m * arr[0] +" + " 3 * x1m * x1m * x * arr[1] +" + " 3 * x1m * x * x * arr[2] +" + " x * x * x * arr[3];\n" + "}\n" + + "void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]) {\n" + " int start = max(range.x, y - 3);\n" + " ivec4 splitOffset[4];\n" + + " mat[0] = texelFetch(transform, ivec2(0, start), 0).xz;\n" + " mat[1] = texelFetch(transform, ivec2(0, start + 1), 0).xz;\n" + " mat[2] = texelFetch(transform, ivec2(0, start + 2), 0).xz;\n" + " mat[3] = texelFetch(transform, ivec2(0, start + 3), 0).xz;\n" + + " splitOffset[0] = texelFetch(transform, ivec2(1, start + 0), 0);\n" + " splitOffset[1] = texelFetch(transform, ivec2(1, start + 1), 0);\n" + " splitOffset[2] = texelFetch(transform, ivec2(1, start + 2), 0);\n" + " splitOffset[3] = texelFetch(transform, ivec2(1, start + 3), 0);\n" + + " aff[0] = (splitOffset[0].xz & 0xFFFF) + (splitOffset[0].yw << 16);\n" + " aff[1] = (splitOffset[1].xz & 0xFFFF) + (splitOffset[1].yw << 16);\n" + " aff[2] = (splitOffset[2].xz & 0xFFFF) + (splitOffset[2].yw << 16);\n" + " aff[3] = (splitOffset[3].xz & 0xFFFF) + (splitOffset[3].yw << 16);\n" + + " if (y - 3 < range.x) {\n" + " ivec2 tempMat[3];\n" + " ivec2 tempAff[3];\n" + " tempMat[0] = ivec2(interpolate(mat, -0.75));\n" + " tempMat[1] = ivec2(interpolate(mat, -0.5));\n" + " tempMat[2] = ivec2(interpolate(mat, -0.25));\n" + " tempAff[0] = ivec2(interpolate(aff, -0.75));\n" + " tempAff[1] = ivec2(interpolate(aff, -0.5));\n" + " tempAff[2] = ivec2(interpolate(aff, -0.25));\n" + " if (range.x == y) {\n" + " mat[3] = mat[0];\n" + " mat[2] = tempMat[2];\n" + " mat[1] = tempMat[1];\n" + " mat[0] = tempMat[0];\n" + " aff[3] = aff[0];\n" + " aff[2] = tempAff[2];\n" + " aff[1] = tempAff[1];\n" + " aff[0] = tempAff[0];\n" + " } else if (range.x == y - 1) {\n" + " mat[3] = mat[1];\n" + " mat[2] = mat[0];\n" + " mat[1] = tempMat[2];\n" + " mat[0] = tempMat[1];\n" + " aff[3] = aff[1];\n" + " aff[2] = aff[0];\n" + " aff[1] = tempAff[2];\n" + " aff[0] = tempAff[1];\n" + " } else if (range.x == y - 2) {\n" + " mat[3] = mat[2];\n" + " mat[2] = mat[1];\n" + " mat[1] = mat[0];\n" + " mat[0] = tempMat[0];\n" + " aff[3] = aff[2];\n" + " aff[2] = aff[1];\n" + " aff[1] = aff[0];\n" + " aff[0] = tempAff[0];\n" + " }\n" + " }\n" + "}\n"; + static const char* const _renderMode2 = "in vec2 texCoord;\n" "uniform sampler2D vram;\n"@@ -179,15 +265,18 @@ "uniform int screenBase;\n"
"uniform int charBase;\n" "uniform int size;\n" "uniform ivec4 inflags;\n" - "uniform ivec2[4] offset;\n" - "uniform ivec2[4] transform;\n" + "uniform isampler2D transform;\n" + "uniform ivec2 range;\n" + "uniform ivec2 mosaic;\n" "out vec4 color;\n" "out vec4 flags;\n" - "const vec4 flagCoeff = vec4(32., 32., 16., 16.);\n" + FLAG_CONST "precision highp float;\n" "precision highp int;\n" "vec4 fetchTile(ivec2 coord);\n" + "vec2 interpolate(ivec2 arr[4], float x);\n" + "void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n" "vec4 renderTile(ivec2 coord) {\n" " int map = (coord.x >> 11) + (((coord.y >> 7) & 0x7F0) << size);\n"@@ -206,20 +295,147 @@ " color.a = 1.;\n"
" return color;\n" "}\n" - "vec2 interpolate(ivec2 arr[4], float x) {\n" - " float x1m = 1. - x;\n" - " return x1m * x1m * x1m * arr[0] +" - " 3 * x1m * x1m * x * arr[1] +" - " 3 * x1m * x * x * arr[2] +" - " x * x * x * arr[3];\n" - "}\n" + "void main() {\n" + " ivec2 mat[4];\n" + " ivec2 offset[4];\n" + " loadAffine(int(texCoord.y), mat, offset);\n" + " vec2 coord = texCoord;\n" + " if (mosaic.x > 1) {\n" + " coord.x -= mod(coord.x, mosaic.x);\n" + " }\n" + " if (mosaic.y > 1) {\n" + " coord.y -= mod(coord.y, mosaic.y);\n" + " }\n" + " float y = fract(coord.y);\n" + " float lin = 0.75 + y * 0.25;\n" + " vec2 mixedTransform = interpolate(mat, lin);\n" + " vec2 mixedOffset = interpolate(offset, lin);\n" + " color = fetchTile(ivec2(mixedTransform * coord.x + mixedOffset));\n" + " flags = inflags / flagCoeff;\n" + "}"; + +static const struct GBAVideoGLUniform _uniformsMode35[] = { + { "loc", GBA_GL_VS_LOC, }, + { "maxPos", GBA_GL_VS_MAXPOS, }, + { "vram", GBA_GL_BG_VRAM, }, + { "charBase", GBA_GL_BG_CHARBASE, }, + { "size", GBA_GL_BG_SIZE, }, + { "inflags", GBA_GL_BG_INFLAGS, }, + { "offset", GBA_GL_BG_OFFSET, }, + { "transform", GBA_GL_BG_TRANSFORM, }, + { "range", GBA_GL_BG_RANGE, }, + { "mosaic", GBA_GL_BG_MOSAIC, }, + { 0 } +}; + +static const char* const _renderMode35 = + "in vec2 texCoord;\n" + "uniform sampler2D vram;\n" + "uniform int charBase;\n" + "uniform ivec2 size;\n" + "uniform ivec4 inflags;\n" + "uniform isampler2D transform;\n" + "uniform ivec2 range;\n" + "uniform ivec2 mosaic;\n" + "out vec4 color;\n" + "out vec4 flags;\n" + FLAG_CONST + "precision highp float;\n" + "precision highp int;\n" + + "vec2 interpolate(ivec2 arr[4], float x);\n" + "void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n" "void main() {\n" - " float y = fract(texCoord.y);\n" - " float lin = 0.5 - y / ceil(y) * 0.25;\n" - " vec2 mixedTransform = interpolate(transform, lin);\n" + " ivec2 mat[4];\n" + " ivec2 offset[4];\n" + " loadAffine(int(texCoord.y), mat, offset);\n" + " vec2 incoord = texCoord;\n" + " if (mosaic.x > 1) {\n" + " incoord.x -= mod(incoord.x, mosaic.x);\n" + " }\n" + " if (mosaic.y > 1) {\n" + " incoord.y -= mod(incoord.y, mosaic.y);\n" + " }\n" + " float y = fract(incoord.y);\n" + " float lin = 0.75 + y * 0.25;\n" + " vec2 mixedTransform = interpolate(mat, lin);\n" + " vec2 mixedOffset = interpolate(offset, lin);\n" + " ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n" + " if (coord.x < 0 || coord.x >= (size.x << 8)) {\n" + " discard;\n" + " }\n" + " if (coord.y < 0 || coord.y >= (size.y << 8)) {\n" + " discard;\n" + " }\n" + " int address = charBase + (coord.x >> 8) + (coord.y >> 8) * size.x;\n" + " ivec4 entry = ivec4(texelFetch(vram, ivec2(address & 255, address >> 8), 0) * 15.9);\n" + " int sixteen = (entry.x << 12) | (entry.y << 8) | (entry.z << 4) | entry.w;\n" + " color = vec4((sixteen & 0x1F) / 31., ((sixteen >> 5) & 0x1F) / 31., ((sixteen >> 10) & 0x1F) / 31., 1.);\n" + " flags = inflags / flagCoeff;\n" + "}"; + +static const struct GBAVideoGLUniform _uniformsMode4[] = { + { "loc", GBA_GL_VS_LOC, }, + { "maxPos", GBA_GL_VS_MAXPOS, }, + { "vram", GBA_GL_BG_VRAM, }, + { "palette", GBA_GL_BG_PALETTE, }, + { "charBase", GBA_GL_BG_CHARBASE, }, + { "size", GBA_GL_BG_SIZE, }, + { "inflags", GBA_GL_BG_INFLAGS, }, + { "offset", GBA_GL_BG_OFFSET, }, + { "transform", GBA_GL_BG_TRANSFORM, }, + { "range", GBA_GL_BG_RANGE, }, + { "mosaic", GBA_GL_BG_MOSAIC, }, + { 0 } +}; + +static const char* const _renderMode4 = + "in vec2 texCoord;\n" + "uniform sampler2D vram;\n" + "uniform sampler2D palette;\n" + "uniform int charBase;\n" + "uniform ivec2 size;\n" + "uniform ivec4 inflags;\n" + "uniform isampler2D transform;\n" + "uniform ivec2 range;\n" + "uniform ivec2 mosaic;\n" + "out vec4 color;\n" + "out vec4 flags;\n" + FLAG_CONST + "precision highp float;\n" + "precision highp int;\n" + + "vec2 interpolate(ivec2 arr[4], float x);\n" + "void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n" + + "void main() {\n" + " ivec2 mat[4];\n" + " ivec2 offset[4];\n" + " loadAffine(int(texCoord.y), mat, offset);\n" + " vec2 incoord = texCoord;\n" + " if (mosaic.x > 1) {\n" + " incoord.x -= mod(incoord.x, mosaic.x);\n" + " }\n" + " if (mosaic.y > 1) {\n" + " incoord.y -= mod(incoord.y, mosaic.y);\n" + " }\n" + " float y = fract(incoord.y);\n" + " float lin = 0.75 + y * 0.25;\n" + " vec2 mixedTransform = interpolate(mat, lin);\n" " vec2 mixedOffset = interpolate(offset, lin);\n" - " color = fetchTile(ivec2(mixedTransform * texCoord.x + mixedOffset));\n" + " ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n" + " if (coord.x < 0 || coord.x >= (size.x << 8)) {\n" + " discard;\n" + " }\n" + " if (coord.y < 0 || coord.y >= (size.y << 8)) {\n" + " discard;\n" + " }\n" + " int address = charBase + (coord.x >> 8) + (coord.y >> 8) * size.x;\n" + " vec4 twoEntries = texelFetch(vram, ivec2((address >> 1) & 255, address >> 9), 0);\n" + " ivec2 entry = ivec2(twoEntries[3 - 2 * (address & 1)] * 15.9, twoEntries[2 - 2 * (address & 1)] * 15.9);\n" + " color = texelFetch(palette, entry, 0);\n" + " color.a = 1;\n" " flags = inflags / flagCoeff;\n" "}";@@ -235,6 +451,7 @@ { "inflags", GBA_GL_OBJ_INFLAGS, },
{ "transform", GBA_GL_OBJ_TRANSFORM, }, { "dims", GBA_GL_OBJ_DIMS, }, { "objwin", GBA_GL_OBJ_OBJWIN, }, + { "mosaic", GBA_GL_OBJ_MOSAIC, }, { 0 } };@@ -248,16 +465,29 @@ "uniform int localPalette;\n"
"uniform ivec4 inflags;\n" "uniform mat2x2 transform;\n" "uniform ivec4 dims;\n" - "uniform vec3 objwin;\n" + "uniform vec4 objwin;\n" + "uniform ivec4 mosaic;\n" "out vec4 color;\n" "out vec4 flags;\n" - "out vec2 window;\n" - "const vec4 flagCoeff = vec4(32., 32., 16., 16.);\n" + "out vec3 window;\n" + FLAG_CONST "vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n" "void main() {\n" - " ivec2 coord = ivec2(transform * (texCoord - dims.zw / 2) + dims.xy / 2);\n" + " vec2 incoord = texCoord;\n" + " if (mosaic.x > 1) {\n" + " int x = int(incoord.x);\n" + " incoord.x = clamp(x - int(mod(mosaic.z + x, mosaic.x)), 0, dims.z - 1);\n" + " } else if (mosaic.x < -1) {\n" + " int x = dims.z - int(incoord.x) - 1;\n" + " incoord.x = clamp(dims.z - x + int(mod(mosaic.z + x, -mosaic.x)) - 1, 0, dims.z - 1);\n" + " }\n" + " if (mosaic.y > 1) {\n" + " int y = int(incoord.y);\n" + " incoord.y = clamp(y - int(mod(mosaic.w + y, mosaic.y)), 0, dims.w - 1);\n" + " }\n" + " ivec2 coord = ivec2(transform * (incoord - dims.zw / 2) + dims.xy / 2);\n" " if ((coord & ~(dims.xy - 1)) != ivec2(0, 0)) {\n" " discard;\n" " }\n"@@ -267,7 +497,7 @@ " pix.a = 0;\n"
" }\n" " color = pix;\n" " flags = inflags / flagCoeff;\n" - " window = objwin.yz;\n" + " window = objwin.yzw;\n" "}"; static const struct GBAVideoGLUniform _uniformsFinalize[] = {@@ -288,9 +518,9 @@ "uniform int scale;\n"
"uniform sampler2D layers[5];\n" "uniform sampler2D flags[5];\n" "uniform sampler2D window;\n" - "uniform vec4 backdrop;\n" - "uniform vec4 backdropFlags;\n" - "const vec4 flagCoeff = vec4(32., 32., 16., 16.);\n" + "uniform sampler2D backdrop;\n" + "uniform sampler2D backdropFlags;\n" + FLAG_CONST "out vec4 color;\n" "void composite(vec4 pixel, ivec4 flags, inout vec4 topPixel, inout ivec4 topFlags, inout vec4 bottomPixel, inout ivec4 bottomFlags) {\n"@@ -312,49 +542,47 @@ " }\n"
"}\n" "void main() {\n" - " ivec2 windowFlags = ivec2(texelFetch(window, ivec2(texCoord * scale), 0).xy * 32);\n" - " int layerWindow = windowFlags.x | (windowFlags.y << 4);\n" - " vec4 topPixel = backdrop;\n" - " vec4 bottomPixel = backdrop;\n" - " ivec4 topFlags = ivec4(backdropFlags * flagCoeff);\n" - " ivec4 bottomFlags = ivec4(backdropFlags * flagCoeff);\n" + " vec4 topPixel = texelFetch(backdrop, ivec2(0, texCoord.y), 0);\n" + " vec4 bottomPixel = topPixel;\n" + " ivec4 topFlags = ivec4(texelFetch(backdropFlags, ivec2(0, texCoord.y), 0) * flagCoeff);\n" + " ivec4 bottomFlags = topFlags;\n" + " vec4 windowFlags = texelFetch(window, ivec2(texCoord * scale), 0);\n" + " int layerWindow = int(windowFlags.x * 128);\n" " if ((layerWindow & 1) == 0) {\n" " vec4 pix = texelFetch(layers[0], ivec2(texCoord * scale), 0);\n" - " ivec4 inflags = ivec4(texelFetch(flags[0], ivec2(texCoord * scale), 0) * flagCoeff);\n" + " ivec4 inflags = ivec4(texelFetch(flags[0], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " }\n" " if ((layerWindow & 2) == 0) {\n" " vec4 pix = texelFetch(layers[1], ivec2(texCoord * scale), 0);\n" - " ivec4 inflags = ivec4(texelFetch(flags[1], ivec2(texCoord * scale), 0) * flagCoeff);\n" + " ivec4 inflags = ivec4(texelFetch(flags[1], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " }\n" " if ((layerWindow & 4) == 0) {\n" " vec4 pix = texelFetch(layers[2], ivec2(texCoord * scale), 0);\n" - " ivec4 inflags = ivec4(texelFetch(flags[2], ivec2(texCoord * scale), 0) * flagCoeff);\n" + " ivec4 inflags = ivec4(texelFetch(flags[2], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " }\n" " if ((layerWindow & 8) == 0) {\n" " vec4 pix = texelFetch(layers[3], ivec2(texCoord * scale), 0);\n" - " ivec4 inflags = ivec4(texelFetch(flags[3], ivec2(texCoord * scale), 0) * flagCoeff);\n" + " ivec4 inflags = ivec4(texelFetch(flags[3], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " }\n" - " if ((layerWindow & 32) != 0) {\n" - " topFlags.y &= ~1;\n" - " }\n" " if ((layerWindow & 16) == 0) {\n" " vec4 pix = texelFetch(layers[4], ivec2(texCoord * scale), 0);\n" " ivec4 inflags = ivec4(texelFetch(flags[4], ivec2(texCoord * scale), 0) * flagCoeff);\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " }\n" - " if ((topFlags.y & 13) == 5) {\n" - " if ((bottomFlags.y & 2) == 2) {\n" - " topPixel *= topFlags.z / 16.;\n" - " topPixel += bottomPixel * bottomFlags.w / 16.;\n" - " }\n" + " if ((layerWindow & 32) != 0) {\n" + " topFlags.y &= ~1;\n" + " }\n" + " if (((topFlags.y & 13) == 5 || topFlags.w > 0) && (bottomFlags.y & 2) == 2) {\n" + " topPixel *= topFlags.z / 16.;\n" + " topPixel += bottomPixel * windowFlags.y;\n" " } else if ((topFlags.y & 13) == 9) {\n" - " topPixel += (1. - topPixel) * topFlags.z / 16.;\n" + " topPixel += (1. - topPixel) * windowFlags.z;\n" " } else if ((topFlags.y & 13) == 13) {\n" - " topPixel -= topPixel * topFlags.z / 16.;\n" + " topPixel -= topPixel * windowFlags.z;\n" " }\n" " color = topPixel;\n" "}";@@ -388,7 +616,7 @@
renderer->scale = 1; } -void _compileShader(struct GBAVideoGLRenderer* glRenderer, struct GBAVideoGLShader* shader, const char** shaderBuffer, int shaderBufferLines, GLuint vs, const struct GBAVideoGLUniform* uniforms, char* log) { +static void _compileShader(struct GBAVideoGLRenderer* glRenderer, struct GBAVideoGLShader* shader, const char** shaderBuffer, int shaderBufferLines, GLuint vs, const struct GBAVideoGLUniform* uniforms, char* log) { GLuint program = glCreateProgram(); shader->program = program;@@ -416,12 +644,18 @@ glGenVertexArrays(1, &shader->vao);
glBindVertexArray(shader->vao); glBindBuffer(GL_ARRAY_BUFFER, glRenderer->vbo); GLuint positionLocation = glGetAttribLocation(program, "position"); + glEnableVertexAttribArray(positionLocation); glVertexAttribPointer(positionLocation, 2, GL_INT, GL_FALSE, 0, NULL); size_t i; for (i = 0; uniforms[i].name; ++i) { shader->uniforms[uniforms[i].type] = glGetUniformLocation(program, uniforms[i].name); } +} + +static void _deleteShader(struct GBAVideoGLShader* shader) { + glDeleteProgram(shader->program); + glDeleteVertexArrays(1, &shader->vao); } static void _initFramebufferTexture(GLuint tex, GLenum format, GLenum attachment, int scale) {@@ -430,7 +664,7 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, format, GBA_VIDEO_HORIZONTAL_PIXELS * scale, GBA_VIDEO_VERTICAL_PIXELS * scale, 0, format, GL_UNSIGNED_BYTE, 0); + glTexImage2D(GL_TEXTURE_2D, 0, format, scale > 0 ? GBA_VIDEO_HORIZONTAL_PIXELS * scale : 1, GBA_VIDEO_VERTICAL_PIXELS * (scale > 0 ? scale : 1), 0, format, GL_UNSIGNED_BYTE, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0); }@@ -452,12 +686,30 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0); + glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_2]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, NULL); + glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_3]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, NULL); + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]); _initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_COLOR], GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale); _initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_FLAGS], GL_RGBA, GL_COLOR_ATTACHMENT1, glRenderer->scale); + _initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGBA, GL_COLOR_ATTACHMENT2, glRenderer->scale); + + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]); + _initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_COLOR], GL_RGB, GL_COLOR_ATTACHMENT0, 0); + _initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_FLAGS], GL_RGBA, GL_COLOR_ATTACHMENT1, 0); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_WINDOW]); - _initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RG, GL_COLOR_ATTACHMENT0, glRenderer->scale); + _initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]); _initFramebufferTexture(glRenderer->outputTex, GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale);@@ -486,24 +738,29 @@ bg->x = 0;
bg->y = 0; bg->refx = 0; bg->refy = 0; - bg->affine[0].dx = 256; - bg->affine[0].dmx = 0; - bg->affine[0].dy = 0; - bg->affine[0].dmy = 256; - bg->affine[0].sx = 0; - bg->affine[0].sy = 0; + bg->affine.dx = 256; + bg->affine.dmx = 0; + bg->affine.dy = 0; + bg->affine.dmy = 256; + bg->affine.sx = 0; + bg->affine.sy = 0; glGenFramebuffers(1, &bg->fbo); glGenTextures(1, &bg->tex); glGenTextures(1, &bg->flags); glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo); _initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale); - _initFramebufferTexture(bg->flags, GL_RGBA, GL_COLOR_ATTACHMENT1, glRenderer->scale); + _initFramebufferTexture(bg->flags, GL_RGB, GL_COLOR_ATTACHMENT1, glRenderer->scale); glBindFramebuffer(GL_FRAMEBUFFER, 0); } char log[1024]; - const GLchar* shaderBuffer[8]; - shaderBuffer[0] = _gl3Header; + const GLchar* shaderBuffer[4]; + const GLubyte* version = glGetString(GL_VERSION); + if (strncmp((const char*) version, "OpenGL ES ", strlen("OpenGL ES "))) { + shaderBuffer[0] = _gl3Header; + } else { + shaderBuffer[0] = _gles3Header; + } GLuint vs = glCreateShader(GL_VERTEX_SHADER); shaderBuffer[1] = _vertexShader;@@ -523,12 +780,21 @@ shaderBuffer[2] = _renderTile256;
_compileShader(glRenderer, &glRenderer->bgShader[1], shaderBuffer, 3, vs, _uniformsMode0, log); shaderBuffer[1] = _renderMode2; + shaderBuffer[2] = _interpolate; - shaderBuffer[2] = _fetchTileOverflow; - _compileShader(glRenderer, &glRenderer->bgShader[2], shaderBuffer, 3, vs, _uniformsMode2, log); + shaderBuffer[3] = _fetchTileOverflow; + _compileShader(glRenderer, &glRenderer->bgShader[2], shaderBuffer, 4, vs, _uniformsMode2, log); - shaderBuffer[2] = _fetchTileNoOverflow; - _compileShader(glRenderer, &glRenderer->bgShader[3], shaderBuffer, 3, vs, _uniformsMode2, log); + shaderBuffer[3] = _fetchTileNoOverflow; + _compileShader(glRenderer, &glRenderer->bgShader[3], shaderBuffer, 4, vs, _uniformsMode2, log); + + shaderBuffer[1] = _renderMode4; + shaderBuffer[2] = _interpolate; + _compileShader(glRenderer, &glRenderer->bgShader[4], shaderBuffer, 3, vs, _uniformsMode4, log); + + shaderBuffer[1] = _renderMode35; + shaderBuffer[2] = _interpolate; + _compileShader(glRenderer, &glRenderer->bgShader[5], shaderBuffer, 3, vs, _uniformsMode35, log); shaderBuffer[1] = _renderObj;@@ -562,6 +828,22 @@ glDeleteFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
glDeleteTextures(GBA_GL_TEX_MAX, glRenderer->layers); glDeleteTextures(1, &glRenderer->paletteTex); glDeleteTextures(1, &glRenderer->vramTex); + + _deleteShader(&glRenderer->bgShader[0]); + _deleteShader(&glRenderer->bgShader[1]); + _deleteShader(&glRenderer->bgShader[2]); + _deleteShader(&glRenderer->bgShader[3]); + _deleteShader(&glRenderer->objShader[0]); + _deleteShader(&glRenderer->objShader[1]); + _deleteShader(&glRenderer->finalizeShader); + + int i; + for (i = 0; i < 4; ++i) { + struct GBAVideoGLBackground* bg = &glRenderer->bg[i]; + glDeleteFramebuffers(1, &bg->fbo); + glDeleteTextures(1, &bg->tex); + glDeleteTextures(1, &bg->flags); + } } void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {@@ -570,6 +852,9 @@
glRenderer->paletteDirty = true; glRenderer->vramDirty = 0xFFFFFF; glRenderer->firstAffine = -1; + glRenderer->firstY = -1; + glRenderer->dispcnt = 0; + glRenderer->mosaic = 0; } void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {@@ -600,107 +885,159 @@ if (renderer->cache) {
GBAVideoCacheWriteVideoRegister(renderer->cache, address, value); } + bool dirty = true; switch (address) { case REG_DISPCNT: value &= 0xFFF7; - glRenderer->dispcnt = value; - GBAVideoGLRendererUpdateDISPCNT(glRenderer); break; case REG_BG0CNT: - value &= 0xDFFF; - GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[0], value); - break; case REG_BG1CNT: value &= 0xDFFF; - GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[1], value); - break; - case REG_BG2CNT: - value &= 0xFFFF; - GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[2], value); - break; - case REG_BG3CNT: - value &= 0xFFFF; - GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[3], value); break; case REG_BG0HOFS: - value &= 0x01FF; - glRenderer->bg[0].x = value; - break; case REG_BG0VOFS: - value &= 0x01FF; - glRenderer->bg[0].y = value; - break; case REG_BG1HOFS: - value &= 0x01FF; - glRenderer->bg[1].x = value; - break; case REG_BG1VOFS: - value &= 0x01FF; - glRenderer->bg[1].y = value; - break; case REG_BG2HOFS: - value &= 0x01FF; - glRenderer->bg[2].x = value; - break; case REG_BG2VOFS: - value &= 0x01FF; - glRenderer->bg[2].y = value; - break; case REG_BG3HOFS: - value &= 0x01FF; - glRenderer->bg[3].x = value; - break; case REG_BG3VOFS: value &= 0x01FF; - glRenderer->bg[3].y = value; break; case REG_BG2PA: - glRenderer->bg[2].affine[0].dx = value; + glRenderer->bg[2].affine.dx = value; + dirty = false; break; case REG_BG2PB: - glRenderer->bg[2].affine[0].dmx = value; + glRenderer->bg[2].affine.dmx = value; + dirty = false; break; case REG_BG2PC: - glRenderer->bg[2].affine[0].dy = value; + glRenderer->bg[2].affine.dy = value; + dirty = false; break; case REG_BG2PD: - glRenderer->bg[2].affine[0].dmy = value; + glRenderer->bg[2].affine.dmy = value; + dirty = false; break; case REG_BG2X_LO: GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[2], value); + dirty = false; break; case REG_BG2X_HI: GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[2], value); + dirty = false; break; case REG_BG2Y_LO: GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[2], value); + dirty = false; break; case REG_BG2Y_HI: GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[2], value); + dirty = false; break; case REG_BG3PA: - glRenderer->bg[3].affine[0].dx = value; + glRenderer->bg[3].affine.dx = value; + dirty = false; break; case REG_BG3PB: - glRenderer->bg[3].affine[0].dmx = value; + glRenderer->bg[3].affine.dmx = value; + dirty = false; break; case REG_BG3PC: - glRenderer->bg[3].affine[0].dy = value; + glRenderer->bg[3].affine.dy = value; + dirty = false; break; case REG_BG3PD: - glRenderer->bg[3].affine[0].dmy = value; + glRenderer->bg[3].affine.dmy = value; + dirty = false; break; case REG_BG3X_LO: GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[3], value); + dirty = false; break; case REG_BG3X_HI: GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[3], value); + dirty = false; break; case REG_BG3Y_LO: GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[3], value); + dirty = false; break; case REG_BG3Y_HI: GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[3], value); + dirty = false; + break; + case REG_BLDALPHA: + value &= 0x1F1F; + break; + case REG_BLDY: + value &= 0x1F; + if (value > 0x10) { + value = 0x10; + } + break; + case REG_WININ: + value &= 0x3F3F; + break; + case REG_WINOUT: + value &= 0x3F3F; + break; + default: + break; + } + if (glRenderer->shadowRegs[address >> 1] == value) { + dirty = false; + } else { + glRenderer->shadowRegs[address >> 1] = value; + } + if (dirty) { + glRenderer->regsDirty |= 1ULL << (address >> 1); + } + return value; +} + +void _cleanRegister(struct GBAVideoGLRenderer* glRenderer, int address, uint16_t value) { + switch (address) { + case REG_DISPCNT: + glRenderer->dispcnt = value; + GBAVideoGLRendererUpdateDISPCNT(glRenderer); + break; + case REG_BG0CNT: + GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[0], value); + break; + case REG_BG1CNT: + GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[1], value); + break; + case REG_BG2CNT: + GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[2], value); + break; + case REG_BG3CNT: + GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[3], value); + break; + case REG_BG0HOFS: + glRenderer->bg[0].x = value; + break; + case REG_BG0VOFS: + glRenderer->bg[0].y = value; + break; + case REG_BG1HOFS: + glRenderer->bg[1].x = value; + break; + case REG_BG1VOFS: + glRenderer->bg[1].y = value; + break; + case REG_BG2HOFS: + glRenderer->bg[2].x = value; + break; + case REG_BG2VOFS: + glRenderer->bg[2].y = value; + break; + case REG_BG3HOFS: + glRenderer->bg[3].x = value; + break; + case REG_BG3VOFS: + glRenderer->bg[3].y = value; break; case REG_BLDCNT: GBAVideoGLRendererWriteBLDCNT(glRenderer, value);@@ -718,35 +1055,31 @@ }
value &= 0x1F1F; break; case REG_BLDY: - value &= 0x1F; - if (value > 0x10) { - value = 0x10; - } glRenderer->bldy = value; break; case REG_WIN0H: - glRenderer->winN[0].h.end = value; - glRenderer->winN[0].h.start = value >> 8; - if (glRenderer->winN[0].h.start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h.start > glRenderer->winN[0].h.end) { - glRenderer->winN[0].h.start = 0; + glRenderer->winN[0].h[0].end = value; + glRenderer->winN[0].h[0].start = value >> 8; + if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h[0].start > glRenderer->winN[0].h[0].end) { + glRenderer->winN[0].h[0].start = 0; } - if (glRenderer->winN[0].h.end > GBA_VIDEO_HORIZONTAL_PIXELS) { - glRenderer->winN[0].h.end = GBA_VIDEO_HORIZONTAL_PIXELS; - if (glRenderer->winN[0].h.start > GBA_VIDEO_HORIZONTAL_PIXELS) { - glRenderer->winN[0].h.start = GBA_VIDEO_HORIZONTAL_PIXELS; + if (glRenderer->winN[0].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) { + glRenderer->winN[0].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS; + if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) { + glRenderer->winN[0].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS; } } break; case REG_WIN1H: - glRenderer->winN[1].h.end = value; - glRenderer->winN[1].h.start = value >> 8; - if (glRenderer->winN[1].h.start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h.start > glRenderer->winN[1].h.end) { - glRenderer->winN[1].h.start = 0; + glRenderer->winN[1].h[0].end = value; + glRenderer->winN[1].h[0].start = value >> 8; + if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h[0].start > glRenderer->winN[1].h[0].end) { + glRenderer->winN[1].h[0].start = 0; } - if (glRenderer->winN[1].h.end > GBA_VIDEO_HORIZONTAL_PIXELS) { - glRenderer->winN[1].h.end = GBA_VIDEO_HORIZONTAL_PIXELS; - if (glRenderer->winN[1].h.start > GBA_VIDEO_HORIZONTAL_PIXELS) { - glRenderer->winN[1].h.start = GBA_VIDEO_HORIZONTAL_PIXELS; + if (glRenderer->winN[1].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) { + glRenderer->winN[1].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS; + if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) { + glRenderer->winN[1].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS; } } break;@@ -777,12 +1110,10 @@ }
} break; case REG_WININ: - value &= 0x3F3F; glRenderer->winN[0].control = value; glRenderer->winN[1].control = value >> 8; break; case REG_WINOUT: - value &= 0x3F3F; glRenderer->winout = value; glRenderer->objwin = value >> 8; break;@@ -792,11 +1123,42 @@ break;
default: break; } - return value; } void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; + + memcpy(&glRenderer->affine[0][y], &glRenderer->bg[2].affine, sizeof(struct GBAVideoGLAffine)); + memcpy(&glRenderer->affine[1][y], &glRenderer->bg[3].affine, sizeof(struct GBAVideoGLAffine)); + if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) { + if (glRenderer->firstAffine < 0) { + glRenderer->firstAffine = y; + } + } else { + glRenderer->firstAffine = -1; + } + + if (glRenderer->paletteDirty || glRenderer->vramDirty || glRenderer->oamDirty || glRenderer->regsDirty) { + if (glRenderer->firstY >= 0) { + _drawScanlines(glRenderer, y - 1); + } + } + if (glRenderer->firstY < 0) { + glRenderer->firstY = y; + } + + memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion)); + memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion)); + + int i; + for (i = 0; i < 0x30; ++i) { + if (!(glRenderer->regsDirty & (1ULL << i))) { + continue; + } + _cleanRegister(glRenderer, i << 1, glRenderer->shadowRegs[i]); + } + glRenderer->regsDirty = 0; + if (glRenderer->paletteDirty) { glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex); #ifdef BUILD_GLES3@@ -806,26 +1168,30 @@ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, glRenderer->d.palette);
#endif glRenderer->paletteDirty = false; } - int i; - for (i = 0; i < 16; ++i) { - if (!(glRenderer->vramDirty & (1 << i))) { - continue; - } - // TODO: PBOs - glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 8 * i, 256, 8, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, &glRenderer->d.vramBG[2048 * i]); - } - for (i = 16; i < 24; ++i) { + + int first = -1; + glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex); + for (i = 0; i < 25; ++i) { if (!(glRenderer->vramDirty & (1 << i))) { - continue; + if (first >= 0) { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 8 * first, 256, 8 * (i - first), GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, &glRenderer->d.vramBG[2048 * first]); + first = -1; + } + } else if (first < 0) { + first = i; } - // TODO: PBOs - glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 8 * i, 256, 8, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, &glRenderer->d.vramOBJ[2048 * (i - 16)]); } glRenderer->vramDirty = 0; + if (glRenderer->oamDirty) { + glRenderer->oamMax = GBAVideoRendererCleanOAM(glRenderer->d.oam->obj, glRenderer->sprites, 0, GBA_VIDEO_VERTICAL_PIXELS, false); + glRenderer->oamDirty = false; + } + if (y == 0) { + memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion)); + memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion)); + glDisable(GL_SCISSOR_TEST); glClearColor(0, 0, 0, 0); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);@@ -837,37 +1203,43 @@ glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo);
glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); glClear(GL_COLOR_BUFFER_BIT); } - glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); } - glEnable(GL_SCISSOR_TEST); if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) { - if (glRenderer->firstAffine < 0) { - memcpy(&glRenderer->bg[2].affine[3], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[3].affine[3], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[2].affine[2], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[3].affine[2], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[2].affine[1], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[3].affine[1], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine)); - glRenderer->firstAffine = y; - } else if (y - glRenderer->firstAffine == 1) { - memcpy(&glRenderer->bg[2].affine[1], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[3].affine[1], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine)); - } - } else { - glRenderer->firstAffine = -1; + glRenderer->bg[2].affine.sx += glRenderer->bg[2].affine.dmx; + glRenderer->bg[2].affine.sy += glRenderer->bg[2].affine.dmy; + glRenderer->bg[3].affine.sx += glRenderer->bg[3].affine.dmx; + glRenderer->bg[3].affine.sy += glRenderer->bg[3].affine.dmy; + } +} + +void _drawScanlines(struct GBAVideoGLRenderer* glRenderer, int y) { + if (glRenderer->firstAffine >= 0) { + glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_2]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, glRenderer->affine[0]); + glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_3]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, glRenderer->affine[1]); } + glEnable(GL_SCISSOR_TEST); + + uint32_t backdrop = M_RGB5_TO_RGB8(glRenderer->d.palette[0]); + glViewport(0, 0, 1, GBA_VIDEO_VERTICAL_PIXELS); + glScissor(0, glRenderer->firstY, 1, y - glRenderer->firstY + 1); + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); + glClearColor(((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f); + glClear(GL_COLOR_BUFFER_BIT); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT1 }); + glClearColor(1, (glRenderer->target1Bd | (glRenderer->target2Bd * 2) | (glRenderer->blendEffect * 4)) / 32.f, glRenderer->blda / 16.f, 0); + glClear(GL_COLOR_BUFFER_BIT); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); GBAVideoGLRendererDrawWindow(glRenderer, y); if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) { - if (glRenderer->oamDirty) { - glRenderer->oamMax = GBAVideoRendererCleanOAM(glRenderer->d.oam->obj, glRenderer->sprites, 0, GBA_VIDEO_VERTICAL_PIXELS, false); - glRenderer->oamDirty = false; - } int i; for (i = glRenderer->oamMax; i--;) { struct GBAVideoRendererSprite* sprite = &glRenderer->sprites[i]; - if ((y < sprite->y && (sprite->endY - 256 < 0 || y >= sprite->endY - 256)) || y >= sprite->endY) { + if ((y < sprite->y && (sprite->endY - 256 < 0 || glRenderer->firstY >= sprite->endY - 256)) || glRenderer->firstY >= sprite->endY) { continue; }@@ -891,13 +1263,13 @@ case 2:
GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[2], y); break; case 3: - //GBAVideoGLRendererDrawBackgroundMode3(glRenderer, &glRenderer->bg[2], y); + GBAVideoGLRendererDrawBackgroundMode3(glRenderer, &glRenderer->bg[2], y); break; case 4: - //GBAVideoGLRendererDrawBackgroundMode4(glRenderer, &glRenderer->bg[2], y); + GBAVideoGLRendererDrawBackgroundMode4(glRenderer, &glRenderer->bg[2], y); break; case 5: - //GBAVideoGLRendererDrawBackgroundMode5(glRenderer, &glRenderer->bg[2], y); + GBAVideoGLRendererDrawBackgroundMode5(glRenderer, &glRenderer->bg[2], y); break; } }@@ -911,31 +1283,20 @@ GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[3], y);
break; } } - _finalizeLayers(glRenderer, y); - - if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) { - memcpy(&glRenderer->bg[2].affine[3], &glRenderer->bg[2].affine[2], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[3].affine[3], &glRenderer->bg[3].affine[2], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[2].affine[2], &glRenderer->bg[2].affine[1], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[3].affine[2], &glRenderer->bg[3].affine[1], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[2].affine[1], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->bg[3].affine[1], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine)); - - glRenderer->bg[2].affine[0].sx += glRenderer->bg[2].affine[0].dmx; - glRenderer->bg[2].affine[0].sy += glRenderer->bg[2].affine[0].dmy; - glRenderer->bg[3].affine[0].sx += glRenderer->bg[3].affine[0].dmx; - glRenderer->bg[3].affine[0].sy += glRenderer->bg[3].affine[0].dmy; - } + glRenderer->firstY = -1; } void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer) { struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; + _drawScanlines(glRenderer, GBA_VIDEO_VERTICAL_PIXELS - 1); + _finalizeLayers(glRenderer); + glBindVertexArray(0); glRenderer->firstAffine = -1; - glRenderer->bg[2].affine[0].sx = glRenderer->bg[2].refx; - glRenderer->bg[2].affine[0].sy = glRenderer->bg[2].refy; - glRenderer->bg[3].affine[0].sx = glRenderer->bg[3].refx; - glRenderer->bg[3].affine[0].sy = glRenderer->bg[3].refy; - glFlush(); + glRenderer->firstY = -1; + glRenderer->bg[2].affine.sx = glRenderer->bg[2].refx; + glRenderer->bg[2].affine.sy = glRenderer->bg[2].refy; + glRenderer->bg[3].affine.sx = glRenderer->bg[3].refx; + glRenderer->bg[3].affine.sy = glRenderer->bg[3].refy; } void GBAVideoGLRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {@@ -990,26 +1351,26 @@ }
static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value) { bg->refx = (bg->refx & 0xFFFF0000) | value; - bg->affine[0].sx = bg->refx; + bg->affine.sx = bg->refx; } static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value) { bg->refx = (bg->refx & 0x0000FFFF) | (value << 16); bg->refx <<= 4; bg->refx >>= 4; - bg->affine[0].sx = bg->refx; + bg->affine.sx = bg->refx; } static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value) { bg->refy = (bg->refy & 0xFFFF0000) | value; - bg->affine[0].sy = bg->refy; + bg->affine.sy = bg->refy; } static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value) { bg->refy = (bg->refy & 0x0000FFFF) | (value << 16); bg->refy <<= 4; bg->refy >>= 4; - bg->affine[0].sy = bg->refy; + bg->affine.sy = bg->refy; } static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value) {@@ -1029,11 +1390,11 @@ renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value); } -void _finalizeLayers(struct GBAVideoGLRenderer* renderer, int y) { +void _finalizeLayers(struct GBAVideoGLRenderer* renderer) { const GLuint* uniforms = renderer->finalizeShader.uniforms; glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_OUTPUT]); glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale); - glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale); + glScissor(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale); glUseProgram(renderer->finalizeShader.program); glBindVertexArray(renderer->finalizeShader.vao); glActiveTexture(GL_TEXTURE0);@@ -1058,18 +1419,20 @@ glActiveTexture(GL_TEXTURE0 + 9);
glBindTexture(GL_TEXTURE_2D, renderer->bg[3].tex); glActiveTexture(GL_TEXTURE0 + 10); glBindTexture(GL_TEXTURE_2D, renderer->bg[3].flags); + glActiveTexture(GL_TEXTURE0 + 11); + glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_COLOR]); + glActiveTexture(GL_TEXTURE0 + 12); + glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_FLAGS]); - uint32_t backdrop = M_RGB5_TO_RGB8(renderer->d.palette[0]); - glUniform2i(uniforms[GBA_GL_VS_LOC], 1, y); + glUniform2i(uniforms[GBA_GL_VS_LOC], GBA_VIDEO_VERTICAL_PIXELS, 0); glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); glUniform1i(uniforms[GBA_GL_FINALIZE_SCALE], renderer->scale); glUniform1iv(uniforms[GBA_GL_FINALIZE_LAYERS], 5, (GLint[]) { 3, 5, 7, 9, 1 }); glUniform1iv(uniforms[GBA_GL_FINALIZE_FLAGS], 5, (GLint[]) { 4, 6, 8, 10, 2 }); glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0); - glUniform4f(uniforms[GBA_GL_FINALIZE_BACKDROP], ((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f); - glUniform4f(uniforms[GBA_GL_FINALIZE_BACKDROPFLAGS], 1, (renderer->target1Bd | (renderer->target2Bd * 2) | (renderer->blendEffect * 4)) / 32.f, - (renderer->blendEffect == BLEND_ALPHA ? renderer->blda : renderer->bldy) / 16.f, renderer->bldb / 16.f); - glEnableVertexAttribArray(0); + glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0); + glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROP], 11); + glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROPFLAGS], 12); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glBindFramebuffer(GL_FRAMEBUFFER, 0); }@@ -1088,10 +1451,6 @@ if (spriteY + height >= 256) {
spriteY -= 256; } - if (!GBAObjAttributesAIsTransformed(sprite->a) && GBAObjAttributesBIsVFlip(sprite->b)) { - spriteY = (y - height) + (y - spriteY) + 1; - } - int totalWidth = width; int totalHeight = height; if (GBAObjAttributesAIsTransformed(sprite->a) && GBAObjAttributesAIsDoubleSize(sprite->a)) {@@ -1099,29 +1458,27 @@ totalWidth <<= 1;
totalHeight <<= 1; } - enum GBAVideoBlendEffect blendEffect = GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT ? BLEND_ALPHA : renderer->blendEffect; - const struct GBAVideoGLShader* shader = &renderer->objShader[GBAObjAttributesAGet256Color(sprite->a)]; const GLuint* uniforms = shader->uniforms; glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_OBJ]); glViewport(x * renderer->scale, spriteY * renderer->scale, totalWidth * renderer->scale, totalHeight * renderer->scale); - glScissor(x * renderer->scale, y * renderer->scale, totalWidth * renderer->scale, renderer->scale); + glScissor(x * renderer->scale, renderer->firstY * renderer->scale, totalWidth * renderer->scale, (y - renderer->firstY + 1) * renderer->scale); glUseProgram(shader->program); glBindVertexArray(shader->vao); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, renderer->vramTex); glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_2D, renderer->paletteTex); - glUniform2i(uniforms[GBA_GL_VS_LOC], 1, y - spriteY); - glUniform2i(uniforms[GBA_GL_VS_MAXPOS], (GBAObjAttributesBIsHFlip(sprite->b) && !GBAObjAttributesAIsTransformed(sprite->a)) ? -totalWidth : totalWidth, totalHeight); + glUniform2i(uniforms[GBA_GL_VS_LOC], totalHeight, 0); + glUniform2i(uniforms[GBA_GL_VS_MAXPOS], totalWidth, totalHeight); glUniform1i(uniforms[GBA_GL_OBJ_VRAM], 0); glUniform1i(uniforms[GBA_GL_OBJ_PALETTE], 1); glUniform1i(uniforms[GBA_GL_OBJ_CHARBASE], charBase); glUniform1i(uniforms[GBA_GL_OBJ_STRIDE], stride); glUniform1i(uniforms[GBA_GL_OBJ_LOCALPALETTE], GBAObjAttributesCGetPalette(sprite->c)); glUniform4i(uniforms[GBA_GL_OBJ_INFLAGS], GBAObjAttributesCGetPriority(sprite->c) << 3, - (renderer->target1Obj || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT) | (renderer->target2Obj * 2) | (blendEffect * 4), - blendEffect == BLEND_ALPHA ? renderer->blda : renderer->bldy, renderer->bldb); + (renderer->target1Obj || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT) | (renderer->target2Obj * 2) | (renderer->blendEffect * 4), + renderer->blda, GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT); if (GBAObjAttributesAIsTransformed(sprite->a)) { struct GBAOAMMatrix mat; LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);@@ -1131,133 +1488,242 @@ LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d);
glUniformMatrix2fv(uniforms[GBA_GL_OBJ_TRANSFORM], 1, GL_FALSE, (GLfloat[]) { mat.a / 256.f, mat.c / 256.f, mat.b / 256.f, mat.d / 256.f }); } else { - glUniformMatrix2fv(uniforms[GBA_GL_OBJ_TRANSFORM], 1, GL_FALSE, (GLfloat[]) { 1.f, 0, 0, 1.f }); + int flipX = 1; + int flipY = 1; + if (GBAObjAttributesBIsHFlip(sprite->b)) { + flipX = -1; + } + if (GBAObjAttributesBIsVFlip(sprite->b)) { + flipY = -1; + } + glUniformMatrix2fv(uniforms[GBA_GL_OBJ_TRANSFORM], 1, GL_FALSE, (GLfloat[]) { flipX, 0, 0, flipY }); } glUniform4i(uniforms[GBA_GL_OBJ_DIMS], width, height, totalWidth, totalHeight); if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN) { - int window = ~renderer->objwin & 0xFF; - glUniform3f(uniforms[GBA_GL_OBJ_OBJWIN], 1, (window & 0xF) / 32.f, (window >> 4) / 32.f); + int window = ~renderer->objwin & 0x3F; + glUniform4f(uniforms[GBA_GL_OBJ_OBJWIN], 1, window / 128.f, renderer->bldb / 16.f, renderer->bldy / 16.f); glDrawBuffers(3, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }); } else { - glUniform3f(uniforms[GBA_GL_OBJ_OBJWIN], 0, 0, 0); + glUniform4f(uniforms[GBA_GL_OBJ_OBJWIN], 0, 0, 0, 0); glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); } - glEnableVertexAttribArray(0); + if (GBAObjAttributesAIsMosaic(sprite->a) && GBAObjAttributesAGetMode(sprite->a) != OBJ_MODE_OBJWIN) { + int mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1; + if (GBAObjAttributesBIsHFlip(sprite->b)) { + mosaicH = -mosaicH; + } + glUniform4i(uniforms[GBA_GL_OBJ_MOSAIC], mosaicH, GBAMosaicControlGetObjV(renderer->mosaic) + 1, x, spriteY); + } else { + glUniform4i(uniforms[GBA_GL_OBJ_MOSAIC], 0, 0, 0, 0); + } glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); } -void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { - int inY = y + background->y; - int yBase = inY & 0xFF; - if (background->size == 2) { - yBase += inY & 0x100; - } else if (background->size == 3) { - yBase += (inY & 0x100) << 1; - } - - const struct GBAVideoGLShader* shader = &renderer->bgShader[background->multipalette ? 1 : 0]; - const GLuint* uniforms = shader->uniforms; +void _prepareBackground(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, const GLuint* uniforms) { glBindFramebuffer(GL_FRAMEBUFFER, background->fbo); glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale); - glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale); - glUseProgram(shader->program); - glBindVertexArray(shader->vao); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, renderer->vramTex); glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_2D, renderer->paletteTex); - glUniform2i(uniforms[GBA_GL_VS_LOC], 1, y); glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); glUniform1i(uniforms[GBA_GL_BG_VRAM], 0); glUniform1i(uniforms[GBA_GL_BG_PALETTE], 1); + if (background->mosaic) { + glUniform2i(uniforms[GBA_GL_BG_MOSAIC], GBAMosaicControlGetBgV(renderer->mosaic) + 1, GBAMosaicControlGetBgH(renderer->mosaic) + 1); + } else { + glUniform2i(uniforms[GBA_GL_BG_MOSAIC], 0, 0); + } + glUniform4i(uniforms[GBA_GL_BG_INFLAGS], (background->priority << 3) + (background->index << 1) + 1, + background->target1 | (background->target2 * 2) | (renderer->blendEffect * 4), + renderer->blda, 0); + glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); +} + +void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { + int inY0 = renderer->firstY + background->y; + int yDiv = (((y + background->y) & ~0xFF) - background->y) & 0xFF; + int inY1 = yDiv + background->y; + int yBase0 = inY0 & 0xFF; + int yBase1 = inY1 & 0xFF; + if (background->size == 2) { + yBase0 += inY0 & 0x100; + yBase1 += inY1 & 0x100; + } else if (background->size == 3) { + yBase0 += (inY0 & 0x100) << 1; + yBase1 += (inY1 & 0x100) << 1; + } + + const struct GBAVideoGLShader* shader = &renderer->bgShader[background->multipalette ? 1 : 0]; + const GLuint* uniforms = shader->uniforms; + glUseProgram(shader->program); + glBindVertexArray(shader->vao); + _prepareBackground(renderer, background, uniforms); glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase); glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase); glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size); - glUniform2i(uniforms[GBA_GL_BG_OFFSET], background->x, yBase - y); - glUniform4i(uniforms[GBA_GL_BG_INFLAGS], (background->priority << 3) + (background->index << 1) + 1, - background->target1 | (background->target2 * 2) | (renderer->blendEffect * 4), - renderer->blendEffect == BLEND_ALPHA ? renderer->blda : renderer->bldy, renderer->bldb); - glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); - glEnableVertexAttribArray(0); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + if (yDiv > renderer->firstY) { + int end = yDiv - 1; + if (end > y) { + end = y; + } + glScissor(0, renderer->firstY * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, (end - renderer->firstY + 1) * renderer->scale); + glUniform2i(uniforms[GBA_GL_VS_LOC], end - renderer->firstY + 1, renderer->firstY); + glUniform2i(uniforms[GBA_GL_BG_OFFSET], background->x, yBase0 - renderer->firstY); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + if (y >= yDiv) { + int start = yDiv; + if (yDiv < renderer->firstY) { + start = renderer->firstY; + } + glScissor(0, start * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, (y - start + 1) * renderer->scale); + glUniform2i(uniforms[GBA_GL_VS_LOC], y - start + 1, start); + glUniform2i(uniforms[GBA_GL_BG_OFFSET], background->x, yBase1 - yDiv); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); } +void _prepareTransform(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, const GLuint* uniforms, int y) { + glScissor(0, renderer->firstY * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale * (y - renderer->firstY + 1)); + glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY); + glUniform2i(uniforms[GBA_GL_BG_RANGE], renderer->firstAffine, y); + + glActiveTexture(GL_TEXTURE0 + 2); + glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_AFFINE_2 + background->index - 2]); + glUniform1i(uniforms[GBA_GL_BG_TRANSFORM], 2); + _prepareBackground(renderer, background, uniforms); +} + void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { const struct GBAVideoGLShader* shader = &renderer->bgShader[background->overflow ? 2 : 3]; const GLuint* uniforms = shader->uniforms; - glBindFramebuffer(GL_FRAMEBUFFER, background->fbo); - glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale); - glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale); glUseProgram(shader->program); glBindVertexArray(shader->vao); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, renderer->vramTex); - glActiveTexture(GL_TEXTURE0 + 1); - glBindTexture(GL_TEXTURE_2D, renderer->paletteTex); - glUniform2i(uniforms[GBA_GL_VS_LOC], 1, y); - glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); - glUniform1i(uniforms[GBA_GL_BG_VRAM], 0); - glUniform1i(uniforms[GBA_GL_BG_PALETTE], 1); + _prepareTransform(renderer, background, uniforms, y); glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase); glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase); glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size); - glUniform4i(uniforms[GBA_GL_BG_INFLAGS], (background->priority << 3) + (background->index << 1) + 1, - background->target1 | (background->target2 * 2) | (renderer->blendEffect * 4), - renderer->blendEffect == BLEND_ALPHA ? renderer->blda : renderer->bldy, renderer->bldb); - if (renderer->scale > 1) { - glUniform2iv(uniforms[GBA_GL_BG_OFFSET], 4, (GLint[]) { - background->affine[0].sx, background->affine[0].sy, - background->affine[1].sx, background->affine[1].sy, - background->affine[2].sx, background->affine[2].sy, - background->affine[3].sx, background->affine[3].sy, - }); - glUniform2iv(uniforms[GBA_GL_BG_TRANSFORM], 4, (GLint[]) { - background->affine[0].dx, background->affine[0].dy, - background->affine[1].dx, background->affine[1].dy, - background->affine[2].dx, background->affine[2].dy, - background->affine[3].dx, background->affine[3].dy, - }); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); +} + +void GBAVideoGLRendererDrawBackgroundMode3(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { + const struct GBAVideoGLShader* shader = &renderer->bgShader[5]; + const GLuint* uniforms = shader->uniforms; + glBindFramebuffer(GL_FRAMEBUFFER, background->fbo); + glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale); + glUseProgram(shader->program); + glBindVertexArray(shader->vao); + _prepareTransform(renderer, background, uniforms, y); + glUniform1i(uniforms[GBA_GL_BG_CHARBASE], 0); + glUniform2i(uniforms[GBA_GL_BG_SIZE], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); +} + +void GBAVideoGLRendererDrawBackgroundMode4(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { + const struct GBAVideoGLShader* shader = &renderer->bgShader[4]; + const GLuint* uniforms = shader->uniforms; + glBindFramebuffer(GL_FRAMEBUFFER, background->fbo); + glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale); + glUseProgram(shader->program); + glBindVertexArray(shader->vao); + _prepareTransform(renderer, background, uniforms, y); + glUniform1i(uniforms[GBA_GL_BG_CHARBASE], GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt) ? 0xA000 : 0); + glUniform2i(uniforms[GBA_GL_BG_SIZE], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); +} + +void GBAVideoGLRendererDrawBackgroundMode5(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { + const struct GBAVideoGLShader* shader = &renderer->bgShader[5]; + const GLuint* uniforms = shader->uniforms; + glBindFramebuffer(GL_FRAMEBUFFER, background->fbo); + glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale); + glUseProgram(shader->program); + glBindVertexArray(shader->vao); + _prepareTransform(renderer, background, uniforms, y); + glUniform1i(uniforms[GBA_GL_BG_CHARBASE], GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt) ? 0x5000 : 0); + glUniform2i(uniforms[GBA_GL_BG_SIZE], 160, 128); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); +} + +static void _scissorWindow(int start, int end, int y, int lines, int scale) { + if (start > end) { + _scissorWindow(start, GBA_VIDEO_HORIZONTAL_PIXELS * scale, y, lines, scale); + _scissorWindow(0, end, y, lines, scale); + return; + } + glScissor(start, y, end - start, lines); + glClear(GL_COLOR_BUFFER_BIT); +} + +static void _scissorWindowN(const struct GBAVideoWindowRegion* region, const struct GBAVideoWindowRegion* v, const struct GBAVideoWindowRegion* y, int scale) { + int sdelta = region[0].start - region[1].start; + int edelta = region[0].end - region[1].end; + int maxDelta = 0; + if (sdelta > maxDelta) { + maxDelta = sdelta; + } else if (-sdelta > maxDelta) { + maxDelta = -sdelta; + } + if (edelta > maxDelta) { + maxDelta = edelta; + } else if (-edelta > maxDelta) { + maxDelta = -edelta; + } + int startY = y->start; + int endY = y->end; + if (startY < v->start) { + startY = v->start; + } + if (endY >= v->end) { + endY = v->end - 1; + } + if (!(sdelta | edelta) || maxDelta >= GBA_VIDEO_VERTICAL_PIXELS / 2) { + _scissorWindow(region[0].start * scale, region[0].end * scale, startY * scale, (endY - startY + 1) * scale, scale); } else { - glUniform2iv(uniforms[GBA_GL_BG_OFFSET], 4, (GLint[]) { - background->affine[0].sx, background->affine[0].sy, - background->affine[0].sx, background->affine[0].sy, - background->affine[0].sx, background->affine[0].sy, - background->affine[0].sx, background->affine[0].sy, - }); - glUniform2iv(uniforms[GBA_GL_BG_TRANSFORM], 4, (GLint[]) { - background->affine[0].dx, background->affine[0].dy, - background->affine[0].dx, background->affine[0].dy, - background->affine[0].dx, background->affine[0].dy, - background->affine[0].dx, background->affine[0].dy, - }); + int i; + for (i = 0; i < scale * (endY - startY + 1); ++i) { + int start = region[1].start * scale + sdelta * i; + int end = region[1].end * scale + edelta * i; + _scissorWindow(start, end, startY * scale + i, 1, scale); + } } - glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); - glEnableVertexAttribArray(0); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); } -static void _clearWindow(GBAWindowControl window, int start, int end, int y, int scale) { - glScissor(start, y, end - start, scale); - window = ~window & 0xFF; - glClearColor((window & 0xF) / 32.f, (window >> 4) / 32.f, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); +static void _clearWindow(GBAWindowControl window, int bldb, int bldy) { + window = ~window & 0x3F; + glClearColor(window / 128.f, bldb / 16.f, bldy / 16.f, 0); } void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y) { glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_WINDOW]); int dispcnt = ((renderer->dispcnt >> 8) & 0x1F) | 0x20; if (!(renderer->dispcnt & 0xE000)) { - _clearWindow(dispcnt, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, y * renderer->scale, renderer->scale); + _clearWindow(dispcnt, renderer->bldb, renderer->bldy); + _scissorWindow(0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->firstY * renderer->scale, (y - renderer->firstY + 1) * renderer->scale, renderer->scale); } else { - _clearWindow(renderer->winout & dispcnt, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, y * renderer->scale, renderer->scale); - if (GBARegisterDISPCNTIsWin1Enable(renderer->dispcnt) && y >= renderer->winN[1].v.start && y < renderer->winN[1].v.end) { - _clearWindow(renderer->winN[1].control & dispcnt, renderer->winN[1].h.start * renderer->scale, renderer->winN[1].h.end * renderer->scale, y * renderer->scale, renderer->scale); + _clearWindow(renderer->winout & dispcnt, renderer->bldb, renderer->bldy); + _scissorWindow(0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->firstY * renderer->scale, (y - renderer->firstY + 1) * renderer->scale, renderer->scale); + struct GBAVideoWindowRegion yRegion = { + y, + renderer->firstY + }; + if (GBARegisterDISPCNTIsWin1Enable(renderer->dispcnt) && y >= renderer->winN[1].v.start && renderer->firstY < renderer->winN[1].v.end) { + _clearWindow(renderer->winN[1].control & dispcnt, renderer->bldb, renderer->bldy); + _scissorWindowN(renderer->winN[1].h, &renderer->winN[1].v, &yRegion, renderer->scale); } - if (GBARegisterDISPCNTIsWin0Enable(renderer->dispcnt) && y >= renderer->winN[0].v.start && y < renderer->winN[0].v.end) { - _clearWindow(renderer->winN[0].control & dispcnt, renderer->winN[0].h.start * renderer->scale, renderer->winN[0].h.end * renderer->scale, y * renderer->scale, renderer->scale); + if (GBARegisterDISPCNTIsWin0Enable(renderer->dispcnt) && y >= renderer->winN[0].v.start && renderer->firstY < renderer->winN[0].v.end) { + _clearWindow(renderer->winN[0].control & dispcnt, renderer->bldb, renderer->bldy); + _scissorWindowN(renderer->winN[0].h, &renderer->winN[0].v, &yRegion, renderer->scale); } } }
@@ -17,19 +17,12 @@
#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \ SPRITE_YBASE_ ## DEPTH(inY); \ unsigned tileData; \ - if (outX % mosaicH) { \ - if (!inX && xOffset > 0) { \ - inX = mosaicH - (outX % mosaicH); \ - outX += mosaicH - (outX % mosaicH); \ - } else if (inX == width - xOffset) { \ - inX = mosaicH + (outX % mosaicH); \ - outX += mosaicH - (outX % mosaicH); \ - } \ - } \ for (; outX < condition; ++outX, inX += xOffset) { \ int localX = inX - xOffset * (outX % mosaicH); \ - if (localX < 0 || localX > width - 1) { \ - continue; \ + if (localX < 0) { \ + localX = 0; \ + } else if (localX > width - 1) {\ + localX = width - 1; \ } \ SPRITE_XBASE_ ## DEPTH(localX); \ SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \@@ -48,6 +41,31 @@ int localY = yAccum >> 8; \
\ if (localX & widthMask || localY & heightMask) { \ break; \ + } \ + \ + SPRITE_YBASE_ ## DEPTH(localY); \ + SPRITE_XBASE_ ## DEPTH(localX); \ + SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \ + } + +#define SPRITE_TRANSFORMED_MOSAIC_LOOP(DEPTH, TYPE) \ + unsigned tileData; \ + unsigned widthMask = ~(width - 1); \ + unsigned heightMask = ~(height - 1); \ + int localX = xAccum >> 8; \ + int localY = yAccum >> 8; \ + for (; outX < condition; ++outX, ++inX) { \ + renderer->spriteCyclesRemaining -= 2; \ + xAccum += mat.a; \ + yAccum += mat.c; \ + \ + if (outX % mosaicH == 0) { \ + localX = xAccum >> 8; \ + localY = yAccum >> 8; \ + } \ + \ + if (localX & widthMask || localY & heightMask) { \ + continue; \ } \ \ SPRITE_YBASE_ ## DEPTH(localY); \@@ -281,6 +299,13 @@ int inX = outX - x;
if (end < condition) { condition = end; } + int mosaicH = 1; + if (GBAObjAttributesAIsMosaic(sprite->a)) { + mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1; + if (condition % mosaicH) { + condition += mosaicH - (condition % mosaicH); + } + } int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1)) + (width << 7); int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1)) + (height << 7);@@ -340,6 +365,13 @@ } else if (!GBAObjAttributesAIs256Color(sprite->a)) {
palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4]; if (flags & FLAG_OBJWIN) { SPRITE_TRANSFORMED_LOOP(16, OBJWIN); + } else if (mosaicH > 1) { + if (objwinSlowPath) { + objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4]; + SPRITE_TRANSFORMED_MOSAIC_LOOP(16, NORMAL_OBJWIN); + } else { + SPRITE_TRANSFORMED_MOSAIC_LOOP(16, NORMAL); + } } else if (objwinSlowPath) { objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4]; SPRITE_TRANSFORMED_LOOP(16, NORMAL_OBJWIN);@@ -358,6 +390,12 @@ } else {
palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 8]; if (flags & FLAG_OBJWIN) { SPRITE_TRANSFORMED_LOOP(256, OBJWIN); + } else if (mosaicH > 1) { + if (objwinSlowPath) { + SPRITE_TRANSFORMED_MOSAIC_LOOP(256, NORMAL_OBJWIN); + } else { + SPRITE_TRANSFORMED_MOSAIC_LOOP(256, NORMAL); + } } else if (objwinSlowPath) { objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8]; SPRITE_TRANSFORMED_LOOP(256, NORMAL_OBJWIN);
@@ -942,11 +942,17 @@ for (i = 0; i < renderer->oamMax; ++i) {
struct GBAVideoRendererSprite* sprite = &renderer->sprites[i]; int localY = y; renderer->end = 0; + if ((y < sprite->y && (sprite->endY - 256 < 0 || y >= sprite->endY - 256)) || y >= sprite->endY) { + continue; + } if (GBAObjAttributesAIsMosaic(sprite->obj.a)) { localY = mosaicY; - } - if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) { - continue; + if (localY < sprite->y) { + localY = sprite->y; + } + if (localY >= sprite->endY) { + localY = sprite->endY - 1; + } } for (w = 0; w < renderer->nWindows; ++w) { if (renderer->spriteCyclesRemaining <= 0) {
@@ -16,6 +16,7 @@
static int cheatsSetup(void** state) { struct mCore* core = GBACoreCreate(); core->init(core); + mCoreInitConfig(core, NULL); core->cheatDevice(core); *state = core; return 0;@@ -26,6 +27,7 @@ if (!*state) {
return 0; } struct mCore* core = *state; + mCoreConfigDeinit(&core->config); core->deinit(core); return 0; }
@@ -27,7 +27,9 @@ M_TEST_DEFINE(reset) {
struct mCore* core = GBACoreCreate(); assert_non_null(core); assert_true(core->init(core)); + mCoreInitConfig(core, NULL); core->reset(core); + mCoreConfigDeinit(&core->config); core->deinit(core); }@@ -36,7 +38,9 @@ struct mCore* core = GBACoreCreate();
assert_non_null(core); assert_true(core->init(core)); assert_false(core->loadROM(core, NULL)); + mCoreInitConfig(core, NULL); core->reset(core); + mCoreConfigDeinit(&core->config); core->deinit(core); }
@@ -110,8 +110,7 @@
memset(video->palette, 0, sizeof(video->palette)); memset(video->oam.raw, 0, sizeof(video->oam.raw)); - video->renderer->deinit(video->renderer); - video->renderer->init(video->renderer); + video->renderer->reset(video->renderer); } void GBAVideoDeinit(struct GBAVideo* video) {
@@ -136,9 +136,11 @@ mGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, false, 0, 0);
glBindVertexArray(context->initialShader.vao); glBindBuffer(GL_ARRAY_BUFFER, context->vbo); + glEnableVertexAttribArray(context->initialShader.positionLocation); glVertexAttribPointer(context->initialShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); glBindVertexArray(context->finalShader.vao); glBindBuffer(GL_ARRAY_BUFFER, context->vbo); + glEnableVertexAttribArray(context->finalShader.positionLocation); glVertexAttribPointer(context->finalShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); glBindVertexArray(0);@@ -246,7 +248,7 @@ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, drawW, drawH, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, oldTex); } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST); glUseProgram(shader->program); glUniform1i(shader->texLocation, 0);@@ -303,7 +305,6 @@ glUniformMatrix4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x4);
break; } } - glEnableVertexAttribArray(shader->positionLocation); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glBindTexture(GL_TEXTURE_2D, shader->tex); }@@ -327,6 +328,7 @@ glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
_drawShader(context, &context->finalShader); glBindFramebuffer(GL_FRAMEBUFFER, 0); glUseProgram(0); + glBindVertexArray(0); } void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {@@ -463,8 +465,8 @@ glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(context->shaders[i].vao); glBindBuffer(GL_ARRAY_BUFFER, context->vbo); - glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(context->shaders[i].positionLocation); + glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); } glBindVertexArray(0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -67,6 +67,10 @@ if (!m_enabled) {
return; } + if (m_exclusive && !m_booleanFunction) { + active = true; + } + if (m_function && active) { m_function(); }
@@ -291,6 +291,7 @@ find_library(QTPCRE NAMES qtpcre2 qtpcre)
if(WIN32) list(APPEND QT_LIBRARIES qwindows dwmapi imm32 uxtheme Qt5EventDispatcherSupport Qt5FontDatabaseSupport Qt5ThemeSupport Qt5WindowsUIAutomationSupport) set_target_properties(Qt5::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};version;winmm;ssl;crypto;ws2_32;iphlpapi;crypt32;userenv;netapi32;wtsapi32") + set_target_properties(Qt5::Gui PROPERTIES INTERFACE_LINK_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) elseif(APPLE) find_package(Cups) find_package(Qt5PrintSupport)
@@ -86,6 +86,7 @@ controller->m_activeBuffer = &controller->m_buffers[0];
context->core->setVideoBuffer(context->core, reinterpret_cast<color_t*>(controller->m_activeBuffer->data()), controller->screenDimensions().width()); } + QMetaObject::invokeMethod(controller, "didReset"); controller->finishFrame(); };@@ -196,9 +197,6 @@ if (m_cacheSet) {
mCacheSetDeinit(m_cacheSet.get()); m_cacheSet.reset(); } - - mCoreConfigDeinit(&m_threadContext.core->config); - m_threadContext.core->deinit(m_threadContext.core); } const color_t* CoreController::drawContext() {@@ -368,7 +366,6 @@ setDebugger(nullptr);
#endif setPaused(false); mCoreThreadEnd(&m_threadContext); - emit stopping(); } void CoreController::reset() {@@ -445,13 +442,21 @@ emit rewound();
} void CoreController::setFastForward(bool enable) { + if (m_fastForward == enable) { + return; + } m_fastForward = enable; updateFastForward(); + emit fastForwardChanged(enable); } void CoreController::forceFastForward(bool enable) { + if (m_fastForwardForced == enable) { + return; + } m_fastForwardForced = enable; updateFastForward(); + emit fastForwardChanged(enable || m_fastForward); } void CoreController::loadState(int slot) {
@@ -99,6 +99,9 @@
void setInputController(InputController*); void setLogger(LogController*); + bool audioSync() const { return m_audioSync; } + bool videoSync() const { return m_videoSync; } + public slots: void start(); void stop();@@ -167,6 +170,7 @@ void stopping();
void crashed(const QString& errorMessage); void failed(); void frameAvailable(); + void didReset(); void stateLoaded(); void rewound();
@@ -20,7 +20,7 @@ Display* Display::create(QWidget* parent) {
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) QSurfaceFormat format; format.setSwapInterval(1); - format.setSwapBehavior(QSurfaceFormat::TripleBuffer); + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); #endif switch (s_driver) {
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2019 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@@ -34,17 +34,32 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
: Display(parent) , m_gl(nullptr) { + setAttribute(Qt::WA_NativeWindow); + windowHandle()->create(); + // This can spontaneously re-enter into this->resizeEvent before creation is done, so we // need to make sure it's initialized to nullptr before we assign the new object to it m_gl = new QOpenGLContext; m_gl->setFormat(format); m_gl->create(); - setAttribute(Qt::WA_NativeWindow); + + m_gl->makeCurrent(windowHandle()); +#if defined(_WIN32) && defined(USE_EPOXY) + epoxy_handle_external_wglMakeCurrent(); +#endif + int majorVersion = m_gl->format().majorVersion(); + QStringList extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' '); + m_gl->doneCurrent(); + + if (majorVersion == 2 && !extensions.contains("GL_ARB_framebuffer_object")) { + QSurfaceFormat newFormat(format); + newFormat.setVersion(1, 4); + m_gl->setFormat(newFormat); + m_gl->create(); + } + m_painter = new PainterGL(&m_videoProxy, windowHandle(), m_gl); setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions - - connect(&m_videoProxy, &VideoProxy::dataAvailable, &m_videoProxy, &VideoProxy::processData); - connect(&m_videoProxy, &VideoProxy::eventPosted, &m_videoProxy, &VideoProxy::handleEvent); } DisplayGL::~DisplayGL() {@@ -98,6 +113,7 @@ #else
messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatio()); #endif resizePainter(); + connect(m_context.get(), &CoreController::didReset, this, &DisplayGL::resizeContext); } void DisplayGL::stopDrawing() {@@ -249,10 +265,9 @@ }
#endif m_backend->swap = [](VideoBackend* v) { PainterGL* painter = static_cast<PainterGL*>(v->user); - if (!painter->m_gl->isValid()) { - return; + if (!painter->m_swapTimer.isActive()) { + QMetaObject::invokeMethod(&painter->m_swapTimer, "start"); } - painter->m_gl->swapBuffers(painter->m_gl->surface()); }; m_backend->init(m_backend, 0);@@ -270,6 +285,10 @@
for (int i = 0; i < 2; ++i) { m_free.append(new uint32_t[256 * 512]); } + + m_swapTimer.setInterval(16); + m_swapTimer.setSingleShot(true); + connect(&m_swapTimer, &QTimer::timeout, this, &PainterGL::swap); } PainterGL::~PainterGL() {@@ -305,18 +324,8 @@ if (!m_context) {
return; } - if (!m_active) { - m_gl->makeCurrent(m_surface); -#if defined(_WIN32) && defined(USE_EPOXY) - epoxy_handle_external_wglMakeCurrent(); -#endif - } - QSize size = m_context->screenDimensions(); m_backend->setDimensions(m_backend, size.width(), size.height()); - if (!m_active) { - m_gl->doneCurrent(); - } } void PainterGL::setMessagePainter(MessagePainter* messagePainter) {@@ -368,26 +377,21 @@ if (m_queue.isEmpty()) {
return; } + if (m_needsUnlock) { + QTimer::singleShot(0, this, &PainterGL::draw); + return; + } + if (mCoreSyncWaitFrameStart(&m_context->thread()->impl->sync) || !m_queue.isEmpty()) { dequeue(); - mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync); - m_painter.begin(m_window); - performDraw(); - m_painter.end(); - m_backend->swap(m_backend); - if (!m_delayTimer.isValid()) { - m_delayTimer.start(); - } else if (m_gl->format().swapInterval()) { - while (m_delayTimer.elapsed() < 15) { - QThread::usleep(100); - } - m_delayTimer.restart(); + forceDraw(); + if (m_context->thread()->impl->sync.videoFrameWait) { + m_needsUnlock = true; + } else { + mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync); } } else { mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync); - } - if (!m_queue.isEmpty()) { - QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection); } }@@ -404,6 +408,9 @@ m_started = false;
dequeueAll(); m_backend->clear(m_backend); m_backend->swap(m_backend); + if (m_videoProxy) { + m_videoProxy->reset(); + } m_gl->doneCurrent(); m_gl->moveToThread(m_surface->thread()); m_context.reset();@@ -428,6 +435,30 @@ m_painter.endNativePainting();
if (m_messagePainter) { m_messagePainter->paint(&m_painter); } + m_frameReady = true; +} + +void PainterGL::swap() { + if (!m_gl->isValid()) { + return; + } + if (m_frameReady) { + m_gl->swapBuffers(m_surface); + m_gl->makeCurrent(m_surface); +#if defined(_WIN32) && defined(USE_EPOXY) + epoxy_handle_external_wglMakeCurrent(); +#endif + m_frameReady = false; + } + if (m_needsUnlock) { + mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync); + m_needsUnlock = false; + } + if (!m_queue.isEmpty()) { + QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection); + } else { + m_swapTimer.start(); + } } void PainterGL::enqueue(const uint32_t* backing) {@@ -480,12 +511,6 @@ if (!supportsShaders()) {
return; } #ifdef BUILD_GLES2 - if (!m_active) { - m_gl->makeCurrent(m_surface); -#if defined(_WIN32) && defined(USE_EPOXY) - epoxy_handle_external_wglMakeCurrent(); -#endif - } if (m_shader.passes) { mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend)); mGLES2ShaderFree(&m_shader);@@ -493,9 +518,6 @@ }
mGLES2ShaderLoad(&m_shader, dir); if (m_started) { mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses); - } - if (!m_active) { - m_gl->doneCurrent(); } #endif }@@ -505,18 +527,9 @@ if (!supportsShaders()) {
return; } #ifdef BUILD_GLES2 - if (!m_active) { - m_gl->makeCurrent(m_surface); -#if defined(_WIN32) && defined(USE_EPOXY) - epoxy_handle_external_wglMakeCurrent(); -#endif - } if (m_shader.passes) { mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend)); mGLES2ShaderFree(&m_shader); - } - if (!m_active) { - m_gl->doneCurrent(); } #endif }
@@ -16,13 +16,13 @@ #define GLdouble GLdouble
#endif #endif -#include <QElapsedTimer> #include <QOpenGLContext> #include <QList> #include <QMouseEvent> #include <QPainter> #include <QQueue> #include <QThread> +#include <QTimer> #include "VideoProxy.h"@@ -105,6 +105,9 @@ VideoShader* shaders();
int glTex(); +private slots: + void swap(); + private: void performDraw(); void dequeue();@@ -125,7 +128,9 @@ VideoShader m_shader{};
VideoBackend* m_backend = nullptr; QSize m_size; MessagePainter* m_messagePainter = nullptr; - QElapsedTimer m_delayTimer; + QTimer m_swapTimer{this}; + bool m_needsUnlock = false; + bool m_frameReady = false; VideoProxy* m_videoProxy; };
@@ -127,6 +127,9 @@ }
void LogController::setLogFile(const QString& file) { m_logStream.reset(); + if (file.isEmpty()) { + return; + } m_logFile = std::make_unique<QFile>(file); m_logFile->open(QIODevice::Append | QIODevice::Text); m_logStream = std::make_unique<QTextStream>(m_logFile.get());
@@ -104,7 +104,7 @@ <property name="minimum">
<number>1</number> </property> <property name="maximum"> - <number>4</number> + <number>8</number> </property> </widget> </item>
@@ -59,7 +59,7 @@ <property name="minimum">
<number>1</number> </property> <property name="maximum"> - <number>6</number> + <number>8</number> </property> </widget> </item>
@@ -57,6 +57,10 @@ m_ui.muteFf->setChecked(e);
} }); + connect(m_ui.nativeGB, &QAbstractButton::pressed, [this]() { + m_ui.fpsTarget->setValue(double(GBA_ARM7TDMI_FREQUENCY) / double(VIDEO_TOTAL_LENGTH)); + }); + if (m_ui.savegamePath->text().isEmpty()) { m_ui.savegameSameDir->setChecked(true); }@@ -364,7 +368,6 @@ saveSetting("sampleRate", m_ui.sampleRate);
saveSetting("videoSync", m_ui.videoSync); saveSetting("audioSync", m_ui.audioSync); saveSetting("frameskip", m_ui.frameskip); - saveSetting("fpsTarget", m_ui.fpsTarget); saveSetting("autofireThreshold", m_ui.autofireThreshold); saveSetting("lockAspectRatio", m_ui.lockAspectRatio); saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling);@@ -395,12 +398,19 @@ saveSetting("logToFile", m_ui.logToFile);
saveSetting("logToStdout", m_ui.logToStdout); saveSetting("logFile", m_ui.logFile); saveSetting("useDiscordPresence", m_ui.useDiscordPresence); - saveSetting("audioHle", m_ui.audioHle); + saveSetting("gba.audioHle", m_ui.audioHle); if (m_ui.fastForwardUnbounded->isChecked()) { saveSetting("fastForwardRatio", "-1"); } else { saveSetting("fastForwardRatio", m_ui.fastForwardRatio); + } + + double nativeFps = double(GBA_ARM7TDMI_FREQUENCY) / double(VIDEO_TOTAL_LENGTH); + if (nativeFps - m_ui.fpsTarget->value() < 0.0001) { + m_controller->setOption("fpsTarget", QVariant(nativeFps)); + } else { + saveSetting("fpsTarget", m_ui.fpsTarget); } switch (m_ui.idleOptimization->currentIndex() + IDLE_LOOP_IGNORE) {@@ -549,7 +559,7 @@ loadSetting("logToFile", m_ui.logToFile);
loadSetting("logToStdout", m_ui.logToStdout); loadSetting("logFile", m_ui.logFile); loadSetting("useDiscordPresence", m_ui.useDiscordPresence); - loadSetting("audioHle", m_ui.audioHle); + loadSetting("gba.audioHle", m_ui.audioHle); loadSetting("videoScale", m_ui.videoScale, 1); m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt());
@@ -6,8 +6,8 @@ <property name="geometry">
<rect> <x>0</x> <y>0</y> - <width>790</width> - <height>686</height> + <width>849</width> + <height>753</height> </rect> </property> <property name="sizePolicy">@@ -40,7 +40,7 @@ </sizepolicy>
</property> <property name="maximumSize"> <size> - <width>200</width> + <width>180</width> <height>16777215</height> </size> </property>@@ -399,21 +399,21 @@ </widget>
</item> </layout> </item> - <item row="9" column="0" colspan="2"> + <item row="10" column="0" colspan="2"> <widget class="Line" name="line_5"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> - <item row="10" column="0"> + <item row="11" column="0"> <widget class="QLabel" name="label_2"> <property name="text"> <string>Sync:</string> </property> </widget> </item> - <item row="10" column="1"> + <item row="11" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_10"> <item> <widget class="QCheckBox" name="videoSync">@@ -431,24 +431,31 @@ </widget>
</item> </layout> </item> - <item row="11" column="1"> + <item row="12" column="1"> <widget class="QCheckBox" name="lockAspectRatio"> <property name="text"> <string>Lock aspect ratio</string> </property> </widget> </item> - <item row="12" column="1"> + <item row="13" column="1"> <widget class="QCheckBox" name="lockIntegerScaling"> <property name="text"> <string>Force integer scaling</string> </property> </widget> </item> - <item row="13" column="1"> + <item row="14" column="1"> <widget class="QCheckBox" name="resampleVideo"> <property name="text"> <string>Bilinear filtering</string> + </property> + </widget> + </item> + <item row="9" column="1"> + <widget class="QPushButton" name="nativeGB"> + <property name="text"> + <string>Native (59.7275)</string> </property> </widget> </item>@@ -894,7 +901,7 @@ <property name="minimum">
<number>1</number> </property> <property name="maximum"> - <number>13</number> + <number>16</number> </property> </widget> </item>@@ -903,9 +910,6 @@ </widget>
</item> <item row="1" column="1"> <widget class="QCheckBox" name="audioHle"> - <property name="enabled"> - <bool>false</bool> - </property> <property name="text"> <string>XQ GBA audio (experimental)</string> </property>
@@ -126,7 +126,7 @@ <property name="minimum">
<number>1</number> </property> <property name="maximum"> - <number>4</number> + <number>8</number> </property> </widget> </item>
@@ -24,6 +24,9 @@
m_logger.d.writeData = &callback<bool, const void*, size_t>::func<&VideoProxy::writeData>; m_logger.d.readData = &callback<bool, void*, size_t, bool>::func<&VideoProxy::readData>; m_logger.d.postEvent = &callback<void, enum mVideoLoggerEvent>::func<&VideoProxy::postEvent>; + + connect(this, &VideoProxy::dataAvailable, this, &VideoProxy::processData); + connect(this, &VideoProxy::eventPosted, this, &VideoProxy::handleEvent); } void VideoProxy::attach(CoreController* controller) {@@ -41,7 +44,10 @@ RingFIFOInit(&m_dirtyQueue, 0x80000);
} void VideoProxy::reset() { + m_mutex.lock(); RingFIFOClear(&m_dirtyQueue); + m_toThreadCond.wakeAll(); + m_mutex.unlock(); } void VideoProxy::deinit() {@@ -92,11 +98,13 @@ m_mutex.unlock();
} void VideoProxy::wait() { + m_mutex.lock(); while (RingFIFOSize(&m_dirtyQueue)) { emit dataAvailable(); m_toThreadCond.wakeAll(); m_fromThreadCond.wait(&m_mutex, 1); } + m_mutex.unlock(); } void VideoProxy::wake(int y) {
@@ -30,11 +30,11 @@ void eventPosted(int);
public slots: void processData(); + void reset(); void handleEvent(int); private: void init(); - void reset(); void deinit(); bool writeData(const void* data, size_t length);
@@ -648,8 +648,12 @@ m_config->setOption("height", GBA_VIDEO_VERTICAL_PIXELS * m_savedScale);
m_config->setOption("width", GBA_VIDEO_HORIZONTAL_PIXELS * m_savedScale); } saveConfig(); - m_display.reset(); - QMainWindow::closeEvent(event); + if (m_controller) { + event->ignore(); + m_pendingClose = true; + } else { + m_display.reset(); + } } void Window::focusInEvent(QFocusEvent*) {@@ -784,7 +788,6 @@ resizeFrame(size * m_savedScale);
} attachWidget(m_display.get()); setMouseTracking(true); - m_display->setMinimumSize(size); setFocus(); #ifndef Q_OS_MAC@@ -792,7 +795,6 @@ if (isFullScreen()) {
menuBar()->hide(); } #endif - m_display->startDrawing(m_controller); reloadAudioDriver(); multiplayerChanged();@@ -843,6 +845,11 @@ }
void Window::gameStopped() { m_controller.reset(); + m_display->stopDrawing(); + if (m_pendingClose) { + m_display.reset(); + close(); + } for (Action* action : m_platformActions) { action->setEnabled(true); }@@ -929,7 +936,6 @@ m_shaderView.reset();
m_shaderView = std::make_unique<ShaderSelector>(m_display.get(), m_config); #endif - connect(this, &Window::shutdown, m_display.get(), &Display::stopDrawing); connect(m_display.get(), &Display::hideCursor, [this]() { if (static_cast<QStackedLayout*>(m_screenWidget->layout())->currentWidget() == m_display.get()) { m_screenWidget->setCursor(Qt::BlankCursor);@@ -954,8 +960,6 @@ }
#endif if (m_controller) { - m_display->setMinimumSize(m_controller->screenDimensions()); - connect(m_controller.get(), &CoreController::stopping, m_display.get(), &Display::stopDrawing); connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::resizeContext); connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::forceDraw); connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw);@@ -966,13 +970,12 @@ connect(m_controller.get(), &CoreController::statusPosted, m_display.get(), &Display::showMessage);
attachWidget(m_display.get()); m_display->startDrawing(m_controller); - } else { + } #ifdef M_CORE_GB - m_display->setMinimumSize(GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS); + m_display->setMinimumSize(GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS); #elif defined(M_CORE_GBA) - m_display->setMinimumSize(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); + m_display->setMinimumSize(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); #endif - } } void Window::reloadAudioDriver() {@@ -991,6 +994,7 @@ m_audioProcessor->setBufferSamples(opts->audioBuffers);
m_audioProcessor->requestSampleRate(opts->sampleRate); m_audioProcessor->start(); connect(m_controller.get(), &CoreController::stopping, m_audioProcessor.get(), &AudioProcessor::stop); + connect(m_controller.get(), &CoreController::fastForwardChanged, m_audioProcessor.get(), &AudioProcessor::inputParametersChanged); } void Window::tryMakePortable() {@@ -1372,7 +1376,7 @@ #endif
m_actions.addMenu(tr("Audio/&Video"), "av"); m_actions.addMenu(tr("Frame size"), "frame", "av"); - for (int i = 1; i <= 6; ++i) { + for (int i = 1; i <= 8; ++i) { Action* setSize = m_actions.addAction(tr("%1×").arg(QString::number(i)), QString("frame.%1x").arg(QString::number(i)), [this, i]() { Action* setSize = m_frameSizes[i]; showNormal();@@ -1739,6 +1743,9 @@ void Window::setController(CoreController* controller, const QString& fname) {
if (!controller) { return; } + if (m_pendingClose) { + return; + } if (m_controller) { m_controller->stop();@@ -1771,12 +1778,14 @@ m_controller = std::shared_ptr<CoreController>(controller);
m_inputController.recalibrateAxes(); m_controller->setInputController(&m_inputController); m_controller->setLogger(&m_log); + m_display->startDrawing(m_controller); connect(this, &Window::shutdown, [this]() { if (!m_controller) { return; } m_controller->stop(); + disconnect(m_controller.get(), &CoreController::started, this, &Window::gameStarted); }); connect(m_controller.get(), &CoreController::started, this, &Window::gameStarted);@@ -1804,7 +1813,6 @@ connect(m_controller.get(), &CoreController::unpaused, [this]() {
emit paused(false); }); - connect(m_controller.get(), &CoreController::stopping, m_display.get(), &Display::stopDrawing); connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::resizeContext); connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::forceDraw); connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw);
@@ -207,6 +207,7 @@ bool m_wasOpened = false;
QString m_pendingPatch; QString m_pendingState; bool m_pendingPause = false; + bool m_pendingClose = false; bool m_hitUnimplementedBiosCall;
@@ -30,9 +30,15 @@ <translation>{projectName} desea agradecer a los siguientes patrocinadores desde Patreon:</translation>
</message> <message> <location filename="../AboutScreen.ui" line="86"/> + <source>© 2013 – 2019 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 – 2019 Jeffrey Pfau, licenciado bajo la Mozilla Public License, versión 2.0 +Game Boy Advance es una marca registrada de Nintendo Co., Ltd.</translation> + </message> + <message> <source>© 2013 – 2018 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 – 2018 Jeffrey Pfau, licenciado bajo la Mozilla Public License, versión 2.0 + <translation type="vanished">© 2013 – 2018 Jeffrey Pfau, licenciado bajo la Mozilla Public License, versión 2.0 Game Boy Advance es una marca registrada de Nintendo Co., Ltd.</translation> </message> <message>@@ -126,6 +132,84 @@ <translation>0x00 (00)</translation>
</message> </context> <context> + <name>BattleChipView</name> + <message> + <location filename="../BattleChipView.ui" line="14"/> + <source>BattleChip Gate</source> + <translation>BattleChip Gate</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="67"/> + <source>Chip name</source> + <translation>Nombre del chip</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="76"/> + <source>Insert</source> + <translation>Insertar</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="87"/> + <source>Save</source> + <translation>Guardar</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="94"/> + <source>Load</source> + <translation>Cargar</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="114"/> + <source>Add</source> + <translation>Agregar</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="121"/> + <source>Remove</source> + <translation>Quitar</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="138"/> + <source>Gate type</source> + <translation>Tipo de Gate</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="145"/> + <source>Ba&ttleChip Gate</source> + <translation>Ba&ttleChip Gate</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="155"/> + <source>Progress &Gate</source> + <translation>Progress &Gate</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="162"/> + <source>Beast &Link Gate</source> + <translation>Beast &Link Gate</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="180"/> + <source>Inserted</source> + <translation>Insertado</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="194"/> + <source>Chip ID</source> + <translation>ID de chip</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="207"/> + <source>Update Chip data</source> + <translation>Actualizar datos del chip</translation> + </message> + <message> + <location filename="../BattleChipView.ui" line="219"/> + <source>Show advanced</source> + <translation>Mostrar ajustes avanzados</translation> + </message> +</context> +<context> <name>CheatsView</name> <message> <location filename="../CheatsView.ui" line="14"/>@@ -201,12 +285,12 @@ </message>
<message> <location filename="../GIFView.ui" line="101"/> <source>Frameskip</source> - <translation>Salto de cuadros</translation> + <translation>Salto</translation> </message> <message> <location filename="../GIFView.ui" line="115"/> <source>Frame delay (ms)</source> - <translation>Retraso entre cuadros (ms)</translation> + <translation>Retraso (ms)</translation> </message> <message> <location filename="../GIFView.ui" line="122"/>@@ -219,7 +303,7 @@ <name>IOViewer</name>
<message> <location filename="../IOViewer.ui" line="14"/> <source>I/O Viewer</source> - <translation>Visor de I/O</translation> + <translation>Visor de E/S</translation> </message> <message> <location filename="../IOViewer.ui" line="26"/>@@ -417,7 +501,7 @@ </message>
<message> <location filename="../LogView.ui" line="28"/> <source>Debug</source> - <translation>Depuración (Debug)</translation> + <translation>Debug</translation> </message> <message> <location filename="../LogView.ui" line="38"/>@@ -427,12 +511,12 @@ </message>
<message> <location filename="../LogView.ui" line="48"/> <source>Info</source> - <translation>Información (Info)</translation> + <translation>Info</translation> </message> <message> <location filename="../LogView.ui" line="58"/> <source>Warning</source> - <translation>Advertencia (Warning)</translation> + <translation>Warning</translation> </message> <message> <location filename="../LogView.ui" line="68"/>@@ -457,7 +541,7 @@ </message>
<message> <location filename="../LogView.ui" line="130"/> <source>Max Lines</source> - <translation>Máximo de líneas</translation> + <translation>Líneas max.</translation> </message> </context> <context>@@ -631,57 +715,69 @@ <source>Set Alignment:</source>
<translation>Alinear a:</translation> </message> <message> + <source>1 Byte</source> + <translation type="vanished">1 byte</translation> + </message> + <message> + <source>2 Bytes</source> + <translation type="vanished">2 bytes</translation> + </message> + <message> + <source>4 Bytes</source> + <translation type="vanished">4 bytes</translation> + </message> + <message> <location filename="../MemoryView.ui" line="101"/> - <source>1 Byte</source> - <translation>1 byte</translation> + <source>&1 Byte</source> + <translation></translation> </message> <message> <location filename="../MemoryView.ui" line="124"/> - <source>2 Bytes</source> - <translation>2 bytes</translation> + <source>&2 Bytes</source> + <translation></translation> </message> <message> <location filename="../MemoryView.ui" line="144"/> - <source>4 Bytes</source> - <translation>4 bytes</translation> + <source>&4 Bytes</source> + <translation></translation> </message> <message> - <location filename="../MemoryView.ui" line="181"/> + <location filename="../MemoryView.ui" line="180"/> <source>Unsigned Integer:</source> <translation>Entero sin signo:</translation> </message> <message> - <location filename="../MemoryView.ui" line="199"/> + <location filename="../MemoryView.ui" line="198"/> <source>Signed Integer:</source> <translation>Entero con signo:</translation> </message> <message> - <location filename="../MemoryView.ui" line="217"/> + <location filename="../MemoryView.ui" line="216"/> <source>String:</source> - <translation>Cadena:</translation> + <translation>Cadena de texto:</translation> </message> <message> - <location filename="../MemoryView.ui" line="231"/> + <location filename="../MemoryView.ui" line="230"/> <source>Load TBL</source> <translation>Cargar TBL</translation> </message> <message> - <location filename="../MemoryView.ui" line="244"/> + <location filename="../MemoryView.ui" line="243"/> <source>Copy Selection</source> <translation>Copiar selección</translation> </message> <message> - <location filename="../MemoryView.ui" line="251"/> + <location filename="../MemoryView.ui" line="250"/> <source>Paste</source> <translation>Pegar</translation> </message> <message> - <location filename="../MemoryView.ui" line="271"/> + <location filename="../MemoryView.ui" line="270"/> <source>Save Selection</source> <translation>Guardar selección</translation> </message> <message> - <location filename="../MemoryView.ui" line="278"/> + <location filename="../MemoryView.ui" line="277"/> <source>Load</source> <translation>Cargar</translation> </message>@@ -717,7 +813,7 @@ </message>
<message> <location filename="../ObjView.ui" line="93"/> <source>Transform</source> - <translation>Transformación</translation> + <translation>Transform</translation> </message> <message> <location filename="../ObjView.ui" line="113"/>@@ -936,7 +1032,7 @@ </message>
<message> <location filename="../OverrideView.ui" line="258"/> <source>Super Game Boy (SGB)</source> - <translation></translation> + <translation>Super Game Boy (SGB)</translation> </message> <message> <location filename="../OverrideView.ui" line="263"/>@@ -1204,6 +1300,35 @@ <translation>No se puede iniciar un procesador de audio sin entrada</translation>
</message> </context> <context> + <name>QGBA::BattleChipView</name> + <message> + <location filename="../BattleChipView.cpp" line="114"/> + <source>BattleChip data missing</source> + <translation>Datos del BattleChip no encontrados</translation> + </message> + <message> + <location filename="../BattleChipView.cpp" line="115"/> + <source>BattleChip data is missing. BattleChip Gates will still work, but some graphics will be missing. Would you like to download the data now?</source> + <translation>Faltan los datos de BattleChip. Las BattleChip Gates seguirán funcionando, pero faltarán algunos gráficos. ¿Quieres descargar los datos ahora?</translation> + </message> + <message> + <location filename="../BattleChipView.cpp" line="181"/> + <location filename="../BattleChipView.cpp" line="200"/> + <source>Select deck file</source> + <translation>Elegir archivo de baraja</translation> + </message> + <message> + <location filename="../BattleChipView.cpp" line="212"/> + <source>Incompatible deck</source> + <translation>Baraja incompatible</translation> + </message> + <message> + <location filename="../BattleChipView.cpp" line="213"/> + <source>The selected deck is not compatible with this Chip Gate</source> + <translation>La baraja seleccionada no es compatible con esta Chip Gate</translation> + </message> +</context> +<context> <name>QGBA::CheatsModel</name> <message> <location filename="../CheatsModel.cpp" line="54"/>@@ -1249,22 +1374,22 @@ </context>
<context> <name>QGBA::CoreController</name> <message> - <location filename="../CoreController.cpp" line="557"/> + <location filename="../CoreController.cpp" line="568"/> <source>Failed to open save file: %1</source> <translation>Error al abrir el archivo de guardado: %1</translation> </message> <message> - <location filename="../CoreController.cpp" line="586"/> + <location filename="../CoreController.cpp" line="597"/> <source>Failed to open game file: %1</source> <translation>Error al abrir el archivo del juego: %1</translation> </message> <message> - <location filename="../CoreController.cpp" line="651"/> + <location filename="../CoreController.cpp" line="662"/> <source>Failed to open snapshot file for reading: %1</source> <translation>Error al leer del archivo de captura: %1</translation> </message> <message> - <location filename="../CoreController.cpp" line="667"/> + <location filename="../CoreController.cpp" line="678"/> <source>Failed to open snapshot file for writing: %1</source> <translation>Error al escribir al archivo de captura: %1</translation> </message>@@ -1278,11 +1403,19 @@ <translation>Error al abrir el archivo del juego: %1</translation>
</message> </context> <context> + <name>QGBA::GBAApp</name> + <message> + <location filename="../GBAApp.cpp" line="69"/> + <source>Enable Discord Rich Presence</source> + <translation>Habilitar Rich Presence en Discord</translation> + </message> +</context> +<context> <name>QGBA::GBAKeyEditor</name> <message> <location filename="../GBAKeyEditor.cpp" line="68"/> <source>Clear Button</source> - <translation>Limpiar botón</translation> + <translation>Limpiar botones</translation> </message> <message> <location filename="../GBAKeyEditor.cpp" line="80"/>@@ -2790,39 +2923,93 @@ <translation>Espacio %1</translation>
</message> </context> <context> + <name>QGBA::LogConfigModel</name> + <message> + <location filename="../LogConfigModel.cpp" line="79"/> + <location filename="../LogConfigModel.cpp" line="100"/> + <source>Default</source> + <translation>Por defecto</translation> + </message> + <message> + <location filename="../LogConfigModel.cpp" line="81"/> + <source>Fatal</source> + <translation>Fatal</translation> + </message> + <message> + <location filename="../LogConfigModel.cpp" line="83"/> + <source>Error</source> + <translation>Error</translation> + </message> + <message> + <location filename="../LogConfigModel.cpp" line="85"/> + <source>Warning</source> + <translation>Advertencia (Warning)</translation> + </message> + <message> + <location filename="../LogConfigModel.cpp" line="87"/> + <source>Info</source> + <translation>Información (Info)</translation> + </message> + <message> + <location filename="../LogConfigModel.cpp" line="89"/> + <source>Debug</source> + <translation>Depuración (Debug)</translation> + </message> + <message> + <location filename="../LogConfigModel.cpp" line="91"/> + <source>Stub</source> + <translation>Stub</translation> + </message> + <message> + <location filename="../LogConfigModel.cpp" line="93"/> + <source>Game Error</source> + <translation>Error de juego</translation> + </message> +</context> +<context> <name>QGBA::LogController</name> <message> - <location filename="../LogController.cpp" line="65"/> + <location filename="../LogController.cpp" line="62"/> + <source>[%1] %2: %3</source> + <translation>[%1] %2: %3</translation> + </message> + <message> + <location filename="../LogController.cpp" line="73"/> + <source>An error occurred</source> + <translation>Ocurrió un error</translation> + </message> + <message> + <location filename="../LogController.cpp" line="145"/> <source>DEBUG</source> <translation>DEPURACIÓN</translation> </message> <message> - <location filename="../LogController.cpp" line="67"/> + <location filename="../LogController.cpp" line="147"/> <source>STUB</source> <translation>STUB</translation> </message> <message> - <location filename="../LogController.cpp" line="69"/> + <location filename="../LogController.cpp" line="149"/> <source>INFO</source> <translation>INFORMACIÓN</translation> </message> <message> - <location filename="../LogController.cpp" line="71"/> + <location filename="../LogController.cpp" line="151"/> <source>WARN</source> <translation>ADVERTENCIA</translation> </message> <message> - <location filename="../LogController.cpp" line="73"/> + <location filename="../LogController.cpp" line="153"/> <source>ERROR</source> <translation>ERROR</translation> </message> <message> - <location filename="../LogController.cpp" line="75"/> + <location filename="../LogController.cpp" line="155"/> <source>FATAL</source> <translation>FATAL</translation> </message> <message> - <location filename="../LogController.cpp" line="77"/> + <location filename="../LogController.cpp" line="157"/> <source>GAME ERROR</source> <translation>ERROR DE JUEGO</translation> </message>@@ -2830,47 +3017,47 @@ </context>
<context> <name>QGBA::MapView</name> <message> - <location filename="../MapView.cpp" line="84"/> + <location filename="../MapView.cpp" line="87"/> <source>Map Addr.</source> <translation>Dir de mapa</translation> </message> <message> - <location filename="../MapView.cpp" line="85"/> + <location filename="../MapView.cpp" line="88"/> <source>Mirror</source> <translation>Espejar</translation> </message> <message> - <location filename="../MapView.cpp" line="113"/> + <location filename="../MapView.cpp" line="116"/> <source>None</source> <translation>Ninguno</translation> </message> <message> - <location filename="../MapView.cpp" line="115"/> + <location filename="../MapView.cpp" line="118"/> <source>Both</source> <translation>Ambos</translation> </message> <message> - <location filename="../MapView.cpp" line="117"/> + <location filename="../MapView.cpp" line="120"/> <source>Horizontal</source> <translation>Horizontal</translation> </message> <message> - <location filename="../MapView.cpp" line="119"/> + <location filename="../MapView.cpp" line="122"/> <source>Vertical</source> <translation>Vertical</translation> </message> <message> - <location filename="../MapView.cpp" line="172"/> + <location filename="../MapView.cpp" line="194"/> <source>Export map</source> <translation>Exportar mapa</translation> </message> <message> - <location filename="../MapView.cpp" line="173"/> + <location filename="../MapView.cpp" line="195"/> <source>Portable Network Graphics (*.png)</source> <translation>Gráficos de red portátiles (*.png)</translation> </message> <message> - <location filename="../MapView.cpp" line="176"/> + <location filename="../MapView.cpp" line="198"/> <source>Failed to open output PNG file: %1</source> <translation>Error al abrir el archivo PNG de salida: %1</translation> </message>@@ -2976,54 +3163,54 @@ </context>
<context> <name>QGBA::ObjView</name> <message> - <location filename="../ObjView.cpp" line="145"/> - <location filename="../ObjView.cpp" line="239"/> + <location filename="../ObjView.cpp" line="148"/> + <location filename="../ObjView.cpp" line="242"/> <source>0x%0</source> <translation>0x%0</translation> </message> <message> - <location filename="../ObjView.cpp" line="156"/> + <location filename="../ObjView.cpp" line="159"/> <source>Off</source> <translation>No</translation> </message> <message> - <location filename="../ObjView.cpp" line="161"/> + <location filename="../ObjView.cpp" line="164"/> <source>Normal</source> <translation>Normal</translation> </message> <message> - <location filename="../ObjView.cpp" line="164"/> + <location filename="../ObjView.cpp" line="167"/> <source>Trans</source> <translation>Trans</translation> </message> <message> - <location filename="../ObjView.cpp" line="167"/> + <location filename="../ObjView.cpp" line="170"/> <source>OBJWIN</source> <translation>OBJWIN</translation> </message> <message> - <location filename="../ObjView.cpp" line="170"/> + <location filename="../ObjView.cpp" line="173"/> <source>Invalid</source> <translation>Inválido</translation> </message> <message> - <location filename="../ObjView.cpp" line="246"/> - <location filename="../ObjView.cpp" line="247"/> + <location filename="../ObjView.cpp" line="249"/> + <location filename="../ObjView.cpp" line="250"/> <source>N/A</source> <translation>n/d</translation> </message> <message> - <location filename="../ObjView.cpp" line="253"/> + <location filename="../ObjView.cpp" line="256"/> <source>Export sprite</source> <translation>Exportar sprite</translation> </message> <message> - <location filename="../ObjView.cpp" line="254"/> + <location filename="../ObjView.cpp" line="257"/> <source>Portable Network Graphics (*.png)</source> <translation>Portable Network Graphics (*.png)</translation> </message> <message> - <location filename="../ObjView.cpp" line="257"/> + <location filename="../ObjView.cpp" line="260"/> <source>Failed to open output PNG file: %1</source> <translation>Error al abrir el archivo PNG de salida: %1</translation> </message>@@ -3107,59 +3294,59 @@ </context>
<context> <name>QGBA::SettingsView</name> <message> - <location filename="../SettingsView.cpp" line="142"/> - <location filename="../SettingsView.cpp" line="184"/> + <location filename="../SettingsView.cpp" line="144"/> + <location filename="../SettingsView.cpp" line="187"/> <source>Qt Multimedia</source> <translation>Qt Multimedia</translation> </message> <message> - <location filename="../SettingsView.cpp" line="149"/> + <location filename="../SettingsView.cpp" line="151"/> <source>SDL</source> <translation>SDL</translation> </message> <message> - <location filename="../SettingsView.cpp" line="157"/> + <location filename="../SettingsView.cpp" line="159"/> <source>Software (Qt)</source> <translation>Software (Qt)</translation> </message> <message> - <location filename="../SettingsView.cpp" line="163"/> + <location filename="../SettingsView.cpp" line="165"/> <source>OpenGL</source> <translation>OpenGL</translation> </message> <message> - <location filename="../SettingsView.cpp" line="170"/> + <location filename="../SettingsView.cpp" line="172"/> <source>OpenGL (force version 1.x)</source> <translation>OpenGL (forzar versión 1.x)</translation> </message> <message> - <location filename="../SettingsView.cpp" line="178"/> + <location filename="../SettingsView.cpp" line="180"/> <source>None (Still Image)</source> <translation>Nada (imagen estática)</translation> </message> <message> - <location filename="../SettingsView.cpp" line="259"/> + <location filename="../SettingsView.cpp" line="271"/> <source>Keyboard</source> <translation>Teclado</translation> </message> <message> - <location filename="../SettingsView.cpp" line="268"/> + <location filename="../SettingsView.cpp" line="280"/> <source>Controllers</source> <translation>Controladores</translation> </message> <message> - <location filename="../SettingsView.cpp" line="300"/> + <location filename="../SettingsView.cpp" line="324"/> <source>Shortcuts</source> <translation>Atajos de teclado</translation> </message> <message> - <location filename="../SettingsView.cpp" line="312"/> - <location filename="../SettingsView.cpp" line="322"/> + <location filename="../SettingsView.cpp" line="336"/> + <location filename="../SettingsView.cpp" line="346"/> <source>Shaders</source> <translation>Shaders</translation> </message> <message> - <location filename="../SettingsView.cpp" line="329"/> + <location filename="../SettingsView.cpp" line="353"/> <source>Select BIOS</source> <translation>Seleccionar BIOS</translation> </message>@@ -3200,17 +3387,32 @@ </context>
<context> <name>QGBA::ShortcutController</name> <message> - <location filename="../ShortcutController.cpp" line="64"/> + <source>Action</source> + <translation type="vanished">Acción</translation> + </message> + <message> + <source>Keyboard</source> + <translation type="vanished">Teclado</translation> + </message> + <message> + <source>Gamepad</source> + <translation type="vanished">Mando</translation> + </message> +</context> +<context> + <name>QGBA::ShortcutModel</name> + <message> + <location filename="../ShortcutModel.cpp" line="67"/> <source>Action</source> <translation>Acción</translation> </message> <message> - <location filename="../ShortcutController.cpp" line="66"/> + <location filename="../ShortcutModel.cpp" line="69"/> <source>Keyboard</source> <translation>Teclado</translation> </message> <message> - <location filename="../ShortcutController.cpp" line="68"/> + <location filename="../ShortcutModel.cpp" line="71"/> <source>Gamepad</source> <translation>Mando</translation> </message>@@ -3236,108 +3438,108 @@ </context>
<context> <name>QGBA::Window</name> <message> - <location filename="../Window.cpp" line="274"/> + <location filename="../Window.cpp" line="292"/> <source>Game Boy Advance ROMs (%1)</source> <translation>ROMs de Game Boy Advance (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="291"/> + <location filename="../Window.cpp" line="309"/> <source>Game Boy ROMs (%1)</source> <translation>ROMs de Game Boy (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="295"/> + <location filename="../Window.cpp" line="313"/> <source>All ROMs (%1)</source> <translation>Todas las ROMs (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="296"/> + <location filename="../Window.cpp" line="314"/> <source>%1 Video Logs (*.mvl)</source> <translation>Video-registros de %1 (*.mvl)</translation> </message> <message> - <location filename="../Window.cpp" line="311"/> + <location filename="../Window.cpp" line="329"/> <source>Archives (%1)</source> <translation>Contenedores (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="316"/> - <location filename="../Window.cpp" line="324"/> - <location filename="../Window.cpp" line="351"/> + <location filename="../Window.cpp" line="334"/> + <location filename="../Window.cpp" line="342"/> + <location filename="../Window.cpp" line="369"/> <source>Select ROM</source> <translation>Seleccionar ROM</translation> </message> <message> - <location filename="../Window.cpp" line="342"/> + <location filename="../Window.cpp" line="360"/> <source>Select folder</source> <translation>Seleccionar carpeta</translation> </message> <message> - <location filename="../Window.cpp" line="359"/> + <location filename="../Window.cpp" line="377"/> <source>Game Boy Advance save files (%1)</source> <translation>Archivos de guardado de Game Boy Advance (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="360"/> - <location filename="../Window.cpp" line="421"/> - <location filename="../Window.cpp" line="428"/> + <location filename="../Window.cpp" line="378"/> + <location filename="../Window.cpp" line="439"/> + <location filename="../Window.cpp" line="446"/> <source>Select save</source> <translation>Seleccionar guardado</translation> </message> <message> - <location filename="../Window.cpp" line="368"/> + <location filename="../Window.cpp" line="386"/> <source>mGBA savestate files (%1)</source> <translation>Archivos de estado de guardado de mGBA (%1)</translation> </message> <message> - <location filename="../Window.cpp" line="370"/> - <location filename="../Window.cpp" line="375"/> + <location filename="../Window.cpp" line="388"/> + <location filename="../Window.cpp" line="393"/> <source>Select savestate</source> <translation>Elegir estado de guardado</translation> </message> <message> - <location filename="../Window.cpp" line="397"/> + <location filename="../Window.cpp" line="415"/> <source>Select patch</source> <translation>Seleccionar parche</translation> </message> <message> - <location filename="../Window.cpp" line="397"/> + <location filename="../Window.cpp" line="415"/> <source>Patches (*.ips *.ups *.bps)</source> <translation>Parches (*.ips *.ups *.bps)</translation> </message> <message> - <location filename="../Window.cpp" line="414"/> + <location filename="../Window.cpp" line="432"/> <source>Select image</source> <translation>Seleccionar imagen</translation> </message> <message> - <location filename="../Window.cpp" line="414"/> + <location filename="../Window.cpp" line="432"/> <source>Image file (*.png *.gif *.jpg *.jpeg);;All files (*)</source> <translation>Archivo de imagen (*.png *.gif *.jpg *.jpeg);;Todos los archivos (*)</translation> </message> <message> - <location filename="../Window.cpp" line="421"/> - <location filename="../Window.cpp" line="428"/> + <location filename="../Window.cpp" line="439"/> + <location filename="../Window.cpp" line="446"/> <source>GameShark saves (*.sps *.xps)</source> <translation>Guardados de GameShark (*.sps *.xps)</translation> </message> <message> - <location filename="../Window.cpp" line="453"/> + <location filename="../Window.cpp" line="473"/> <source>Select video log</source> <translation>Seleccionar video-registro</translation> </message> <message> - <location filename="../Window.cpp" line="453"/> + <location filename="../Window.cpp" line="473"/> <source>Video logs (*.mvl)</source> <translation>Video-registros (*.mvl)</translation> </message> <message> - <location filename="../Window.cpp" line="799"/> + <location filename="../Window.cpp" line="831"/> <source>Crash</source> <translation>Error fatal</translation> </message> <message> - <location filename="../Window.cpp" line="800"/> + <location filename="../Window.cpp" line="832"/> <source>The game has crashed with the following error: %1</source>@@ -3346,433 +3548,424 @@
%1</translation> </message> <message> - <location filename="../Window.cpp" line="808"/> + <location filename="../Window.cpp" line="839"/> <source>Couldn't Load</source> <translation>No se pudo cargar</translation> </message> <message> - <location filename="../Window.cpp" line="809"/> + <location filename="../Window.cpp" line="840"/> <source>Could not load game. Are you sure it's in the correct format?</source> <translation>No se pudo cargar el juego. ¿Estás seguro de que está en el formato correcto?</translation> </message> <message> - <location filename="../Window.cpp" line="822"/> + <location filename="../Window.cpp" line="853"/> <source>Unimplemented BIOS call</source> <translation>Llamada a BIOS no implementada</translation> </message> <message> - <location filename="../Window.cpp" line="823"/> + <location filename="../Window.cpp" line="854"/> <source>This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience.</source> <translation>Este juego utiliza una llamada al BIOS que no se ha implementado. Utiliza el BIOS oficial para obtener la mejor experiencia.</translation> </message> <message> - <location filename="../Window.cpp" line="905"/> + <location filename="../Window.cpp" line="937"/> <source>Really make portable?</source> <translation>¿Hacer "portable"?</translation> </message> <message> - <location filename="../Window.cpp" line="906"/> + <location filename="../Window.cpp" line="938"/> <source>This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?</source> <translation>Esto hará que el emulador cargue su configuración desde el mismo directorio que el ejecutable. ¿Quieres continuar?</translation> </message> <message> - <location filename="../Window.cpp" line="914"/> + <location filename="../Window.cpp" line="946"/> <source>Restart needed</source> <translation>Reinicio necesario</translation> </message> <message> - <location filename="../Window.cpp" line="915"/> + <location filename="../Window.cpp" line="947"/> <source>Some changes will not take effect until the emulator is restarted.</source> <translation>Algunos cambios no surtirán efecto hasta que se reinicie el emulador.</translation> </message> <message> - <location filename="../Window.cpp" line="963"/> + <location filename="../Window.cpp" line="995"/> <source> - Player %1 of %2</source> <translation> - Jugador %1 de %2</translation> </message> <message> - <location filename="../Window.cpp" line="974"/> + <location filename="../Window.cpp" line="1006"/> <source>%1 - %2</source> <translation>%1 - %2</translation> </message> <message> - <location filename="../Window.cpp" line="976"/> + <location filename="../Window.cpp" line="1008"/> <source>%1 - %2 - %3</source> <translation>%1 - %2 - %3</translation> </message> <message> - <location filename="../Window.cpp" line="978"/> + <location filename="../Window.cpp" line="1010"/> <source>%1 - %2 (%3 fps) - %4</source> <translation>%1 - %2 (%3 fps) - %4</translation> </message> <message> - <location filename="../Window.cpp" line="1014"/> + <location filename="../Window.cpp" line="1048"/> <source>&File</source> <translation>&Archivo</translation> </message> <message> - <location filename="../Window.cpp" line="1017"/> + <location filename="../Window.cpp" line="1050"/> <source>Load &ROM...</source> <translation>Cargar &ROM...</translation> </message> <message> - <location filename="../Window.cpp" line="1020"/> + <location filename="../Window.cpp" line="1053"/> <source>Load ROM in archive...</source> <translation>Cargar ROM desde contenedor...</translation> </message> <message> - <location filename="../Window.cpp" line="1022"/> + <location filename="../Window.cpp" line="1054"/> <source>Add folder to library...</source> <translation>Agregar carpeta a la biblioteca...</translation> </message> <message> - <location filename="../Window.cpp" line="1026"/> + <location filename="../Window.cpp" line="1057"/> <source>Load alternate save...</source> <translation>Cargar guardado alternativo...</translation> </message> <message> - <location filename="../Window.cpp" line="1031"/> + <location filename="../Window.cpp" line="1060"/> <source>Load temporary save...</source> <translation>Cargar guardado temporal...</translation> </message> <message> - <location filename="../Window.cpp" line="1036"/> + <location filename="../Window.cpp" line="1064"/> <source>Load &patch...</source> <translation>Cargar &parche...</translation> </message> <message> - <location filename="../Window.cpp" line="1039"/> + <location filename="../Window.cpp" line="1067"/> <source>Boot BIOS</source> <translation>Arrancar BIOS</translation> </message> <message> - <location filename="../Window.cpp" line="1046"/> + <location filename="../Window.cpp" line="1072"/> <source>Replace ROM...</source> <translation>Reemplazar ROM...</translation> </message> <message> - <location filename="../Window.cpp" line="1048"/> + <location filename="../Window.cpp" line="1074"/> <source>ROM &info...</source> <translation>&Información de la ROM...</translation> </message> <message> - <location filename="../Window.cpp" line="1053"/> + <location filename="../Window.cpp" line="1076"/> <source>Recent</source> <translation>Recientes</translation> </message> <message> - <location filename="../Window.cpp" line="1057"/> + <location filename="../Window.cpp" line="1079"/> <source>Make portable</source> <translation>Hacer "portable"</translation> </message> <message> - <location filename="../Window.cpp" line="1061"/> + <location filename="../Window.cpp" line="1082"/> <source>&Load state</source> <translation>Ca&rgar estado</translation> </message> <message> - <location filename="../Window.cpp" line="1062"/> + <location filename="../Window.cpp" line="1164"/> + <source>About...</source> + <translation>Acerca de...</translation> + </message> + <message> <source>F10</source> - <translation>F10</translation> + <translation type="vanished">F10</translation> </message> <message> - <location filename="../Window.cpp" line="1068"/> + <location filename="../Window.cpp" line="1087"/> <source>Load state file...</source> <translation>Cargar archivo de estado...</translation> </message> <message> - <location filename="../Window.cpp" line="1074"/> + <location filename="../Window.cpp" line="1092"/> <source>&Save state</source> <translation>Guardar e&stado</translation> </message> <message> - <location filename="../Window.cpp" line="1075"/> <source>Shift+F10</source> - <translation>Shift+F10</translation> + <translation type="vanished">Shift+F10</translation> </message> <message> - <location filename="../Window.cpp" line="1081"/> + <location filename="../Window.cpp" line="1097"/> <source>Save state file...</source> <translation>Guardar archivo de estado...</translation> </message> <message> - <location filename="../Window.cpp" line="1087"/> + <location filename="../Window.cpp" line="1102"/> <source>Quick load</source> <translation>Cargado rápido</translation> </message> <message> - <location filename="../Window.cpp" line="1088"/> + <location filename="../Window.cpp" line="1103"/> <source>Quick save</source> <translation>Guardado rápido</translation> </message> <message> - <location filename="../Window.cpp" line="1092"/> + <location filename="../Window.cpp" line="1105"/> <source>Load recent</source> <translation>Cargar reciente</translation> </message> <message> - <location filename="../Window.cpp" line="1100"/> + <location filename="../Window.cpp" line="1110"/> <source>Save recent</source> <translation>Guardar reciente</translation> </message> <message> - <location filename="../Window.cpp" line="1111"/> + <location filename="../Window.cpp" line="1118"/> <source>Undo load state</source> <translation>Deshacer cargar estado</translation> </message> <message> - <location filename="../Window.cpp" line="1112"/> <source>F11</source> - <translation>F11</translation> + <translation type="vanished">F11</translation> </message> <message> - <location filename="../Window.cpp" line="1120"/> + <location filename="../Window.cpp" line="1123"/> <source>Undo save state</source> <translation>Deshacer guardar estado</translation> </message> <message> - <location filename="../Window.cpp" line="1121"/> <source>Shift+F11</source> - <translation>Shift+F11</translation> + <translation type="vanished">Shift+F11</translation> </message> <message> - <location filename="../Window.cpp" line="1134"/> - <location filename="../Window.cpp" line="1143"/> + <location filename="../Window.cpp" line="1132"/> + <location filename="../Window.cpp" line="1137"/> <source>State &%1</source> <translation>Estado &%1</translation> </message> <message> - <location filename="../Window.cpp" line="1135"/> <source>F%1</source> - <translation>F%1</translation> + <translation type="vanished">F%1</translation> </message> <message> - <location filename="../Window.cpp" line="1144"/> <source>Shift+F%1</source> - <translation>Shift+F%1</translation> + <translation type="vanished">Shift+F%1</translation> </message> <message> - <location filename="../Window.cpp" line="1154"/> + <location filename="../Window.cpp" line="1144"/> <source>Load camera image...</source> <translation>Cargar imagen para la cámara...</translation> </message> <message> - <location filename="../Window.cpp" line="1160"/> + <location filename="../Window.cpp" line="1148"/> <source>Import GameShark Save</source> <translation>Importar guardado de GameShark</translation> </message> <message> - <location filename="../Window.cpp" line="1166"/> + <location filename="../Window.cpp" line="1151"/> <source>Export GameShark Save</source> <translation>Exportar guardado de GameShark</translation> </message> <message> - <location filename="../Window.cpp" line="1174"/> + <location filename="../Window.cpp" line="1156"/> <source>New multiplayer window</source> <translation>Nueva ventana multijugador</translation> </message> <message> - <location filename="../Window.cpp" line="1184"/> <source>About</source> - <translation>Acerca de</translation> + <translation type="vanished">Acerca de</translation> </message> <message> - <location filename="../Window.cpp" line="1189"/> + <location filename="../Window.cpp" line="1167"/> <source>E&xit</source> <translation>Salir (&X)</translation> </message> <message> - <location filename="../Window.cpp" line="1192"/> + <location filename="../Window.cpp" line="1170"/> <source>&Emulation</source> <translation>&Emulación</translation> </message> <message> - <location filename="../Window.cpp" line="1194"/> + <location filename="../Window.cpp" line="1171"/> <source>&Reset</source> <translation>&Reinicializar</translation> </message> <message> - <location filename="../Window.cpp" line="1195"/> <source>Ctrl+R</source> - <translation>Ctrl+R</translation> + <translation type="vanished">Ctrl+R</translation> </message> <message> - <location filename="../Window.cpp" line="1202"/> + <location filename="../Window.cpp" line="1175"/> <source>Sh&utdown</source> <translation>Apagar (&U)</translation> </message> <message> - <location filename="../Window.cpp" line="1210"/> + <location filename="../Window.cpp" line="1180"/> <source>Yank game pak</source> <translation>Tirar del cartucho</translation> </message> <message> - <location filename="../Window.cpp" line="1220"/> + <location filename="../Window.cpp" line="1187"/> <source>&Pause</source> <translation>&Pausar</translation> </message> <message> - <location filename="../Window.cpp" line="1223"/> <source>Ctrl+P</source> - <translation>Ctrl+P</translation> + <translation type="vanished">Ctrl+P</translation> </message> <message> - <location filename="../Window.cpp" line="1236"/> + <location filename="../Window.cpp" line="1196"/> <source>&Next frame</source> <translation>Cuadro siguie&nte</translation> </message> <message> - <location filename="../Window.cpp" line="1237"/> <source>Ctrl+N</source> - <translation>Ctrl+N</translation> + <translation type="vanished">Ctrl+N</translation> </message> <message> - <location filename="../Window.cpp" line="1254"/> + <location filename="../Window.cpp" line="1202"/> <source>Fast forward (held)</source> <translation>Avance rápido (mantener)</translation> </message> <message> - <location filename="../Window.cpp" line="1256"/> + <location filename="../Window.cpp" line="1208"/> <source>&Fast forward</source> <translation>&Avance rápido</translation> </message> <message> - <location filename="../Window.cpp" line="1259"/> <source>Shift+Tab</source> - <translation>Shift+Tab</translation> + <translation type="vanished">Shift+Tab</translation> </message> <message> - <location filename="../Window.cpp" line="1266"/> + <location filename="../Window.cpp" line="1212"/> <source>Fast forward speed</source> <translation>Velocidad de avance rápido</translation> </message> <message> - <location filename="../Window.cpp" line="1271"/> + <location filename="../Window.cpp" line="1217"/> <source>Unbounded</source> <translation>Sin límite</translation> </message> <message> - <location filename="../Window.cpp" line="1275"/> + <location filename="../Window.cpp" line="1221"/> <source>%0x</source> <translation>%0x</translation> </message> <message> - <location filename="../Window.cpp" line="1287"/> + <location filename="../Window.cpp" line="1225"/> <source>Rewind (held)</source> <translation>Rebobinar (mantener)</translation> </message> <message> - <location filename="../Window.cpp" line="1289"/> + <location filename="../Window.cpp" line="1232"/> <source>Re&wind</source> <translation>Re&bobinar</translation> </message> <message> - <location filename="../Window.cpp" line="1290"/> <source>~</source> - <translation>~</translation> + <translation type="vanished">~</translation> </message> <message> - <location filename="../Window.cpp" line="1298"/> + <location filename="../Window.cpp" line="1237"/> <source>Step backwards</source> <translation>Paso hacia atrás</translation> </message> <message> - <location filename="../Window.cpp" line="1299"/> <source>Ctrl+B</source> - <translation>Ctrl+B</translation> + <translation type="vanished">Ctrl+B</translation> </message> <message> - <location filename="../Window.cpp" line="1308"/> + <location filename="../Window.cpp" line="1243"/> <source>Sync to &video</source> <translation>Sincronizar a &video</translation> </message> <message> - <location filename="../Window.cpp" line="1315"/> + <location filename="../Window.cpp" line="1250"/> <source>Sync to &audio</source> <translation>Sincronizar a au&dio</translation> </message> <message> - <location filename="../Window.cpp" line="1323"/> + <location filename="../Window.cpp" line="1258"/> <source>Solar sensor</source> <translation>Sensor solar</translation> </message> <message> - <location filename="../Window.cpp" line="1325"/> + <location filename="../Window.cpp" line="1259"/> <source>Increase solar level</source> <translation>Subir nivel</translation> </message> <message> - <location filename="../Window.cpp" line="1329"/> + <location filename="../Window.cpp" line="1260"/> <source>Decrease solar level</source> <translation>Bajar nivel</translation> </message> <message> - <location filename="../Window.cpp" line="1333"/> + <location filename="../Window.cpp" line="1261"/> <source>Brightest solar level</source> <translation>Más claro</translation> </message> <message> - <location filename="../Window.cpp" line="1337"/> + <location filename="../Window.cpp" line="1264"/> <source>Darkest solar level</source> <translation>Más oscuro</translation> </message> <message> - <location filename="../Window.cpp" line="1343"/> + <location filename="../Window.cpp" line="1270"/> <source>Brightness %1</source> <translation>Brillo %1</translation> </message> <message> - <location filename="../Window.cpp" line="1350"/> + <location filename="../Window.cpp" line="1289"/> <source>Audio/&Video</source> <translation>Audio/&video</translation> </message> <message> - <location filename="../Window.cpp" line="1352"/> + <location filename="../Window.cpp" line="1290"/> <source>Frame size</source> <translation>Tamaño del cuadro</translation> </message> <message> - <location filename="../Window.cpp" line="1355"/> <source>%1x</source> - <translation>%1x</translation> + <translation type="vanished">%1x</translation> </message> <message> - <location filename="../Window.cpp" line="1383"/> + <location filename="../Window.cpp" line="1317"/> <source>Toggle fullscreen</source> <translation>Pantalla completa</translation> </message> <message> - <location filename="../Window.cpp" line="1386"/> + <location filename="../Window.cpp" line="1320"/> <source>Lock aspect ratio</source> <translation>Bloquear proporción de aspecto</translation> </message> <message> - <location filename="../Window.cpp" line="1398"/> + <location filename="../Window.cpp" line="1332"/> <source>Force integer scaling</source> <translation>Forzar escala a enteros</translation> </message> <message> - <location filename="../Window.cpp" line="1410"/> + <location filename="../Window.cpp" line="1344"/> <source>Bilinear filtering</source> <translation>Filtro bilineal</translation> </message> <message> - <location filename="../Window.cpp" line="1418"/> + <location filename="../Window.cpp" line="1352"/> <source>Frame&skip</source> <translation>&Salto de cuadros</translation> </message> <message> - <location filename="../Window.cpp" line="1431"/> + <location filename="../Window.cpp" line="1365"/> <source>Mute</source> <translation>Silenciar</translation> </message> <message> - <location filename="../Window.cpp" line="1438"/> + <location filename="../Window.cpp" line="1371"/> <source>FPS target</source> <translation>Objetivo de FPS</translation> </message> <message> - <location filename="../Window.cpp" line="1446"/> + <location filename="../Window.cpp" line="1379"/> <source>Native (59.7275)</source> <translation>Nativo (59,7275)</translation> </message>@@ -3809,192 +4002,214 @@ <source>240</source>
<translation type="vanished">240</translation> </message> <message> - <location filename="../Window.cpp" line="1461"/> + <location filename="../Window.cpp" line="1394"/> <source>Take &screenshot</source> <translation>Tomar pan&tallazo</translation> </message> <message> - <location filename="../Window.cpp" line="1462"/> + <location filename="../Window.cpp" line="1396"/> <source>F12</source> <translation>F12</translation> </message> <message> - <location filename="../Window.cpp" line="1471"/> <source>Record output...</source> - <translation>Grabar salida...</translation> + <translation type="vanished">Grabar salida...</translation> </message> <message> - <location filename="../Window.cpp" line="1478"/> + <location filename="../Window.cpp" line="1404"/> <source>Record GIF...</source> <translation>Grabar GIF...</translation> </message> <message> - <location filename="../Window.cpp" line="1483"/> <source>Record video log...</source> - <translation>Grabar video-registro...</translation> + <translation type="vanished">Grabar video-registro...</translation> </message> <message> - <location filename="../Window.cpp" line="1488"/> <source>Stop video log</source> - <translation>Detener video-registro</translation> + <translation type="vanished">Detener video-registro</translation> </message> <message> - <location filename="../Window.cpp" line="1496"/> + <location filename="../Window.cpp" line="1276"/> <source>Game Boy Printer...</source> <translation>Game Boy Printer...</translation> </message> <message> - <location filename="../Window.cpp" line="1508"/> + <location filename="../Window.cpp" line="1285"/> + <source>BattleChip Gate...</source> + <translation>BattleChip Gate...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1292"/> + <source>%1×</source> + <translation>%1×</translation> + </message> + <message> + <location filename="../Window.cpp" line="1400"/> + <source>Record A/V...</source> + <translation>Grabar A/V...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1408"/> <source>Video layers</source> <translation>Capas de video</translation> </message> <message> - <location filename="../Window.cpp" line="1511"/> + <location filename="../Window.cpp" line="1409"/> <source>Audio channels</source> <translation>Canales de audio</translation> </message> <message> - <location filename="../Window.cpp" line="1514"/> + <location filename="../Window.cpp" line="1411"/> <source>Adjust layer placement...</source> <translation>Ajustar ubicación de capas...</translation> </message> <message> - <location filename="../Window.cpp" line="1519"/> + <location filename="../Window.cpp" line="1413"/> <source>&Tools</source> <translation>Herramien&tas</translation> </message> <message> - <location filename="../Window.cpp" line="1521"/> + <location filename="../Window.cpp" line="1414"/> <source>View &logs...</source> <translation>Ver re&gistros...</translation> </message> <message> - <location filename="../Window.cpp" line="1525"/> + <location filename="../Window.cpp" line="1416"/> <source>Game &overrides...</source> <translation>Ajustes específic&os por juego...</translation> </message> <message> - <location filename="../Window.cpp" line="1539"/> + <location filename="../Window.cpp" line="1428"/> <source>Game &Pak sensors...</source> <translation>Sensores del Game &Pak...</translation> </message> <message> - <location filename="../Window.cpp" line="1552"/> + <location filename="../Window.cpp" line="1439"/> <source>&Cheats...</source> <translation>Tru&cos...</translation> </message> <message> - <location filename="../Window.cpp" line="1558"/> + <location filename="../Window.cpp" line="1442"/> <source>Settings...</source> <translation>Ajustes...</translation> </message> <message> - <location filename="../Window.cpp" line="1564"/> + <location filename="../Window.cpp" line="1446"/> <source>Open debugger console...</source> <translation>Abrir consola de depuración...</translation> </message> <message> - <location filename="../Window.cpp" line="1570"/> + <location filename="../Window.cpp" line="1448"/> <source>Start &GDB server...</source> <translation>Iniciar servidor &GDB...</translation> </message> <message> - <location filename="../Window.cpp" line="1578"/> + <location filename="../Window.cpp" line="1454"/> <source>View &palette...</source> <translation>Ver &paleta...</translation> </message> <message> - <location filename="../Window.cpp" line="1583"/> + <location filename="../Window.cpp" line="1455"/> <source>View &sprites...</source> <translation>Ver &sprites...</translation> </message> <message> - <location filename="../Window.cpp" line="1588"/> + <location filename="../Window.cpp" line="1456"/> <source>View &tiles...</source> <translation>Ver &tiles...</translation> </message> <message> - <location filename="../Window.cpp" line="1593"/> + <location filename="../Window.cpp" line="1457"/> <source>View &map...</source> <translation>Ver &mapa...</translation> </message> <message> - <location filename="../Window.cpp" line="1598"/> + <location filename="../Window.cpp" line="1458"/> <source>View memory...</source> <translation>Ver memoria...</translation> </message> <message> - <location filename="../Window.cpp" line="1603"/> + <location filename="../Window.cpp" line="1459"/> <source>Search memory...</source> <translation>Buscar memoria...</translation> </message> <message> - <location filename="../Window.cpp" line="1609"/> + <location filename="../Window.cpp" line="1462"/> <source>View &I/O registers...</source> <translation>Ver registros &I/O...</translation> </message> <message> - <location filename="../Window.cpp" line="1693"/> + <location filename="../Window.cpp" line="1467"/> + <source>Record debug video log...</source> + <translation>Grabar registro de depuración de video...</translation> + </message> + <message> + <location filename="../Window.cpp" line="1468"/> + <source>Stop debug video log</source> + <translation>Detener registro de depuración de video</translation> + </message> + <message> + <location filename="../Window.cpp" line="1549"/> <source>Exit fullscreen</source> <translation>Salir de pantalla completa</translation> </message> <message> - <location filename="../Window.cpp" line="1706"/> + <location filename="../Window.cpp" line="1551"/> <source>GameShark Button (held)</source> <translation>Botón GameShark (mantener)</translation> </message> <message> - <location filename="../Window.cpp" line="1708"/> + <location filename="../Window.cpp" line="1557"/> <source>Autofire</source> <translation>Disparo automático</translation> </message> <message> - <location filename="../Window.cpp" line="1715"/> + <location filename="../Window.cpp" line="1558"/> <source>Autofire A</source> <translation>Disparo automático A</translation> </message> <message> - <location filename="../Window.cpp" line="1721"/> + <location filename="../Window.cpp" line="1563"/> <source>Autofire B</source> <translation>Disparo automático B</translation> </message> <message> - <location filename="../Window.cpp" line="1727"/> + <location filename="../Window.cpp" line="1568"/> <source>Autofire L</source> <translation>Disparo automático L</translation> </message> <message> - <location filename="../Window.cpp" line="1733"/> + <location filename="../Window.cpp" line="1573"/> <source>Autofire R</source> <translation>Disparo automático R</translation> </message> <message> - <location filename="../Window.cpp" line="1739"/> + <location filename="../Window.cpp" line="1578"/> <source>Autofire Start</source> <translation>Disparo automático Start</translation> </message> <message> - <location filename="../Window.cpp" line="1745"/> + <location filename="../Window.cpp" line="1583"/> <source>Autofire Select</source> <translation>Disparo automático Select</translation> </message> <message> - <location filename="../Window.cpp" line="1751"/> + <location filename="../Window.cpp" line="1588"/> <source>Autofire Up</source> <translation>Disparo automático Arriba</translation> </message> <message> - <location filename="../Window.cpp" line="1757"/> + <location filename="../Window.cpp" line="1593"/> <source>Autofire Right</source> <translation>Disparo automático Derecha</translation> </message> <message> - <location filename="../Window.cpp" line="1763"/> + <location filename="../Window.cpp" line="1598"/> <source>Autofire Down</source> <translation>Disparo automático Abajo</translation> </message> <message> - <location filename="../Window.cpp" line="1769"/> + <location filename="../Window.cpp" line="1603"/> <source>Autofire Left</source> <translation>Disparo automático Izquierda</translation> </message>@@ -4158,505 +4373,588 @@ <source>Settings</source>
<translation>Ajustes</translation> </message> <message> - <location filename="../SettingsView.ui" line="45"/> + <location filename="../SettingsView.ui" line="52"/> <source>Audio/Video</source> <translation>Audio/video</translation> </message> <message> - <location filename="../SettingsView.ui" line="50"/> + <location filename="../SettingsView.ui" line="57"/> <source>Interface</source> <translation>Interfaz</translation> </message> <message> - <location filename="../SettingsView.ui" line="55"/> + <location filename="../SettingsView.ui" line="62"/> <source>Emulation</source> <translation>Emulación</translation> </message> <message> - <location filename="../SettingsView.ui" line="60"/> + <location filename="../SettingsView.ui" line="67"/> + <source>Enhancements</source> + <translation>Mejoras</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="72"/> <source>BIOS</source> <translation>BIOS</translation> </message> <message> - <location filename="../SettingsView.ui" line="65"/> + <location filename="../SettingsView.ui" line="77"/> <source>Paths</source> <translation>Rutas</translation> </message> <message> - <location filename="../SettingsView.ui" line="70"/> + <location filename="../SettingsView.ui" line="82"/> + <source>Logging</source> + <translation>Registros</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="87"/> <source>Game Boy</source> <translation>Game Boy</translation> </message> <message> - <location filename="../SettingsView.ui" line="95"/> + <location filename="../SettingsView.ui" line="105"/> <source>Audio driver:</source> <translation>Sistema de audio:</translation> </message> <message> - <location filename="../SettingsView.ui" line="112"/> + <location filename="../SettingsView.ui" line="122"/> <source>Audio buffer:</source> <translation>Búfer de audio:</translation> </message> <message> - <location filename="../SettingsView.ui" line="124"/> - <location filename="../SettingsView.ui" line="146"/> + <location filename="../SettingsView.ui" line="134"/> + <location filename="../SettingsView.ui" line="156"/> <source>1536</source> <translation>1536</translation> </message> <message> - <location filename="../SettingsView.ui" line="131"/> + <location filename="../SettingsView.ui" line="141"/> <source>512</source> <translation>512</translation> </message> <message> - <location filename="../SettingsView.ui" line="136"/> + <location filename="../SettingsView.ui" line="146"/> <source>768</source> <translation>768</translation> </message> <message> - <location filename="../SettingsView.ui" line="141"/> + <location filename="../SettingsView.ui" line="151"/> <source>1024</source> <translation>1024</translation> </message> <message> - <location filename="../SettingsView.ui" line="151"/> + <location filename="../SettingsView.ui" line="161"/> <source>2048</source> <translation>2048</translation> </message> <message> - <location filename="../SettingsView.ui" line="156"/> + <location filename="../SettingsView.ui" line="166"/> <source>3072</source> <translation>3072</translation> </message> <message> - <location filename="../SettingsView.ui" line="161"/> + <location filename="../SettingsView.ui" line="171"/> <source>4096</source> <translation>4096</translation> </message> <message> - <location filename="../SettingsView.ui" line="169"/> + <location filename="../SettingsView.ui" line="179"/> <source>samples</source> <translation>muestras</translation> </message> <message> - <location filename="../SettingsView.ui" line="178"/> + <location filename="../SettingsView.ui" line="188"/> <source>Sample rate:</source> <translation>Tasa de muestreo:</translation> </message> <message> - <location filename="../SettingsView.ui" line="190"/> - <location filename="../SettingsView.ui" line="207"/> + <location filename="../SettingsView.ui" line="200"/> + <location filename="../SettingsView.ui" line="217"/> <source>44100</source> <translation>44100</translation> </message> <message> - <location filename="../SettingsView.ui" line="197"/> + <location filename="../SettingsView.ui" line="207"/> <source>22050</source> <translation>22050</translation> </message> <message> - <location filename="../SettingsView.ui" line="202"/> + <location filename="../SettingsView.ui" line="212"/> <source>32000</source> <translation>32000</translation> </message> <message> - <location filename="../SettingsView.ui" line="212"/> + <location filename="../SettingsView.ui" line="222"/> <source>48000</source> <translation>48000</translation> </message> <message> - <location filename="../SettingsView.ui" line="220"/> + <location filename="../SettingsView.ui" line="230"/> <source>Hz</source> <translation>Hz</translation> </message> <message> - <location filename="../SettingsView.ui" line="229"/> + <location filename="../SettingsView.ui" line="239"/> <source>Volume:</source> <translation>Volumen:</translation> </message> <message> - <location filename="../SettingsView.ui" line="260"/> - <location filename="../SettingsView.ui" line="300"/> + <location filename="../SettingsView.ui" line="270"/> + <location filename="../SettingsView.ui" line="310"/> <source>Mute</source> <translation>Silenciar</translation> </message> <message> - <location filename="../SettingsView.ui" line="269"/> + <location filename="../SettingsView.ui" line="279"/> <source>Fast forward volume:</source> <translation>Vol. durante av. rápido:</translation> </message> <message> - <location filename="../SettingsView.ui" line="316"/> + <location filename="../SettingsView.ui" line="326"/> <source>Display driver:</source> <translation>Sistema de video:</translation> </message> <message> - <location filename="../SettingsView.ui" line="333"/> + <location filename="../SettingsView.ui" line="343"/> <source>Frameskip:</source> <translation>Salto de cuadros:</translation> </message> <message> - <location filename="../SettingsView.ui" line="342"/> + <location filename="../SettingsView.ui" line="352"/> <source>Skip every</source> <translation>Saltar cada</translation> </message> <message> - <location filename="../SettingsView.ui" line="352"/> - <location filename="../SettingsView.ui" line="705"/> + <location filename="../SettingsView.ui" line="362"/> + <location filename="../SettingsView.ui" line="722"/> <source>frames</source> <translation>cuadros</translation> </message> <message> - <location filename="../SettingsView.ui" line="361"/> + <location filename="../SettingsView.ui" line="371"/> <source>FPS target:</source> <translation>Objetivo de FPS:</translation> </message> <message> - <location filename="../SettingsView.ui" line="386"/> + <location filename="../SettingsView.ui" line="396"/> <source>frames per second</source> <translation>cuadros por segundo</translation> </message> <message> - <location filename="../SettingsView.ui" line="402"/> + <location filename="../SettingsView.ui" line="412"/> <source>Sync:</source> <translation>Sincronizar con:</translation> </message> <message> - <location filename="../SettingsView.ui" line="411"/> + <location filename="../SettingsView.ui" line="421"/> <source>Video</source> <translation>Video</translation> </message> <message> - <location filename="../SettingsView.ui" line="418"/> + <location filename="../SettingsView.ui" line="428"/> <source>Audio</source> <translation>Audio</translation> </message> <message> - <location filename="../SettingsView.ui" line="427"/> + <location filename="../SettingsView.ui" line="437"/> <source>Lock aspect ratio</source> <translation>Bloquear proporción de aspecto</translation> </message> <message> - <location filename="../SettingsView.ui" line="441"/> + <location filename="../SettingsView.ui" line="451"/> <source>Bilinear filtering</source> <translation>Filtro bilineal</translation> </message> <message> - <location filename="../SettingsView.ui" line="434"/> + <location filename="../SettingsView.ui" line="1303"/> + <source>Log to file</source> + <translation>Guardar a archivo</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="1310"/> + <source>Log to console</source> + <translation>Guardar a consola</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="1324"/> + <source>Select Log File</source> + <translation>Seleccionar</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="1337"/> + <source>Game Boy model:</source> + <translation>Modelo de Game Boy:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="1373"/> + <source>Super Game Boy model:</source> + <translation>Modelo de Super Game Boy:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="1409"/> + <source>Game Boy Color model:</source> + <translation>Modelo de Game Boy Color:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="1744"/> + <source>Use GBC colors in GB games</source> + <translation>Usar colores de GBC en juegos GB</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="1751"/> + <source>Camera:</source> + <translation>Cámara:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="444"/> <source>Force integer scaling</source> - <translation>Forzar escalado de enteros</translation> + <translation>Forzar escalado a valores enteros</translation> </message> <message> - <location filename="../SettingsView.ui" line="452"/> + <location filename="../SettingsView.ui" line="462"/> <source>Language</source> <translation>Idioma</translation> </message> <message> - <location filename="../SettingsView.ui" line="460"/> + <location filename="../SettingsView.ui" line="470"/> <source>English</source> <translation>English</translation> </message> <message> - <location filename="../SettingsView.ui" line="475"/> + <location filename="../SettingsView.ui" line="485"/> <source>Library:</source> <translation>Biblioteca:</translation> </message> <message> - <location filename="../SettingsView.ui" line="483"/> + <location filename="../SettingsView.ui" line="493"/> <source>List view</source> <translation>Lista</translation> </message> <message> - <location filename="../SettingsView.ui" line="488"/> + <location filename="../SettingsView.ui" line="498"/> <source>Tree view</source> <translation>Árbol</translation> </message> <message> - <location filename="../SettingsView.ui" line="496"/> + <location filename="../SettingsView.ui" line="506"/> <source>Show when no game open</source> <translation>Mostrar cuando no haya un juego abierto</translation> </message> <message> - <location filename="../SettingsView.ui" line="506"/> + <location filename="../SettingsView.ui" line="516"/> <source>Clear cache</source> <translation>Limpiar caché</translation> </message> <message> - <location filename="../SettingsView.ui" line="520"/> + <location filename="../SettingsView.ui" line="530"/> <source>Allow opposing input directions</source> <translation>Permitir direcciones opuestas al mismo tiempo</translation> </message> <message> - <location filename="../SettingsView.ui" line="527"/> + <location filename="../SettingsView.ui" line="537"/> <source>Suspend screensaver</source> <translation>Suspender protector de pantalla</translation> </message> <message> - <location filename="../SettingsView.ui" line="537"/> + <location filename="../SettingsView.ui" line="547"/> <source>Pause when inactive</source> <translation>Pausar al no estar activo</translation> </message> <message> - <location filename="../SettingsView.ui" line="544"/> + <location filename="../SettingsView.ui" line="554"/> <source>Show FPS in title bar</source> <translation>Mostrar FPS en la barra de título</translation> </message> <message> - <location filename="../SettingsView.ui" line="568"/> + <location filename="../SettingsView.ui" line="578"/> <source>Automatically save cheats</source> <translation>Guardar trucos automáticamente</translation> </message> <message> - <location filename="../SettingsView.ui" line="578"/> + <location filename="../SettingsView.ui" line="588"/> <source>Automatically load cheats</source> <translation>Cargar trucos automáticamente</translation> </message> <message> - <location filename="../SettingsView.ui" line="588"/> + <location filename="../SettingsView.ui" line="598"/> <source>Automatically save state</source> <translation>Guardar estado automáticamente</translation> </message> <message> - <location filename="../SettingsView.ui" line="598"/> + <location filename="../SettingsView.ui" line="608"/> <source>Automatically load state</source> <translation>Cargar estado automáticamente</translation> </message> <message> - <location filename="../SettingsView.ui" line="615"/> + <location filename="../SettingsView.ui" line="618"/> + <source>Enable Discord Rich Presence</source> + <translation>Hablitar Rich Presence en Discord</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="632"/> <source>Fast forward speed:</source> - <translation>Velocidad de avance rápido:</translation> + <translation>Avance rápido:</translation> </message> <message> - <location filename="../SettingsView.ui" line="627"/> + <location filename="../SettingsView.ui" line="644"/> + <location filename="../SettingsView.ui" line="891"/> <source>×</source> <translation>×</translation> </message> <message> - <location filename="../SettingsView.ui" line="646"/> + <location filename="../SettingsView.ui" line="663"/> <source>Unbounded</source> <translation>Sin límite</translation> </message> <message> - <location filename="../SettingsView.ui" line="682"/> + <location filename="../SettingsView.ui" line="699"/> <source>Enable rewind</source> <translation>Habilitar el rebobinar</translation> </message> <message> - <location filename="../SettingsView.ui" line="689"/> + <location filename="../SettingsView.ui" line="706"/> <source>Rewind history:</source> - <translation>Historial de rebobinado:</translation> + <translation>Hist. de rebobinado:</translation> </message> <message> - <location filename="../SettingsView.ui" line="721"/> + <location filename="../SettingsView.ui" line="738"/> <source>Idle loops:</source> <translation>Bucles inactivos:</translation> </message> <message> - <location filename="../SettingsView.ui" line="729"/> + <location filename="../SettingsView.ui" line="746"/> <source>Run all</source> <translation>Ejecutarlos todos</translation> </message> <message> - <location filename="../SettingsView.ui" line="734"/> + <location filename="../SettingsView.ui" line="751"/> <source>Remove known</source> <translation>Eliminar los conocidos</translation> </message> <message> - <location filename="../SettingsView.ui" line="739"/> + <location filename="../SettingsView.ui" line="756"/> <source>Detect and remove</source> <translation>Detectar y eliminar</translation> </message> <message> - <location filename="../SettingsView.ui" line="761"/> + <location filename="../SettingsView.ui" line="778"/> <source>Savestate extra data:</source> - <translation>Guardar datos extra con el estado:</translation> + <translation>Guardar datos extra:</translation> </message> <message> - <location filename="../SettingsView.ui" line="768"/> - <location filename="../SettingsView.ui" line="812"/> + <location filename="../SettingsView.ui" line="785"/> + <location filename="../SettingsView.ui" line="829"/> <source>Screenshot</source> <translation>Pantallazo</translation> </message> <message> - <location filename="../SettingsView.ui" line="778"/> - <location filename="../SettingsView.ui" line="822"/> + <location filename="../SettingsView.ui" line="795"/> + <location filename="../SettingsView.ui" line="839"/> <source>Save data</source> <translation>Datos de guardado</translation> </message> <message> - <location filename="../SettingsView.ui" line="788"/> - <location filename="../SettingsView.ui" line="829"/> + <location filename="../SettingsView.ui" line="805"/> + <location filename="../SettingsView.ui" line="846"/> <source>Cheat codes</source> <translation>Trucos</translation> </message> <message> - <location filename="../SettingsView.ui" line="805"/> + <location filename="../SettingsView.ui" line="822"/> <source>Load extra data:</source> - <translation>Cargar datos extra con el estado:</translation> + <translation>Cargar datos extra:</translation> </message> <message> <source>Rewind affects save data</source> <translation type="vanished">El rebobinar afecta los datos de guardado</translation> </message> <message> - <location filename="../SettingsView.ui" line="747"/> + <location filename="../SettingsView.ui" line="764"/> <source>Preload entire ROM into memory</source> <translation>Cargar ROM completa a la memoria</translation> </message> <message> - <location filename="../SettingsView.ui" line="658"/> + <location filename="../SettingsView.ui" line="675"/> <source>Autofire interval:</source> - <translation>Intervalo de disparo automático:</translation> + <translation>Intervalo de turbo:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="857"/> + <source>Video renderer:</source> + <translation>Renderizador de video:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="865"/> + <source>Software</source> + <translation>Software</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="870"/> + <source>OpenGL</source> + <translation>OpenGL</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="878"/> + <source>OpenGL enhancements</source> + <translation>Mejoras para OpenGL</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="884"/> + <source>High-resolution scale:</source> + <translation>Escala de alta resolución:</translation> </message> <message> - <location filename="../SettingsView.ui" line="840"/> + <location filename="../SettingsView.ui" line="907"/> + <source>XQ GBA audio (experimental)</source> + <translation>Mejorar audio GBA (experimental)</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="918"/> <source>GB BIOS file:</source> <translation>Archivo BIOS GB:</translation> </message> <message> - <location filename="../SettingsView.ui" line="859"/> - <location filename="../SettingsView.ui" line="897"/> - <location filename="../SettingsView.ui" line="932"/> - <location filename="../SettingsView.ui" line="960"/> - <location filename="../SettingsView.ui" line="1001"/> - <location filename="../SettingsView.ui" line="1049"/> - <location filename="../SettingsView.ui" line="1097"/> - <location filename="../SettingsView.ui" line="1145"/> - <location filename="../SettingsView.ui" line="1193"/> + <location filename="../SettingsView.ui" line="937"/> + <location filename="../SettingsView.ui" line="975"/> + <location filename="../SettingsView.ui" line="1010"/> + <location filename="../SettingsView.ui" line="1038"/> + <location filename="../SettingsView.ui" line="1079"/> + <location filename="../SettingsView.ui" line="1127"/> + <location filename="../SettingsView.ui" line="1175"/> + <location filename="../SettingsView.ui" line="1223"/> + <location filename="../SettingsView.ui" line="1271"/> <source>Browse</source> <translation>Examinar</translation> </message> <message> - <location filename="../SettingsView.ui" line="868"/> + <location filename="../SettingsView.ui" line="946"/> <source>Use BIOS file if found</source> <translation>Usar archivo BIOS si fue encontrado</translation> </message> <message> - <location filename="../SettingsView.ui" line="878"/> + <location filename="../SettingsView.ui" line="956"/> <source>Skip BIOS intro</source> <translation>Saltar animación de entrada del BIOS</translation> </message> <message> - <location filename="../SettingsView.ui" line="906"/> + <location filename="../SettingsView.ui" line="984"/> <source>GBA BIOS file:</source> <translation>Archivo BIOS GBA:</translation> </message> <message> - <location filename="../SettingsView.ui" line="913"/> + <location filename="../SettingsView.ui" line="991"/> <source>GBC BIOS file:</source> <translation>Archivo BIOS GBC:</translation> </message> <message> - <location filename="../SettingsView.ui" line="941"/> + <location filename="../SettingsView.ui" line="1019"/> <source>SGB BIOS file:</source> - <translation>SGB BIOS file:</translation> + <translation>Archivo BIOS SGB:</translation> </message> <message> - <location filename="../SettingsView.ui" line="976"/> + <location filename="../SettingsView.ui" line="1054"/> <source>Save games</source> <translation>Datos de guardado</translation> </message> <message> - <location filename="../SettingsView.ui" line="1010"/> - <location filename="../SettingsView.ui" line="1058"/> - <location filename="../SettingsView.ui" line="1106"/> - <location filename="../SettingsView.ui" line="1154"/> - <location filename="../SettingsView.ui" line="1202"/> + <location filename="../SettingsView.ui" line="1088"/> + <location filename="../SettingsView.ui" line="1136"/> + <location filename="../SettingsView.ui" line="1184"/> + <location filename="../SettingsView.ui" line="1232"/> + <location filename="../SettingsView.ui" line="1280"/> <source>Same directory as the ROM</source> <translation>Al mismo directorio que la ROM</translation> </message> <message> - <location filename="../SettingsView.ui" line="1024"/> + <location filename="../SettingsView.ui" line="1102"/> <source>Save states</source> <translation>Estados de guardado</translation> </message> <message> - <location filename="../SettingsView.ui" line="1072"/> + <location filename="../SettingsView.ui" line="1150"/> <source>Screenshots</source> <translation>Pantallazos</translation> </message> <message> - <location filename="../SettingsView.ui" line="1120"/> + <location filename="../SettingsView.ui" line="1198"/> <source>Patches</source> <translation>Parches</translation> </message> <message> - <location filename="../SettingsView.ui" line="1168"/> + <location filename="../SettingsView.ui" line="1246"/> <source>Cheats</source> <translation>Trucos</translation> </message> <message> - <location filename="../SettingsView.ui" line="1213"/> <source>Game Boy model</source> - <translation>Modelo de Game Boy</translation> + <translation type="vanished">Modelo de Game Boy</translation> </message> <message> - <location filename="../SettingsView.ui" line="1221"/> - <location filename="../SettingsView.ui" line="1257"/> - <location filename="../SettingsView.ui" line="1293"/> + <location filename="../SettingsView.ui" line="1345"/> + <location filename="../SettingsView.ui" line="1381"/> + <location filename="../SettingsView.ui" line="1417"/> <source>Autodetect</source> <translation>Detección automática</translation> </message> <message> - <location filename="../SettingsView.ui" line="1226"/> - <location filename="../SettingsView.ui" line="1262"/> - <location filename="../SettingsView.ui" line="1298"/> + <location filename="../SettingsView.ui" line="1350"/> + <location filename="../SettingsView.ui" line="1386"/> + <location filename="../SettingsView.ui" line="1422"/> <source>Game Boy (DMG)</source> <translation>Game Boy (DMG)</translation> </message> <message> - <location filename="../SettingsView.ui" line="1231"/> - <location filename="../SettingsView.ui" line="1267"/> - <location filename="../SettingsView.ui" line="1303"/> + <location filename="../SettingsView.ui" line="1355"/> + <location filename="../SettingsView.ui" line="1391"/> + <location filename="../SettingsView.ui" line="1427"/> <source>Super Game Boy (SGB)</source> <translation></translation> </message> <message> - <location filename="../SettingsView.ui" line="1236"/> - <location filename="../SettingsView.ui" line="1272"/> - <location filename="../SettingsView.ui" line="1308"/> + <location filename="../SettingsView.ui" line="1360"/> + <location filename="../SettingsView.ui" line="1396"/> + <location filename="../SettingsView.ui" line="1432"/> <source>Game Boy Color (CGB)</source> <translation>Game Boy Color (CGB)</translation> </message> <message> - <location filename="../SettingsView.ui" line="1241"/> - <location filename="../SettingsView.ui" line="1277"/> - <location filename="../SettingsView.ui" line="1313"/> + <location filename="../SettingsView.ui" line="1365"/> + <location filename="../SettingsView.ui" line="1401"/> + <location filename="../SettingsView.ui" line="1437"/> <source>Game Boy Advance (AGB)</source> <translation>Game Boy Advance (AGB)</translation> </message> <message> - <location filename="../SettingsView.ui" line="1249"/> <source>Super Game Boy model</source> - <translation>Modelo de Super Game Boy</translation> + <translation type="vanished">Modelo de Super Game Boy</translation> </message> <message> - <location filename="../SettingsView.ui" line="1285"/> <source>Game Boy Color model</source> - <translation>Modelo de Game Boy Color</translation> + <translation type="vanished">Modelo de Game Boy Color</translation> </message> <message> - <location filename="../SettingsView.ui" line="1328"/> + <location filename="../SettingsView.ui" line="1452"/> <source>Default BG colors:</source> <translation>Colores de fondo por defecto:</translation> </message> <message> - <location filename="../SettingsView.ui" line="1495"/> + <location filename="../SettingsView.ui" line="1619"/> <source>Super Game Boy borders</source> <translation>Bordes de Super Game Boy</translation> </message> <message> - <location filename="../SettingsView.ui" line="1509"/> + <location filename="../SettingsView.ui" line="1633"/> <source>Camera driver:</source> <translation>Controlador de cámara:</translation> </message> <message> - <location filename="../SettingsView.ui" line="1606"/> + <location filename="../SettingsView.ui" line="1730"/> <source>Default sprite colors 1:</source> <translation>Colores de sprite 1 por defecto:</translation> </message> <message> - <location filename="../SettingsView.ui" line="1613"/> + <location filename="../SettingsView.ui" line="1737"/> <source>Default sprite colors 2:</source> <translation>Colores de sprite 2 por defecto:</translation> </message>@@ -4730,19 +5028,29 @@ <source>Tiles</source>
<translation>Tiles</translation> </message> <message> - <location filename="../TileView.ui" line="140"/> + <location filename="../TileView.ui" line="110"/> <source>256 colors</source> <translation>256 colores</translation> </message> <message> - <location filename="../TileView.ui" line="31"/> + <location filename="../TileView.ui" line="123"/> <source>×</source> <translation>×</translation> </message> <message> - <location filename="../TileView.ui" line="44"/> + <location filename="../TileView.ui" line="136"/> <source>Magnification</source> <translation>Ampliación</translation> + </message> + <message> + <location filename="../TileView.ui" line="159"/> + <source>Tiles per row</source> + <translation>Tiles por fila</translation> + </message> + <message> + <location filename="../TileView.ui" line="166"/> + <source>Fit to window</source> + <translation>Ajustar a ventana</translation> </message> </context> <context>@@ -4882,86 +5190,91 @@ <translation>HEVC</translation>
</message> <message> <location filename="../VideoView.ui" line="274"/> + <source>HEVC (NVENC)</source> + <translation>HEVC (NVENC)</translation> + </message> + <message> + <location filename="../VideoView.ui" line="279"/> <source>VP8</source> <translation>VP8</translation> </message> <message> - <location filename="../VideoView.ui" line="279"/> + <location filename="../VideoView.ui" line="284"/> <source>VP9</source> <translation>VP9</translation> </message> <message> - <location filename="../VideoView.ui" line="284"/> + <location filename="../VideoView.ui" line="289"/> <source>FFV1</source> <translation>FFV1</translation> </message> <message> - <location filename="../VideoView.ui" line="296"/> + <location filename="../VideoView.ui" line="301"/> <source>FLAC</source> <translation>FLAC</translation> </message> <message> - <location filename="../VideoView.ui" line="301"/> + <location filename="../VideoView.ui" line="306"/> <source>Opus</source> <translation>Opus</translation> </message> <message> - <location filename="../VideoView.ui" line="306"/> + <location filename="../VideoView.ui" line="311"/> <source>Vorbis</source> <translation>Vorbis</translation> </message> <message> - <location filename="../VideoView.ui" line="311"/> + <location filename="../VideoView.ui" line="316"/> <source>MP3</source> <translation>MP3</translation> </message> <message> - <location filename="../VideoView.ui" line="316"/> + <location filename="../VideoView.ui" line="321"/> <source>AAC</source> <translation>AAC</translation> </message> <message> - <location filename="../VideoView.ui" line="321"/> + <location filename="../VideoView.ui" line="326"/> <source>Uncompressed</source> <translation>Sin comprimir</translation> </message> <message> - <location filename="../VideoView.ui" line="332"/> + <location filename="../VideoView.ui" line="337"/> <source> Bitrate (kbps)</source> <translation> Tasa de bits (kbps)</translation> </message> <message> - <location filename="../VideoView.ui" line="338"/> + <location filename="../VideoView.ui" line="343"/> <source>VBR </source> <translation>VBR </translation> </message> <message> - <location filename="../VideoView.ui" line="383"/> + <location filename="../VideoView.ui" line="388"/> <source>ABR</source> <translation>ABR</translation> </message> <message> - <location filename="../VideoView.ui" line="399"/> + <location filename="../VideoView.ui" line="404"/> <source>Dimensions</source> <translation>Dimensiones</translation> </message> <message> - <location filename="../VideoView.ui" line="405"/> + <location filename="../VideoView.ui" line="410"/> <source>:</source> <translation>:</translation> </message> <message> - <location filename="../VideoView.ui" line="415"/> + <location filename="../VideoView.ui" line="420"/> <source>×</source> <translation>×</translation> </message> <message> - <location filename="../VideoView.ui" line="465"/> + <location filename="../VideoView.ui" line="470"/> <source>Lock aspect ratio</source> <translation>Bloquear proporción de aspecto</translation> </message> <message> - <location filename="../VideoView.ui" line="480"/> + <location filename="../VideoView.ui" line="485"/> <source>Show advanced</source> <translation>Mostrar ajustes avanzados</translation> </message>
@@ -4243,21 +4243,26 @@ <translation>模拟器</translation>
</message> <message> <location filename="../SettingsView.ui" line="67"/> + <source>Enhancements</source> + <translation>增强</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="72"/> <source>BIOS</source> <translation>BIOS</translation> </message> <message> - <location filename="../SettingsView.ui" line="72"/> + <location filename="../SettingsView.ui" line="77"/> <source>Paths</source> <translation>路径</translation> </message> <message> - <location filename="../SettingsView.ui" line="77"/> + <location filename="../SettingsView.ui" line="82"/> <source>Logging</source> <translation>日志记录</translation> </message> <message> - <location filename="../SettingsView.ui" line="82"/> + <location filename="../SettingsView.ui" line="87"/> <source>Game Boy</source> <translation>Game Boy</translation> </message>@@ -4272,491 +4277,522 @@ <source>Audio buffer:</source>
<translation>音频缓冲:</translation> </message> <message> - <location filename="../SettingsView.ui" line="129"/> - <location filename="../SettingsView.ui" line="151"/> + <location filename="../SettingsView.ui" line="134"/> + <location filename="../SettingsView.ui" line="156"/> <source>1536</source> <translation>1536</translation> </message> <message> - <location filename="../SettingsView.ui" line="136"/> + <location filename="../SettingsView.ui" line="141"/> <source>512</source> <translation>512</translation> </message> <message> - <location filename="../SettingsView.ui" line="141"/> + <location filename="../SettingsView.ui" line="146"/> <source>768</source> <translation>768</translation> </message> <message> - <location filename="../SettingsView.ui" line="146"/> + <location filename="../SettingsView.ui" line="151"/> <source>1024</source> <translation>1024</translation> </message> <message> - <location filename="../SettingsView.ui" line="156"/> + <location filename="../SettingsView.ui" line="161"/> <source>2048</source> <translation>2048</translation> </message> <message> - <location filename="../SettingsView.ui" line="161"/> + <location filename="../SettingsView.ui" line="166"/> <source>3072</source> <translation>3072</translation> </message> <message> - <location filename="../SettingsView.ui" line="166"/> + <location filename="../SettingsView.ui" line="171"/> <source>4096</source> <translation>4096</translation> </message> <message> - <location filename="../SettingsView.ui" line="174"/> + <location filename="../SettingsView.ui" line="179"/> <source>samples</source> <translation>采样</translation> </message> <message> - <location filename="../SettingsView.ui" line="183"/> + <location filename="../SettingsView.ui" line="188"/> <source>Sample rate:</source> <translation>采样率:</translation> </message> <message> - <location filename="../SettingsView.ui" line="195"/> - <location filename="../SettingsView.ui" line="212"/> + <location filename="../SettingsView.ui" line="200"/> + <location filename="../SettingsView.ui" line="217"/> <source>44100</source> <translation>44100</translation> </message> <message> - <location filename="../SettingsView.ui" line="202"/> + <location filename="../SettingsView.ui" line="207"/> <source>22050</source> <translation>22050</translation> </message> <message> - <location filename="../SettingsView.ui" line="207"/> + <location filename="../SettingsView.ui" line="212"/> <source>32000</source> <translation>32000</translation> </message> <message> - <location filename="../SettingsView.ui" line="217"/> + <location filename="../SettingsView.ui" line="222"/> <source>48000</source> <translation>48000</translation> </message> <message> - <location filename="../SettingsView.ui" line="225"/> + <location filename="../SettingsView.ui" line="230"/> <source>Hz</source> <translation>Hz</translation> </message> <message> - <location filename="../SettingsView.ui" line="234"/> + <location filename="../SettingsView.ui" line="239"/> <source>Volume:</source> <translation>音量:</translation> </message> <message> - <location filename="../SettingsView.ui" line="265"/> - <location filename="../SettingsView.ui" line="305"/> + <location filename="../SettingsView.ui" line="270"/> + <location filename="../SettingsView.ui" line="310"/> <source>Mute</source> <translation>静音</translation> </message> <message> - <location filename="../SettingsView.ui" line="274"/> + <location filename="../SettingsView.ui" line="279"/> <source>Fast forward volume:</source> <translation>快进音量:</translation> </message> <message> - <location filename="../SettingsView.ui" line="321"/> + <location filename="../SettingsView.ui" line="326"/> <source>Display driver:</source> <translation>显示驱动:</translation> </message> <message> - <location filename="../SettingsView.ui" line="338"/> + <location filename="../SettingsView.ui" line="343"/> <source>Frameskip:</source> <translation>跳帧:</translation> </message> <message> - <location filename="../SettingsView.ui" line="347"/> + <location filename="../SettingsView.ui" line="352"/> <source>Skip every</source> <translation>每间隔</translation> </message> <message> - <location filename="../SettingsView.ui" line="357"/> - <location filename="../SettingsView.ui" line="717"/> + <location filename="../SettingsView.ui" line="362"/> + <location filename="../SettingsView.ui" line="722"/> <source>frames</source> <translation>帧</translation> </message> <message> - <location filename="../SettingsView.ui" line="366"/> + <location filename="../SettingsView.ui" line="371"/> <source>FPS target:</source> <translation>目标 FPS:</translation> </message> <message> - <location filename="../SettingsView.ui" line="391"/> + <location filename="../SettingsView.ui" line="396"/> <source>frames per second</source> <translation>帧每秒</translation> </message> <message> - <location filename="../SettingsView.ui" line="407"/> + <location filename="../SettingsView.ui" line="412"/> <source>Sync:</source> <translation>同步:</translation> </message> <message> - <location filename="../SettingsView.ui" line="416"/> + <location filename="../SettingsView.ui" line="421"/> <source>Video</source> <translation>视频</translation> </message> <message> - <location filename="../SettingsView.ui" line="423"/> + <location filename="../SettingsView.ui" line="428"/> <source>Audio</source> <translation>音频</translation> </message> <message> - <location filename="../SettingsView.ui" line="432"/> + <location filename="../SettingsView.ui" line="437"/> <source>Lock aspect ratio</source> <translation>锁定纵横比</translation> </message> <message> - <location filename="../SettingsView.ui" line="439"/> + <location filename="../SettingsView.ui" line="444"/> <source>Force integer scaling</source> <translation>强制整数缩放</translation> </message> <message> - <location filename="../SettingsView.ui" line="446"/> + <location filename="../SettingsView.ui" line="451"/> <source>Bilinear filtering</source> <translation>双线性过滤</translation> </message> <message> - <location filename="../SettingsView.ui" line="457"/> + <location filename="../SettingsView.ui" line="462"/> <source>Language</source> <translation>语言</translation> </message> <message> - <location filename="../SettingsView.ui" line="465"/> + <location filename="../SettingsView.ui" line="470"/> <source>English</source> <translation>英语</translation> </message> <message> - <location filename="../SettingsView.ui" line="480"/> + <location filename="../SettingsView.ui" line="485"/> <source>Library:</source> <translation>库:</translation> </message> <message> - <location filename="../SettingsView.ui" line="488"/> + <location filename="../SettingsView.ui" line="493"/> <source>List view</source> <translation>列表查看</translation> </message> <message> - <location filename="../SettingsView.ui" line="493"/> + <location filename="../SettingsView.ui" line="498"/> <source>Tree view</source> <translation>树状查看</translation> </message> <message> - <location filename="../SettingsView.ui" line="501"/> + <location filename="../SettingsView.ui" line="506"/> <source>Show when no game open</source> <translation>未打开游戏时显示</translation> </message> <message> - <location filename="../SettingsView.ui" line="511"/> + <location filename="../SettingsView.ui" line="516"/> <source>Clear cache</source> <translation>清除缓存</translation> </message> <message> - <location filename="../SettingsView.ui" line="525"/> + <location filename="../SettingsView.ui" line="530"/> <source>Allow opposing input directions</source> <translation>允许逆向输入</translation> </message> <message> - <location filename="../SettingsView.ui" line="532"/> + <location filename="../SettingsView.ui" line="537"/> <source>Suspend screensaver</source> <translation>停用屏幕保护程序</translation> </message> <message> - <location filename="../SettingsView.ui" line="542"/> + <location filename="../SettingsView.ui" line="547"/> <source>Pause when inactive</source> <translation>非活动时暂停</translation> </message> <message> - <location filename="../SettingsView.ui" line="549"/> + <location filename="../SettingsView.ui" line="554"/> <source>Show FPS in title bar</source> <translation>在标题栏显示 FPS</translation> </message> <message> - <location filename="../SettingsView.ui" line="573"/> + <location filename="../SettingsView.ui" line="578"/> <source>Automatically save cheats</source> <translation>自动保存作弊码</translation> </message> <message> - <location filename="../SettingsView.ui" line="583"/> + <location filename="../SettingsView.ui" line="588"/> <source>Automatically load cheats</source> <translation>自动载入作弊码</translation> </message> <message> - <location filename="../SettingsView.ui" line="593"/> + <location filename="../SettingsView.ui" line="598"/> <source>Automatically save state</source> <translation>自动存档</translation> </message> <message> - <location filename="../SettingsView.ui" line="603"/> + <location filename="../SettingsView.ui" line="608"/> <source>Automatically load state</source> <translation>自动读档</translation> </message> - <message> - <location filename="../SettingsView.ui" line="613"/> + <message> + <location filename="../SettingsView.ui" line="618"/> <source>Enable Discord Rich Presence</source> <translation>启用 Enable Discord Rich Presence</translation> </message> <message> - <location filename="../SettingsView.ui" line="627"/> + <location filename="../SettingsView.ui" line="632"/> <source>Fast forward speed:</source> <translation>快进速度:</translation> </message> <message> - <location filename="../SettingsView.ui" line="639"/> + <location filename="../SettingsView.ui" line="644"/> + <location filename="../SettingsView.ui" line="891"/> <source>×</source> <translation>×</translation> </message> <message> - <location filename="../SettingsView.ui" line="658"/> + <location filename="../SettingsView.ui" line="663"/> <source>Unbounded</source> <translation>不限制</translation> </message> <message> - <location filename="../SettingsView.ui" line="670"/> + <location filename="../SettingsView.ui" line="675"/> <source>Autofire interval:</source> <translation>连发间隔:</translation> </message> <message> - <location filename="../SettingsView.ui" line="694"/> + <location filename="../SettingsView.ui" line="699"/> <source>Enable rewind</source> <translation>启用回退</translation> </message> <message> - <location filename="../SettingsView.ui" line="701"/> + <location filename="../SettingsView.ui" line="706"/> <source>Rewind history:</source> <translation>回退历史:</translation> </message> <message> - <location filename="../SettingsView.ui" line="733"/> + <location filename="../SettingsView.ui" line="738"/> <source>Idle loops:</source> <translation>空循环:</translation> </message> <message> - <location filename="../SettingsView.ui" line="741"/> + <location filename="../SettingsView.ui" line="746"/> <source>Run all</source> <translation>运行所有</translation> </message> <message> - <location filename="../SettingsView.ui" line="746"/> + <location filename="../SettingsView.ui" line="751"/> <source>Remove known</source> <translation>移除选定</translation> </message> <message> - <location filename="../SettingsView.ui" line="751"/> + <location filename="../SettingsView.ui" line="756"/> <source>Detect and remove</source> <translation>检测并移除</translation> </message> <message> - <location filename="../SettingsView.ui" line="759"/> + <location filename="../SettingsView.ui" line="764"/> <source>Preload entire ROM into memory</source> <translation>将整个 ROM 预加载到内存中</translation> </message> <message> - <location filename="../SettingsView.ui" line="773"/> + <location filename="../SettingsView.ui" line="778"/> <source>Savestate extra data:</source> <translation>即时存档额外数据:</translation> </message> <message> - <location filename="../SettingsView.ui" line="780"/> - <location filename="../SettingsView.ui" line="824"/> + <location filename="../SettingsView.ui" line="785"/> + <location filename="../SettingsView.ui" line="829"/> <source>Screenshot</source> <translation>截图</translation> </message> <message> - <location filename="../SettingsView.ui" line="790"/> - <location filename="../SettingsView.ui" line="834"/> + <location filename="../SettingsView.ui" line="795"/> + <location filename="../SettingsView.ui" line="839"/> <source>Save data</source> <translation>保存数据</translation> </message> <message> - <location filename="../SettingsView.ui" line="800"/> - <location filename="../SettingsView.ui" line="841"/> + <location filename="../SettingsView.ui" line="805"/> + <location filename="../SettingsView.ui" line="846"/> <source>Cheat codes</source> <translation>作弊码</translation> </message> <message> - <location filename="../SettingsView.ui" line="817"/> + <location filename="../SettingsView.ui" line="822"/> <source>Load extra data:</source> - <translation>读档时载入额外数据:</translation> + <translation>载入额外数据:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="857"/> + <source>Video renderer:</source> + <translation>视频渲染器:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="865"/> + <source>Software</source> + <translation>软件</translation> </message> <message> - <location filename="../SettingsView.ui" line="852"/> - <source>GB BIOS file:</source> - <translation>GB BIOS 文件:</translation> + <location filename="../SettingsView.ui" line="870"/> + <source>OpenGL</source> + <translation>OpenGL</translation> </message> <message> - <location filename="../SettingsView.ui" line="871"/> - <location filename="../SettingsView.ui" line="909"/> - <location filename="../SettingsView.ui" line="944"/> - <location filename="../SettingsView.ui" line="972"/> - <location filename="../SettingsView.ui" line="1013"/> - <location filename="../SettingsView.ui" line="1061"/> - <location filename="../SettingsView.ui" line="1109"/> - <location filename="../SettingsView.ui" line="1157"/> - <location filename="../SettingsView.ui" line="1205"/> + <location filename="../SettingsView.ui" line="878"/> + <source>OpenGL enhancements</source> + <translation>OpenGL 增强</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="884"/> + <source>High-resolution scale:</source> + <translation>高分辨率比例:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="907"/> + <source>XQ GBA audio (experimental)</source> + <translation>XQ GBA 音频 (实验)</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="937"/> + <location filename="../SettingsView.ui" line="975"/> + <location filename="../SettingsView.ui" line="1010"/> + <location filename="../SettingsView.ui" line="1038"/> + <location filename="../SettingsView.ui" line="1079"/> + <location filename="../SettingsView.ui" line="1127"/> + <location filename="../SettingsView.ui" line="1175"/> + <location filename="../SettingsView.ui" line="1223"/> + <location filename="../SettingsView.ui" line="1271"/> <source>Browse</source> <translation>浏览</translation> </message> <message> - <location filename="../SettingsView.ui" line="880"/> + <location filename="../SettingsView.ui" line="918"/> + <source>GB BIOS file:</source> + <translation>GB BIOS 文件:</translation> + </message> + <message> + <location filename="../SettingsView.ui" line="946"/> <source>Use BIOS file if found</source> <translation>当可用时使用 BIOS 文件</translation> </message> <message> - <location filename="../SettingsView.ui" line="890"/> + <location filename="../SettingsView.ui" line="956"/> <source>Skip BIOS intro</source> <translation>跳过 BIOS 启动画面</translation> </message> <message> - <location filename="../SettingsView.ui" line="918"/> + <location filename="../SettingsView.ui" line="984"/> <source>GBA BIOS file:</source> <translation>GBA BIOS 文件:</translation> </message> <message> - <location filename="../SettingsView.ui" line="925"/> + <location filename="../SettingsView.ui" line="991"/> <source>GBC BIOS file:</source> <translation>GBC BIOS 文件:</translation> </message> <message> - <location filename="../SettingsView.ui" line="953"/> + <location filename="../SettingsView.ui" line="1019"/> <source>SGB BIOS file:</source> <translation>SGB BIOS 文件:</translation> </message> <message> - <location filename="../SettingsView.ui" line="988"/> + <location filename="../SettingsView.ui" line="1054"/> <source>Save games</source> - <translation>已保存的游戏</translation> + <translation>游戏存档</translation> </message> <message> - <location filename="../SettingsView.ui" line="1022"/> - <location filename="../SettingsView.ui" line="1070"/> - <location filename="../SettingsView.ui" line="1118"/> - <location filename="../SettingsView.ui" line="1166"/> - <location filename="../SettingsView.ui" line="1214"/> + <location filename="../SettingsView.ui" line="1088"/> + <location filename="../SettingsView.ui" line="1136"/> + <location filename="../SettingsView.ui" line="1184"/> + <location filename="../SettingsView.ui" line="1232"/> + <location filename="../SettingsView.ui" line="1280"/> <source>Same directory as the ROM</source> <translation>保存在 ROM 所在目录</translation> </message> <message> <location filename="../SettingsView.ui" line="1036"/> <source>Save states</source> - <translation>保存即时存档</translation> + <translation>即时存档</translation> </message> <message> - <location filename="../SettingsView.ui" line="1084"/> + <location filename="../SettingsView.ui" line="1150"/> <source>Screenshots</source> <translation>截图</translation> </message> <message> - <location filename="../SettingsView.ui" line="1132"/> + <location filename="../SettingsView.ui" line="1198"/> <source>Patches</source> <translation>补丁</translation> </message> <message> - <location filename="../SettingsView.ui" line="1180"/> + <location filename="../SettingsView.ui" line="1246"/> <source>Cheats</source> <translation>作弊码</translation> </message> <message> - <location filename="../SettingsView.ui" line="1237"/> + <location filename="../SettingsView.ui" line="1303"/> <source>Log to file</source> <translation>记录日志到文件</translation> </message> <message> - <location filename="../SettingsView.ui" line="1244"/> + <location filename="../SettingsView.ui" line="1310"/> <source>Log to console</source> <translation>记录日志到控制台</translation> </message> <message> - <location filename="../SettingsView.ui" line="1258"/> + <location filename="../SettingsView.ui" line="1324"/> <source>Select Log File</source> <translation>选择日志文件</translation> </message> <message> - <location filename="../SettingsView.ui" line="1271"/> - <source>Game Boy model</source> - <translation>Game Boy 模型</translation> + <location filename="../SettingsView.ui" line="1337"/> + <source>Game Boy model:</source> + <translation>Game Boy 模型:</translation> </message> <message> - <location filename="../SettingsView.ui" line="1279"/> - <location filename="../SettingsView.ui" line="1315"/> - <location filename="../SettingsView.ui" line="1351"/> + <location filename="../SettingsView.ui" line="1345"/> + <location filename="../SettingsView.ui" line="1381"/> + <location filename="../SettingsView.ui" line="1417"/> <source>Autodetect</source> <translation>自动检测</translation> </message> <message> - <location filename="../SettingsView.ui" line="1284"/> - <location filename="../SettingsView.ui" line="1320"/> - <location filename="../SettingsView.ui" line="1356"/> + <location filename="../SettingsView.ui" line="1350"/> + <location filename="../SettingsView.ui" line="1386"/> + <location filename="../SettingsView.ui" line="1422"/> <source>Game Boy (DMG)</source> <translation>Game Boy (DMG)</translation> </message> <message> - <location filename="../SettingsView.ui" line="1289"/> - <location filename="../SettingsView.ui" line="1325"/> - <location filename="../SettingsView.ui" line="1361"/> + <location filename="../SettingsView.ui" line="1355"/> + <location filename="../SettingsView.ui" line="1391"/> + <location filename="../SettingsView.ui" line="1427"/> <source>Super Game Boy (SGB)</source> <translation>Super Game Boy (SGB)</translation> </message> <message> - <location filename="../SettingsView.ui" line="1294"/> - <location filename="../SettingsView.ui" line="1330"/> - <location filename="../SettingsView.ui" line="1366"/> + <location filename="../SettingsView.ui" line="1360"/> + <location filename="../SettingsView.ui" line="1396"/> + <location filename="../SettingsView.ui" line="1432"/> <source>Game Boy Color (CGB)</source> <translation>Game Boy Color (CGB)</translation> </message> <message> - <location filename="../SettingsView.ui" line="1299"/> - <location filename="../SettingsView.ui" line="1335"/> - <location filename="../SettingsView.ui" line="1371"/> + <location filename="../SettingsView.ui" line="1365"/> + <location filename="../SettingsView.ui" line="1401"/> + <location filename="../SettingsView.ui" line="1437"/> <source>Game Boy Advance (AGB)</source> <translation>Game Boy Advance (AGB)</translation> </message> <message> - <location filename="../SettingsView.ui" line="1307"/> + <location filename="../SettingsView.ui" line="1373"/> <source>Super Game Boy model:</source> <translation>Super Game Boy 模型:</translation> </message> <message> - <location filename="../SettingsView.ui" line="1343"/> + <location filename="../SettingsView.ui" line="1409"/> <source>Game Boy Color model:</source> <translation>Game Boy Color 模型:</translation> </message> <message> - <location filename="../SettingsView.ui" line="1386"/> + <location filename="../SettingsView.ui" line="1452"/> <source>Default BG colors:</source> <translation>默认背景颜色:</translation> </message> <message> - <location filename="../SettingsView.ui" line="1553"/> + <location filename="../SettingsView.ui" line="1619"/> <source>Super Game Boy borders</source> <translation>Super Game Boy 边框</translation> </message> <message> - <location filename="../SettingsView.ui" line="1567"/> + <location filename="../SettingsView.ui" line="1633"/> <source>Camera driver:</source> <translation>相机驱动:</translation> </message> <message> - <location filename="../SettingsView.ui" line="1664"/> + <location filename="../SettingsView.ui" line="1730"/> <source>Default sprite colors 1:</source> <translation>默认精灵图颜色 1:</translation> </message> <message> - <location filename="../SettingsView.ui" line="1671"/> + <location filename="../SettingsView.ui" line="1737"/> <source>Default sprite colors 2:</source> <translation>默认精灵图颜色 2:</translation> </message> <message> - <location filename="../SettingsView.ui" line="1678"/> + <location filename="../SettingsView.ui" line="1744"/> <source>Use GBC colors in GB games</source> <translation>在 GB 游戏中使用 GBC 颜色</translation> </message> <message> - <location filename="../SettingsView.ui" line="1685"/> + <location filename="../SettingsView.ui" line="1751"/> <source>Camera:</source> <translation>相机</translation> </message>
@@ -51,9 +51,9 @@ }
#else SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); #ifdef COLOR_16_BIT - SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_OPENGL | SDL_RESIZABLE | (SDL_FULLSCREEN * renderer->fullscreen)); + SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_OPENGL | SDL_RESIZABLE | (SDL_FULLSCREEN * renderer->player.fullscreen)); #else - SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_OPENGL | SDL_RESIZABLE | (SDL_FULLSCREEN * renderer->fullscreen)); + SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_OPENGL | SDL_RESIZABLE | (SDL_FULLSCREEN * renderer->player.fullscreen)); #endif SDL_WM_SetCaption(projectName, ""); #endif
@@ -13,7 +13,7 @@
#include <mgba/core/core.h> #include <mgba/core/thread.h> -#ifndef __APPLE__ +#ifdef __linux__ #include <malloc.h> #endif@@ -37,7 +37,7 @@
size_t size = renderer->width * renderer->height * BYTES_PER_PIXEL; #ifdef _WIN32 renderer->outputBuffer = _aligned_malloc(size, 16); -#elif !defined(__APPLE__) +#elif defined(__linux__) renderer->outputBuffer = memalign(16, size); #else posix_memalign((void**) &renderer->outputBuffer, 16, size);
@@ -131,12 +131,8 @@ mCoreLoadConfig(renderer.core);
renderer.viewportWidth = renderer.core->opts.width; renderer.viewportHeight = renderer.core->opts.height; -#if SDL_VERSION_ATLEAST(2, 0, 0) renderer.player.fullscreen = renderer.core->opts.fullscreen; renderer.player.windowUpdated = 0; -#else - renderer.fullscreen = renderer.core->opts.fullscreen; -#endif renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio; renderer.lockIntegerScaling = renderer.core->opts.lockIntegerScaling;
@@ -54,8 +54,6 @@ SDL_Window* window;
SDL_Texture* sdlTex; SDL_Renderer* sdlRenderer; SDL_GLContext* glCtx; -#else - bool fullscreen; #endif unsigned width;
@@ -63,10 +63,10 @@ struct mSDLPlayer {
size_t playerId; struct mInputMap* bindings; struct SDL_JoystickCombo* joystick; + int fullscreen; int windowUpdated; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Window* window; - int fullscreen; struct mSDLRumble { struct mRumble d;
@@ -22,9 +22,9 @@ }
bool mSDLSWInit(struct mSDLRenderer* renderer) { #ifdef COLOR_16_BIT - SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_DOUBLEBUF | SDL_HWSURFACE | (SDL_FULLSCREEN * renderer->fullscreen)); + SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_DOUBLEBUF | SDL_HWSURFACE | (SDL_FULLSCREEN * renderer->player.fullscreen)); #else - SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_DOUBLEBUF | SDL_HWSURFACE | (SDL_FULLSCREEN * renderer->fullscreen)); + SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_DOUBLEBUF | SDL_HWSURFACE | (SDL_FULLSCREEN * renderer->player.fullscreen)); #endif SDL_WM_SetCaption(projectName, "");